LOCODUINO

Forum de discussion
Dépôt GIT Locoduino
Flux RSS

samedi 22 juillet 2017

21 visiteurs en ce moment

Un décodeur DCC pour 16 feux tricolores

Basé (ou pas) sur la bibliothèque UAD

. Par : Dominique, JPClaude, Thierry

Cet article a pour objectif de réaliser des décodeurs de signaux ferroviaires. L’approche se fera en trois parties.

  • La première consiste à réaliser ces décodeurs en se basant sur l’article de Nicolas Zin : Un décodeur d’accessoire DCC versatile basé sur Arduino. Dans cette approche le programme reçoit un message DCC   qu’il doit entièrement gérer.
  • La seconde approche se base sur la bibliothèque UAD de Thierry Paris qui fournit une grande quantité de méthodes facilitant grandement la réalisation des commandes DCC  . Cette bibliothèque est décrite dans l’article Un décodeur d’accessoires universel (1) et permet de construire toutes sortes de décodeurs en associant une carte Arduino avec un shield  , voire sans shield   du tout.
  • Enfin la dernière solution propose une interface avec la bibliothèque UAD, cachant sa complexité et facilitant la configuration de décodeurs de signaux ferroviaires à deux et/ou trois feux.

L’Arduino Mega 2560 peut-il alimenter 16 leds continuellement ?

16 feux tricolores à led, cela fait 48 leds. Mais dans chaque feu, une seule led est allumée et consomme moins de 10 milli-ampères.

Un Arduino Mega 2560 dispose de 54 ports, donc peut piloter 16 feux, et les 16 leds allumées en permanence ne mettent pas l’Arduino en surcharge.

L’information ne saute pas aux yeux sur la fiche des caractéristiques.

Mais après croisement de nombreuses informations, il en ressort que :

  • La somme de tous les courants (entrant ou sortant) des ports J0-J7 (digital pins 14 et 15), G2 (digital pin 39), A0-A7 (digital pins 22 à 29) ne doit pas dépasser 200 mA.
  • La somme de tous les courants (entrant ou sortant) des ports C0-C7 (digital pins 30 à 37), G0-G1 (digital pins 40 et 41), D0-D7 (digital pins 18 à 21 et 38), L0-L7 (digital pins 42 à 49) ne doit pas dépasser 200 mA.
  • La somme de tous les courants (entrant ou sortant) des ports G3-G4 (aucune pin), B0-B7 (digital pins 10 à 13 et 50 à 53), H0-H7 (digital pins 6 à 9 et 16, 17) ne doit pas dépasser 200 mA.
  • La somme de tous les courants (entrant ou sortant) des ports E0-E7 (digital pins 0, 1, 2, 3, 5), G5 (digital pin 4) ne doit pas dépasser 100 mA.
  • La somme de tous les courants (entrant ou sortant) des ports F0-F7 (analog pins 0 à 7), K0-K7 (analog pins 8 à 15) ne doit pas dépasser 100 mA.

Au total le courant total du Mega 2560 ne doit pas dépasser 800mA (dont 80mA de consommation interne).

Donc, les leds de nos feux consommant moins de 10 mA, cela fait 160 mA maximum, répartis sur plusieurs ports. Nous sommes loin de la limite ! Ouf !

A noter que l’expression « courant entrant » (sink en anglais) signifie courant absorbé : c’est le cas quand la led est connectée au +5V par son anode (via une résistance) et à une pin de l’Arduino par sa cathode. La led s’allume quand la pin est au niveau 0V (LOW).

A l’inverse, l’expression « courant sortant » (source en anglais) signifie courant fourni : c’est le cas quand la led est connectée une pin de l’Arduino par son anode et au 0V par sa cathode (via une résistance). La led s’allume quand la pin est au niveau 5V (HIGH).

La norme DCC   et les commandes d’accessoires

On trouvera dans cet article une présentation de la norme DCC  , comment se présente un signal DCC sur les rails, généré par votre centrale. Mais cet article se concentre sur les commandes des locomotives avant tout.

Ici nous avons affaire aux commandes d’accessoires qui sont réalisées avec des trames DCC différentes des commandes de locos.

Le document de référence de la NMRA est RP-9.2.1-Extended packet [1] (pour ceux qui sont prêts à affronter la chose en anglais).

Nous allons maintenant tenter de vous éviter cela :-)

Les adresses correspondant aux décodeurs d’accessoires basiques (Basic Accessory) sont codées sur 9 bits d’adresses, et celles des décodeurs d’accessoires étendus (Extended Accessory) sont codées sur 11 bits d’adresse.

Ces adresses n’ont rien à voir avec les adresses des locos, du fait que les commandes DCC sont différentes. On n’a donc pas besoin de choisir des valeurs d’adresses différentes.

Curieusement, les centrales semblent utiliser les commandes d’accessoires basiques à toutes les sauces, y compris les feux, alors que ces derniers semblent devoir être commandés plutôt par des commandes étendues.

