Forums LR PRESSE

Où il est question de trains, petits et grands

  • Advertisement

Pilote de signaux lumineux

Toutes les discussions sur l'Arduino !

Modérateur: MOD

Pilote de signaux lumineux

Publié: Mar 10 Mars 2015, 23:02 
Bonjour à tous,

Je viens vous présenter mes "travaux" utilisant une carte Arduino pour piloter des signaux lumineux.

Présentation du système :

Le système gère des signaux lumineux grâce à une carte Arduino. Cela permet de se baser sur un circuit alimenté en 5V (voire en 3,3V) distinct du circuit "haute tension" utilisé pour piloter les motrices et surdimensionné pour simplement allumer des diodes.
Le pilotage des aiguilles peut aussi être intégré à ce circuit "basse tension" si on les meut avec des servomoteurs, tout comme des détecteurs de présence à partir d'ILS par exemple.
Pour un modèle UNO, on peut disposer de 12 ports digitaux de sorties pour alimenter les feux des signaux (je garde les deux derniers, #0-Rx et #1-Tx, pour les communications série avec d'autres modules).
Le pilote est capable ainsi de gérer un maximum de 6 signaux (affichant au minimum 2 feux chacun), mais le plus souvent ce sera deux ou trois signaux par carte. Ces valeurs peuvent être changées si on choisit d'utiliser une carte MEGA 2560 qui dispose de bien plus de sorties.

Les commandes sont envoyées par l'ordinateur connecté à l'Arduino. On utilise la librairie "CmdMessenger" pour traiter les commandes reçues. Ces commandes permettent de :
  • Initialiser la liste des signaux gérés par la carte,
  • Donner à un signal un aspect par défaut (en général carré ou sémaphore pour simuler une mise en sécurité du canton),
  • Donner un aspect à un signal.

Une commande est une chaîne de caractères comportant un identifiant et une liste de paramètres, séparés par des virgules. La librairie "CmdMessenger" permet d'affecter une fonction pour traiter chacune des commandes définies et d'extraire facilement les valeurs des paramètres.

Description des commandes :

Initialiser
Définit pour chaque signal un numéro qui permettra de l'identifier.
Permet également d'associer à chaque signal une série de ports de sortie pour alimenter ses feux. Ces sorties sont choisies de manière consécutive en commençant par celle dont le numéro est le plus bas (par défaut le port #2).

Id 1
Param1 int nombre signaux
Param2 int identifiant signal
Param3 int aspect panneau
...
Param n int identifiant signal
Param n+1int aspect panneau

Tous les feux des signaux définis sont initialisés dans le mode "éteint".

Reset
Positionne un aspect par défaut : un feu allumé pour les indicateurs de direction; carré, sémaphore, carré violet pour les autres aspects de panneaux.

Id 2
Param1 int identifiant signal

Cette fonction n'est pas essentielle. Elle peut être facilement remplacée par la suivante.

SetAspect
Positionne un aspect : récupère la matrice des feux à allumer en fonction de l'aspect du panneau et l'aspect des feux demandé.
Change l'état des feux : éteint, clignotant, allumé

Id 3
Param1 int identifiant signal
Param2 int aspect (C, CV, S, (S), A, (A), (VL), VL, M, (M), R, RR, (R), (RR), (R)+(A), RR+(A), (RR)+(A), RR+A, (RR)+A, ID1, ID2, ID3, ID4, ID5, ID6) (les lettres entre parenthèses désigne les lampes clignotantes)

Le programme gère la famille des panneaux SNCF de type "Châssis-Ecran" de 600 mm de large : A, C, F, H, K, et les indicateurs de direction ID2 à ID6. Un panneau d'un aspect (disposition de ses feux sur la cible) donné peut afficher un certain nombre d'aspects des feux présentés dans une première énumération de 24 valeurs (par exemple "SAVL" pour sémaphore, avertissement et voie libre sur un panneau de type A disposant de 3 feux). Une seconde énumération de 26 valeurs liste tous les aspects des feux possibles (éteint, carré, carré violet, sémaphore,...).
Une table à double entrée permet de faire la correspondance entre l'aspect du panneau, la fonction à afficher et les états de ses feux (éteint, allumé, clignotant). Les feux d'un panneau sont indexés en commençant par le feu au bas du panneau (index "0"). Les feux cachés sont ignorés afin d'économiser les sorties de la carte. Cette table définit les aspects des panneaux sans tenir compte de l'oeilleton mais elle peut être facilement adaptée pour le prendre en compte. Il est également possible de construire des tables pour d'autres familles de signaux.

La suite :

Quand j'ai découvert les cartes Arduino et après m'être acheté une UNO, je pensais développer des circuits à base de composants comme des MCP23017 ou des 74HC595 pour augmenter le nombre de ports de sorties ou d'entrées de ma carte. Cependant pour faire cela on tombe rapidement sur le problème de l'organisation de ces puces sur un circuit imprimé. Il faut savoir le concevoir, et surtout pouvoir le fabriquer ... pour un prix raisonnable. Je n'ai rien trouvé qui me convienne, le coût du moindre petit circuit imprimé est vite élevé.
Je suis alors tombé sur un article qui présentait la famille Arduino et notamment les modèle "Mini pro". En fouillant sur eBay j'ai rapidement constaté qu'on en trouvait à moins de 2 euros, prix auquel il faut ajouter pour pouvoir les utiliser un câble USB-FT232 (5€). J'en ai acheté une et testé mon programme, ça fonctionne aussi bien que sur ma UNO pour dix fois moins cher.

Je vais maintenant travailler sur une seconde version de mon programme pour pouvoir envoyer des commandes sur plusieurs Arduino branchées en réseau. Pour faire ce réseau je vais tester des modules RS485 achetés sur eBay pour 0,80€ pièce. Il sera composé d'une carte "mère" connecté à l'ordinateur qui distribuera les commandes reçues sur un ou plusieurs bus RS485 vers la carte destinataire du message. Toutefois cela avancera lentement car je m'occupe en même temps du programme sur l'ordinateur pour envoyer les commandes à l'Arduino.

Code: Tout sélectionner
#include <avr/pgmspace.h>
#include <CmdMessenger.h>

// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);

// Intervalle pour la fréquence de clignotement
static unsigned long kDefautInterval = 120;
//Compteur pour interruption pour le cligntoement
static byte kCompteurInterruption = 0;

static byte    gErrorCode;
static boolean gBlinkingState;
static unsigned long gInterval = kDefautInterval;

// Etat du clignotement
unsigned long previousBlink = 0;

// Numéro du premier port digital de l'Arduino utilisé comme sortie de feu
const byte kFirstDigitalPort = 2;

// Identifiants commandes
enum
{
  kAcknowledge, // Command to acknowledge that cmd was received
  kCommandInit,
  kCommandReset,
  kCommandSetAspect
};

// Aspects des panneaux Chassis-Ecran 600 mm
enum
{
  kAspectPanneau_A_SAVL,
  kAspectPanneau_A_CVM,
  kAspectPanneau_A_SVL,
  kAspectPanneau_C_CSMAVL,
  kAspectPanneau_C_CVSMAVL,
  kAspectPanneau_C_CSAVL,
  kAspectPanneau_F_CSMARVL,
  kAspectPanneau_F_CVSMARVL,
  kAspectPanneau_F_CSARVL,
  kAspectPanneau_F_CVSARVL,
  kAspectPanneau_F_SARVL,
  kAspectPanneau_H_CSMRRARVL,
  kAspectPanneau_H_CVSMRRARVL,
  kAspectPanneau_H_CSRRARVL,
  kAspectPanneau_H_CSMRRAVL,
  kAspectPanneau_H_CVSMRRAVL,
  kAspectPanneau_H_CSRRAVL,
  kAspectPanneau_H_CVSRRAVL,
  kAspectPanneau_K_CVM,
  kAspectPanneau_ID2_ID2,
  kAspectPanneau_ID3_ID3,
  kAspectPanneau_ID4_ID4,
  kAspectPanneau_ID5_ID5,
  kAspectPanneau_ID6_ID6,
  kAspectPanneau_NB, // nombre d'aspects des signaux = 24
  kAspectPanneau_NA = 255 // non défini
};

// Aspects des feux d'un signal
enum
{
  kAspectFeu_Off,    // Eteint
  kAspectFeu_C,      // Carré
  kAspectFeu_CV,     // Carré violet
  kAspectFeu_S,      // Sémaphore
  kAspectFeu_PS,     // Sémaphore sans arrêt
  kAspectFeu_A,      // Avertissement
  kAspectFeu_PA,     // Feu jaune cignotant
  kAspectFeu_VL,     // Voie libre
  kAspectFeu_PVL,    // Feu vert cignotant
  kAspectFeu_M,      // Manoeuvre
  kAspectFeu_PM,     // Manoeuvre réduite
  kAspectFeu_R,      // Ralentissement 30
  kAspectFeu_RR,     // Rappel de ralentissement 30
  kAspectFeu_PR,     // Ralentissement 60
  kAspectFeu_PRR,    // Rappel de ralentissement 60
  kAspectFeu_PR_PA,  // Ralentissement 60 + Feu jaune cignotant
  kAspectFeu_RR_PA,  // Rappel 30 + Feu jaune cignotant
  kAspectFeu_PRR_PA, // Rappel 60 + Feu jaune cignotant
  kAspectFeu_RR_A,   // Rappel 30 + Avertissement
  kAspectFeu_PRR_A,  // Rappel 60 + Avertissement
  kAspectFeu_ID1,    // Direction 1
  kAspectFeu_ID2,    // Direction 2
  kAspectFeu_ID3,    // Direction 3
  kAspectFeu_ID4,    // Direction 4
  kAspectFeu_ID5,    // Direction 5
  kAspectFeu_ID6,    // Direction 6
  kAspectFeu_NB      // 25 + Off
};

// Etats feux
enum
{
  kEtatFeuxEteint,
  kEtatFeuxCligntotant,
  kEtatFeuxAllume,
  kEtatFeux_NB
};

// Table des aspects des feux pilotés
const byte kNbAspectsSignaux = 6;
static byte gIdSignauxArray[kNbAspectsSignaux] = {0, 0, 0, 0, 0, 0};
static byte gAspectsSignauxArray[kNbAspectsSignaux] = {kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA};

// Table des identifiants des feux pilotés pour chacun des ports de sortie
const byte kNbPortsSortiesFeux = 12; // Arduino UNO ports digitaux [2, 13]
static byte gPortsSortiesIdSignauxArray[kNbPortsSortiesFeux] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};

// Table des états des feux pilotés pour chacun des ports de sortie
static byte gEtatFeuxArray[kNbPortsSortiesFeux] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};

// Nombre de feux en fonction de l'aspect du signal
const byte kNbFeuxSignal600[kAspectPanneau_NB] = {3, 2, 2, 5, 5, 4, 7, 7, 6, 6, 5, 9, 9, 8, 7, 7, 6, 6, 2, 2, 3, 4, 5, 6};

// Tables des aspects des feux en fonction des aspects des panneaux

// Panneau de type A

PROGMEM const byte kAspectPanneau_A_SAVL_S  [3] = {0, 2, 0};
PROGMEM const byte kAspectPanneau_A_SAVL_PS [3] = {0, 1, 0};
PROGMEM const byte kAspectPanneau_A_SAVL_A  [3] = {2, 0, 0};
PROGMEM const byte kAspectPanneau_A_SAVL_PA [3] = {1, 0, 0};
PROGMEM const byte kAspectPanneau_A_SAVL_VL [3] = {0, 0, 2};
PROGMEM const byte kAspectPanneau_A_SAVL_PVL[3] = {0, 0, 1};

