Un décodeur DCC pour les signaux à deux ou trois feux sur Arduino NANO/UNO

. Par : JPClaude. URL : https://www.locoduino.org/spip.php?article161

Sur nos réseaux il est nécessaire de contrôler un certain nombre d’articles comme la détection de présence avec le cantonnement et la position des aiguillages qui ont un impact direct avec le roulement de nos trains et d’autres articles, plus pour l’esthétique, comme les animations ou les feux de signalisation. La détection a été abordée avec le bus S88 et maintenant avec le travail de mes collègues avec le bus CAN beaucoup plus performant. Le contrôle des aiguillages à solénoïde a également été abordé dans un article précédent. Nous souhaitons ici proposer une solution pour réaliser un décodeur de feux de signalisation à deux ou trois feux gérés par des commandes DCC.

Réalisation de la carte décodeur

Les feux de signalisation sur nos réseaux n’ont aucune influence sur le roulement de nos trains, ils sont juste là pour créer un environnement réaliste. Si vous êtes comme moi un peu réfractaire à comprendre la complexité des signaux SNCF, je me limite, au moins pour l’instant, à des signaux à deux ou trois feux. Je vous propose donc de réaliser un décodeur pour ce type de signaux.

Feux de signalisation

Le principe est assez simple puisque cela revient à allumer ou éteindre des leds, ce que nos Arduino font très bien. Les signaux en DCC sont considérés comme des accessoires possédant une adresse (dans le cas des signaux à deux feux) ou deux adresses (dans le cas des signaux à trois feux). Il suffit donc de coller un capteur de signaux DCC à un Arduino qui exécutera la commande reçue et allumera (ou éteindra) les bonnes leds. Dans notre approche l’alimentation des leds est faite par le courant DCC.
Le schéma suivant reprend la capture des signaux DCC classique basée sur un 6N137 auquel on ajoute en parallèle un pont et un régulateur 8V pour l’alimentation de l’Arduino et des leds. Cela est raisonnable car nous le verrons dans le cas des signaux à trois feux il n’y aura que 5 leds allumées et dans le cas des signaux à deux feux, 8 leds allumées. Un strap a été prévu suivant la polarité des signaux (leds avec commun positif ou négatif). L’Arduino reçoit les commandes DCC sur la broche 2 (interruption 0) et renvoie le résultat sur les broches 3 à A4 suivant les cas. Cela tient sur une carte classique 6x10 et est la même que ce soit des signaux à deux ou trois feux.

Carte décodeur signalisation
Architecture de la carte

Et voici le typon correspondant :

Typon de la carte décodeur

(NB : petite astuce pour ceux qui se lance dans la réalisation, le problème est d’aligner convenablement le perçage pour l’Arduino NANO. Pour cela j’utilise une plaque d’essai qui me sert de guide :

Perçage de la carte décodeur

Et tout se passe bien.)
Pour ceux qui connaissent Eagle et qui font leur carte via des fournisseurs voici les fichiers Eagle :

Fichiers Eagle

et le zip Gerber qui va avec :

Fichier Gerber

et obtient des cartes quasi professionnelles :

signalisation carte
signalisation carte montée

L’algorithme

Le programme permet de gérer aussi bien les signaux à deux feux que ceux à trois feux. Par contre on ne peut mélanger les deux types sur une même carte.
Pour les signaux à trois feux la carte admet 5 signaux dont les leds sont contrôlées par les broches 3 à A3 en triplet.

Signal1 broches 3,4,5
Signal2 broches 6,7,8
Signal3 broches 9,10,11
Signal4 broches 12,13,A0
Signal5 broches A1,A2,A3

Les adresses paires correspondent aux leds vertes et rouges.
Les adresses impaires correspondent aux leds jaunes.

Pour les signaux à deux feux la carte admet 8 signaux dont les leds sont contrôlées par les broches 3 à A4 par paire.

Signal1 broches 3,4
Signal2 broches 5,6
Signal3 broches 7,8
Signal4 broches 9,10
Signal5 broches 11,12
Signal6 broches 13, A0
Signal7 broches A1, A2
Signal8 broches A3, A4

Chaque signal possède une adresse unique.

L’algorithme est simple et ne demande que trois paramètres de configuration :
MODE : détermine le niveau de commande des leds en fonction de la polarité commune, LOW si le commun est positif, HIGH si le commun est négatif.
FIRST_ID_DCC : correspond à la première adresse des signaux, les adresses des signaux suivants en découleront (linéaire dans le cas BICOLOR, par paire dans le cas TRICOLOR).
NB_TRAFFIC_LIGHT : détermine la nature des signaux, TRICOLOR pour des signaux à trois feux ou BICOLOR pour des signaux à deux feux.
Un dernier paramètre ne sert qu’à permettre de visualiser la bonne configuration et peut être omis par la suite, c’est le paramètre CONSOLE.