Nous allons donc nous concentrer sur les commandes basiques seulement dans l’exemple qui va suivre. Un autre article reviendra ultérieurement sur les commandes étendues.

Le format des paquets DCC pour la commande des décodeurs d’accessoires basiques

Le format d’une commande destinée à un décodeur d’accessoires est :

preambule 0 10AAAAAA 0 1AAACDDD 0 EEEEEEEE 1

L’adresse est en 2 parties AAAAAA (6 bits dans le 1er octet qui représentent les poids faibles) et AAA (3 bits dans le 2ème octet qui représentent les poids forts).
La première partie est codée simplement de 0 (000000) à 63 (111111) et la deuxième partie est codée en complément à 1 (pourquoi faire simple quand on peut faire compliqué !).
Par exemple 111 binaire vaut 000 en complément à 1 donc 0 en décimal et on a les adresses de 0 à 63 ; ensuite,
110 vaut 1 donc les adresses vont de 64 à 127 ;
101 vaut 2 donc les adresses vont de 128 à 191,
100 vaut 3 donc les adresses vont de 192 à 255,
011 vaut 4 donc les adresses vont de 256 à 319,
010 vaut 5 donc les adresses vont de 320 à 383,
001 vaut 6 donc les adresses vont de 384 à 447, et
000 vaut 7 pour les adresses de 448 à 511.

Le bit C sert à activer et désactiver un accessoire soit de façon momentanée, soit de façon permanente. L’activation ou la désactivation (si l’activation était permanente) se fait en envoyant 2 commandes, l’une avec C=1, puis une autre avec C=0, après quelques millisecondes. La durée de l’activation est déterminée par les variables de configuration CVs #515 à 518 (ce qui correspond à 4 paires de sorties).

Le décodeur peut piloter 4 paires de sorties :
Dans DDD, les DD de poids fort (à gauche) définissent quelle sortie est concernée et le dernier D de poids faible (à droite) définit l’élément dans une paire.

La combinaison de l’adresse et des bits DD de poids fort est souvent utilisée par les centrales pour simuler 4 accessoires simples comme des moteurs d’aiguilles classiques ou pour des feux ou des relais. Cette combinaison permet donc d’adresser un peu moins de 2048 accessoires (quelques unes sont réservées selon le fabricant).
Dans ce cas l’adressage indiqué dans la documentation des centrales peut prendre les formes 101/0, 101/1, 102/0, 102/1, 103/0, 103/1, 104/0, 104/1 qui correspondent toutes à l’adresse 26, paires de sorties 1, 2, 3, et 4.

On vous avait bien dit que ce n’est pas simple !

Le format des paquets DCC pour la commande des décodeurs d’accessoires étendus

Ce format est destiné à transmettre des commandes d’aspect plus particulièrement pour les décodeurs de feux ou des octets des données pour des décodeurs plus complexes. Chaque commande pilote un seul aspect à la fois d’un feu complexe.

preambule 0 10AAAAAA 0 0AAA0AA1 0 000XXXXX 0 EEEEEEEE 1

On y retrouve les 9 bits d’adresse AAAAAA et AAA comme dans la commande basique, auxquels s’ajoutent 2 bit supplémentaires en bits 1 et 2 du 2ème octet.

XXXXX concerne un seul feu. la valeur 00000 indique un Stop absolu.
Tous les autres aspects représentés par les autres valeurs de XXXXX sont déterminés par rapport à un modèle de système de signalisation.

Les commandes de Variables de Configuration des accessoires

Chaque décodeur d’accessoires contient des paramètres programmables par des commandes DCC. Son adresse en est l’exemple typique. Celle-ci est programmée dans le CV 1 (poids faibles) et le CV 9 (poids forts).

Les CVs des décodeurs d’accessoires peuvent être configurés au même titre que les décodeurs de locomotives par des commandes DCC.

Programmation des CVs d’un décodeur d’accessoire basique :

preambule 10AAAAAA 0 1AAACDDD 0 (1110CCVV 0 VVVVVVVV 0 DDDDDDDD) 0 EEEEEEEE 1

AAAAAA AAA est l’adresse comme expliqué plus haut.

DDD indique la sortie pour laquelle les CVs sont modifiés avec C=1.

Si CDDD= 0000 alors les CVs concernent le décodeur dans sa totalité (toutes les sorties).
(1110CCVV 0 VVVVVVVV 0 DDDDDDDD) est l’instruction de configuration des CVs.

Programmation des CVs d’un décodeur d’accessoires étendu :

preamble 10AAAAAA 0 0AAA0AA1 0 (1110CCVV 0 VVVVVVVV 0 DDDDDDDD) 0 EEEEEEEE 1

Notez que le bit 3 à 0 dans l’octet 2 garantit que ce paquet ne peut pas être confondu avec celui d’un accessoire basique.

La construction du décodeur