PROGMEM const byte* kAspectPanneau_A_SAVL_Array[kAspectFeu_NB] =
{
  0, 0, 0,
  kAspectPanneau_A_SAVL_S,
  kAspectPanneau_A_SAVL_PS,
  kAspectPanneau_A_SAVL_A,
  kAspectPanneau_A_SAVL_PA,
  kAspectPanneau_A_SAVL_VL,
  kAspectPanneau_A_SAVL_PVL,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_A_CVM_CV[2] = {2, 0};
PROGMEM const byte kAspectPanneau_A_CVM_M [2] = {0, 2};
PROGMEM const byte kAspectPanneau_A_CVM_PM[2] = {0, 1};

PROGMEM const byte* kAspectPanneau_A_CVM_Array[kAspectFeu_NB] =
{
  0, 0,
  kAspectPanneau_A_CVM_CV,
  0, 0, 0, 0, 0, 0,
  kAspectPanneau_A_CVM_M,
  kAspectPanneau_A_CVM_PM,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_A_SVL_S  [2] = {2, 0};
PROGMEM const byte kAspectPanneau_A_SVL_PS [2] = {1, 0};
PROGMEM const byte kAspectPanneau_A_SVL_VL [2] = {0, 2};
PROGMEM const byte kAspectPanneau_A_SVL_PVL[2] = {0, 1};

PROGMEM const byte* kAspectPanneau_A_SVL_Array[kAspectFeu_NB] =
{
  0, 0, 0,
  kAspectPanneau_A_SVL_S,
  kAspectPanneau_A_SVL_PS,
  0, 0,
  kAspectPanneau_A_SVL_VL,
  kAspectPanneau_A_SVL_PVL,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

// Panneau de type C

PROGMEM const byte kAspectPanneau_C_CSMAVL_C  [5] = {0, 2, 0, 0, 2};
PROGMEM const byte kAspectPanneau_C_CSMAVL_S  [5] = {0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CSMAVL_PS [5] = {0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CSMAVL_M  [5] = {0, 0, 0, 2, 0};
PROGMEM const byte kAspectPanneau_C_CSMAVL_PM [5] = {0, 0, 0, 1, 0};
PROGMEM const byte kAspectPanneau_C_CSMAVL_A  [5] = {2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CSMAVL_PA [5] = {1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CSMAVL_VL [5] = {0, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_C_CSMAVL_PVL[5] = {0, 0, 1, 0, 0};

PROGMEM const byte* kAspectPanneau_C_CSMAVL_Array[kAspectFeu_NB] =
{
  0,
  kAspectPanneau_C_CSMAVL_C,
  0,
  kAspectPanneau_C_CSMAVL_S,
  kAspectPanneau_C_CSMAVL_PS,
  kAspectPanneau_C_CSMAVL_A,
  kAspectPanneau_C_CSMAVL_PA,
  kAspectPanneau_C_CSMAVL_VL,
  kAspectPanneau_C_CSMAVL_PVL,
  kAspectPanneau_C_CSMAVL_M,
  kAspectPanneau_C_CSMAVL_PM,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_C_CVSMAVL_CV [5] = {0, 0, 0, 0, 2};
PROGMEM const byte kAspectPanneau_C_CVSMAVL_S  [5] = {0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CVSMAVL_PS [5] = {0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CVSMAVL_M  [5] = {0, 0, 0, 2, 0};
PROGMEM const byte kAspectPanneau_C_CVSMAVL_PM [5] = {0, 0, 0, 1, 0};
PROGMEM const byte kAspectPanneau_C_CVSMAVL_A  [5] = {2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CVSMAVL_PA [5] = {1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CVSMAVL_VL [5] = {0, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_C_CVSMAVL_PVL[5] = {0, 0, 1, 0, 0};

PROGMEM const byte* kAspectPanneau_C_CVSMAVL_Array[kAspectFeu_NB] =
{
  0, 0,
  kAspectPanneau_C_CVSMAVL_CV,
  kAspectPanneau_C_CVSMAVL_S,
  kAspectPanneau_C_CVSMAVL_PS,
  kAspectPanneau_C_CVSMAVL_A,
  kAspectPanneau_C_CVSMAVL_PA,
  kAspectPanneau_C_CVSMAVL_VL,
  kAspectPanneau_C_CVSMAVL_PVL,
  kAspectPanneau_C_CVSMAVL_M,
  kAspectPanneau_C_CVSMAVL_PM,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_C_CSAVL_C  [4] = {0, 2, 0, 2};
PROGMEM const byte kAspectPanneau_C_CSAVL_S  [4] = {0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_C_CSAVL_PS [4] = {0, 1, 0, 0};
PROGMEM const byte kAspectPanneau_C_CSAVL_A  [4] = {2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CSAVL_PA [4] = {1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_C_CSAVL_VL [4] = {0, 0, 2, 0};
PROGMEM const byte kAspectPanneau_C_CSAVL_PVL[4] = {0, 0, 1, 0};

PROGMEM const byte* kAspectPanneau_C_CSAVL_Array[kAspectFeu_NB] =
{
  0,
  kAspectPanneau_C_CSAVL_C,
  0,
  kAspectPanneau_C_CSAVL_S,
  kAspectPanneau_C_CSAVL_PS,
  kAspectPanneau_C_CSAVL_A,
  kAspectPanneau_C_CSAVL_PA,
  kAspectPanneau_C_CSAVL_VL,
  kAspectPanneau_C_CSAVL_PVL,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

// Panneau de type F

PROGMEM const byte kAspectPanneau_F_CSMARVL_C    [7] = {0, 2, 0, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSMARVL_S    [7] = {0, 2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSMARVL_PS   [7] = {0, 1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSMARVL_A    [7] = {2, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSMARVL_PA   [7] = {1, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSMARVL_VL   [7] = {0, 0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSMARVL_PVL  [7] = {0, 0, 1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSMARVL_M    [7] = {0, 0, 0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSMARVL_PM   [7] = {0, 0, 0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSMARVL_R    [7] = {0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_F_CSMARVL_PR   [7] = {0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_F_CSMARVL_PR_PA[7] = {1, 0, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_F_CSMARVL_Array[kAspectFeu_NB]    =
{
  0,
  kAspectPanneau_F_CSMARVL_C,
  0,
  kAspectPanneau_F_CSMARVL_S,
  kAspectPanneau_F_CSMARVL_PS,
  kAspectPanneau_F_CSMARVL_A,
  kAspectPanneau_F_CSMARVL_PA,
  kAspectPanneau_F_CSMARVL_VL,
  kAspectPanneau_F_CSMARVL_PVL,
  kAspectPanneau_F_CSMARVL_M,
  kAspectPanneau_F_CSMARVL_PM,
  kAspectPanneau_F_CSMARVL_R,
  0,
  kAspectPanneau_F_CSMARVL_PR,
  0,
  kAspectPanneau_F_CSMARVL_PR_PA,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_F_CVSMARVL_C    [7] = {0, 0, 0, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_S    [7] = {0, 2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_PS   [7] = {0, 1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_A    [7] = {2, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_PA   [7] = {1, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_VL   [7] = {0, 0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_PVL  [7] = {0, 0, 1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_M    [7] = {0, 0, 0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_PM   [7] = {0, 0, 0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_R    [7] = {0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_PR   [7] = {0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_F_CVSMARVL_PR_PA[7] = {1, 0, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_F_CVSMARVL_Array[kAspectFeu_NB]   =
{
  0,
  kAspectPanneau_F_CVSMARVL_C,
  0,
  kAspectPanneau_F_CVSMARVL_S,
  kAspectPanneau_F_CVSMARVL_PS,
  kAspectPanneau_F_CVSMARVL_A,
  kAspectPanneau_F_CVSMARVL_PA,
  kAspectPanneau_F_CVSMARVL_VL,
  kAspectPanneau_F_CVSMARVL_PVL,
  kAspectPanneau_F_CVSMARVL_M,
  kAspectPanneau_F_CVSMARVL_PM,
  kAspectPanneau_F_CVSMARVL_R,
  0,
  kAspectPanneau_F_CVSMARVL_PR,
  0,
  kAspectPanneau_F_CVSMARVL_PR_PA,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_F_CSARVL_C    [6] = {0, 2, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSARVL_S    [6] = {0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSARVL_PS   [6] = {0, 1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSARVL_A    [6] = {2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSARVL_PA   [6] = {1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSARVL_VL   [6] = {0, 0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSARVL_PVL  [6] = {0, 0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CSARVL_R    [6] = {0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_F_CSARVL_PR   [6] = {0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_F_CSARVL_PR_PA[6] = {1, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_F_CSARVL_Array[kAspectFeu_NB]     =
{
  0,
  kAspectPanneau_F_CSARVL_C,
  0,
  kAspectPanneau_F_CSARVL_S,
  kAspectPanneau_F_CSARVL_PS,
  kAspectPanneau_F_CSARVL_A,
  kAspectPanneau_F_CSARVL_PA,
  kAspectPanneau_F_CSARVL_VL,
  kAspectPanneau_F_CSARVL_PVL,
  0, 0,
  kAspectPanneau_F_CSARVL_R,
  0,
  kAspectPanneau_F_CSARVL_PR,
  0,
  kAspectPanneau_F_CSARVL_PR_PA,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_F_CVSARVL_C    [6] = {0, 0, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSARVL_S    [6] = {0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSARVL_PS   [6] = {0, 1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSARVL_A    [6] = {2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSARVL_PA   [6] = {1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSARVL_VL   [6] = {0, 0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSARVL_PVL  [6] = {0, 0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_CVSARVL_R    [6] = {0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_F_CVSARVL_PR   [6] = {0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_F_CVSARVL_PR_PA[6] = {1, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_F_CVSARVL_Array[kAspectFeu_NB]    =
{
  0,
  kAspectPanneau_F_CVSARVL_C,
  0,
  kAspectPanneau_F_CVSARVL_S,
  kAspectPanneau_F_CVSARVL_PS,
  kAspectPanneau_F_CVSARVL_A,
  kAspectPanneau_F_CVSARVL_PA,
  kAspectPanneau_F_CVSARVL_VL,
  kAspectPanneau_F_CVSARVL_PVL,
  0, 0,
  kAspectPanneau_F_CVSARVL_R,
  0,
  kAspectPanneau_F_CVSARVL_PR,
  0,
  kAspectPanneau_F_CVSARVL_PR_PA,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_F_SARVL_C    [5] = {0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_SARVL_S    [5] = {0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_SARVL_PS   [5] = {0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_SARVL_A    [5] = {2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_SARVL_PA   [5] = {1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_F_SARVL_VL   [5] = {0, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_F_SARVL_PVL  [5] = {0, 0, 1, 0, 0};
PROGMEM const byte kAspectPanneau_F_SARVL_R    [5] = {0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_F_SARVL_PR   [5] = {0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_F_SARVL_PR_PA[5] = {1, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_F_SARVL_Array[kAspectFeu_NB]     =
{
  0, 0, 0,
  kAspectPanneau_F_SARVL_S,
  kAspectPanneau_F_SARVL_PS,
  kAspectPanneau_F_SARVL_A,
  kAspectPanneau_F_SARVL_PA,
  kAspectPanneau_F_SARVL_VL,
  kAspectPanneau_F_SARVL_PVL,
  0, 0,
  kAspectPanneau_F_SARVL_R,
  0,
  kAspectPanneau_F_SARVL_PR,
  0,
  kAspectPanneau_F_SARVL_PR_PA,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

// Panneau de type H

PROGMEM const byte kAspectPanneau_H_CSMRRARVL_C     [9] = {0, 2, 0, 0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_S     [9] = {0, 2, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_PS    [9] = {0, 1, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_A     [9] = {2, 0, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_PA    [9] = {1, 0, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_VL    [9] = {0, 0, 2, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_PVL   [9] = {0, 0, 1, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_M     [9] = {0, 0, 0, 2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_PM    [9] = {0, 0, 0, 1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_R     [9] = {0, 0, 0, 0, 0, 2, 2, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_RR    [9] = {0, 0, 0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_PR    [9] = {0, 0, 0, 0, 0, 1, 1, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_PRR   [9] = {0, 0, 0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_PR_PA [9] = {1, 0, 0, 0, 0, 1, 1, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_RR_PA [9] = {1, 0, 0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_PRR_PA[9] = {1, 0, 0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_RR_A  [9] = {2, 0, 0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSMRRARVL_PRR_A [9] = {2, 0, 0, 0, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_H_CSMRRARVL_Array[kAspectFeu_NB]    =
{
  0,
  kAspectPanneau_H_CSMRRARVL_C,
  0,
  kAspectPanneau_H_CSMRRARVL_S,
  kAspectPanneau_H_CSMRRARVL_PS,
  kAspectPanneau_H_CSMRRARVL_A,
  kAspectPanneau_H_CSMRRARVL_PA,
  kAspectPanneau_H_CSMRRARVL_VL,
  kAspectPanneau_H_CSMRRARVL_PVL,
  kAspectPanneau_H_CSMRRARVL_M,
  kAspectPanneau_H_CSMRRARVL_PM,
  kAspectPanneau_H_CSMRRARVL_R,
  kAspectPanneau_H_CSMRRARVL_RR,
  kAspectPanneau_H_CSMRRARVL_PR,
  kAspectPanneau_H_CSMRRARVL_PRR,
  kAspectPanneau_H_CSMRRARVL_PR_PA,
  kAspectPanneau_H_CSMRRARVL_RR_PA,
  kAspectPanneau_H_CSMRRARVL_PRR_PA,
  kAspectPanneau_H_CSMRRARVL_RR_A,
  kAspectPanneau_H_CSMRRARVL_PRR_A,
  0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_CV    [9] = {0, 0, 0, 0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_S     [9] = {0, 2, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_PS    [9] = {0, 1, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_A     [9] = {2, 0, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_PA    [9] = {1, 0, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_VL    [9] = {0, 0, 2, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_PVL   [9] = {0, 0, 1, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_M     [9] = {0, 0, 0, 2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_PM    [9] = {0, 0, 0, 1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_R     [9] = {0, 0, 0, 0, 0, 2, 2, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_RR    [9] = {0, 0, 0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_PR    [9] = {0, 0, 0, 0, 0, 1, 1, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_PRR   [9] = {0, 0, 0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_PR_PA [9] = {1, 0, 0, 0, 0, 1, 1, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_RR_PA [9] = {1, 0, 0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_PRR_PA[9] = {1, 0, 0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_RR_A  [9] = {2, 0, 0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CVSMRRARVL_PRR_A [9] = {2, 0, 0, 0, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_H_CVSMRRARVL_Array[kAspectFeu_NB]    =
{
  0,
  0,
  kAspectPanneau_H_CVSMRRARVL_CV,
  kAspectPanneau_H_CVSMRRARVL_S,
  kAspectPanneau_H_CVSMRRARVL_PS,
  kAspectPanneau_H_CVSMRRARVL_A,
  kAspectPanneau_H_CVSMRRARVL_PA,
  kAspectPanneau_H_CVSMRRARVL_VL,
  kAspectPanneau_H_CVSMRRARVL_PVL,
  kAspectPanneau_H_CVSMRRARVL_M,
  kAspectPanneau_H_CVSMRRARVL_PM,
  kAspectPanneau_H_CVSMRRARVL_R,
  kAspectPanneau_H_CVSMRRARVL_RR,
  kAspectPanneau_H_CVSMRRARVL_PR,
  kAspectPanneau_H_CVSMRRARVL_PRR,
  kAspectPanneau_H_CVSMRRARVL_PR_PA,
  kAspectPanneau_H_CVSMRRARVL_RR_PA,
  kAspectPanneau_H_CVSMRRARVL_PRR_PA,
  kAspectPanneau_H_CVSMRRARVL_RR_A,
  kAspectPanneau_H_CVSMRRARVL_PRR_A,
  0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_H_CSRRARVL_C     [8] = {0, 2, 0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_S     [8] = {0, 2, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_PS    [8] = {0, 1, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_A     [8] = {2, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_PA    [8] = {1, 0, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_VL    [8] = {0, 0, 2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_PVL   [8] = {0, 0, 1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_R     [8] = {0, 0, 0, 0, 2, 2, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_RR    [8] = {0, 0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_PR    [8] = {0, 0, 0, 0, 1, 1, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_PRR   [8] = {0, 0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_PR_PA [8] = {1, 0, 0, 0, 1, 1, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_RR_PA [8] = {1, 0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_PRR_PA[8] = {1, 0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_RR_A  [8] = {2, 0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSRRARVL_PRR_A [8] = {2, 0, 0, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_H_CSRRARVL_Array[kAspectFeu_NB]    =
{
  0,
  kAspectPanneau_H_CSRRARVL_C,
  0,
  kAspectPanneau_H_CSRRARVL_S,
  kAspectPanneau_H_CSRRARVL_PS,
  kAspectPanneau_H_CSRRARVL_A,
  kAspectPanneau_H_CSRRARVL_PA,
  kAspectPanneau_H_CSRRARVL_VL,
  kAspectPanneau_H_CSRRARVL_PVL,
  0,
  0,
  kAspectPanneau_H_CSRRARVL_R,
  kAspectPanneau_H_CSRRARVL_RR,
  kAspectPanneau_H_CSRRARVL_PR,
  kAspectPanneau_H_CSRRARVL_PRR,
  kAspectPanneau_H_CSRRARVL_PR_PA,
  kAspectPanneau_H_CSRRARVL_RR_PA,
  kAspectPanneau_H_CSRRARVL_PRR_PA,
  kAspectPanneau_H_CSRRARVL_RR_A,
  kAspectPanneau_H_CSRRARVL_PRR_A,
  0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_H_CSMRRAVL_C     [7] = {0, 2, 0, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_S     [7] = {0, 2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_PS    [7] = {0, 1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_A     [7] = {2, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_PA    [7] = {1, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_VL    [7] = {0, 0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_PVL   [7] = {0, 0, 1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_M     [7] = {0, 0, 0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_PM    [7] = {0, 0, 0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_RR    [7] = {0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_PRR   [7] = {0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_RR_PA [7] = {1, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_PRR_PA[7] = {1, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_RR_A  [7] = {2, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSMRRAVL_PRR_A [7] = {2, 0, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_H_CSMRRAVL_Array[kAspectFeu_NB]    =
{
  0,
  kAspectPanneau_H_CSMRRAVL_C,
  0,
  kAspectPanneau_H_CSMRRAVL_S,
  kAspectPanneau_H_CSMRRAVL_PS,
  kAspectPanneau_H_CSMRRAVL_A,
  kAspectPanneau_H_CSMRRAVL_PA,
  kAspectPanneau_H_CSMRRAVL_VL,
  kAspectPanneau_H_CSMRRAVL_PVL,
  kAspectPanneau_H_CSMRRAVL_M,
  kAspectPanneau_H_CSMRRAVL_PM,
  0,
  kAspectPanneau_H_CSMRRAVL_RR,
  0,
  kAspectPanneau_H_CSMRRAVL_PRR,
  0,
  kAspectPanneau_H_CSMRRAVL_RR_PA,
  kAspectPanneau_H_CSMRRAVL_PRR_PA,
  kAspectPanneau_H_CSMRRAVL_RR_A,
  kAspectPanneau_H_CSMRRAVL_PRR_A,
  0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_CV    [7] = {0, 0, 0, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_S     [7] = {0, 2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_PS    [7] = {0, 1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_A     [7] = {2, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_PA    [7] = {1, 0, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_VL    [7] = {0, 0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_PVL   [7] = {0, 0, 1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_M     [7] = {0, 0, 0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_PM    [7] = {0, 0, 0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_RR    [7] = {0, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_PRR   [7] = {0, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_RR_PA [7] = {1, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_PRR_PA[7] = {1, 0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_RR_A  [7] = {2, 0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CVSMRRAVL_PRR_A [7] = {2, 0, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_H_CVSMRRAVL_Array[kAspectFeu_NB]    =
{
  0,
  0,
  kAspectPanneau_H_CVSMRRAVL_CV,
  kAspectPanneau_H_CVSMRRAVL_S,
  kAspectPanneau_H_CVSMRRAVL_PS,
  kAspectPanneau_H_CVSMRRAVL_A,
  kAspectPanneau_H_CVSMRRAVL_PA,
  kAspectPanneau_H_CVSMRRAVL_VL,
  kAspectPanneau_H_CVSMRRAVL_PVL,
  kAspectPanneau_H_CVSMRRAVL_M,
  kAspectPanneau_H_CVSMRRAVL_PM,
  0,
  kAspectPanneau_H_CVSMRRAVL_RR,
  0,
  kAspectPanneau_H_CVSMRRAVL_PRR,
  0,
  kAspectPanneau_H_CVSMRRAVL_RR_PA,
  kAspectPanneau_H_CVSMRRAVL_PRR_PA,
  kAspectPanneau_H_CVSMRRAVL_RR_A,
  kAspectPanneau_H_CVSMRRAVL_PRR_A,
  0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_H_CSRRAVL_C     [6] = {0, 2, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_S     [6] = {0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_PS    [6] = {0, 1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_A     [6] = {2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_PA    [6] = {1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_VL    [6] = {0, 0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_PVL   [6] = {0, 0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_RR    [6] = {0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_PRR   [6] = {0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_RR_PA [6] = {1, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_PRR_PA[6] = {1, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_RR_A  [6] = {2, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CSRRAVL_PRR_A [6] = {2, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_H_CSRRAVL_Array[kAspectFeu_NB]    =
{
  0,
  kAspectPanneau_H_CSRRAVL_C,
  0,
  kAspectPanneau_H_CSRRAVL_S,
  kAspectPanneau_H_CSRRAVL_PS,
  kAspectPanneau_H_CSRRAVL_A,
  kAspectPanneau_H_CSRRAVL_PA,
  kAspectPanneau_H_CSRRAVL_VL,
  kAspectPanneau_H_CSRRAVL_PVL,
  0,
  0,
  0,
  kAspectPanneau_H_CSRRAVL_RR,
  0,
  kAspectPanneau_H_CSRRAVL_PRR,
  0,
  kAspectPanneau_H_CSRRAVL_RR_PA,
  kAspectPanneau_H_CSRRAVL_PRR_PA,
  kAspectPanneau_H_CSRRAVL_RR_A,
  kAspectPanneau_H_CSRRAVL_PRR_A,
  0, 0, 0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_H_CVSRRAVL_CV    [6] = {0, 0, 0, 2, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_S     [6] = {0, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_PS    [6] = {0, 1, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_A     [6] = {2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_PA    [6] = {1, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_VL    [6] = {0, 0, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_PVL   [6] = {0, 0, 1, 0, 0, 0};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_RR    [6] = {0, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_PRR   [6] = {0, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_RR_PA [6] = {1, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_PRR_PA[6] = {1, 0, 0, 0, 1, 1};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_RR_A  [6] = {2, 0, 0, 0, 2, 2};
PROGMEM const byte kAspectPanneau_H_CVSRRAVL_PRR_A [6] = {2, 0, 0, 0, 1, 1};

PROGMEM const byte* kAspectPanneau_H_CVSRRAVL_Array[kAspectFeu_NB]    =
{
  0,
  0,
  kAspectPanneau_H_CVSRRAVL_CV,
  kAspectPanneau_H_CVSRRAVL_S,
  kAspectPanneau_H_CVSRRAVL_PS,
  kAspectPanneau_H_CVSRRAVL_A,
  kAspectPanneau_H_CVSRRAVL_PA,
  kAspectPanneau_H_CVSRRAVL_VL,
  kAspectPanneau_H_CVSRRAVL_PVL,
  0,
  0,
  0,
  kAspectPanneau_H_CVSRRAVL_RR,
  0,
  kAspectPanneau_H_CVSRRAVL_PRR,
  0,
  kAspectPanneau_H_CVSRRAVL_RR_PA,
  kAspectPanneau_H_CVSRRAVL_PRR_PA,
  kAspectPanneau_H_CVSRRAVL_RR_A,
  kAspectPanneau_H_CVSRRAVL_PRR_A,
  0, 0, 0, 0, 0, 0
};

// Panneau de type K

PROGMEM const byte kAspectPanneau_K_CVM_CV [2] = {0, 2};
PROGMEM const byte kAspectPanneau_K_CVM_M  [2] = {2, 0};
PROGMEM const byte kAspectPanneau_K_CVM_PM [2] = {1, 0};

PROGMEM const byte* kAspectPanneau_K_CVM_Array[kAspectFeu_NB] =
{
  0,
  0,
  kAspectPanneau_K_CVM_CV,
  0, 0, 0, 0, 0, 0,
  kAspectPanneau_K_CVM_M,
  kAspectPanneau_K_CVM_PM,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

// Panneau de type indicateurs de direction

PROGMEM const byte kAspectPanneau_ID2_ID2_ID1[2] = {2, 0};
PROGMEM const byte kAspectPanneau_ID2_ID2_ID2[2] = {2, 2};

PROGMEM const byte* kAspectPanneau_ID2_ID2_Array[kAspectFeu_NB] =
{
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  kAspectPanneau_ID2_ID2_ID1, kAspectPanneau_ID2_ID2_ID2,
  0, 0, 0, 0
};

PROGMEM const byte kAspectPanneau_ID3_ID3_ID1[3] = {2, 0, 0};
PROGMEM const byte kAspectPanneau_ID3_ID3_ID2[3] = {2, 2, 0};
PROGMEM const byte kAspectPanneau_ID3_ID3_ID3[3] = {2, 2, 2};

PROGMEM const byte* kAspectPanneau_ID3_ID3_Array[kAspectFeu_NB] =
{
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  kAspectPanneau_ID3_ID3_ID1, kAspectPanneau_ID3_ID3_ID2, kAspectPanneau_ID3_ID3_ID3,
  0, 0, 0
};

PROGMEM const byte kAspectPanneau_ID4_ID4_ID1[4] = {2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_ID4_ID4_ID2[4] = {2, 2, 0, 0};
PROGMEM const byte kAspectPanneau_ID4_ID4_ID3[4] = {2, 2, 2, 0};
PROGMEM const byte kAspectPanneau_ID4_ID4_ID4[4] = {2, 2, 2, 2};

PROGMEM const byte* kAspectPanneau_ID4_ID4_Array[kAspectFeu_NB] =
{
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  kAspectPanneau_ID4_ID4_ID1, kAspectPanneau_ID4_ID4_ID2, kAspectPanneau_ID4_ID4_ID3, kAspectPanneau_ID4_ID4_ID4,
  0, 0
};

PROGMEM const byte kAspectPanneau_ID5_ID5_ID1[5] = {2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_ID5_ID5_ID2[5] = {2, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_ID5_ID5_ID3[5] = {2, 2, 2, 0, 0};
PROGMEM const byte kAspectPanneau_ID5_ID5_ID4[5] = {2, 2, 2, 2, 0};
PROGMEM const byte kAspectPanneau_ID5_ID5_ID5[5] = {2, 2, 2, 2, 2};

PROGMEM const byte* kAspectPanneau_ID5_ID5_Array[kAspectFeu_NB] =
{
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  kAspectPanneau_ID5_ID5_ID1, kAspectPanneau_ID5_ID5_ID2, kAspectPanneau_ID5_ID5_ID3, kAspectPanneau_ID5_ID5_ID4, kAspectPanneau_ID5_ID5_ID5,
  0
};

PROGMEM const byte kAspectPanneau_ID6_ID6_ID1[6] = {2, 0, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_ID6_ID6_ID2[6] = {2, 2, 0, 0, 0, 0};
PROGMEM const byte kAspectPanneau_ID6_ID6_ID3[6] = {2, 2, 2, 0, 0, 0};
PROGMEM const byte kAspectPanneau_ID6_ID6_ID4[6] = {2, 2, 2, 2, 0, 0};
PROGMEM const byte kAspectPanneau_ID6_ID6_ID5[6] = {2, 2, 2, 2, 2, 0};
PROGMEM const byte kAspectPanneau_ID6_ID6_ID6[6] = {2, 2, 2, 2, 2, 2};

PROGMEM const byte* kAspectPanneau_ID6_ID6_Array[kAspectFeu_NB] =
{
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  kAspectPanneau_ID6_ID6_ID1, kAspectPanneau_ID6_ID6_ID2, kAspectPanneau_ID6_ID6_ID3, kAspectPanneau_ID6_ID6_ID4, kAspectPanneau_ID6_ID6_ID5, kAspectPanneau_ID6_ID6_ID6
};

// Table pour la première entrée : liste les aspects de différents types de panneaux

PROGMEM const byte** kAspectPanneau_Array[24] =
{
  kAspectPanneau_A_SAVL_Array,
  kAspectPanneau_A_CVM_Array,
  kAspectPanneau_A_SVL_Array,
  kAspectPanneau_C_CSMAVL_Array,
  kAspectPanneau_C_CVSMAVL_Array,
  kAspectPanneau_C_CSAVL_Array,
  kAspectPanneau_F_CSMARVL_Array,
  kAspectPanneau_F_CVSMARVL_Array,
  kAspectPanneau_F_CSARVL_Array,
  kAspectPanneau_F_CVSARVL_Array,
  kAspectPanneau_F_SARVL_Array,
  kAspectPanneau_H_CSMRRARVL_Array,
  kAspectPanneau_H_CVSMRRARVL_Array,
  kAspectPanneau_H_CSRRARVL_Array,
  kAspectPanneau_H_CSMRRAVL_Array,
  kAspectPanneau_H_CVSMRRAVL_Array,
  kAspectPanneau_H_CSRRAVL_Array,
  kAspectPanneau_H_CVSRRAVL_Array,
  kAspectPanneau_K_CVM_Array,
  kAspectPanneau_ID2_ID2_Array,
  kAspectPanneau_ID3_ID3_Array,
  kAspectPanneau_ID4_ID4_Array,
  kAspectPanneau_ID5_ID5_Array,
  kAspectPanneau_ID6_ID6_Array
};

// ------------------  C A L L B A C K S -----------------------

// Défintions des actions associées à chaque commande.
void attachCommandCallbacks()
{
  // Attach callback methods
  cmdMessenger.attach(onUnknownCommand);
  cmdMessenger.attach(kCommandInit, onCommandInit);
  cmdMessenger.attach(kCommandReset, onCommandReset);
  cmdMessenger.attach(kCommandSetAspect, onCommandSetAspect);
}

// Appelée quand on commande n'a pas d'action associée.
void onUnknownCommand()
{
  Serial.println(F("Command without attached callback"));
}

// Fonction Callback qui répond que Arduino est pret (a booté).
void OnArduinoReady()
{
  cmdMessenger.sendCmd(kAcknowledge,"Arduino ready");
}

// Format commande : kCommandInit (0)
// Param1   int   nombreSignaux
// Param2   int   idSignal (> 0)
// Param3   int   aspects
// ...
// Param n   int   idSignal
// Param n+1   int   aspects
//
// Initialise les signaux gérés par le pilote.
// Remet tous les tableaux à zéro avec de lire les nouvelles données.
// Eteint tous les feux.
void onCommandInit()
{
    Serial.println(F("deb onCommandInit"));
   
    byte nbSignauxInitialises = 0;
    byte nbFeuxInitialises = 0;

    for(int i = 0; i < kNbPortsSortiesFeux; i++)
    {
      gPortsSortiesIdSignauxArray[i] = 0;
    }

    for(int i = 0; i < kNbPortsSortiesFeux; i++)
    {
      gEtatFeuxArray[i] = 0;
      digitalWrite(i, LOW);
    }

    for(int i = 0; i < kNbAspectsSignaux; i++)
    {
      gIdSignauxArray[i] = 0;
    }

    for(int i = 0; i < kNbAspectsSignaux; i++)
    {
      gAspectsSignauxArray[i] = kAspectPanneau_NA;
    }
   
    byte nombreSignaux = cmdMessenger.readIntArg();
   
    for(int i = 0; i < nombreSignaux; i++)
    {
      byte idSignal = cmdMessenger.readIntArg();
     
      if(idSignal > 0)
      {
        byte aspectSignal = cmdMessenger.readIntArg();
       
        if(aspectSignal >= 0 && aspectSignal < kAspectPanneau_NB)
        {
          byte nbFeuxSignal = kNbFeuxSignal600[aspectSignal];
          byte nbFeuxInitialisesFutur = nbFeuxInitialises + nbFeuxSignal;
         
          // Vérifie le nombre maximal de feux pouvant etre géré
          if(nbFeuxInitialisesFutur > kNbPortsSortiesFeux)
          {
            Serial.print(F("error onCommandInit : trop de feux à géré : "));
            Serial.println(nbFeuxInitialisesFutur);
            break;
          }
          else
          {
            for(int j = 0; j < nbFeuxSignal; j++)
            {
              gPortsSortiesIdSignauxArray[j + nbFeuxInitialises] = idSignal;
            }

            nbFeuxInitialises = nbFeuxInitialisesFutur;
           
            gIdSignauxArray[i] = idSignal;
            gAspectsSignauxArray[i] = aspectSignal;
          }
        }
        else
        {
          Serial.print(F("error onCommandInit : aspect de signal non géré : {"));
          Serial.print(aspectSignal);
          Serial.println(F("}"));
        }       
      }
      else
      {
        Serial.print(F("error onCommandInit : id signal non valide : "));
        Serial.println(idSignal);
      }
    }

    Serial.println(F("fin onCommandInit"));
}

// Format commande : kCommandReset (1)
// Param1   int   idSignal (> 0)
//
// Positionne le signal sur son aspect par défaut.
void onCommandReset()
{
    Serial.println(F("deb onCommandReset"));

    byte idSignal = cmdMessenger.readIntArg();
 
    byte index = getIndexForIdSignal(idSignal);
 
    if(index == 255)
    {
      Serial.print(F("error onCommandReset : id signal non valide : "));
      Serial.println(idSignal);
      return;
    }
   
    switch(gAspectsSignauxArray[index])
    {
        // Sémaphore
        case kAspectPanneau_A_SAVL:
        case kAspectPanneau_A_SVL:
        case kAspectPanneau_F_SARVL:
          setAspect(idSignal, kAspectFeu_S);
          break;
        // Carré
        case kAspectPanneau_C_CSMAVL:
        case kAspectPanneau_C_CSAVL:
        case kAspectPanneau_F_CSMARVL:
        case kAspectPanneau_F_CSARVL:
        case kAspectPanneau_H_CSRRAVL:
        case kAspectPanneau_H_CSMRRARVL:
        case kAspectPanneau_H_CSRRARVL:
        case kAspectPanneau_H_CSMRRAVL:
          setAspect(idSignal, kAspectFeu_C);
          break;
        // Carré violet
        case kAspectPanneau_A_CVM:
        case kAspectPanneau_C_CVSMAVL:
        case kAspectPanneau_F_CVSMARVL:
        case kAspectPanneau_F_CVSARVL:
        case kAspectPanneau_H_CVSMRRAVL:
        case kAspectPanneau_H_CVSRRAVL:
        case kAspectPanneau_H_CVSMRRARVL:
        case kAspectPanneau_K_CVM:
          setAspect(idSignal, kAspectFeu_CV);
          break;
        // 1 feu allumé
        case kAspectPanneau_ID2_ID2:
        case kAspectPanneau_ID3_ID3:
        case kAspectPanneau_ID4_ID4:
        case kAspectPanneau_ID5_ID5:
        case kAspectPanneau_ID6_ID6:
          setAspect(idSignal, kAspectFeu_ID1);
          break;
        // Eteint 
        default:
          setAspect(idSignal, kAspectFeu_Off);
          break;
    }
   
    Serial.println(F("fin onCommandReset"));
}

// Format commande : kCommandSetAspect (2)
// Param1   int   idSignal (> 0)
// Param2   int   aspect
//
// Positionne le signal sur l'aspect demandé.
void onCommandSetAspect()
{
  Serial.println(F("deb onCommandSetAspect"));
 
  byte idSignal = cmdMessenger.readIntArg();
  byte aspectSignal = cmdMessenger.readIntArg();
 
  setAspect(idSignal, aspectSignal);
   
  Serial.println(F("fin onCommandSetAspect"));
}

// ------------------  F O N C T I O N S  -----------------------

// Retourne pour un identifiant donné l'index dans le tableau des identifiants des signaux.
// Retourne 255 si non trouvé.
byte getIndexForIdSignal(byte idSignal)
{
  for(int i = 0; i < kNbAspectsSignaux; i++)
  {
    if(gIdSignauxArray[i] == idSignal)
    {
      return i;
    }
  }
 
  return 255;
}

// Positionne un aspect pour les feux d'un signal.
void setAspect(byte idSignal, byte aspect)
{
  Serial.print(F("deb setAspect : "));
 
  boolean bDone = false;
 
  byte indexIdSignal = getIndexForIdSignal(idSignal);
 
  if(indexIdSignal == 255)
  {
    Serial.print(F("error setAspect : id signal non valide : "));
    Serial.println(idSignal);
    return;
  }
 
  byte indexFeu = 0;
  int i = 0;
 
  for(i = 0; i < kNbPortsSortiesFeux; i++)
  {
    if(gPortsSortiesIdSignauxArray[i] == idSignal)
    {
      // Lit l'adresse pour la première dimension de la table.
      int addr1 = (int) pgm_read_byte_near(kAspectPanneau_Array) + 26 * 2 * gAspectsSignauxArray[indexIdSignal];

      // Lit l'adresse pour la deuxième dimension de la table.
      int addr2 = (int) pgm_read_byte_near(addr1 + 2 * aspect);
      int addr3 = (int) pgm_read_byte_near(addr1 + 2 * aspect + 1);
      int addr4 = (addr3 << 8) + addr2;

      if(addr4 > 0 || aspect == kAspectFeu_Off)
      {
        // Met à jour les états des feux du panneau
        for(int j = 0; j < kNbFeuxSignal600[gAspectsSignauxArray[indexIdSignal]]; j++)
        {
          int etatFeu = kEtatFeuxEteint;
         
          if(aspect != kAspectFeu_Off)
          {
            // Lit l'état du feu pour l'aspect demandé
            etatFeu = (int)pgm_read_byte_near(addr4 + j);
          }
         
          switch(etatFeu)
          {
             case kEtatFeuxEteint:
               //Serial.print("Eteindre feu ");
               //Serial.println(i + j + kFirstDigitalPort);
               digitalWrite(i + j + kFirstDigitalPort, LOW);
               break;
             case kEtatFeuxCligntotant:
               //Serial.print("Clignoter feu ");
               //Serial.println(i + j + kFirstDigitalPort);
               digitalWrite(i + j + kFirstDigitalPort, HIGH);
               break;
             case kEtatFeuxAllume:
               //Serial.print("Allumer feu ");
               //Serial.println(i + j + kFirstDigitalPort);
               digitalWrite(i + j + kFirstDigitalPort, HIGH);
               break;
             default:
               //Serial.print("Eteindre (defaut) feu ");
               //Serial.println(i + j + kFirstDigitalPort);
               digitalWrite(i + j + kFirstDigitalPort, LOW);
           }
           
           // Met à jour la table des états des feux
           gEtatFeuxArray[i+j] = etatFeu;
        }
      }
     
      break;
    }
  }
   
  if(i == kNbPortsSortiesFeux)
  {
    Serial.print("error setAspect : ");
    Serial.print(gIdSignauxArray[indexIdSignal]);
    Serial.print(", ");
    Serial.println(aspect);
  }

  Serial.println(F("fin setAspect"));
}

// ------------------  S E T U P  -----------------------

void setup()
{
  // Initialise la communication série avec l'ordinateur
  Serial.begin(9600);
  Serial.println(F("deb Setup"));

  // Ajoute un retour charriot à chaque commande
  cmdMessenger.printLfCr();   

  // Attache les fonctions 'callback'
  attachCommandCallbacks();

  // Envoie le  status à l'ordinateur indiquant que l'Arduino a booté
  cmdMessenger.sendCmd(kAcknowledge,"Arduino has started!");

  // Initialise l'état du clignotement
  gBlinkingState = false;
 
  // Positionne les sorties feux à la valeur 'LOW'
  for(int i = 0; i < kNbPortsSortiesFeux; i++)
  {
    pinMode(i + kFirstDigitalPort, OUTPUT);
    digitalWrite(i + kFirstDigitalPort, LOW);
  }

  // Initialise le Timer 2 pour déclencher les interruptions à intervalle régulier
  TCCR2A = 0; //default
  TCCR2B = 0b00000110; // clk/256 est incrémenté toutes les 16uS 
  TIMSK2 = 0b00000001; // TOIE2
  sei();               // autorise les interruptions

  Serial.println(F("fin Setup"));
}

// ------------------  L O O P  -----------------------

void loop()
{
  // Traitement des messages entrants
  cmdMessenger.feedinSerialData();
}

// Gère le clignotement des feux
void blink()
{
  // Inverse l'état du clignotement
   if(gBlinkingState)
  {   
    gBlinkingState = false;
  }
  else
  {
    gBlinkingState = true;
  }
 
  // Change l'état des feux clignotants
  for(int i = 0; i < kNbPortsSortiesFeux; i++)
  {
    if(gEtatFeuxArray[i] == kEtatFeuxCligntotant)
    {
      if(gBlinkingState)
      {
        digitalWrite(i + kFirstDigitalPort, LOW);
      }
      else
      {
        digitalWrite(i + kFirstDigitalPort, HIGH);
      }
    }
  }
}

// Routine d'interruption du timer
ISR (TIMER2_OVF_vect)

  // 256 - 6 --> 250 x 16us = 4 ms 
  // Recharge le timer pour que la prochaine interruption se déclenche dans 4 ms
  TCNT2 = 6;
 
  if(kCompteurInterruption++ == kDefautInterval)
  {
    // 120 * 4ms = 480 ms - la Led est allumée 480 ms et éteinte 480 ms
    kCompteurInterruption = 0; 
    blink();
  } 
}


Suite au prochain épisode ...
notix
 
Messages: 30
Inscrit le: Lun 09 Mars 2015, 22:52
Echelle pratiquée: N

Re: Pilote de signaux lumineux

Publié: Mer 11 Mars 2015, 19:48 
Bonjour
notix a écrit:Quand j'ai découvert les cartes Arduino et après m'être acheté une UNO, je pensais développer des circuits à base de composants comme des MCP23017 ou des 74HC595 pour augmenter le nombre de ports de sorties ou d'entrées de ma carte. Cependant pour faire cela on tombe rapidement sur le problème de l'organisation de ces puces sur un circuit imprimé. Il faut savoir le concevoir, et surtout pouvoir le fabriquer ... pour un prix raisonnable. Je n'ai rien trouvé qui me convienne, le coût du moindre petit circuit imprimé est vite élevé.

Pour développer des circuits j'utilise des plaques à bandes. L'électronique de mon réseau est ainsi réalisée sur une trentaine de plaques à bandes 15x10. Comme la plupart de ces plaques sont différentes je n'ai pas eu le courage de concevoir et de fabriquer des circuits imprimés spéciaux.

Avec un peu d'habitude on arrive très vite à utiliser les plaques à bandes, même pour des circuits complexes. Maintenant je monte aussi des ATmega328 sur les plaques à bande me permettant d'avoir un Arduino Uno sur ma plaque avec tous les autre circuits intégrés a coté. J'en ai déjà parlé dans un autre fil "Arduino ou comment s'en passer par transfert" :

http://forum.e-train.fr/viewtopic.php?f=63&t=75315&start=60

Cordialement

Pierre
Avatar de l’utilisateur
Pierre59
Papotier
 
Messages: 147
Inscrit le: Dim 07 Mars 2010, 10:17
Localisation: Villeneuve d'Ascq (59650)
Âge: 75
Echelle pratiquée: HO
Club: Lille Modélisme

Re: Pilote de signaux lumineux

Publié: Jeu 12 Mars 2015, 20:01 
Voilà un projet bien mené et très intéressant.

:applause:
Cordialement,

Christian.
Avatar de l’utilisateur
likiki
Causant
 
Messages: 257
Inscrit le: Dim 29 Avr 2012, 15:21
Localisation: Corbeil Essonne
Âge: 51
Echelle pratiquée: H0 3R
Prénom: Christian

Re: Pilote de signaux lumineux

Publié: Dim 15 Mars 2015, 10:08 
Bonjour,

J'ai mis à jour mon IDE Arduino et la nouvelle version du compilateur n'accepte plus mon code. Voici une version corrigé pour l'IDE Arduino 1.6.1 :
Code: Tout sélectionner
/*
* Pilote de signaux SNCF pour des cibles "Châssis-Ecran 600 mm"
*
* Version : 20150315
* Version compilateur : IDE Arduino 1.6.1
*/
#include <avr/pgmspace.h>
#include <CmdMessenger.h>

// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);

// Intervalle pour la fréquence de clignotement
static unsigned long kDefautInterval = 120;
//Compteur pour interruption pour le cligntoement
static byte kCompteurInterruption = 0;

static byte    gErrorCode;
static boolean gBlinkingState;
static unsigned long gInterval = kDefautInterval;

// Etat du clignotement
unsigned long previousBlink = 0;

// Numéro du premier port digital de l'Arduino utilisé comme sortie de feu
const byte kFirstDigitalPort = 2;

// Identifiants commandes
enum
{
  kAcknowledge, // Command to acknowledge that cmd was received
  kCommandInit,
  kCommandReset,
  kCommandSetAspect
};

// Aspects des panneaux Chassis-Ecran 600 mm
enum
{
  kAspectPanneau_A_SAVL,
  kAspectPanneau_A_CVM,
  kAspectPanneau_A_SVL,
  kAspectPanneau_C_CSMAVL,
  kAspectPanneau_C_CVSMAVL,
  kAspectPanneau_C_CSAVL,
  kAspectPanneau_F_CSMARVL,
  kAspectPanneau_F_CVSMARVL,
  kAspectPanneau_F_CSARVL,
  kAspectPanneau_F_CVSARVL,
  kAspectPanneau_F_SARVL,
  kAspectPanneau_H_CSMRRARVL,
  kAspectPanneau_H_CVSMRRARVL,
  kAspectPanneau_H_CSRRARVL,
  kAspectPanneau_H_CSMRRAVL,
  kAspectPanneau_H_CVSMRRAVL,
  kAspectPanneau_H_CSRRAVL,
  kAspectPanneau_H_CVSRRAVL,
  kAspectPanneau_K_CVM,
  kAspectPanneau_ID2_ID2,
  kAspectPanneau_ID3_ID3,
  kAspectPanneau_ID4_ID4,
  kAspectPanneau_ID5_ID5,
  kAspectPanneau_ID6_ID6,
  kAspectPanneau_NB, // nombre d'aspects des signaux = 24
  kAspectPanneau_NA = 255 // non défini
};

// Aspects des feux d'un signal
enum
{
  kAspectFeu_Off,    // Eteint
  kAspectFeu_C,      // Carré
  kAspectFeu_CV,     // Carré violet
  kAspectFeu_S,      // Sémaphore
  kAspectFeu_PS,     // Sémaphore sans arrêt
  kAspectFeu_A,      // Avertissement
  kAspectFeu_PA,     // Feu jaune cignotant
  kAspectFeu_VL,     // Voie libre
  kAspectFeu_PVL,    // Feu vert cignotant
  kAspectFeu_M,      // Manoeuvre
  kAspectFeu_PM,     // Manoeuvre réduite
  kAspectFeu_R,      // Ralentissement 30
  kAspectFeu_RR,     // Rappel de ralentissement 30
  kAspectFeu_PR,     // Ralentissement 60
  kAspectFeu_PRR,    // Rappel de ralentissement 60
  kAspectFeu_PR_PA,  // Ralentissement 60 + Feu jaune cignotant
  kAspectFeu_RR_PA,  // Rappel 30 + Feu jaune cignotant
  kAspectFeu_PRR_PA, // Rappel 60 + Feu jaune cignotant
  kAspectFeu_RR_A,   // Rappel 30 + Avertissement
  kAspectFeu_PRR_A,  // Rappel 60 + Avertissement
  kAspectFeu_ID1,    // Direction 1
  kAspectFeu_ID2,    // Direction 2
  kAspectFeu_ID3,    // Direction 3
  kAspectFeu_ID4,    // Direction 4
  kAspectFeu_ID5,    // Direction 5
  kAspectFeu_ID6,    // Direction 6
  kAspectFeu_NB      // 25 + Off
};

// Etats feux
enum
{
  kEtatFeuxEteint,
  kEtatFeuxCligntotant,
  kEtatFeuxAllume,
  kEtatFeux_NB
};

// Table des aspects des feux pilotés
const byte kNbAspectsSignaux = 6;
static byte gIdSignauxArray[kNbAspectsSignaux] = {0, 0, 0, 0, 0, 0};
static byte gAspectsSignauxArray[kNbAspectsSignaux] = {kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA};

// Table des identifiants des feux pilotés pour chacun des ports de sortie
const byte kNbPortsSortiesFeux = 12; // Arduino UNO ports digitaux [2, 13]
static byte gPortsSortiesIdSignauxArray[kNbPortsSortiesFeux] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};

// Table des états des feux pilotés pour chacun des ports de sortie
static byte gEtatFeuxArray[kNbPortsSortiesFeux] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};

// Nombre de feux en fonction de l'aspect du signal
const byte kNbFeuxSignal600[kAspectPanneau_NB] = {3, 2, 2, 5, 5, 4, 7, 7, 6, 6, 5, 9, 9, 8, 7, 7, 6, 6, 2, 2, 3, 4, 5, 6};

// Tables des aspects des feux en fonction des aspects des panneaux

// Panneau de type A

const byte kAspectPanneau_A_SAVL_S  [3] PROGMEM = {0, 2, 0};
const byte kAspectPanneau_A_SAVL_PS [3] PROGMEM = {0, 1, 0};
const byte kAspectPanneau_A_SAVL_A  [3] PROGMEM = {2, 0, 0};
const byte kAspectPanneau_A_SAVL_PA [3] PROGMEM = {1, 0, 0};
const byte kAspectPanneau_A_SAVL_VL [3] PROGMEM = {0, 0, 2};
const byte kAspectPanneau_A_SAVL_PVL[3] PROGMEM = {0, 0, 1};

const byte* const kAspectPanneau_A_SAVL_Array[] PROGMEM =
{
  0, 0, 0,
  kAspectPanneau_A_SAVL_S,
  kAspectPanneau_A_SAVL_PS,
  kAspectPanneau_A_SAVL_A,
  kAspectPanneau_A_SAVL_PA,
  kAspectPanneau_A_SAVL_VL,
  kAspectPanneau_A_SAVL_PVL,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0
};

const byte kAspectPanneau_A_CVM_CV[2] PROGMEM = {2, 0};
const byte kAspectPanneau_A_CVM_M [2] PROGMEM = {0, 2};
const byte kAspectPanneau_A_CVM_PM[2] PROGMEM = {0, 1};

const byte*const kAspectPanneau_A_CVM_Array[] PROGMEM =
{
  0, 0,
  kAspectPanneau_A_CVM_CV,
  0, 0, 0, 0, 0, 0,
  kAspectPanneau_A_CVM_M,
  kAspectPanneau_A_CVM_PM,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0
};

const byte kAspectPanneau_A_SVL_S  [2] PROGMEM = {2, 0};
const byte kAspectPanneau_A_SVL_PS [2] PROGMEM = {1, 0};
const byte kAspectPanneau_A_SVL_VL [2] PROGMEM = {0, 2};
const byte kAspectPanneau_A_SVL_PVL[2] PROGMEM = {0, 1};

const byte* const kAspectPanneau_A_SVL_Array[] PROGMEM =
{
  0, 0, 0,
  kAspectPanneau_A_SVL_S,
  kAspectPanneau_A_SVL_PS,
  0, 0,
  kAspectPanneau_A_SVL_VL,
  kAspectPanneau_A_SVL_PVL,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0
};

// Panneau de type C

const byte kAspectPanneau_C_CSMAVL_C  [5] PROGMEM = {0, 2, 0, 0, 2};
const byte kAspectPanneau_C_CSMAVL_S  [5] PROGMEM = {0, 2, 0, 0, 0};
const byte kAspectPanneau_C_CSMAVL_PS [5] PROGMEM = {0, 1, 0, 0, 0};
const byte kAspectPanneau_C_CSMAVL_M  [5] PROGMEM = {0, 0, 0, 2, 0};
const byte kAspectPanneau_C_CSMAVL_PM [5] PROGMEM = {0, 0, 0, 1, 0};
const byte kAspectPanneau_C_CSMAVL_A  [5] PROGMEM = {2, 0, 0, 0, 0};
const byte kAspectPanneau_C_CSMAVL_PA [5] PROGMEM = {1, 0, 0, 0, 0};
const byte kAspectPanneau_C_CSMAVL_VL [5] PROGMEM = {0, 0, 2, 0, 0};
const byte kAspectPanneau_C_CSMAVL_PVL[5] PROGMEM = {0, 0, 1, 0, 0};

const byte* const kAspectPanneau_C_CSMAVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_C_CSMAVL_C,
  0,
  kAspectPanneau_C_CSMAVL_S,
  kAspectPanneau_C_CSMAVL_PS,
  kAspectPanneau_C_CSMAVL_A,
  kAspectPanneau_C_CSMAVL_PA,
  kAspectPanneau_C_CSMAVL_VL,
  kAspectPanneau_C_CSMAVL_PVL,
  kAspectPanneau_C_CSMAVL_M,
  kAspectPanneau_C_CSMAVL_PM,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0
};

const byte kAspectPanneau_C_CVSMAVL_CV [5] PROGMEM = {0, 0, 0, 0, 2};
const byte kAspectPanneau_C_CVSMAVL_S  [5] PROGMEM = {0, 2, 0, 0, 0};
const byte kAspectPanneau_C_CVSMAVL_PS [5] PROGMEM = {0, 1, 0, 0, 0};
const byte kAspectPanneau_C_CVSMAVL_M  [5] PROGMEM = {0, 0, 0, 2, 0};
const byte kAspectPanneau_C_CVSMAVL_PM [5] PROGMEM = {0, 0, 0, 1, 0};
const byte kAspectPanneau_C_CVSMAVL_A  [5] PROGMEM = {2, 0, 0, 0, 0};
const byte kAspectPanneau_C_CVSMAVL_PA [5] PROGMEM = {1, 0, 0, 0, 0};
const byte kAspectPanneau_C_CVSMAVL_VL [5] PROGMEM = {0, 0, 2, 0, 0};
const byte kAspectPanneau_C_CVSMAVL_PVL[5] PROGMEM = {0, 0, 1, 0, 0};

const byte* const kAspectPanneau_C_CVSMAVL_Array[] PROGMEM =
{
  0, 0,
  kAspectPanneau_C_CVSMAVL_CV,
  kAspectPanneau_C_CVSMAVL_S,
  kAspectPanneau_C_CVSMAVL_PS,
  kAspectPanneau_C_CVSMAVL_A,
  kAspectPanneau_C_CVSMAVL_PA,
  kAspectPanneau_C_CVSMAVL_VL,
  kAspectPanneau_C_CVSMAVL_PVL,
  kAspectPanneau_C_CVSMAVL_M,
  kAspectPanneau_C_CVSMAVL_PM,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0
};

const byte kAspectPanneau_C_CSAVL_C  [4] PROGMEM = {0, 2, 0, 2};
const byte kAspectPanneau_C_CSAVL_S  [4] PROGMEM = {0, 2, 0, 0};
const byte kAspectPanneau_C_CSAVL_PS [4] PROGMEM = {0, 1, 0, 0};
const byte kAspectPanneau_C_CSAVL_A  [4] PROGMEM = {2, 0, 0, 0};
const byte kAspectPanneau_C_CSAVL_PA [4] PROGMEM = {1, 0, 0, 0};
const byte kAspectPanneau_C_CSAVL_VL [4] PROGMEM = {0, 0, 2, 0};
const byte kAspectPanneau_C_CSAVL_PVL[4] PROGMEM = {0, 0, 1, 0};

const byte* const kAspectPanneau_C_CSAVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_C_CSAVL_C,
  0,
  kAspectPanneau_C_CSAVL_S,
  kAspectPanneau_C_CSAVL_PS,
  kAspectPanneau_C_CSAVL_A,
  kAspectPanneau_C_CSAVL_PA,
  kAspectPanneau_C_CSAVL_VL,
  kAspectPanneau_C_CSAVL_PVL,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0
};

// Panneau de type F

const byte kAspectPanneau_F_CSMARVL_C    [7] PROGMEM = {0, 2, 0, 0, 2, 0, 0};
const byte kAspectPanneau_F_CSMARVL_S    [7] PROGMEM = {0, 2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSMARVL_PS   [7] PROGMEM = {0, 1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSMARVL_A    [7] PROGMEM = {2, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSMARVL_PA   [7] PROGMEM = {1, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSMARVL_VL   [7] PROGMEM = {0, 0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSMARVL_PVL  [7] PROGMEM = {0, 0, 1, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSMARVL_M    [7] PROGMEM = {0, 0, 0, 2, 0, 0, 0};
const byte kAspectPanneau_F_CSMARVL_PM   [7] PROGMEM = {0, 0, 0, 1, 0, 0, 0};
const byte kAspectPanneau_F_CSMARVL_R    [7] PROGMEM = {0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_F_CSMARVL_PR   [7] PROGMEM = {0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_F_CSMARVL_PR_PA[7] PROGMEM = {1, 0, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_F_CSMARVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_F_CSMARVL_C,
  0,
  kAspectPanneau_F_CSMARVL_S,
  kAspectPanneau_F_CSMARVL_PS,
  kAspectPanneau_F_CSMARVL_A,
  kAspectPanneau_F_CSMARVL_PA,
  kAspectPanneau_F_CSMARVL_VL,
  kAspectPanneau_F_CSMARVL_PVL,
  kAspectPanneau_F_CSMARVL_M,
  kAspectPanneau_F_CSMARVL_PM,
  kAspectPanneau_F_CSMARVL_R,
  0,
  kAspectPanneau_F_CSMARVL_PR,
  0,
  kAspectPanneau_F_CSMARVL_PR_PA,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0
};

const byte kAspectPanneau_F_CVSMARVL_C    [7] PROGMEM = {0, 0, 0, 0, 2, 0, 0};
const byte kAspectPanneau_F_CVSMARVL_S    [7] PROGMEM = {0, 2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSMARVL_PS   [7] PROGMEM = {0, 1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSMARVL_A    [7] PROGMEM = {2, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSMARVL_PA   [7] PROGMEM = {1, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSMARVL_VL   [7] PROGMEM = {0, 0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSMARVL_PVL  [7] PROGMEM = {0, 0, 1, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSMARVL_M    [7] PROGMEM = {0, 0, 0, 2, 0, 0, 0};
const byte kAspectPanneau_F_CVSMARVL_PM   [7] PROGMEM = {0, 0, 0, 1, 0, 0, 0};
const byte kAspectPanneau_F_CVSMARVL_R    [7] PROGMEM = {0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_F_CVSMARVL_PR   [7] PROGMEM = {0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_F_CVSMARVL_PR_PA[7] PROGMEM = {1, 0, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_F_CVSMARVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_F_CVSMARVL_C,
  0,
  kAspectPanneau_F_CVSMARVL_S,
  kAspectPanneau_F_CVSMARVL_PS,
  kAspectPanneau_F_CVSMARVL_A,
  kAspectPanneau_F_CVSMARVL_PA,
  kAspectPanneau_F_CVSMARVL_VL,
  kAspectPanneau_F_CVSMARVL_PVL,
  kAspectPanneau_F_CVSMARVL_M,
  kAspectPanneau_F_CVSMARVL_PM,
  kAspectPanneau_F_CVSMARVL_R,
  0,
  kAspectPanneau_F_CVSMARVL_PR,
  0,
  kAspectPanneau_F_CVSMARVL_PR_PA,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0
};

const byte kAspectPanneau_F_CSARVL_C    [6] PROGMEM = {0, 2, 0, 2, 0, 0};
const byte kAspectPanneau_F_CSARVL_S    [6] PROGMEM = {0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSARVL_PS   [6] PROGMEM = {0, 1, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSARVL_A    [6] PROGMEM = {2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSARVL_PA   [6] PROGMEM = {1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CSARVL_VL   [6] PROGMEM = {0, 0, 2, 0, 0, 0};
const byte kAspectPanneau_F_CSARVL_PVL  [6] PROGMEM = {0, 0, 1, 0, 0, 0};
const byte kAspectPanneau_F_CSARVL_R    [6] PROGMEM = {0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_F_CSARVL_PR   [6] PROGMEM = {0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_F_CSARVL_PR_PA[6] PROGMEM = {1, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_F_CSARVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_F_CSARVL_C,
  0,
  kAspectPanneau_F_CSARVL_S,
  kAspectPanneau_F_CSARVL_PS,
  kAspectPanneau_F_CSARVL_A,
  kAspectPanneau_F_CSARVL_PA,
  kAspectPanneau_F_CSARVL_VL,
  kAspectPanneau_F_CSARVL_PVL,
  0, 0,
  kAspectPanneau_F_CSARVL_R,
  0,
  kAspectPanneau_F_CSARVL_PR,
  0,
  kAspectPanneau_F_CSARVL_PR_PA,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0
};

const byte kAspectPanneau_F_CVSARVL_C    [6] PROGMEM = {0, 0, 0, 2, 0, 0};
const byte kAspectPanneau_F_CVSARVL_S    [6] PROGMEM = {0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSARVL_PS   [6] PROGMEM = {0, 1, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSARVL_A    [6] PROGMEM = {2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSARVL_PA   [6] PROGMEM = {1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_F_CVSARVL_VL   [6] PROGMEM = {0, 0, 2, 0, 0, 0};
const byte kAspectPanneau_F_CVSARVL_PVL  [6] PROGMEM = {0, 0, 1, 0, 0, 0};
const byte kAspectPanneau_F_CVSARVL_R    [6] PROGMEM = {0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_F_CVSARVL_PR   [6] PROGMEM = {0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_F_CVSARVL_PR_PA[6] PROGMEM = {1, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_F_CVSARVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_F_CVSARVL_C,
  0,
  kAspectPanneau_F_CVSARVL_S,
  kAspectPanneau_F_CVSARVL_PS,
  kAspectPanneau_F_CVSARVL_A,
  kAspectPanneau_F_CVSARVL_PA,
  kAspectPanneau_F_CVSARVL_VL,
  kAspectPanneau_F_CVSARVL_PVL,
  0, 0,
  kAspectPanneau_F_CVSARVL_R,
  0,
  kAspectPanneau_F_CVSARVL_PR,
  0,
  kAspectPanneau_F_CVSARVL_PR_PA,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0
};

const byte kAspectPanneau_F_SARVL_C    [5] PROGMEM = {0, 2, 0, 0, 0};
const byte kAspectPanneau_F_SARVL_S    [5] PROGMEM = {0, 2, 0, 0, 0};
const byte kAspectPanneau_F_SARVL_PS   [5] PROGMEM = {0, 1, 0, 0, 0};
const byte kAspectPanneau_F_SARVL_A    [5] PROGMEM = {2, 0, 0, 0, 0};
const byte kAspectPanneau_F_SARVL_PA   [5] PROGMEM = {1, 0, 0, 0, 0};
const byte kAspectPanneau_F_SARVL_VL   [5] PROGMEM = {0, 0, 2, 0, 0};
const byte kAspectPanneau_F_SARVL_PVL  [5] PROGMEM = {0, 0, 1, 0, 0};
const byte kAspectPanneau_F_SARVL_R    [5] PROGMEM = {0, 0, 0, 2, 2};
const byte kAspectPanneau_F_SARVL_PR   [5] PROGMEM = {0, 0, 0, 1, 1};
const byte kAspectPanneau_F_SARVL_PR_PA[5] PROGMEM = {1, 0, 0, 1, 1};

const byte* const kAspectPanneau_F_SARVL_Array[] PROGMEM =
{
  0, 0, 0,
  kAspectPanneau_F_SARVL_S,
  kAspectPanneau_F_SARVL_PS,
  kAspectPanneau_F_SARVL_A,
  kAspectPanneau_F_SARVL_PA,
  kAspectPanneau_F_SARVL_VL,
  kAspectPanneau_F_SARVL_PVL,
  0, 0,
  kAspectPanneau_F_SARVL_R,
  0,
  kAspectPanneau_F_SARVL_PR,
  0,
  kAspectPanneau_F_SARVL_PR_PA,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0
};

// Panneau de type H

const byte kAspectPanneau_H_CSMRRARVL_C     [9] PROGMEM = {0, 2, 0, 0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_S     [9] PROGMEM = {0, 2, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_PS    [9] PROGMEM = {0, 1, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_A     [9] PROGMEM = {2, 0, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_PA    [9] PROGMEM = {1, 0, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_VL    [9] PROGMEM = {0, 0, 2, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_PVL   [9] PROGMEM = {0, 0, 1, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_M     [9] PROGMEM = {0, 0, 0, 2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_PM    [9] PROGMEM = {0, 0, 0, 1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_R     [9] PROGMEM = {0, 0, 0, 0, 0, 2, 2, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_RR    [9] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSMRRARVL_PR    [9] PROGMEM = {0, 0, 0, 0, 0, 1, 1, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_PRR   [9] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CSMRRARVL_PR_PA [9] PROGMEM = {1, 0, 0, 0, 0, 1, 1, 0, 0};
const byte kAspectPanneau_H_CSMRRARVL_RR_PA [9] PROGMEM = {1, 0, 0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSMRRARVL_PRR_PA[9] PROGMEM = {1, 0, 0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CSMRRARVL_RR_A  [9] PROGMEM = {2, 0, 0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSMRRARVL_PRR_A [9] PROGMEM = {2, 0, 0, 0, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_H_CSMRRARVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_H_CSMRRARVL_C,
  0,
  kAspectPanneau_H_CSMRRARVL_S,
  kAspectPanneau_H_CSMRRARVL_PS,
  kAspectPanneau_H_CSMRRARVL_A,
  kAspectPanneau_H_CSMRRARVL_PA,
  kAspectPanneau_H_CSMRRARVL_VL,
  kAspectPanneau_H_CSMRRARVL_PVL,
  kAspectPanneau_H_CSMRRARVL_M,
  kAspectPanneau_H_CSMRRARVL_PM,
  kAspectPanneau_H_CSMRRARVL_R,
  kAspectPanneau_H_CSMRRARVL_RR,
  kAspectPanneau_H_CSMRRARVL_PR,
  kAspectPanneau_H_CSMRRARVL_PRR,
  kAspectPanneau_H_CSMRRARVL_PR_PA,
  kAspectPanneau_H_CSMRRARVL_RR_PA,
  kAspectPanneau_H_CSMRRARVL_PRR_PA,
  kAspectPanneau_H_CSMRRARVL_RR_A,
  kAspectPanneau_H_CSMRRARVL_PRR_A,
  0, 0, 0, 0, 0,
  0
};

const byte kAspectPanneau_H_CVSMRRARVL_CV    [9] PROGMEM = {0, 0, 0, 0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_S     [9] PROGMEM = {0, 2, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_PS    [9] PROGMEM = {0, 1, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_A     [9] PROGMEM = {2, 0, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_PA    [9] PROGMEM = {1, 0, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_VL    [9] PROGMEM = {0, 0, 2, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_PVL   [9] PROGMEM = {0, 0, 1, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_M     [9] PROGMEM = {0, 0, 0, 2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_PM    [9] PROGMEM = {0, 0, 0, 1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_R     [9] PROGMEM = {0, 0, 0, 0, 0, 2, 2, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_RR    [9] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CVSMRRARVL_PR    [9] PROGMEM = {0, 0, 0, 0, 0, 1, 1, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_PRR   [9] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CVSMRRARVL_PR_PA [9] PROGMEM = {1, 0, 0, 0, 0, 1, 1, 0, 0};
const byte kAspectPanneau_H_CVSMRRARVL_RR_PA [9] PROGMEM = {1, 0, 0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CVSMRRARVL_PRR_PA[9] PROGMEM = {1, 0, 0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CVSMRRARVL_RR_A  [9] PROGMEM = {2, 0, 0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CVSMRRARVL_PRR_A [9] PROGMEM = {2, 0, 0, 0, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_H_CVSMRRARVL_Array[] PROGMEM =
{
  0,
  0,
  kAspectPanneau_H_CVSMRRARVL_CV,
  kAspectPanneau_H_CVSMRRARVL_S,
  kAspectPanneau_H_CVSMRRARVL_PS,
  kAspectPanneau_H_CVSMRRARVL_A,
  kAspectPanneau_H_CVSMRRARVL_PA,
  kAspectPanneau_H_CVSMRRARVL_VL,
  kAspectPanneau_H_CVSMRRARVL_PVL,
  kAspectPanneau_H_CVSMRRARVL_M,
  kAspectPanneau_H_CVSMRRARVL_PM,
  kAspectPanneau_H_CVSMRRARVL_R,
  kAspectPanneau_H_CVSMRRARVL_RR,
  kAspectPanneau_H_CVSMRRARVL_PR,
  kAspectPanneau_H_CVSMRRARVL_PRR,
  kAspectPanneau_H_CVSMRRARVL_PR_PA,
  kAspectPanneau_H_CVSMRRARVL_RR_PA,
  kAspectPanneau_H_CVSMRRARVL_PRR_PA,
  kAspectPanneau_H_CVSMRRARVL_RR_A,
  kAspectPanneau_H_CVSMRRARVL_PRR_A,
  0, 0, 0, 0, 0,
  0
};

const byte kAspectPanneau_H_CSRRARVL_C     [8] PROGMEM = {0, 2, 0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_S     [8] PROGMEM = {0, 2, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_PS    [8] PROGMEM = {0, 1, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_A     [8] PROGMEM = {2, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_PA    [8] PROGMEM = {1, 0, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_VL    [8] PROGMEM = {0, 0, 2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_PVL   [8] PROGMEM = {0, 0, 1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_R     [8] PROGMEM = {0, 0, 0, 0, 2, 2, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_RR    [8] PROGMEM = {0, 0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSRRARVL_PR    [8] PROGMEM = {0, 0, 0, 0, 1, 1, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_PRR   [8] PROGMEM = {0, 0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CSRRARVL_PR_PA [8] PROGMEM = {1, 0, 0, 0, 1, 1, 0, 0};
const byte kAspectPanneau_H_CSRRARVL_RR_PA [8] PROGMEM = {1, 0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSRRARVL_PRR_PA[8] PROGMEM = {1, 0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CSRRARVL_RR_A  [8] PROGMEM = {2, 0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSRRARVL_PRR_A [8] PROGMEM = {2, 0, 0, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_H_CSRRARVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_H_CSRRARVL_C,
  0,
  kAspectPanneau_H_CSRRARVL_S,
  kAspectPanneau_H_CSRRARVL_PS,
  kAspectPanneau_H_CSRRARVL_A,
  kAspectPanneau_H_CSRRARVL_PA,
  kAspectPanneau_H_CSRRARVL_VL,
  kAspectPanneau_H_CSRRARVL_PVL,
  0,
  0,
  kAspectPanneau_H_CSRRARVL_R,
  kAspectPanneau_H_CSRRARVL_RR,
  kAspectPanneau_H_CSRRARVL_PR,
  kAspectPanneau_H_CSRRARVL_PRR,
  kAspectPanneau_H_CSRRARVL_PR_PA,
  kAspectPanneau_H_CSRRARVL_RR_PA,
  kAspectPanneau_H_CSRRARVL_PRR_PA,
  kAspectPanneau_H_CSRRARVL_RR_A,
  kAspectPanneau_H_CSRRARVL_PRR_A,
  0, 0, 0, 0, 0,
  0
};

const byte kAspectPanneau_H_CSMRRAVL_C     [7] PROGMEM = {0, 2, 0, 0, 2, 0, 0};
const byte kAspectPanneau_H_CSMRRAVL_S     [7] PROGMEM = {0, 2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRAVL_PS    [7] PROGMEM = {0, 1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRAVL_A     [7] PROGMEM = {2, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRAVL_PA    [7] PROGMEM = {1, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRAVL_VL    [7] PROGMEM = {0, 0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRAVL_PVL   [7] PROGMEM = {0, 0, 1, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRAVL_M     [7] PROGMEM = {0, 0, 0, 2, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRAVL_PM    [7] PROGMEM = {0, 0, 0, 1, 0, 0, 0};
const byte kAspectPanneau_H_CSMRRAVL_RR    [7] PROGMEM = {0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSMRRAVL_PRR   [7] PROGMEM = {0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CSMRRAVL_RR_PA [7] PROGMEM = {1, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSMRRAVL_PRR_PA[7] PROGMEM = {1, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CSMRRAVL_RR_A  [7] PROGMEM = {2, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSMRRAVL_PRR_A [7] PROGMEM = {2, 0, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_H_CSMRRAVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_H_CSMRRAVL_C,
  0,
  kAspectPanneau_H_CSMRRAVL_S,
  kAspectPanneau_H_CSMRRAVL_PS,
  kAspectPanneau_H_CSMRRAVL_A,
  kAspectPanneau_H_CSMRRAVL_PA,
  kAspectPanneau_H_CSMRRAVL_VL,
  kAspectPanneau_H_CSMRRAVL_PVL,
  kAspectPanneau_H_CSMRRAVL_M,
  kAspectPanneau_H_CSMRRAVL_PM,
  0,
  kAspectPanneau_H_CSMRRAVL_RR,
  0,
  kAspectPanneau_H_CSMRRAVL_PRR,
  0,
  kAspectPanneau_H_CSMRRAVL_RR_PA,
  kAspectPanneau_H_CSMRRAVL_PRR_PA,
  kAspectPanneau_H_CSMRRAVL_RR_A,
  kAspectPanneau_H_CSMRRAVL_PRR_A,
  0, 0, 0, 0, 0,
  0
};

const byte kAspectPanneau_H_CVSMRRAVL_CV    [7] PROGMEM = {0, 0, 0, 0, 2, 0, 0};
const byte kAspectPanneau_H_CVSMRRAVL_S     [7] PROGMEM = {0, 2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRAVL_PS    [7] PROGMEM = {0, 1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRAVL_A     [7] PROGMEM = {2, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRAVL_PA    [7] PROGMEM = {1, 0, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRAVL_VL    [7] PROGMEM = {0, 0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRAVL_PVL   [7] PROGMEM = {0, 0, 1, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRAVL_M     [7] PROGMEM = {0, 0, 0, 2, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRAVL_PM    [7] PROGMEM = {0, 0, 0, 1, 0, 0, 0};
const byte kAspectPanneau_H_CVSMRRAVL_RR    [7] PROGMEM = {0, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CVSMRRAVL_PRR   [7] PROGMEM = {0, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CVSMRRAVL_RR_PA [7] PROGMEM = {1, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CVSMRRAVL_PRR_PA[7] PROGMEM = {1, 0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CVSMRRAVL_RR_A  [7] PROGMEM = {2, 0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CVSMRRAVL_PRR_A [7] PROGMEM = {2, 0, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_H_CVSMRRAVL_Array[] PROGMEM =
{
  0,
  0,
  kAspectPanneau_H_CVSMRRAVL_CV,
  kAspectPanneau_H_CVSMRRAVL_S,
  kAspectPanneau_H_CVSMRRAVL_PS,
  kAspectPanneau_H_CVSMRRAVL_A,
  kAspectPanneau_H_CVSMRRAVL_PA,
  kAspectPanneau_H_CVSMRRAVL_VL,
  kAspectPanneau_H_CVSMRRAVL_PVL,
  kAspectPanneau_H_CVSMRRAVL_M,
  kAspectPanneau_H_CVSMRRAVL_PM,
  0,
  kAspectPanneau_H_CVSMRRAVL_RR,
  0,
  kAspectPanneau_H_CVSMRRAVL_PRR,
  0,
  kAspectPanneau_H_CVSMRRAVL_RR_PA,
  kAspectPanneau_H_CVSMRRAVL_PRR_PA,
  kAspectPanneau_H_CVSMRRAVL_RR_A,
  kAspectPanneau_H_CVSMRRAVL_PRR_A,
  0, 0, 0, 0, 0,
  0
};

const byte kAspectPanneau_H_CSRRAVL_C     [6] PROGMEM = {0, 2, 0, 2, 0, 0};
const byte kAspectPanneau_H_CSRRAVL_S     [6] PROGMEM = {0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRAVL_PS    [6] PROGMEM = {0, 1, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRAVL_A     [6] PROGMEM = {2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRAVL_PA    [6] PROGMEM = {1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CSRRAVL_VL    [6] PROGMEM = {0, 0, 2, 0, 0, 0};
const byte kAspectPanneau_H_CSRRAVL_PVL   [6] PROGMEM = {0, 0, 1, 0, 0, 0};
const byte kAspectPanneau_H_CSRRAVL_RR    [6] PROGMEM = {0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSRRAVL_PRR   [6] PROGMEM = {0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CSRRAVL_RR_PA [6] PROGMEM = {1, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSRRAVL_PRR_PA[6] PROGMEM = {1, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CSRRAVL_RR_A  [6] PROGMEM = {2, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CSRRAVL_PRR_A [6] PROGMEM = {2, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_H_CSRRAVL_Array[] PROGMEM =
{
  0,
  kAspectPanneau_H_CSRRAVL_C,
  0,
  kAspectPanneau_H_CSRRAVL_S,
  kAspectPanneau_H_CSRRAVL_PS,
  kAspectPanneau_H_CSRRAVL_A,
  kAspectPanneau_H_CSRRAVL_PA,
  kAspectPanneau_H_CSRRAVL_VL,
  kAspectPanneau_H_CSRRAVL_PVL,
  0,
  0,
  0,
  kAspectPanneau_H_CSRRAVL_RR,
  0,
  kAspectPanneau_H_CSRRAVL_PRR,
  0,
  kAspectPanneau_H_CSRRAVL_RR_PA,
  kAspectPanneau_H_CSRRAVL_PRR_PA,
  kAspectPanneau_H_CSRRAVL_RR_A,
  kAspectPanneau_H_CSRRAVL_PRR_A,
  0, 0, 0, 0, 0,
  0
};

const byte kAspectPanneau_H_CVSRRAVL_CV    [6] PROGMEM = {0, 0, 0, 2, 0, 0};
const byte kAspectPanneau_H_CVSRRAVL_S     [6] PROGMEM = {0, 2, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSRRAVL_PS    [6] PROGMEM = {0, 1, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSRRAVL_A     [6] PROGMEM = {2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSRRAVL_PA    [6] PROGMEM = {1, 0, 0, 0, 0, 0};
const byte kAspectPanneau_H_CVSRRAVL_VL    [6] PROGMEM = {0, 0, 2, 0, 0, 0};
const byte kAspectPanneau_H_CVSRRAVL_PVL   [6] PROGMEM = {0, 0, 1, 0, 0, 0};
const byte kAspectPanneau_H_CVSRRAVL_RR    [6] PROGMEM = {0, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CVSRRAVL_PRR   [6] PROGMEM = {0, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CVSRRAVL_RR_PA [6] PROGMEM = {1, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CVSRRAVL_PRR_PA[6] PROGMEM = {1, 0, 0, 0, 1, 1};
const byte kAspectPanneau_H_CVSRRAVL_RR_A  [6] PROGMEM = {2, 0, 0, 0, 2, 2};
const byte kAspectPanneau_H_CVSRRAVL_PRR_A [6] PROGMEM = {2, 0, 0, 0, 1, 1};

const byte* const kAspectPanneau_H_CVSRRAVL_Array[] PROGMEM =
{
  0, 0,
  kAspectPanneau_H_CVSRRAVL_CV,
  kAspectPanneau_H_CVSRRAVL_S,
  kAspectPanneau_H_CVSRRAVL_PS,
  kAspectPanneau_H_CVSRRAVL_A,
  kAspectPanneau_H_CVSRRAVL_PA,
  kAspectPanneau_H_CVSRRAVL_VL,
  kAspectPanneau_H_CVSRRAVL_PVL,
  0, 0, 0,
  kAspectPanneau_H_CVSRRAVL_RR,
  0,
  kAspectPanneau_H_CVSRRAVL_PRR,
  0,
  kAspectPanneau_H_CVSRRAVL_RR_PA,
  kAspectPanneau_H_CVSRRAVL_PRR_PA,
  kAspectPanneau_H_CVSRRAVL_RR_A,
  kAspectPanneau_H_CVSRRAVL_PRR_A,
  0, 0, 0, 0, 0,
  0
};

// Panneau de type K

const byte kAspectPanneau_K_CVM_CV [2] PROGMEM = {0, 2};
const byte kAspectPanneau_K_CVM_M  [2] PROGMEM = {2, 0};
const byte kAspectPanneau_K_CVM_PM [2] PROGMEM = {1, 0};

const byte* const kAspectPanneau_K_CVM_Array[] PROGMEM =
{
  0, 0,
  kAspectPanneau_K_CVM_CV,
  0, 0, 0, 0, 0, 0,
  kAspectPanneau_K_CVM_M,
  kAspectPanneau_K_CVM_PM,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0
};

// Panneau de type indicateurs de direction

const byte kAspectPanneau_ID2_ID2_ID1[2] PROGMEM = {2, 0};
const byte kAspectPanneau_ID2_ID2_ID2[2] PROGMEM = {2, 2};

const byte* const kAspectPanneau_ID2_ID2_Array[] PROGMEM =
{
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  kAspectPanneau_ID2_ID2_ID1, kAspectPanneau_ID2_ID2_ID2,
  0, 0, 0, 0
};

const byte kAspectPanneau_ID3_ID3_ID1[3] PROGMEM = {2, 0, 0};
const byte kAspectPanneau_ID3_ID3_ID2[3] PROGMEM = {2, 2, 0};
const byte kAspectPanneau_ID3_ID3_ID3[3] PROGMEM = {2, 2, 2};

const byte* const kAspectPanneau_ID3_ID3_Array[] PROGMEM =
{
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  kAspectPanneau_ID3_ID3_ID1, kAspectPanneau_ID3_ID3_ID2, kAspectPanneau_ID3_ID3_ID3,
  0, 0, 0
};

const byte kAspectPanneau_ID4_ID4_ID1[4] PROGMEM = {2, 0, 0, 0};
const byte kAspectPanneau_ID4_ID4_ID2[4] PROGMEM = {2, 2, 0, 0};
const byte kAspectPanneau_ID4_ID4_ID3[4] PROGMEM = {2, 2, 2, 0};
const byte kAspectPanneau_ID4_ID4_ID4[4] PROGMEM = {2, 2, 2, 2};

const byte* const kAspectPanneau_ID4_ID4_Array[] PROGMEM =
{
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  kAspectPanneau_ID4_ID4_ID1, kAspectPanneau_ID4_ID4_ID2, kAspectPanneau_ID4_ID4_ID3, kAspectPanneau_ID4_ID4_ID4,
  0, 0
};

const byte kAspectPanneau_ID5_ID5_ID1[5] PROGMEM = {2, 0, 0, 0, 0};
const byte kAspectPanneau_ID5_ID5_ID2[5] PROGMEM = {2, 2, 0, 0, 0};
const byte kAspectPanneau_ID5_ID5_ID3[5] PROGMEM = {2, 2, 2, 0, 0};
const byte kAspectPanneau_ID5_ID5_ID4[5] PROGMEM = {2, 2, 2, 2, 0};
const byte kAspectPanneau_ID5_ID5_ID5[5] PROGMEM = {2, 2, 2, 2, 2};

const byte* const kAspectPanneau_ID5_ID5_Array[] PROGMEM =
{
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  kAspectPanneau_ID5_ID5_ID1, kAspectPanneau_ID5_ID5_ID2, kAspectPanneau_ID5_ID5_ID3, kAspectPanneau_ID5_ID5_ID4, kAspectPanneau_ID5_ID5_ID5,
  0
};

const byte kAspectPanneau_ID6_ID6_ID1[6] PROGMEM = {2, 0, 0, 0, 0, 0};
const byte kAspectPanneau_ID6_ID6_ID2[6] PROGMEM = {2, 2, 0, 0, 0, 0};
const byte kAspectPanneau_ID6_ID6_ID3[6] PROGMEM = {2, 2, 2, 0, 0, 0};
const byte kAspectPanneau_ID6_ID6_ID4[6] PROGMEM = {2, 2, 2, 2, 0, 0};
const byte kAspectPanneau_ID6_ID6_ID5[6] PROGMEM = {2, 2, 2, 2, 2, 0};
const byte kAspectPanneau_ID6_ID6_ID6[6] PROGMEM = {2, 2, 2, 2, 2, 2};

const byte* const kAspectPanneau_ID6_ID6_Array[] PROGMEM =
{
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  0, 0, 0, 0, 0,
  kAspectPanneau_ID6_ID6_ID1, kAspectPanneau_ID6_ID6_ID2, kAspectPanneau_ID6_ID6_ID3, kAspectPanneau_ID6_ID6_ID4, kAspectPanneau_ID6_ID6_ID5, kAspectPanneau_ID6_ID6_ID6
};

// Table pour la première entrée : liste les aspects de différents types de panneaux

const byte* const* const kAspectPanneau_Array[] PROGMEM =
{
  kAspectPanneau_A_SAVL_Array,
  kAspectPanneau_A_CVM_Array,
  kAspectPanneau_A_SVL_Array,
  kAspectPanneau_C_CSMAVL_Array,
  kAspectPanneau_C_CVSMAVL_Array,
  kAspectPanneau_C_CSAVL_Array,
  kAspectPanneau_F_CSMARVL_Array,
  kAspectPanneau_F_CVSMARVL_Array,
  kAspectPanneau_F_CSARVL_Array,
  kAspectPanneau_F_CVSARVL_Array,
  kAspectPanneau_F_SARVL_Array,
  kAspectPanneau_H_CSMRRARVL_Array,
  kAspectPanneau_H_CVSMRRARVL_Array,
  kAspectPanneau_H_CSRRARVL_Array,
  kAspectPanneau_H_CSMRRAVL_Array,
  kAspectPanneau_H_CVSMRRAVL_Array,
  kAspectPanneau_H_CSRRAVL_Array,
  kAspectPanneau_H_CVSRRAVL_Array,
  kAspectPanneau_K_CVM_Array,
  kAspectPanneau_ID2_ID2_Array,
  kAspectPanneau_ID3_ID3_Array,
  kAspectPanneau_ID4_ID4_Array,
  kAspectPanneau_ID5_ID5_Array,
  kAspectPanneau_ID6_ID6_Array
};

// ------------------  C A L L B A C K S -----------------------

// Défintions des actions associées à chaque commande.
void attachCommandCallbacks()
{
  // Attach callback methods
  cmdMessenger.attach(onUnknownCommand);
  cmdMessenger.attach(kCommandInit, onCommandInit);
  cmdMessenger.attach(kCommandReset, onCommandReset);
  cmdMessenger.attach(kCommandSetAspect, onCommandSetAspect);
}

// Appelée quand on commande n'a pas d'action associée.
void onUnknownCommand()
{
  Serial.println(F("Command without attached callback"));
}

// Fonction Callback qui répond que Arduino est pret (a booté).
void OnArduinoReady()
{
  cmdMessenger.sendCmd(kAcknowledge,"Arduino ready");
}

// Format commande : kCommandInit (0)
// Param1   int   nombreSignaux
// Param2   int   idSignal (> 0)
// Param3   int   aspects
// ...
// Param n   int   idSignal
// Param n+1   int   aspects
//
// Initialise les signaux gérés par le pilote.
// Remet tous les tableaux à zéro avec de lire les nouvelles données.
// Eteint tous les feux.
void onCommandInit()
{
    Serial.println(F("deb onCommandInit"));
   
    byte nbSignauxInitialises = 0;
    byte nbFeuxInitialises = 0;

    for(int i = 0; i < kNbPortsSortiesFeux; i++)
    {
      gPortsSortiesIdSignauxArray[i] = 0;
    }

    for(int i = 0; i < kNbPortsSortiesFeux; i++)
    {
      gEtatFeuxArray[i] = 0;
      digitalWrite(i, LOW);
    }

    for(int i = 0; i < kNbAspectsSignaux; i++)
    {
      gIdSignauxArray[i] = 0;
    }

    for(int i = 0; i < kNbAspectsSignaux; i++)
    {
      gAspectsSignauxArray[i] = kAspectPanneau_NA;
    }
   
    byte nombreSignaux = cmdMessenger.readIntArg();
   
    for(int i = 0; i < nombreSignaux; i++)
    {
      byte idSignal = cmdMessenger.readIntArg();
     
      if(idSignal > 0)
      {
        byte aspectSignal = cmdMessenger.readIntArg();
       
        if(aspectSignal >= 0 && aspectSignal < kAspectPanneau_NB)
        {
          byte nbFeuxSignal = kNbFeuxSignal600[aspectSignal];
          byte nbFeuxInitialisesFutur = nbFeuxInitialises + nbFeuxSignal;
         
          // Vérifie le nombre maximal de feux pouvant etre géré
          if(nbFeuxInitialisesFutur > kNbPortsSortiesFeux)
          {
            Serial.print(F("error onCommandInit : trop de feux à géré : "));
            Serial.println(nbFeuxInitialisesFutur);
            break;
          }
          else
          {
            for(int j = 0; j < nbFeuxSignal; j++)
            {
              gPortsSortiesIdSignauxArray[j + nbFeuxInitialises] = idSignal;
            }
            /**/
            Serial.print(F("info onCommandInit : initialisation signal #"));
            Serial.print(idSignal);
            Serial.print(" : [");
            Serial.print(nbFeuxSignal);
            Serial.print(", ");
            Serial.print(nbFeuxInitialisesFutur);
            Serial.println("]");
            /**/
            nbFeuxInitialises = nbFeuxInitialisesFutur;
           
            gIdSignauxArray[i] = idSignal;
            gAspectsSignauxArray[i] = aspectSignal;
          }
        }
        else
        {
          Serial.print(F("error onCommandInit : aspect de signal non géré : {"));
          Serial.print(aspectSignal);
          Serial.println(F("}"));
        }       
      }
      else
      {
        Serial.print(F("error onCommandInit : id signal non valide : "));
        Serial.println(idSignal);
      }
    }
   
    Serial.println(F("fin onCommandInit"));
}

// Format commande : kCommandReset (1)
// Param1   int   idSignal (> 0)
//
// Positionne le signal sur son aspect par défaut.
void onCommandReset()
{
    Serial.println(F("deb onCommandReset"));

    byte idSignal = cmdMessenger.readIntArg();
 
    byte index = getIndexForIdSignal(idSignal);
 
    if(index == 255)
    {
      Serial.print(F("error onCommandReset : id signal non valide : "));
      Serial.println(idSignal);
      return;
    }
   
    switch(gAspectsSignauxArray[index])
    {
        // Sémaphore
        case kAspectPanneau_A_SAVL:
        case kAspectPanneau_A_SVL:
        case kAspectPanneau_F_SARVL:
          setAspect(idSignal, kAspectFeu_S);
          break;
        // Carré
        case kAspectPanneau_C_CSMAVL:
        case kAspectPanneau_C_CSAVL:
        case kAspectPanneau_F_CSMARVL:
        case kAspectPanneau_F_CSARVL:
        case kAspectPanneau_H_CSRRAVL:
        case kAspectPanneau_H_CSMRRARVL:
        case kAspectPanneau_H_CSRRARVL:
        case kAspectPanneau_H_CSMRRAVL:
          setAspect(idSignal, kAspectFeu_C);
          break;
        // Carré violet
        case kAspectPanneau_A_CVM:
        case kAspectPanneau_C_CVSMAVL:
        case kAspectPanneau_F_CVSMARVL:
        case kAspectPanneau_F_CVSARVL:
        case kAspectPanneau_H_CVSMRRAVL:
        case kAspectPanneau_H_CVSRRAVL:
        case kAspectPanneau_H_CVSMRRARVL:
        case kAspectPanneau_K_CVM:
          setAspect(idSignal, kAspectFeu_CV);
          break;
        // 1 feu allumé
        case kAspectPanneau_ID2_ID2:
        case kAspectPanneau_ID3_ID3:
        case kAspectPanneau_ID4_ID4:
        case kAspectPanneau_ID5_ID5:
        case kAspectPanneau_ID6_ID6:
          setAspect(idSignal, kAspectFeu_ID1);
          break;
        // Eteint 
        default:
          setAspect(idSignal, kAspectFeu_Off);
          break;
    }
   
    Serial.println(F("fin onCommandReset"));
}

// Format commande : kCommandSetAspect (2)
// Param1   int   idSignal (> 0)
// Param2   int   aspect
//
// Positionne le signal sur l'aspect demandé.
void onCommandSetAspect()
{
  Serial.println(F("deb onCommandSetAspect"));
 
  byte idSignal = cmdMessenger.readIntArg();
  byte aspectSignal = cmdMessenger.readIntArg();
 
  setAspect(idSignal, aspectSignal);
   
  Serial.println(F("fin onCommandSetAspect"));
}

// ------------------  F O N C T I O N S  -----------------------

// Retourne pour un identifiant donné l'index dans le tableau des identifiants des signaux.
// Retourne 255 si non trouvé.
byte getIndexForIdSignal(byte idSignal)
{
  for(int i = 0; i < kNbAspectsSignaux; i++)
  {
    if(gIdSignauxArray[i] == idSignal)
    {
      return i;
    }
  }
 
  return 255;
}

// Positionne un aspect pour les feux d'un signal.
void setAspect(byte idSignal, byte aspectFeux)
{
  Serial.print(F("deb setAspect : "));
  Serial.println(idSignal);

  boolean bDone = false;
 
  byte indexIdSignal = getIndexForIdSignal(idSignal);
 
  if(indexIdSignal == 255)
  {
    Serial.print(F("error setAspect : id signal non valide : "));
    Serial.println(idSignal);
    return;
  }
 
  int aspectPanneau = (int) gAspectsSignauxArray[indexIdSignal];

  int addr1 = (int) (&kAspectPanneau_Array);
  int addr2 = (int) pgm_read_byte_near(addr1 + 2 * aspectPanneau);
  int addr3 = (int) pgm_read_byte_near(addr1 + 2 * aspectPanneau + 1);
  int addrAspectPanneau = (addr3 << 8) + addr2;
 
  byte indexFeu = 0;
  int i = 0;
 
  for(i = 0; i < kNbPortsSortiesFeux; i++)
  {
    if(gPortsSortiesIdSignauxArray[i] == idSignal)
    {
        int nbFeux = kNbFeuxSignal600[aspectPanneau];
     
        int addr11 = (int) (&kAspectPanneau_Array);
        int addr21 = (int) pgm_read_byte_near(addrAspectPanneau + 2 * aspectFeux);
        int addr31 = (int) pgm_read_byte_near(addrAspectPanneau + 2 * aspectFeux + 1);
        int addrAspectFeu = (addr31 << 8) + addr21;
       
        for(int j = 0; j < nbFeux; j++)
        {
          byte etatFeu = kEtatFeuxEteint;
         
          if(aspectFeux != kAspectFeu_Off)
          {
            etatFeu = (int)pgm_read_byte_near(addrAspectFeu + j);
          }
         
          switch(etatFeu)
          {
             case kEtatFeuxEteint:
               digitalWrite(i + j + kFirstDigitalPort, LOW);
               break;
             case kEtatFeuxCligntotant:
               digitalWrite(i + j + kFirstDigitalPort, HIGH);
               break;
             case kEtatFeuxAllume:
               digitalWrite(i + j + kFirstDigitalPort, HIGH);
               break;
             default:
               digitalWrite(i + j + kFirstDigitalPort, LOW);
           }
           
           // Met à jour la table des états des feux
           gEtatFeuxArray[i+j] = etatFeu;
      }
     
      break;
    }
  }
   
  if(i == kNbPortsSortiesFeux)
  {
    Serial.print("error setAspect : ");
    Serial.print(aspectPanneau);
    Serial.print(", ");
    Serial.println(aspectFeux);
  }

  Serial.println(F("fin setAspect"));
}

// ------------------  S E T U P  -----------------------

void setup()
{
  // Initialise la communication série avec l'ordinateur
  Serial.begin(9600);
  Serial.println(F("deb Setup"));

  // Ajoute un retour charriot à chaque commande
  cmdMessenger.printLfCr();   

  // Attache les fonctions 'callback'
  attachCommandCallbacks();

  // Envoie le  status à l'ordinateur indiquant que l'Arduino a booté
  cmdMessenger.sendCmd(kAcknowledge,"Arduino has started!");

  // Initialise l'état du clignotement
  gBlinkingState = false;
 
  // Positionne les sorties feux à la valeur 'LOW'
  for(int i = 0; i < kNbPortsSortiesFeux; i++)
  {
    pinMode(i + kFirstDigitalPort, OUTPUT);
    digitalWrite(i + kFirstDigitalPort, LOW);
  }

  // Initialise le Timer 2 pour déclencher les interruptions à intervalle régulier
  TCCR2A = 0; //default
  TCCR2B = 0b00000110; // clk/256 est incrémenté toutes les 16uS 
  TIMSK2 = 0b00000001; // TOIE2
  sei();               // autorise les interruptions

  Serial.println(F("fin Setup"));
}

// ------------------  L O O P  -----------------------

void loop()
{
  // Traitement des messages entrants
  cmdMessenger.feedinSerialData();
}

// Gère le clignotement des feux
void blink()
{
  // Inverse l'état du clignotement
   if(gBlinkingState)
  {   
    gBlinkingState = false;
  }
  else
  {
    gBlinkingState = true;
  }
 
  // Change l'état des feux clignotants
  for(int i = 0; i < kNbPortsSortiesFeux; i++)
  {
    if(gEtatFeuxArray[i] == kEtatFeuxCligntotant)
    {
      if(gBlinkingState)
      {
        digitalWrite(i + kFirstDigitalPort, LOW);
      }
      else
      {
        digitalWrite(i + kFirstDigitalPort, HIGH);
      }
    }
  }
}

// Routine d'interruption du timer
ISR (TIMER2_OVF_vect)

  // 256 - 6 --> 250 x 16us = 4 ms 
  // Recharge le timer pour que la prochaine interruption se déclenche dans 4 ms
  TCNT2 = 6;
 
  if(kCompteurInterruption++ == kDefautInterval)
  {
    // 120 * 4ms = 480 ms - la Led est allumée 480 ms et éteinte 480 ms
    kCompteurInterruption = 0; 
    blink();
  } 
}
notix
 
Messages: 30
Inscrit le: Lun 09 Mars 2015, 22:52
Echelle pratiquée: N

Re: Pilote de signaux lumineux

Publié: Lun 26 Oct 2015, 23:15 
Bonsoir,

Ça y est, après quelques mois d'abstinence, je m'y suis remis. Mon système a évolué, il fonctionne désormais sur deux cartes Arduino : un maître et un esclave, reliés par un bus RS-485.

Le maître reçoit les commandes de l'ordinateur qu'il retransmet à l'esclave. La librairie qui traite les commandes reçues du PC est toujours "CmdMessenger".

Code: Tout sélectionner
/*
* Carte maître pour commander un bus RS-485
*
* Version : 20151026
* Version compilateur : IDE Arduino 1.6.5
*/

#include <CmdMessenger.h>
#include <ICSC_Soft.h>
#include <SoftwareSerial.h>

/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX         10  //Serial Receive pin
#define SSerialTX         11  //Serial Transmit pin
#define SSerialTxControl  12  //RS485 Direction control

#define RS485Transmit     HIGH
#define RS485Receive      LOW
#define BUS_BAUD          57600

#define Pin13LED          13

SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX

// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);

/*-----( Structures messages )-----*/

struct tSIGNAL
{
  byte identifiant;
  byte aspect;
};

struct tINIT
{
  byte    nbSignaux;
  tSIGNAL signaux[6];
};

struct tRESET
{
  byte    idSignal;
};


struct tSET
{
  byte    idSignal;
  byte    aspect;
};

void pinger(unsigned char station, char command, unsigned char len, char *data)
{
  Serial.print("Ping reply: (");
  Serial.print(station,DEC);
  Serial.print(") ");
  for (byte i=0;i<len;i++)
  {
    Serial.print(data[i]);
  }
  Serial.println();
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Master RS 485 Setup"));

  // Ajoute un retour charriot à chaque commande
  cmdMessenger.printLfCr();

  // Attache les fonctions 'callback'
  attachCommandCallbacks();

  pinMode(SSerialTxControl, OUTPUT);
  digitalWrite(SSerialTxControl, RS485Receive);  // Init Transceiver

  ICSC.begin(1, BUS_BAUD, &RS485Serial, SSerialTxControl);

  ICSC.registerCommand(ICSC_SYS_PONG, &pinger);
  ICSC.registerCommand('R', &commandResponse);
}

void loop()
{
  // Traitement des messages entrants
  cmdMessenger.feedinSerialData();

  ICSC.process();
}

void commandResponse(unsigned char station, char command, unsigned char len, char *data)
{
  Serial.print("Slave responds : ");
  Serial.println(data);
}

// ------------------  C A L L B A C K S -----------------------

// Identifiants commandes
enum
{
  kAcknowledge, // Command to acknowledge that cmd was received
  kCommandInit,
  kCommandReset,
  kCommandSetAspect
};

// Défintions des actions associées à chaque commande.
void attachCommandCallbacks()
{
  // Attach callback methods
  cmdMessenger.attach(onUnknownCommand);
  cmdMessenger.attach(kCommandInit, onCommandInit);
  cmdMessenger.attach(kCommandReset, onCommandReset);
  cmdMessenger.attach(kCommandSetAspect, onCommandSetAspect);
}

// Appelée quand on commande n'a pas d'action associée.
void onUnknownCommand()
{
  Serial.println(F("Command without attached callback"));
}

// Fonction Callback qui répond que Arduino est pret (a booté).
void OnArduinoReady()
{
  cmdMessenger.sendCmd(kAcknowledge,"Arduino ready");
}

// Format commande : kCommandInit (I)
// Param1  int nombreSignaux
// Param2 int idSignal (> 0)
// Param3 int aspects
// ...
// Param n  int idSignal
// Param n+1  int aspects
//
// Initialise les signaux gérés par le pilote.
// Remet tous les tableaux à zéro avec de lire les nouvelles données.
// Eteint tous les feux.
void onCommandInit()
{
  Serial.println(F("deb onCommandInit"));

  int msgSize = 0;

  tINIT init;
  digitalWrite(Pin13LED,HIGH);

  init.nbSignaux = cmdMessenger.readIntArg();
  msgSize++;

  if(init.nbSignaux == 0)
  {
    Serial.println(F("fin onCommandInit : no signal"));
    return;
  }

  Serial.print(F("Nb Signaux : "));
  Serial.println(init.nbSignaux);

  for(int i = 0; i < init.nbSignaux; i++)
  {
    init.signaux[i].identifiant = cmdMessenger.readIntArg();
    msgSize++;
    init.signaux[i].aspect = cmdMessenger.readIntArg();
    msgSize++;

    Serial.print(F("Signal ["));
    Serial.print(i);
    Serial.print(F("] : "));
    Serial.print(init.signaux[i].identifiant);
    Serial.print(F(", "));
    Serial.println(init.signaux[i].aspect);
  }

  delay(100);
  ICSC.send(2, 'I',   msgSize, (char *)&init);
  digitalWrite(Pin13LED,LOW);

  Serial.print(F("fin onCommandInit Ok : "));
  Serial.println(msgSize);
}

// Format commande : kCommandReset (R)
// Param1 int idSignal (> 0)
//
// Positionne le signal sur son aspect par défaut.
void onCommandReset()
{
  Serial.println(F("deb onCommandReset"));

  int msgSize = 0;

  tRESET reset;
  digitalWrite(Pin13LED,HIGH);
  reset.idSignal = cmdMessenger.readIntArg();
  msgSize++;

  delay(100);
  ICSC.send(2, 'R',   msgSize, (char *)&reset);
  digitalWrite(Pin13LED,LOW);

  Serial.print(F("fin onCommandReset Ok : "));
  Serial.println(msgSize);
}

// Format commande : kCommandSetAspect (S)
// Param1 int idSignal (> 0)
// Param2 int aspect
//
// Positionne le signal sur l'aspect demandé.
void onCommandSetAspect()
{
  Serial.println(F("deb onCommandSetAspect"));

  int msgSize = 0;

  tSET set;
  digitalWrite(Pin13LED,HIGH);
  set.idSignal = cmdMessenger.readIntArg();
  msgSize++;
  set.aspect = cmdMessenger.readIntArg();
  msgSize++;

  delay(100);
  ICSC.send(2, 'S',   msgSize, (char *)&set);
  digitalWrite(Pin13LED,LOW);

  Serial.print(F("fin onCommandSetAspect Ok : "));
  Serial.println(msgSize);
}

Pour le protocole du bus RS-485, j'ai repris la librairie "ICSC_Soft" proposée par Zebulon91 (voir le fil suivant), qui s'adapte très bien à mon code initial.

L'esclave analyse la commande et agit sur les sorties et donc sur les lampes des feux. Il reprend en grande partie le code la première version. J'ai placé la partie qui décrit les aspects des panneaux dans un fichier à part.

Code: Tout sélectionner
/*
* Pilote de signaux SNCF pour des cibles "Châssis-Ecran 600 mm"
*
* Version : 20151026
* Version compilateur : IDE Arduino 1.6.5
*/
#include <ICSC_Soft.h>
#include <SoftwareSerial.h>

#include <avr/pgmspace.h>

#include "SignauxSncf_ChassisEcran600.h"

/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX         2  //Serial Receive pin
#define SSerialTX         3  //Serial Transmit pin
#define SSerialTxControl  4  //RS485 Direction control

#define RS485Transmit     HIGH
#define RS485Receive      LOW
#define BUS_BAUD          57600

SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX

// Intervalle pour la fréquence de clignotement
static unsigned long kDefautInterval = 120;
//Compteur pour interruption pour le cligntoement
static byte kCompteurInterruption = 0;

static boolean gBlinkingState;
static unsigned long gInterval = kDefautInterval;

// Etat du clignotement
unsigned long previousBlink = 0;

// Numéro du premier port digital de l'Arduino utilisé comme sortie de feu
const byte kFirstDigitalPort = 5;

// Table des aspects des feux pilotés
const byte kNbAspectsSignaux = 6;
static byte gIdSignauxArray[kNbAspectsSignaux] = {0, 0, 0, 0, 0, 0};
static byte gAspectsSignauxArray[kNbAspectsSignaux] = {kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA, kAspectPanneau_NA};

// Table des identifiants des feux pilotés pour chacun des ports de sortie
const byte kNbPortsSortiesFeux = 12; // Arduino UNO ports digitaux [5, A2]
static byte gPortsSortiesIdSignauxArray[kNbPortsSortiesFeux] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};

// Table des états des feux pilotés pour chacun des ports de sortie
static byte gEtatFeuxArray[kNbPortsSortiesFeux] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};

// Nombre de feux en fonction de l'aspect du signal
const byte kNbFeuxSignal600[kAspectPanneau_NB] = {3, 2, 2, 5, 5, 4, 7, 7, 6, 6, 5, 9, 9, 8, 7, 7, 6, 6, 2, 2, 3, 4, 5, 6};

// ------------------  C A L L B A C K S -----------------------

// Format commande : kCommandInit (I)
// Param1   int   nombreSignaux
// Param2   int   idSignal (> 0)
// Param3   int   aspects
// ...
// Param n   int   idSignal
// Param n+1   int   aspects
//
// Initialise les signaux gérés par le pilote.
// Remet tous les tableaux à zéro avec de lire les nouvelles données.
// Eteint tous les feux.
void onCommandInit(unsigned char station, char command, unsigned char len, char *data)
{
  int readIndex = 0;

  Serial.println(F("deb onCommandInit"));

  byte nbFeuxInitialises = 0;

  for(int i = 0; i < kNbPortsSortiesFeux; i++)
  {
    gPortsSortiesIdSignauxArray[i] = 0;
  }

  for(int i = 0; i < kNbPortsSortiesFeux; i++)
  {
    gEtatFeuxArray[i] = 0;
    digitalWrite(i, LOW);
  }

  for(int i = 0; i < kNbAspectsSignaux; i++)
  {
    gIdSignauxArray[i] = 0;
  }

  for(int i = 0; i < kNbAspectsSignaux; i++)
  {
    gAspectsSignauxArray[i] = kAspectPanneau_NA;
  }

  byte nombreSignaux = (int) data[readIndex++];

  for(int i = 0; i < nombreSignaux; i++)
  {
    byte idSignal = (int) data[readIndex++];

    if(idSignal > 0)
    {
      byte aspectSignal = (int) data[readIndex++];

      if(aspectSignal >= 0 && aspectSignal < kAspectPanneau_NB)
      {
        byte nbFeuxSignal = kNbFeuxSignal600[aspectSignal];
        byte nbFeuxInitialisesFutur = nbFeuxInitialises + nbFeuxSignal;

        // Vérifie le nombre maximal de feux pouvant etre géré
        if(nbFeuxInitialisesFutur > kNbPortsSortiesFeux)
        {
          Serial.print(F("error onCommandInit : trop de feux à géré : "));
          Serial.println(nbFeuxInitialisesFutur);
          break;
        }
        else
        {
          for(int j = 0; j < nbFeuxSignal; j++)
          {
            gPortsSortiesIdSignauxArray[j + nbFeuxInitialises] = idSignal;
          }
          /**/
          Serial.print(F("info onCommandInit : initialisation signal #"));
          Serial.print(idSignal);
          Serial.print(" : [");
          Serial.print(nbFeuxSignal);
          Serial.print(", ");
          Serial.print(nbFeuxInitialisesFutur);
          Serial.println("]");
          /**/
          nbFeuxInitialises = nbFeuxInitialisesFutur;

          gIdSignauxArray[i] = idSignal;
          gAspectsSignauxArray[i] = aspectSignal;
        }
      }
      else
      {
        Serial.print(F("error onCommandInit : aspect de signal non géré : {"));
        Serial.print(aspectSignal);
        Serial.println(F("}"));
      }
    }
    else
    {
      Serial.print(F("error onCommandInit : id signal non valide : "));
      Serial.println(idSignal);
    }
  }
  /*
   for(int i = 0; i < kNbPortsSortiesFeux; i++)
   {
   Serial.print("gPortsSortiesIdSignauxArray[");
   Serial.print(i);
   Serial.print("] = ");
   Serial.println(gPortsSortiesIdSignauxArray[i]);
   }

   for(int i = 0; i < kNbPortsSortiesFeux; i++)
   {
   Serial.print("gEtatFeuxArray[");
   Serial.print(i);
   Serial.print("] = ");
   Serial.println(gEtatFeuxArray[i]);
   }

   for(int i = 0; i < kNbAspectsSignaux; i++)
   {
   Serial.print("gIdSignauxArray[");
   Serial.print(i);
   Serial.print("] = ");
   Serial.println(gIdSignauxArray[i]);
   }

   for(int i = 0; i < kNbAspectsSignaux; i++)
   {
   Serial.print("gAspectsSignauxArray[");
   Serial.print(i);
   Serial.print("] = ");
   Serial.println(gAspectsSignauxArray[i]);
   }
   */
  Serial.println(F("fin onCommandInit"));

  ICSC.send(station, 'R', "OK\0");
}

// Format commande : kCommandReset (R)
// Param1   int   idSignal (> 0)
//
// Positionne le signal sur son aspect par défaut.
void onCommandReset(unsigned char station, char command, unsigned char len, char *data)
{
  int readIndex = 0;

  Serial.println(F("deb onCommandReset"));

  byte idSignal = (int) data[readIndex++];

  byte index = getIndexForIdSignal(idSignal);

  if(index == 255)
  {
    Serial.print(F("error onCommandReset : id signal non valide : "));
    Serial.println(idSignal);
    return;
  }

  switch(gAspectsSignauxArray[index])
  {
      // Sémaphore
    case kAspectPanneau_A_SAVL:
    case kAspectPanneau_A_SVL:
    case kAspectPanneau_F_SARVL:
      setAspect(idSignal, kAspectFeu_S);
      break;
      // Carré
    case kAspectPanneau_C_CSMAVL:
    case kAspectPanneau_C_CSAVL:
    case kAspectPanneau_F_CSMARVL:
    case kAspectPanneau_F_CSARVL:
    case kAspectPanneau_H_CSRRAVL:
    case kAspectPanneau_H_CSMRRARVL:
    case kAspectPanneau_H_CSRRARVL:
    case kAspectPanneau_H_CSMRRAVL:
      setAspect(idSignal, kAspectFeu_C);
      break;
      // Carré violet
    case kAspectPanneau_A_CVM:
    case kAspectPanneau_C_CVSMAVL:
    case kAspectPanneau_F_CVSMARVL:
    case kAspectPanneau_F_CVSARVL:
    case kAspectPanneau_H_CVSMRRAVL:
    case kAspectPanneau_H_CVSRRAVL:
    case kAspectPanneau_H_CVSMRRARVL:
    case kAspectPanneau_K_CVM:
      setAspect(idSignal, kAspectFeu_CV);
      break;
      // 1 feu allumé
    case kAspectPanneau_ID2_ID2:
    case kAspectPanneau_ID3_ID3:
    case kAspectPanneau_ID4_ID4:
    case kAspectPanneau_ID5_ID5:
    case kAspectPanneau_ID6_ID6:
      setAspect(idSignal, kAspectFeu_ID1);
      break;
      // Eteint
    default:
      setAspect(idSignal, kAspectFeu_Off);
      break;
  }

  Serial.println(F("fin onCommandReset"));

  ICSC.send(station, 'R', "OK\0");
}

// Format commande : kCommandSetAspect (S)
// Param1   int   idSignal (> 0)
// Param2   int   aspect
//
// Positionne le signal sur l'aspect demandé.
void onCommandSetAspect(unsigned char station, char command, unsigned char len, char *data)
{
  int readIndex = 0;

  Serial.println(F("deb onCommandSetAspect"));

  byte idSignal = (int) data[readIndex++];
  byte aspectSignal = (int) data[readIndex++];

  setAspect(idSignal, aspectSignal);

  Serial.println(F("fin onCommandSetAspect"));

  ICSC.send(station, 'R', "OK\0");
}

// ------------------  F O N C T I O N S  -----------------------

// Retourne pour un identifiant donné l'index dans le tableau des identifiants des signaux.
// Retourne 255 si non trouvé.
byte getIndexForIdSignal(byte idSignal)
{
  for(int i = 0; i < kNbAspectsSignaux; i++)
  {
    if(gIdSignauxArray[i] == idSignal)
    {
      return i;
    }
  }

  return 255;
}

// Positionne un aspect pour les feux d'un signal.
void setAspect(byte idSignal, byte aspectFeux)
{
  Serial.print(F("deb setAspect : "));
  Serial.println(idSignal);

  byte indexIdSignal = getIndexForIdSignal(idSignal);

  if(indexIdSignal == 255)
  {
    Serial.print(F("error setAspect : id signal non valide : "));
    Serial.println(idSignal);
    return;
  }

  int aspectPanneau = (int) gAspectsSignauxArray[indexIdSignal];

  int addr1 = (int) (&kAspectPanneau_Array);
  int addr2 = (int) pgm_read_byte_near(addr1 + 2 * aspectPanneau);
  int addr3 = (int) pgm_read_byte_near(addr1 + 2 * aspectPanneau + 1);
  int addrAspectPanneau = (addr3 << 8) + addr2;

  int i = 0;

  for(i = 0; i < kNbPortsSortiesFeux; i++)
  {
    /*
     Serial.print("Ports sortie id signal[");
     Serial.print(i);
     Serial.print("] : ");
     Serial.println(gPortsSortiesIdSignauxArray[i]);
     /**/
    if(gPortsSortiesIdSignauxArray[i] == idSignal)
    {
      int nbFeux = kNbFeuxSignal600[aspectPanneau];

      //int addr11 = (int) (&kAspectPanneau_Array);
      int addr21 = (int) pgm_read_byte_near(addrAspectPanneau + 2 * aspectFeux);
      int addr31 = (int) pgm_read_byte_near(addrAspectPanneau + 2 * aspectFeux + 1);
      int addrAspectFeu = (addr31 << 8) + addr21;

      for(int j = 0; j < nbFeux; j++)
      {
        byte etatFeu = kEtatFeuxEteint;

        if(aspectFeux != kAspectFeu_Off)
        {
          etatFeu = (int)pgm_read_byte_near(addrAspectFeu + j);
        }

        switch(etatFeu)
        {
          case kEtatFeuxEteint:
            //Serial.print("Eteindre feu ");
            //Serial.println(i + j + kFirstDigitalPort);
            digitalWrite(i + j + kFirstDigitalPort, LOW);
            break;
          case kEtatFeuxCligntotant:
            //Serial.print("Clignoter feu ");
            //Serial.println(i + j + kFirstDigitalPort);
            digitalWrite(i + j + kFirstDigitalPort, HIGH);
            break;
          case kEtatFeuxAllume:
            //Serial.print("Allumer feu ");
            //Serial.println(i + j + kFirstDigitalPort);
            digitalWrite(i + j + kFirstDigitalPort, HIGH);
            break;
          default:
            //Serial.print("Eteindre (defaut) feu ");
            //Serial.println(i + j + kFirstDigitalPort);
            digitalWrite(i + j + kFirstDigitalPort, LOW);
        }

        // Met à jour la table des états des feux
        gEtatFeuxArray[i+j] = etatFeu;
      }

      break;
    }
  }

  if(i == kNbPortsSortiesFeux)
  {
    Serial.print("error setAspect : ");
    Serial.print(aspectPanneau);
    Serial.print(", ");
    Serial.println(aspectFeux);
  }

  Serial.println(F("fin setAspect"));
}

// ------------------  S E T U P  -----------------------

void setup()
{
  // Initialise la communication série avec l'ordinateur
  Serial.begin(9600);
  Serial.println(F("deb Setup"));

  pinMode(SSerialTxControl, OUTPUT);
  digitalWrite(SSerialTxControl, RS485Receive);  // Init Transceiver

  ICSC.begin(2, BUS_BAUD, &RS485Serial, SSerialTxControl);

  //ICSC.registerCommand(ICSC_SYS_PONG, &pinger);
  ICSC.registerCommand('I', &onCommandInit);
  ICSC.registerCommand('R', &onCommandReset);
  ICSC.registerCommand('S', &onCommandSetAspect);

  // Initialise l'état du clignotement
  gBlinkingState = false;

  // Positionne les sorties feux à la valeur 'LOW'
  for(int i = 0; i < kNbPortsSortiesFeux; i++)
  {
    pinMode(i + kFirstDigitalPort, OUTPUT);
    digitalWrite(i + kFirstDigitalPort, LOW);
  }

  noInterrupts();

  // Initialise le Timer 2 pour déclencher les interruptions à intervalle régulier
  TCCR2A = 0;          //default
  TCCR2B = 0b00000110; // clk/256 est incrémenté toutes les 16us
  TIMSK2 = 0b00000001; // TOIE2
  TCNT2  = 6;          // 256 - 6 --> 250 x 16us = 4 ms

  interrupts();

  Serial.println(F("fin Setup"));
}

// ------------------  L O O P  -----------------------

void loop()
{
  ICSC.process();
}

// ------------------  OUTILS  -----------------------

// Gére le clignotement des feux
void blink()
{
  // Inverse l'état du clignotement
  if(gBlinkingState)
  {
    gBlinkingState = false;
  }
  else
  {
    gBlinkingState = true;
  }

  // Change l'état des feux clignotants
  for(int i = 0; i < kNbPortsSortiesFeux; i++)
  {
    if(gEtatFeuxArray[i] == kEtatFeuxCligntotant)
    {
      if(gBlinkingState)
      {
        digitalWrite(i + kFirstDigitalPort, LOW);
      }
      else
      {
        digitalWrite(i + kFirstDigitalPort, HIGH);
      }
    }
  }
}

// Routine d'interruption du timer
ISR (TIMER2_OVF_vect)
{
  // 256 - 6 --> 250 x 16us = 4 ms
  // Recharge le timer pour que la prochaine interruption se déclenche dans 4 ms
  TCNT2 = 6;

  if(kCompteurInterruption++ == kDefautInterval)
  {
    // 120 * 4ms = 480 ms - la Led est allumée 480 ms et éteinte 480 ms
    kCompteurInterruption = 0;
    blink();
  }
}

Pour info, l'IDE Arduino est passé à la version 1.6.5.

La prochaine étape sera de vous proposer un module qui envoie au PC les signaux générés par des ILS activés par le passage d'un convoi.
J'avais aussi prévu un module pour mouvoir des servo-moteurs déplaçant des aiguilles, mais Zebulon91 l'a déjà très bien fait.

A+

Notix
notix
 
Messages: 30
Inscrit le: Lun 09 Mars 2015, 22:52
Echelle pratiquée: N

Re: Pilote de signaux lumineux

Publié: Dim 15 Nov 2015, 00:46 
Un petit schéma pour montrer à quoi le module ressemble :
Image
J'ai mis 16 sorties pour les signaux en exploitant également les sorties analogiques.
Le port JP4 sert à la programmation de l'Arduino mais aussi à simplement alimenter le module en exploitation (pins 4 et 5/6).
notix
 
Messages: 30
Inscrit le: Lun 09 Mars 2015, 22:52
Echelle pratiquée: N

Re: Pilote de signaux lumineux

Publié: Dim 15 Nov 2015, 10:21 
Pour augmenter le nombre de signaux gérés par une carte, il est possible de multiplexer
Avatar de l’utilisateur
PIC18F
Bavard
 
Messages: 72
Inscrit le: Dim 01 Jan 2012, 16:50
Localisation: LE HAVRE (Seine Maririme)
Âge: 64
Echelle pratiquée: HO
Prénom: Philippe
Club: AMHA

Re: Pilote de signaux lumineux

Publié: Dim 15 Nov 2015, 11:22 
Oui, c'est ce que j'avais prévu quand je me suis lancé là dedans. Mais le prix des composants et la complexification du montage (bus IC2 et bus de sortie à 16 pistes pour chaque multiplexeur) étant tels qu'il vaut mieux multiplier le nombre d'Arduino (2 € pièce, 0,75 € pour le module RS-485, "peanuts" pour les connecteurs).
16 sorties permettent de gérer au moins 2 (à 6/7 feux), et jusqu'à 4 (à 4 feux) ou 5 signaux (à 3 feux). Ça permet souvent de se contenter d'un ou deux modules par intersection.
Voilà à quoi ressemble le typon du montage présenté ci-dessus:
Image
Il tient sur une plaque de 5 cm sur 5,5 cm, et semble encore "câblable" sur une plaque de prototypage à pastilles. Enfin ça il faut encore que je l'essaie :D
notix
 
Messages: 30
Inscrit le: Lun 09 Mars 2015, 22:52
Echelle pratiquée: N

Re: Pilote de signaux lumineux

Publié: Dim 15 Nov 2015, 17:54 
En fait on peut faire le multiplexage logiciellement donc pas de surcoût. Il suffi d'attribuer un port pour chaque aspect et d'utiliser un timer convenablement réglé pour balayer les masses communes des leds de chaque signal.
Avatar de l’utilisateur
PIC18F
Bavard
 
Messages: 72
Inscrit le: Dim 01 Jan 2012, 16:50
Localisation: LE HAVRE (Seine Maririme)
Âge: 64
Echelle pratiquée: HO
Prénom: Philippe
Club: AMHA

Re: Pilote de signaux lumineux

Publié: Dim 15 Nov 2015, 18:43 
Si je comprends bien ton idée : un signal comprend jusqu'à 9 feux, auxquels on peut ajouter un 10e avec l'oeilleton (pas prévu dans mon code actuel mais qui peut être facilement ajouté). Donc avec les 16 sorties, on peut connecter en plus des 10 sorties 'feux', 6 sorties 'base commune' pour 6 signaux.
Le temps d'alimentation est découpé en n périodes (n = nombre de signaux) et durant chacune d'elles on positionne une sortie 'base commune' à 5V (ou à 0V suivant le câblage) pour alimenter un des n signaux et les sorties 'feux" suivant l'aspect de ce signal. Le clignotement imposé par ce type d'alimentation ne devant pas être visible. C'est bien cela ?

Ce système demande juste de revoir la conception des broches de sorties. On peut dans une première solution disposer sur les 10 sorties 'feux' de 6 broches et d'une pour chacune des sorties 'base commune'.
Une autre solution de câblage serait d'attribuer une broche par sortie et ainsi de connecter les entrées 'feux' des signaux en "série", alors qu'une sortie 'base commune' commande chacun des signaux. Ce seconde solution est plus générique car elle permet de faire varier la répartition sorties 'feux' et sorties 'bases communes'. Si par exemple on ne souhaite commander que des signaux à 4 feux, on pourrait commander 12 signaux avec un seul module.

A tester ...
notix
 
Messages: 30
Inscrit le: Lun 09 Mars 2015, 22:52
Echelle pratiquée: N

Re: Pilote de signaux lumineux

Publié: Dim 15 Nov 2015, 20:06 
Bonjour

J'utilise le multiplexing, pour contrôler 4 afficheurs LED (7 segments + point = 8 leds), il y a 8 sorties pour les leds et 4 sorties pour les communs (cela fait 8*4=32 leds). On peut monter jusqu'a 64 leds avec 16 sorties. Mais on peut répartir autrement : 7*9, 6*10, 5*11, ...

Par exemple j'ai des signaux lumineux : des carrés a 6 feux et des carrés violet a deux feux, en groupant par paire (carré + carré violet) cela permet d'avoir 8 paires donc 16 signaux ! en pratique j'ai des expandeurs de sorties I2C.

Pierre
Avatar de l’utilisateur
Pierre59
Papotier
 
Messages: 147
Inscrit le: Dim 07 Mars 2010, 10:17
Localisation: Villeneuve d'Ascq (59650)
Âge: 75
Echelle pratiquée: HO
Club: Lille Modélisme

Re: Pilote de signaux lumineux

Publié: Dim 15 Nov 2015, 20:33 
Oui c'est bien cela. Moi perso j'utilise une nappe (à 9 ou 10 fils dans ton cas) qui distribue tout les signaux et le programme n'a à gérer dans le balaye que le couple (aspect, signal).
Pour le débordement du timer, on estime la persistance rétinienne à 50 ms ton timer devra donc déborder toutes les 50/nbSignal soit toutes les 8 ms si ta carte gère 6 signaux.
Si tu as plus de signaux à gérer, je pense qu'il serait préférable de choisir un µC possédant plus de broches.
Avatar de l’utilisateur
PIC18F
Bavard
 
Messages: 72
Inscrit le: Dim 01 Jan 2012, 16:50
Localisation: LE HAVRE (Seine Maririme)
Âge: 64
Echelle pratiquée: HO
Prénom: Philippe
Club: AMHA

Re: Pilote de signaux lumineux

Publié: Lun 16 Nov 2015, 10:02 
Bonjour

En plus je met sur les sorties des communs un transistor (BS170) pour ne pas tirer trop de courant sur la sortie.

Pierre
Avatar de l’utilisateur
Pierre59
Papotier
 
Messages: 147
Inscrit le: Dim 07 Mars 2010, 10:17
Localisation: Villeneuve d'Ascq (59650)
Âge: 75
Echelle pratiquée: HO
Club: Lille Modélisme

Re: Pilote de signaux lumineux

Publié: Lun 16 Nov 2015, 11:30 
Une petite remarque :

La panneau le plus complet en signalisation SNCF est le panneau H comportant 9 feux dont le R et RR qui regroupent chacun 2 feux. Pour commander un tel signal il suffit donc d'avoir 7 ports plus un si on prend en compte l’œilleton soit un maximum de 8 ports. Ta carte signalo doit donc pouvoir gérer 8 signaux complets.

@Pierre : bien sur du point de vue montage électronique, il faut transistor plus résistance à la sortie de chaque port commandant une led. La valeur de cette résistance devra tenir compte de la fréquence du multiplexage pour obtenir l’intensité lumineuse de chaque feux.
Avatar de l’utilisateur
PIC18F
Bavard
 
Messages: 72
Inscrit le: Dim 01 Jan 2012, 16:50
Localisation: LE HAVRE (Seine Maririme)
Âge: 64
Echelle pratiquée: HO
Prénom: Philippe
Club: AMHA

Re: Pilote de signaux lumineux

Publié: Lun 16 Nov 2015, 12:54 
Bonjour

Je ne met un transistor que sur les sorties communes, pas sur les sorties leds, bien sur il faut une résistance sur les sorties leds.

Pierre
Avatar de l’utilisateur
Pierre59
Papotier
 
Messages: 147
Inscrit le: Dim 07 Mars 2010, 10:17
Localisation: Villeneuve d'Ascq (59650)
Âge: 75
Echelle pratiquée: HO
Club: Lille Modélisme

Suivant

Retour vers Arduino

Qui est en ligne ?

Utilisateur(s) parcourant actuellement ce forum : Aucun utilisateur inscrit et 2 invité(s)