Le reste de l’algorithme reste inchangé quel que soit le type de signaux.

Paramètres internes

Ils déterminent les paramètres du DCC (insertion de la librairie Minabay, broche d’interruption), évitent les commandes multiples ayant même adresse et/ou même commande (previous_address/previous_position) et si une mise à jour est nécessaire (update_light).

Ils décrivent les signaux (BICOLOR ou TRICOLOR), la broche de départ des leds des signaux (FIRST_PIN) et les couleurs possibles (GREEN, RED, YELLOW).
Ils définissent la structure d’un signal (light) qui possède une adresse (address), une couleur courante (current_position), les broches des leds associées (green, red et yellow) et si une requête de changement est demandée (activation_request).
L’ensemble des signaux est regroupé dans un tableau (traffic_light[]).
Le setup instancie le DCC et détermine les adresses des signaux (address), linéaire dans le cas BICOLOR, par paire dans le cas TRICOLOR, les broches des leds (green, red, yellow) et positionne tous les signaux au rouge. Voici par exemple des configurations :

Pour des signaux à deux feux :

traffic light0
green led on pin : 3 , DCC address : 90/0
red led on pin : 4 , DCC address : 90/1
traffic light1
green led on pin : 5 , DCC address : 91/0
red led on pin : 6 , DCC address : 91/1
traffic light2
green led on pin : 7 , DCC address : 92/0
red led on pin : 8 , DCC address : 92/1
traffic light3
green led on pin : 9 , DCC address : 93/0
red led on pin : 10 , DCC address : 93/1
traffic light4
green led on pin : 11 , DCC address : 94/0
red led on pin : 12 , DCC address : 94/1
traffic light5
green led on pin : 13 , DCC address : 95/0
red led on pin : 14 , DCC address : 95/1
traffic light6
green led on pin : 15 , DCC address : 96/0
red led on pin : 16 , DCC address : 96/1
traffic light7
green led on pin : 17 , DCC address : 97/0
red led on pin : 18 , DCC address : 97/1

Et des signaux pour trois feux :

traffic light0
green led on pin : 3 , DCC address : 90/0
red led on pin : 4 , DCC address : 90/1
yellow led on pin : 5 , DCC address : 91/0
traffic light1
green led on pin : 6 , DCC address : 92/0
red led on pin : 7 , DCC address : 92/1
yellow led on pin : 8 , DCC address : 93/0
traffic light2
green led on pin : 9 , DCC address : 94/0
red led on pin : 10 , DCC address : 94/1
yellow led on pin : 11 , DCC address : 95/0
traffic light3
green led on pin : 12 , DCC address : 96/0
red led on pin : 13 , DCC address : 96/1
yellow led on pin : 14 , DCC address : 97/0
traffic light4
green led on pin : 15 , DCC address : 98/0
red led on pin : 16 , DCC address : 98/1
yellow led on pin : 17 , DCC address : 99/0

L’algorithme consiste à tester le DCC (méthode BasicDecoderPacket_Handler).
Ce dernier décode l’adresse reçue, vérifie s’il y a un changement depuis la dernière fois (cela évite les commandes multiples). S’il y a changement en fonction du type de signal (BICOLOR ou TRICOLOR) on calcule l’index du signal concerné en fonction de l’adresse reçue et la led concernée. Dans le cas TRICOLOR il y a deux adresses par signal, si l’adresse est impaire c’est le signal jaune qui est concerné. Le DCC met à jour les paramètres du signal et prévient d’un changement.
Si un changement a eu lieu on le propage au bon signal (méthode activation_traffic_light). Cette méthode recherche les signaux en attente de traitement (activation_request) et exécute les commutations des leds du signal en fonction de la demande (current_position).

Le programme Arduino

/*
 * Decoder for traffic light with three or two leds on ARDUINO NANO
 * 
 * by default the red leds are switched on
 * pin 2 receives DCC interrupts
 * 
 * TRAFFIC LIGHT WITH THREE leds (GREEN, RED, YELLOW)
 * 5 decoders of traffic lights with three leds on Arduino NANO/UNO
 * the leds are controlled by pins 3 to A3 by triplet
 * traffic light 1 : 3,4,5
 * traffic light 2 : 6,7,8
 * traffic light 3 : 9,10,11
 * traffic light 4 : 12,13,A0
 * traffic light 5 : A1,A2,A3
 * two addresses by traffic light
 * even addresses for green and red lights
 * odd addresses for yellow light
 * 
 * TRAFFIC LIGHT WITH TWO leds (GREEN, RED)
 * 8 decoders of traffic lights with two leds on Arduino NANO/UNO
 * the leds are controlled by pins 3 to A4 by pair
 * traffic light 1 : 3,4
 * traffic light 2 : 5,6
 * traffic light 3 : 7,8
 * traffic light 4 : 9,10
 * traffic light 5 : 11,12
 * traffic light 6 : 13,A0
 * traffic light 7 : A1,A2
 * traffic light 8 : A3,A4
 * one address by traffic light
 * 
 * CONFIGURATION
 * MODE : determined by the common of the led, LOW if common = HIGH, HIGH if common = LOW
 * FIRST_ID_DCC : DCC address of the first traffic light
 * NB_TRAFFIC_LIGHT : determined by the kind of traffic light, BICOLOR for two leds, TRICOLOR for three leds
 * 
 */