Comme indiqué au début de cet article, j’ai choisi d’utiliser un Arduino Mega 2560 qui permet de brancher les 48 Leds sans matériel extérieur (j’aurais pu utiliser un Arduino plus petit avec une carte contenant 6 x 74HC595, chacun pouvant alimenter 8 Leds, mais ça aurait fait beaucoup de câblage).
De toute façon, pour faire ce décodeur et le rendre facile à utiliser, il faut ajouter une interface DCC isolée par optocoupleur, selon ce schéma éprouvé décrit en détail à l’article Un moniteur de signaux DCC.

Ensuite, il faut ajouter des borniers à vis pour brancher les fils qui vont aux feux.
J’ai donc ajouté à mon Mega 2560 une carte d’extension imprimée à pastilles, avec la place pour les connecteurs mâles qui vont s’enficher dans les connecteurs femelles de l’Arduino :

Sur cette carte d’extension, j’ai câblé l’interface DCC ci-dessus et un régulateur de tension pour fournir le 5V nécessaire au fonctionnement de l’Arduino :

Puis j’ai réalisé une plaquette équipée de borniers à vis et l’ai reliée soigneusement aux Pins de l’Arduino sur la carte d’extension :

Il ne reste plus, ensuite, qu’à installer le programme et connecter les feux.

Le programme sans l’UAD

Après tout, pourquoi ne pas essayer : Je suis parti de l’article de Nicolas Zin : Un décodeur d’accessoire DCC versatile basé sur Arduino.

Voici le code qui est relativement court et facile à comprendre :

Tout d’abord, on déclare les ressources nécessaires :

  1. #include <DCC_Decoder.h> // la bibliothèque de Minabay
  2. #define kDCC_INTERRUPT 3 // la pin 20 du Mega2560 reçoit les interruptions du signal DCC
  3.  
  4. #define NB_FEUX 16 // 16 feux
  5. #define NB_LEDS 3 // 3 leds par feu

Ensuite, les adresses DCC à utiliser, dans un tableau pour permettre l’usage de boucles avec index :

  1. // plage de codes DCC : 100 a 131
  2.  
  3. #define DCC_CODE_START 100
  4.  
  5. // DCC_codes1 = DCC_CODE_START + 2*LED_NUM
  6. // DCC_codes2 = DCC_CODE_START + 2*LED_NUM + 1
  7.  
  8. int dcc_codes[NB_FEUX] = { 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130 };

Puis les pins connectées aux Leds et les variables pour les gérer :

  1. // Pins des Leds des Feux {vert, jaune, rouge, allumage = LOW }
  2.  
  3. int pins[NB_FEUX][NB_LEDS] = {
  4. { 11, 10, 9 },
  5. { 8, 7, 6 },
  6. { 5, 4, 25 },
  7. { 27, 29, 31 },
  8. { 28, 30, 32 },
  9. { 34, 36, 38 },
  10. { 40, 42, 44 },
  11. { 46, 48, 50 },
  12. { 13, 12, 3 },
  13. { 2, 14, 15 },
  14. { 16, 17, 18 },
  15. { 19, 22, 23 },
  16. { 24, 26, 33 },
  17. { 35, 37, 39 },
  18. { 41, 43, 45 },
  19. { 47, 49, 51 }
  20. };
  21.  
  22. int STATE_FEUX[NB_FEUX*2]; // NB_FEUX couples = 4 états, valeurs HIGH ou LOW
  23. boolean VALID_FEUX[NB_FEUX]; // un feu est à traiter si true par Maj_Feux()
  24. int NUM_FEUX;

Maintenant, le routine Maj_Feux() qui va allumer ou éteindre les Leds en fonction de l’état DCC (sur 2 variables car associé à 2 adresses DCC) qui est converti en un état "State" compris entre 0 et 3 pour coder tout cela dans un switch :

  1. void Maj_Feux() {
  2.  
  3. for (int i = 0; i < NB_FEUX; i++)
  4. {
  5. if (VALID_FEUX[i] == true)
  6. {
  7. // conversion des 2 etats en State compris entre 0 et 3
  8. int State = STATE_FEUX[i*2] + STATE_FEUX[i*2+1]*2;
  9. // positionnement des Leds
  10. switch(State)
  11. {
  12. case 0: // rouge
  13. digitalWrite(pins[i][0] , HIGH); //extinction Led verte
  14. digitalWrite(pins[i][1] , HIGH); //extinction Led jaune
  15. digitalWrite(pins[i][2] , LOW); //allumage Led rouge
  16. break;
  17. case 1: // jaune
  18. digitalWrite(pins[i][0] , HIGH); //extinction Led verte
  19. digitalWrite(pins[i][1] , LOW); //allumage Led jaune
  20. digitalWrite(pins[i][2] , HIGH); //extinction Led rouge
  21. break;
  22. case 2: // jaune
  23. digitalWrite(pins[i][0] , HIGH); //extinction Led verte
  24. digitalWrite(pins[i][1] , LOW); //allumage Led jaune
  25. digitalWrite(pins[i][2] , HIGH); //extinction Led rouge
  26. break;
  27. case 3: // vert
  28. digitalWrite(pins[i][0] , LOW); //allumage Led verte
  29. digitalWrite(pins[i][1] , HIGH); //extinction Led jaune
  30. digitalWrite(pins[i][2] , HIGH); //extinction Led rouge
  31. break;
  32. }
  33. VALID_FEUX[i] = false;
  34. }
  35. }
  36. }

Puis la routine (handler en anglais) exécutée par la librairie DCC de Minabay quand une commande est arrivée :

  1. void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data)
  2. {
  3. // Conversion de l'adresse NMRA en adresse décodeur d'accessoire
  4. address -= 1;
  5. address *= 4;
  6. address += 1;
  7. address += (data & 0x06) >> 1;
  8.  
  9. int enable = (data & 0x01) ? HIGH : LOW;
  10.  
  11. if ((address >= DCC_CODE_START) && (address < DCC_CODE_START + NB_FEUX*2))
  12. {
  13. VALID_FEUX[(address - DCC_CODE_START)/2] = true;
  14. STATE_FEUX[address - DCC_CODE_START] = enable;
  15. Serial.print("@ ");
  16. Serial.print(address);
  17. Serial.print(" ");
  18. Serial.print(STATE_FEUX[address - DCC_CODE_START]);
  19. Serial.print(" feu ");
  20. for (int i = 0; i < NB_FEUX; i++)
  21. {
  22. if (VALID_FEUX[i] == true)
  23. {
  24. Serial.print(i);
  25. Serial.print(" etat ");
  26. int State = STATE_FEUX[i*2] + STATE_FEUX[i*2+1]*2;
  27. Serial.println(State);
  28. }
  29. }
  30. }
  31. }

A noter que la conversion d’adresse NMRA est une recette de cuisine qui fonctionne avec plusieurs centrales (heureusement). Les "Serial.print()" qui parsèment cette routine servent à voir ce qui se passe et peuvent être enlevés après la mise au point.

Maintenant le fameux SETUP qui initialise les variables tableaux, le port série, les modes de Pins et la bibliothèque DCC de Minabay :

  1. void setup() {
  2.  
  3. Serial.begin(115200);
  4.  
  5. for (int i=0; i<NB_FEUX; i++)
  6. {
  7. STATE_FEUX[i*2] = LOW; // eteint
  8. STATE_FEUX[i*2+1] = LOW; // eteint
  9. VALID_FEUX[i] = true;
  10. for (int j=0; j<NB_LEDS; j++)
  11. {
  12. pinMode(pins[i][j] , OUTPUT);
  13. digitalWrite(pins[i][j] , LOW); //allumage Led
  14. delay(50);
  15. digitalWrite(pins[i][j] , HIGH); //extinction Led
  16. delay(50);
  17. }
  18. }
  19.  
  20. DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);
  21. // ConfigureDecoder();
  22. DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );
  23.  
  24. Serial.println("DCC 16 Feux en route (V1)");
  25. }

Étonnamment, la LOOP est toute petite :

  1. void loop() {
  2. static unsigned long timer=0;
  3.  
  4. ////////////////////////////////////////////////////////////////
  5. // passer le main à la bibliothèque DCC
  6.  
  7. DCC.loop();
  8.  
  9. ////////////////////////////////////////////////////////////////
  10. // on change l'etat des Leds toutes les 50 millisecondes
  11.  
  12. if (millis()-timer>50) {
  13. Maj_Feux();
  14. timer=millis();
  15. }
  16. }

Et ce programme fonctionne à merveille !

J’ai testé au départ avec ma petite MS2.

En respectant les branchements des Leds conformément à la table des Pins des Leds des Feux ci-dessus, et en programmant ces feux comme "Three Aspect Signal" avec Train Controleur :

Le résultat est immédiat via une console ESU ECOs.

On remarque qu’on a choisi d’allumer le Vert quand les 2 adresses sont au Vert, le rouge quand les 2 adresses sont au rouge et le jaune quand les 2 adresses sont différentes.

Le programme peut être téléchargé à partir d’ici :

dcc16feux

Le programme avec l’UAD

Ce code s’appuie sur la bibliothèque UAD V4.35 du 01/03/2016 [2].

Pour faire 16 feux, de trois leds chacun, avec une led allumée à la fois par un code Dcc, il nous faut passer par plusieurs étapes.

1 : Déclarer les 16x3 ports de l’Arduino.
2 : Déclarer 16 accessoires multi leds avec trois leds chacun.
3 : Associer chaque led aux ports déclarés plus tôt.

On voit bien que le travail va être très répétitif, et peut être consommateur de mémoire programme… On a trois solutions pour parvenir au résultat :