/***************************************************************************************           
 *            CONFIGURATION SETTING
 *            
 ****************************************************************************************/
 
#define CONSOLE                             // output console, delete this after checking the configuration
#define MODE   LOW                          // LOW or HIGH
#define FIRST_ID_DCC   90                   // first DCC address, DCC_CODE
#define NB_TRAFFIC_LIGHT  TRICOLOR           //  TRICOLOR or BICOLOR 

/**********************************************************************************  
 *   DON'T CHANGE THE FOLLOWING
 *********************************************************************************/
 
/******************************************************************************
 *    INTERNAL PARAMETERS
 *    
 ********************************************************************************/
 
//  DCC

#include <DCC_Decoder.h>                 // Minabay library
#define kDCC_INTERRUPT    0             // pin 2 receives DCC interrupts
int previous_address = 0;               // avoids multiple DCC addresses
int previous_position = 2;              // avoids multiple DCC orders
volatile boolean update_light;          // set if an update should be processed after DCC control

// traffic light

#define BICOLOR  8                     // 8 traffic lights with two leds
#define TRICOLOR 5                     // 5 traffic lights with three leds
#define FIRST_PIN         3             // pin of the first traffic light
#define GREEN             0             // address DCC/0
#define RED               1             // address DCC/1 
#define YELLOW            2             // address DCC+1/0

// traffic light definition

struct light {
  int address;                    // its DCC address
  int current_position;           // green / red / yellow
  int green;                      // pin of the green led
  int red;                        // pin of the red led
  int yellow;                     // pin of the yellow led
  boolean activation_request;     // request of activation
};
light traffic_light[NB_TRAFFIC_LIGHT];    // the set of traffic light

/********************************************************************
 * method called if a request is made by the DCC
 * 
 *******************************************************************/
 
void activation_traffic_light() {
  for (int i = 0; i < NB_TRAFFIC_LIGHT; i++)                 // for all traffic lights
  {
    if (traffic_light[i].activation_request == true)  // if the traffic_light is waiting for activation
    {
      switch(traffic_light[i].current_position)            // we look the current position
      { 
        case GREEN :{                                                     // if green led
                      digitalWrite(traffic_light[i].green,MODE);         // switch on green
                      digitalWrite(traffic_light[i].red,!MODE);          // switch off red
                      if ( NB_TRAFFIC_LIGHT == TRICOLOR){digitalWrite(traffic_light[i].yellow,!MODE);} // switch off yellow
                      #ifdef CONSOLE
                        Serial.print("activation -> traffic light");Serial.print(i);Serial.println(" : green led");
                      #endif
                      break;}
        case RED : {                                                  // if red led
                      digitalWrite(traffic_light[i].green,!MODE);      // switch off green
                      digitalWrite(traffic_light[i].red,MODE);         // switch on red
                      if ( NB_TRAFFIC_LIGHT == TRICOLOR){digitalWrite(traffic_light[i].yellow,!MODE);} // switch off yellow
                      #ifdef CONSOLE
                        Serial.print("activation -> traffic light");Serial.print(i);Serial.println(" : red led");
                      #endif
                     break;}
         case YELLOW : {                                                  // if yellow led
                      digitalWrite(traffic_light[i].green,!MODE);         // switch off green 
                      digitalWrite(traffic_light[i].red,!MODE);           // switch off red
                      digitalWrite(traffic_light[i].yellow,MODE);         // switch on yellow
                      #ifdef CONSOLE
                        Serial.print("activation -> traffic light");Serial.print(i);Serial.println(" : yellow led");
                      #endif
                     break;}
         }
           }
       traffic_light[i].activation_request = false;            // the traffic light is updated
      }
  update_light = false;                                        // all updates are made
}

/************************************************************************************* 
 *  DCC method
 *  
 ***********************************************************************************/