1 : Force brute : les éléments sont tous déclarés et construits un par un. Avantage : c’est long, mais c’est le plus simple à coder, et le code est facile à comprendre. Inconvénient : il ne faut pas vouloir changer de méthode pour un feu… Parce qu’il faudra le refaire 16 fois !
2 : Boucles : le but à atteindre se prête très bien à l’utilisation de boucles. Il faut juste rendre configurable un maximum de choses, comme les pins utilisées ou le code Dcc associé à chaque feu. Avantage : si la méthode change pour un feu, elle est automatiquement appliquée à tous les feux. Inconvénient : la lisibilité devient passable…
3 : Version élégante : la voie royale consiste à créer une classe ’Feu’ qui va être instanciée 16 fois. La partie création d’un feu avec toutes ses implications est déporté dans cette classe. Avantage : le code principal est concis, simple à comprendre et maintenable. Inconvénient : le côté C++ s’adresse à des connaisseurs… que vous êtes certainement devenus !

Le code pour un feu

Voyons le code pour un seul feu. Nous l’étendrons ensuite à plusieurs…
Nous allons commencer par une annexe ! Voici la déclaration d’un bouton poussoir qui va servir à tester le fonctionnement, au cas où l’on soit sur un réseau analogique, ou que la centrale Dcc ne soit pas disponible, voire dans un cas extrême (et peu probable…) où l’on ait la flemme d’allumer ladite centrale !

On déclare d’abord :

  1. ButtonsCommander buttonsCommander;

Puis dans setup() :

  1. buttonsCommander.Setup(1,
  2. new ButtonsCommanderPush(3) // trois états à piloter
  3. );
  4.  
  5. PUSH(buttonsCommander, 0)->AddDccId(15, 0);
  6. PUSH(buttonsCommander, 0)->AddDccId(15, 1);
  7. PUSH(buttonsCommander, 0)->AddDccId(16, 0);

Chaque appui sur le bouton enverra un code Dcc parmi les trois, l’appui suivant le code suivant, et ainsi de suite… J’ai choisi les codes Dcc 15/0, 15/1 et 16/0 pour chaque état du feu : vert, jaune, rouge.

Déclarons ensuite les ports sur l’Arduino, un port par Led sur les pins 9, 10 et 11.

  1. arduino = new DriverArduino(3, 0); // trois leds, mais zéro servos...
  2. arduino->Setup();
  3. arduino->SetupPortMotor(0, 9); // vert
  4. arduino->SetupPortMotor(1, 10); // jaune
  5. arduino->SetupPortMotor(2, 11); // rouge

Passons à la liste des accessoires, ici un multi leds avec trois leds :

  1. accessories.Setup(1,
  2. new AccessoryLightMulti(1000, 0, NB_LEDS, 0)
  3. );

Chaque accessoire peut être commandé indépendamment, mais comme ce n’est pas le but ici, j’ai mis un code Dcc volontairement excessif de 1000 pour ne pas risquer une mise en route malencontreuse…

Puis l’affectation de chaque led sur le bon port Arduino :

  1. LIGHTMULTI(accessories, 0)->Setup();
  2. LIGHTMULTI(accessories, 0)->SetupLight(0, arduino, 0); // led verte (0) sur port Arduino vert (0)
  3. LIGHTMULTI(accessories, 0)->SetupLight(1, arduino, 1); // led jaune (0) sur port Arduino jaune (1)
  4. LIGHTMULTI(accessories, 0)->SetupLight(2, arduino, 2); // led rouge (0) sur port Arduino rouge (2)

Tous les éléments sont en place. Il nous faut maintenant déclarer les différents états, un pour chaque led allumée tandis que les autres sont éteintes.

  1. LIGHTMULTI(accessories, 0)->AdjustDccPositionsSize(NB_ETATS);
  2. LIGHTMULTI(accessories, 0)->AddDccPosition(15, 0, B00000001, 0); // 1
  3. LIGHTMULTI(accessories, 0)->AddDccPosition(15, 1, B00000010, 0); // 2
  4. LIGHTMULTI(accessories, 0)->AddDccPosition(16, 0, B00000100, 0); // 4

Le troisième argument de la fonction est un ’champ de bits’ sur un entier 16 bits qui représente la liste des leds de l’accessoire, avec 0 pour celles qui sont éteintes, 1 pour les allumées. Plusieurs notations sont possibles : désigner la troisième led par exemple peut se faire par trois moyens :

  • 4 sous forme d’entier. C’est le plus évident.
  • B00000100 qui représente huit bits, dont le troisième est à 1. Ce type de notation est accessible grâce à l’IDE Arduino qui fournit binary.h avec toutes les combinaisons de bit. Ce n’est pas un élément de langage C ou C++ . Cette notation ne marche plus s’il y a plus de huit leds sur le feu…
  • 1<<2 qui signifie 1 décalé deux fois vers la gauche. C’est à dire 00000001 au départ, puis 00000010, et enfin 00000100.

Si un code Dcc 15/0 est reçu, c’est la led verte qui est allumée, les autres leds seront éteintes. Pour 15/1, c’est la jaune qui va s’allumer, et pour 16/0, c’est la rouge. Si on voulait tout allumer avec le code Dcc 16/1, il faudrait ajouter une position Dcc avec B00000111 . Le quatrième argument a un fonctionnement identique, mais pour en faire clignoter certaines. C’est pourquoi il est à zéro ici.

Le Setup est terminé. Le Loop() est simple, il faut y faire figurer à la fois les Commanders et les accessoires :

  1. void loop()
  2. {
  3. if(Dcccommander.Loop())
  4. {
  5. buttonsCommander.Loop();
  6. accessories.Loop();
  7. }
  8. }

Le code pour 16 feux

J’ai choisi de coder une classe décrivant un seul feu, la troisième possibilité de codage donc. A noter que la classe doit être définie avant son utilisation, c’est pourquoi elle est en tête du fichier.

  1. #include "UniversalAccessoryDecoder.h"
  2.  
  3. //------------------------------------------------------------------------------
  4. // SignalArduino declaration
  5.  
  6. class SignalArduino : public AccessoryLightMulti
  7. {
  8. public:
  9. SignalArduino(DriverArduino *inpDriver, byte inNbLeds, int *inpPins, int inFirstPort = 0);
  10. void SetupSignal(int inStartingDcc);
  11. };
  12.  
  13. //------------------------------------------------------------------------------
  14. // SignalArduino definition
  15.  
  16. SignalArduino::SignalArduino(DriverArduino *inpDriver, byte inNbLeds, int *inpPins, int inFirstPort) : AccessoryLightMulti(0, 0, inNbLeds, 0)
  17. {
  18. for (int led = 0; led < inNbLeds; led++)
  19. {
  20. inpDriver->SetupPortMotor(inFirstPort + led, inpPins[led], DIGITAL_INVERTED);
  21. }
  22.  
  23. for (int led = 0; led < inNbLeds; led++)
  24. {
  25. // Led number is also port number...
  26. this->SetupLight(led, inpDriver, inFirstPort + led, 255);
  27. }
  28. }
  29.  
  30. void SignalArduino::SetupSignal(int inStartingDcc)
  31. {
  32. this->Setup();
  33.  
  34. // Used dcc codes are
  35. // Led 0 1 2
  36. // inStartingDcc / 0 on off off
  37. // inStartingDcc / 1 off on off
  38. // inStartingDcc+1 / 0 off off on
  39. // inStartingDcc+1 / 1 off off off
  40.  
  41. this->AdjustDccPositionsSize(this->GetSize()+1);
  42.  
  43. int dcc = inStartingDcc;
  44. bool etat = false;
  45. for (int i = 0; i < this->GetSize(); i++)
  46. {
  47. if (!etat)
  48. {
  49. this->AddDccPosition(dcc, 0, 1<<i, 0);
  50. etat = true;
  51. }
  52. else
  53. {
  54. this->AddDccPosition(dcc, 1, 1<<i, 0);
  55. dcc++;
  56. etat = false;
  57. }
  58. }
  59.  
  60. this->AddDccPosition(dcc, etat==true?1:0, 0, 0);
  61. }

Cette classe est dérivée de AccessoriesLightMulti et ne comprend que deux fonctions : le constructeur et un Setup.
Vient ensuite le code normal du croquis.

  1. /* kDCC_INTERRUPT values :
  2. Board int.0 int.1 int.2 int.3 int.4 int.5
  3. Uno, Ethernet 2 3
  4. Mega2560 2 3 21 20 19 18
  5. Leonardo 3 2 0 1 7
  6. */
  7. #define kDCC_INTERRUPT 0

J’ai choisi l’interruption 0 sur un Mega2560, donc la broche 2 sera réservée pour l’entrée du signal DCC provenant des rails.

On définit ensuite nos 16 feux, utilisant chacun 3 leds et pouvant avoir 3 états. Ils occupent 48 broches du Mega2560.

  1. #define NB_LEDS 3
  2. #define NB_ETATS 3
  3. #define NB_FEUX 16
  4.  
  5. SignalArduino* signaux[NB_FEUX];
  6.  
  7. int pins[][NB_LEDS] = {
  8. { 5, 6, 7 },
  9. { 8, 9, 10 },
  10. { 11, 12, 13 },
  11. { 14, 15, 16 },
  12. { 17, 18, 19 },
  13. { 20, 21, 22 },
  14. { 22, 23, 24 },
  15. { 25, 26, 27 },
  16. { 28, 29, 30 },
  17. { 31, 32, 33 },
  18. { 34, 35, 36 },
  19. { 37, 38, 39 },
  20. { 40, 41, 42 },
  21. { 43, 44, 45 },
  22. { 46, 47, 48 },
  23. { 49, 50, 51 }
  24. };