void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data)
{  
  
  address -= 1; address *= 4; address += 1; address += (data & 0x06) >> 1;    // DCC address decoding
  int led = (data & 0x01) ? GREEN : RED;                                      // DCC/0 or DCC/1
  int traffic_light_index = address;                                          // index of a traffic light   
  int color = led;                                                            // the color of the led
  boolean activation = false;
  if ((address != previous_address) || ((led != previous_position)&&(address == previous_address))){ // if we change the address or the led
        switch (NB_TRAFFIC_LIGHT) {
          case BICOLOR : {      // if the address is in our range for traffic light with two leds
                    if ((address >= FIRST_ID_DCC) && (address < FIRST_ID_DCC + NB_TRAFFIC_LIGHT)){ 
                      traffic_light_index = address - FIRST_ID_DCC;                                  // index of the traffic light
                      activation = true;}
                        break;}
          case TRICOLOR : {     // if the address is in our range for traffic light with three leds
                    if ((address >= FIRST_ID_DCC) && (address < FIRST_ID_DCC + (2*NB_TRAFFIC_LIGHT))){ 
                      if (address%2 != 0) {  traffic_light_index = address - 1; color = YELLOW;}         // if odd address => yellow led
                        traffic_light_index = (traffic_light_index - FIRST_ID_DCC)/2;                    // index of the traffic light
                        activation = true;}
                        break;}
        }
     traffic_light[traffic_light_index].activation_request =  activation;    // activation is requested
     traffic_light[traffic_light_index].current_position = color;            // state is requested (color of the led)
     update_light = activation;                                              // traffic light update is requested
    }
   previous_address = address; previous_position = led;                    // the current activation is saved
  }
/**********************************************************************************************
 *  setup
 * 
 ******************************************************************************************/

void setup() {
  int pin_jump = 0;                                                     // a jump for traffic light pins
  int traffic_light_jump = 0;                                           // a jump for traffic light number
  for (int i=0; i<NB_TRAFFIC_LIGHT; i++){                               // for all the traffic lights
    traffic_light[i].activation_request = false;                        // no activation request
    traffic_light[i].green = pin_jump + FIRST_PIN;                      // pin number of the green led
    pinMode(traffic_light[i].green, OUTPUT);                            // green led in output(ID DCC/0)                                                                                                                                                                                                                                                                   
    digitalWrite(traffic_light[i].green, !MODE);                       // green led switch off
    traffic_light[i].red = 1+ pin_jump + FIRST_PIN;                    // pin number of the red led
    pinMode(traffic_light[i].red, OUTPUT);                             // red led in output  (ID DCC/1)
    digitalWrite(traffic_light[i].red, MODE);                          // red led switch on
    if (NB_TRAFFIC_LIGHT == TRICOLOR) {                                // if three leds
      traffic_light[i].address = traffic_light_jump + FIRST_ID_DCC + i;   // its DCC ID
      traffic_light[i].yellow = 2+ pin_jump + FIRST_PIN;               // pin number of the yellow led
      pinMode(traffic_light[i].yellow, OUTPUT);                        // yellow led in output  (ID DCC+1/0)
      digitalWrite(traffic_light[i].yellow, !MODE);                    // yellow led switch off
      traffic_light_jump++;                                             // the following traffic light 
      pin_jump+=3;                                                     // the following pin for three leds
          }
    else {                                                             // if two leds
         traffic_light[i].address = FIRST_ID_DCC + i;                  // its DCC ID
        pin_jump+=2;                                                   // the following pin for two leds
        }
 
  }
  
  DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);   // instanciate the DCC
  DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );                                   // its IT
  update_light = false;                                                             // no update
  
  #ifdef CONSOLE
   Serial.begin(115200);
   for (int i=0; i<NB_TRAFFIC_LIGHT; i++){
    Serial.print("traffic light");Serial.println(i);
    Serial.print("\t green led on pin : ");Serial.print(traffic_light[i].green);Serial.print(" , DCC address : ");Serial.print(traffic_light[i].address);Serial.println("/0"); 
    Serial.print("\t red led on pin : ");Serial.print(traffic_light[i].red);Serial.print(" , DCC address : ");Serial.print(traffic_light[i].address);Serial.println("/1");
    if (NB_TRAFFIC_LIGHT == TRICOLOR) {
      Serial.print("\t yellow led on pin : ");Serial.print(traffic_light[i].yellow);Serial.print(" , DCC address : ");Serial.print(traffic_light[i].address+1);Serial.println("/0");
      }
   }
  #endif
}

/*************************************************************************
 * loop
 * 
 ******************************************************************/
 
void loop() {
  DCC.loop();                                              // Is there a DCC command ?
  if (update_light) {activation_traffic_light();}          // if yes, activation of traffic lights
}

Le programme à télécharger ici :

Decoder traffic light

Ce petit décodeur de signaux est facile à réaliser pour un coût très modique. Il marche très bien sur mon réseau avec mon ECOS et TrainController. Pour ceux qui possède une Cameo silhouette on peut même réaliser ses propres feux et potence dont voici une première ébauche.

Potence en papier

et son utilisation avec une ECOS :

signalisation ECOS
signalisation portique

la bibliothèque minabay pour le DCC

minabay dcc decoder