Notez l’absence de valeur dans le premier crochet ’[]’ de la matrice ’pins’. En effet le compilateur déduira le nombre depuis la liste qui suit. Si cette matrice n’avait pas été initialisée tout de suite, il aurait fallu préciser le nombre de feux ! Cette notation est très pratique puisqu’elle permet de modifier le nombre de lignes de la matrice sans rien toucher d’autre. Accessoirement on est sûr de pas se tromper en confiant ce genre de boulot au compilateur ! Par contre le nombre d’entiers par ligne doit être fixé (NB_LEDS).

Puis les objets que nous utilisons dans la bibliothèque UAD et les adresses DCC des 16 feux :

  1. // Accessories
  2. Accessories accessories;
  3. DccCommander dccCommander;
  4. ButtonsCommander buttonsCommander;
  5.  
  6. // Drivers
  7. DriverArduino *arduino;
  8.  
  9. int dcc_codes[] = { 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 33, 34, 35, 36};

Les instructions de la fonction setup()

Initialisation de la librairie UAD :

  1. UAD_StartSetup();
  2.  
  3. dccCommander.Setup(0x00, 0x00, kDCC_INTERRUPT);
  4. dccCommander.SetStatusLedPin(13);

Initialisation du bouton de test des leds de tous les feux. ce bouton est connecté sur le port A2, la plupart des ports digitaux étant utilisés par les feux :

  1. buttonsCommander.Setup(1,
  2. new ButtonsCommanderPush(NB_FEUX * NB_ETATS)
  3. );
  4.  
  5. // Ce petit bouton va permettre de passer en revue tous les codes dcc des feux en séquence...
  6. int dcc = 0;
  7. for (int feu = 0; feu < NB_FEUX; feu++)
  8. {
  9. PUSH(buttonsCommander, 0)->AddDccId(dcc_codes[dcc], 0);
  10. PUSH(buttonsCommander, 0)->AddDccId(dcc_codes[dcc], 1);
  11. PUSH(buttonsCommander, 0)->AddDccId(dcc_codes[dcc]+1, 0);
  12. dcc++;
  13. }
  14.  
  15. PUSH(buttonsCommander, 0)->Setup(A2);

Initialisation des 48 ports connectés aux 48 leds des 16 feux :

  1. arduino = new DriverArduino(NB_LEDS * NB_FEUX, 0);
  2. arduino->Setup();
  3.  
  4. accessories.Setup(NB_FEUX);
  5.  
  6. for (int feu = 0; feu < NB_FEUX; feu++)
  7. {
  8. signaux[feu] = new SignalArduino(arduino, NB_LEDS, pins[feu], feu * 3);
  9. signaux[feu]->SetupSignal(dcc_codes[feu]);
  10. accessories.Add(signaux[feu]);
  11. }
  12.  
  13. UAD_EndSetup();

Fin de l’initialisation et de la fonction setup() :

  1. UAD_EndSetup();

La boucle loop()

Celle-ci devient très simple et se réduit à :

  1. void loop()
  2. {
  3. if (dccCommander.Loop())
  4. {
  5. accessories.Loop();
  6. buttonsCommander.Loop();
  7. }
  8. }

Ce programme est simplement une adaptation de l’exemple "Signals4x3" de la bibliothèque.

Et le code est ici :

Le programme utilisant une interface avec l’UAD

Cette interface permet à des personnes ne souhaitant pas entrer dans les détails informatiques de mettre en place des signaux sur une carte Arduino MEGA. Les signaux possibles peuvent être à 2 et/ou 3 feux, ce qui est le cas le plus général sur nos petits réseaux.

Le principe est le suivant :

On génère d’abord les décodeurs pour les signaux à 2 feux, puis ceux pour les signaux à 3 feux. Il peut donc y avoir mixité, mais toujours les signaux à 2 feux en premier puis les signaux à 3 feux ensuite. La première broche de la première led est la broche 3. Les signaux à 2 feux correspondent à des doublons de broches (x, x+1) et des adresses y/0 et y/1, les signaux à 3 feux correspondent à des triplets de broches (x, x+1, x+2) et des adresses y/0, y/1, y+1/0. Rien n’empêche cependant de ne mettre aucun signal à 2 feux ou à 3 feux et donc de réaliser des décodeurs pour des signaux exclusivement à deux ou trois feux.

L’avantage est sa simplicité, on doit donner :

  • Le nombre de signaux à 2 feux (zéro possible)
  • Le nombre de signaux à 3 feux (zéro possible)
  • La première adresse DCC
  • La broche de la led témoin du DCC
  • La broche du bouton

J’ai testé avec mon ECOS et cela marche très bien. Attention cependant à la limite de courant à ne pas dépasser pour toutes les broches (voir en début d’article). Une valeur de 16 feux semble raisonnable. La bibliothèque TrafficSignal est chargeable à la fin de l’article.

Le croquis ino est très simple :

  • On instancie les signaux
  • TrafficSignal signaux ;
  • On fait le setup
  • Signaux.Setup(nbsignauxa2leds,nbSignauxa3leds, code_DCC, pinLedStatus, pinBouton) ;
  • Puis on boucle
  • Signaux.Loop()

Exemple de programme Arduino

  1. #include <UniversalAccessoryDecoder.h> // la bibliothèque UAD
  2. #include <TrafficSignal.h> // la gestion des signaux
  3.  
  4. #define NB_FEUX_2 8 // nombre de signaux a 2 feux
  5. #define NB_FEUX_3 8 // nombre de signaux a 3 feux
  6. #define ID_DCC 40 // adresse DCC de départ du premier feu
  7. #define LED_STATUS 53 // la broche de la led temoin DCC
  8. #define BUTTON_PIN A15 // la broche du bouton de test
  9.  
  10. TrafficSignal signaux; // une instance des signaux
  11. void setup() {signaux.Setup(NB_FEUX_2,NB_FEUX_3,ID_DCC,LED_STATUS,BUTTON_PIN);} // le setup
  12. void loop() {signaux.Loop();} // la boucle

Voilà les 16 signaux sont opérationnels.

Voici ce que l’on obtient avec l’exemple du programme suivant :


#include <UniversalAccessoryDecoder.h> html  >
#include <TrafficSignal.h> html  >
TrafficSignal signaux ;
void setup() signaux.Setup(8,8,40,53,A15) ;
void loop() signaux.Loop() ;

DriverPortArduino :
2 feux : (3, 4), (5, 6), (7, 8), (9, 10),( 11, 12), (13, 14), (15, 16), (17, 18),
3 feux : (19, 20, 21), (22, 23, 24), (25, 26, 27), (28, 29, 30), (31, 32, 33), (34, 35, 36), (37, 38, 39), (40, 41, 42)

GroupState vert 2 feux : 40/0, 41/0, 42/0, 43/0, 44/0, 45/0, 46/0, 47/0
GroupState rouge 2 feux : 40/1, 41/1, 42/1, 43/1, 44/1, 45/1, 46/1, 47/1

GroupState vert 3 feux : 48/0, 50/0, 52/0, 54/0, 56/0, 58/0, 60/0, 62/0
GroupState rouge 3 feux : 48/1, 50/1, 52/1, 54/1, 56/1, 58/1, 60/1, 62/1
GroupState jaune 3 feux : 49/0, 51/0, 53/0, 55/0, 57/0, 59/0, 61/0, 63/0

Ou de façon plus lisible :

Voici la bibliothèque TrafficSignal

trafficsignal

et le programme Arduino UAD utilisant TrafficSignal

trafficsignal2-3feux

Conclusions

La réalisation de décodeurs pour des signaux ferroviaires est tout à fait réalisable avec des Arduino, ce n’est que de la gestion de Led.
La première solution permet de comprendre la complexité des messages DCC, leur réception, leur décodage et les actions à entreprendre. Cependant cela impose un certain effort de programmation.
La deuxième approche basée sur la bibliothèque UAD, très bien architecturée, soulage le développement mais la grande quantité de ces méthodes demande une investigation non négligeable.
Enfin la dernière approche permet rapidement de réaliser des décodeurs de signaux sans rentrer dans les détails de la bibliothèque UAD.
On peut donc, pour un coût très modique et rapidement, gérer 16 signaux à trois feux sur un Arduino Mega.

Cependant on peut faire quelques remarques. La première concerne l’utilisation de la bibliothèque UAD seulement pour gérer des signaux ferroviaires est un luxe et sera beaucoup plus efficace si on l’utilise avec d’autres types d’activités comme la gestion des barrières de PN ou autres, ou cette bibliothèque montrera toute sa puissance et prendra tout son intérêt. La seconde concerne l’utilisation d’un Arduino MEGA à cause du nombre important de pins (48) utilisées pour 16 feux tricolores. Personnellement je préfère les Nano qui me permettent une meilleure répartition sur le réseau et donc d’avoir moins de câblage.

Le DCC, par sa vocation, est très efficace pour l’envoi des commandes sur le réseau et le bus CAN pour la rétro signalisation (ou la commande d’accessoires de décors). Un prochain article permettra de détailler cette approche distribuée au sein de nos réseaux !

1 Message

Réagissez à « Un décodeur DCC pour 16 feux tricolores »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Cachée »

Mentions légales

À propos de LOCODUINO

Un décodeur DCC pour 16 feux tricolores

La carte servomoteurs CAN et DCC (1)

Les derniers articles

Un décodeur DCC pour 16 feux tricolores


Dominique, JPClaude, Thierry

La carte servomoteurs CAN et DCC (1)


Dominique, Jean-Luc, Thierry

Mentions légales


Jean-Luc

À propos de LOCODUINO


Jean-Luc

Les articles les plus lus

Mentions légales

À propos de LOCODUINO

Un décodeur DCC pour 16 feux tricolores

La carte servomoteurs CAN et DCC (1)