L'interface avec le Contrôleur par liaison série ou ethernetNous avons vu dans l'épisode précédent comment la BaseStation fabrique ses paquets DCC à partir de commandes sous forme textuelle qui sont passées à des fonctions de l'objet RegisterList (qui n'est pas une classe mais une structure, mais bon..).
Les onglets
SerialCommand.h et
SerialCommand.cpp vont se charger d'assurer l'interface entre le canal de communication (série ou ethernet) et les fonctions de
RegisterList.
SerialCommand.h déclare une structure SerialCommand :
struct SerialCommand{
static char commandString[MAX_COMMAND_LENGTH+1];
static volatile RegisterList *mRegs, *pRegs;
static CurrentMonitor *mMonitor;
static void init(volatile RegisterList *, volatile RegisterList *, CurrentMonitor *);
static void parse(char *);
static void process();
}; // SerialCommand
On y trouve pas mal de choses importantes :
- un tableau de caractères commandString: c'est la commande
- deux RegisterList, l'une mRegs pour la voie principale et l'autre pRegs pour la voie de programmation. C'est ce qui permet d'appeler simplement les fonctions de RegisterList vues précédemment
- une structure CurrentMonitor qui permet d'avoir une mesure de courant
- une fonction parse qui se chargera du décodage des commandString et qui va donc appeler les fonctions de RegisterList
- une fonction process qui va lire l'interface série ou ethernet et va passer la commande reçue à la fonction parse
Le code d'exécution se trouve donc dans l'onglet
SerialCommand.cpp :
La fonction
process ne fait pas grand chose d'autre que d'enregistrer les caractères compris entre "<" et ">" et de passer le résultat à
parse.
La fonction
parse analyse le 1er caractère de la commande pour envoyer le reste des caractères (sans cette 1ère lettre) vers la bonne fonction de
RegisterList"t" pour setThrottle (commande DCC)
"f" pour setFunction (commande DCC)
"a" pour setAccessory (commande DCC)
"T" pour une commande d'aiguille directe (commande non DCC)
"Z" pour une commande d'une pin de sortie de l'Arduino (commande non DCC)
"S" pour lire l'état d'un capteur relié à une pin d'entrée de l'Arduino (commande non DCC)
"Q" pour lire l'état de tous les capteurs à la fois (commande non DCC)
"w" pour programmer un CV sur la voie principale en écriture seule sans vérification (commande DCC)
"b" pour programmer un bit particulier d'un CV sur la voie principale en écriture seule sans vérification (commande DCC)
"W" pour programmer un CV sur la voie de programmation avec vérification donc réponse du décodeur (commande DCC)
"B" pour programmer un bit particulier d'un CV sur la voie de programmation avec vérification donc réponse du décodeur (commande DCC)
"R" pour lire un CV sur la voie de programmation avec réponse du décodeur évidemment (commande DCC)
"1" pour appliquer le signal DCC sur les rails (power ON)
"0" pour couper le signal DCC sur les rails (power OFF)
"c" pour lire la valeur du courant sur la voie principale (commande non DCC)
"s" pour envoyer sur la liaison un état du système (commande non DCC)
"E" pour sauvegarder les valeurs des aiguilles et capteurs en EEPROM (commande non DCC)
"e" pour effacer les valeurs enregistrées dans l'EEPROM (commande non DCC)
A cela s'ajoute quelques utilitaires comme :
"M" pour envoyer un paquet DCC de 2 à 5 octets sur la voie principale (commande DCC)
"P" pour envoyer un paquet DCC de 2 à 5 octets sur la voie de programmation (commande DCC)
Si on s'arrête un instant pour regarder la loop() dans DCCp_Uno, on y lit :
///////////////////////////////////////////////////////////////////////////////
// MAIN ARDUINO LOOP
///////////////////////////////////////////////////////////////////////////////
void loop(){
SerialCommand::process(); // check for, and process, and new serial commands
if(CurrentMonitor::checkTime()){ // if sufficient time has elapsed since last update, check current draw on Main and Program Tracks
mainMonitor.check();
progMonitor.check();
}
Sensor::check(); // check sensors for activate/de-activate
} // loop
Ce qui veut dire que le programme BaseStation ne fait rien d'autre que de traiter les commandes ci-dessus venues de la liaison série ou ethernet, ainsi que de surveiller le courant en cas de court-circuit et de tester les capteurs, ce qui se traduit par l'envoi de messages vers la liaison série ou ethernet.
Il ne fait donc rien de lui-même, mais cela nous ouvre la voie vers des réalisations personnelles intéressantes et sophistiquées, avec une telle belle boite à outils.
Mais revenons à la fonction parse :On constate qu'il y a deux familles de fonctions :
- Celles qui sont conformes à la norme DCC NMRA : t, f, a, w, b, W, B, R, M et P
- Celles qui sont spécifiques à BaseStation et ne concernent pas la norme DCC : T, Z, S, Q, 1, 0, c, s, E, e
Les fonctions conformes à la norme DCC sont réalisées par RegisterList comme on l'a vu précédemment. Mais RegisterList ne s'occupe que des fonctions propres au DCC puisqu'il se charge de la gestion des paquets DCC envoyés aux rails.
Alors, pour les autres fonctions, il existe des onglets spécifiques qui traitent des fonctions non DCC :
Les onglets Accessories.h et Accessories.cpp s'occupent des aiguilles en commande directe par l'Arduino (et non en DCC via un décodeur d'accessoire, ce que BaseStation supporte aussi via la fonction setAccessory). Elles gèrent les structures :
struct TurnoutData {
byte tStatus;
byte subAddress;
int id;
int address;
};
struct Turnout{
static Turnout *firstTurnout;
int num;
struct TurnoutData data;
Turnout *nextTurnout;
void activate(int s);
static void parse(char *c);
static Turnout* get(int);
static void remove(int);
static void load();
static void store();
static Turnout *create(int, int, int, int=0);
static void show(int=0);
}; // Turnout
On y retrouve la fonction
parse qui fait le lien avec la fonction
parse de
SerialCommand, pour permettre au contrôleur de commander les aiguilles de cette façon.
Les onglets Outputs.h et Outputs.cpp s'occupent des pins disponibles de l'Arduino qui peuvent être programmées en sortie. Elles gère les structures :
struct OutputData {
byte oStatus;
int id;
byte pin;
byte iFlag;
};
struct Output{
static Output *firstOutput;
int num;
struct OutputData data;
Output *nextOutput;
void activate(int s);
static void parse(char *c);
static Output* get(int);
static void remove(int);
static void load();
static void store();
static Output *create(int, int, int, int=0);
static void show(int=0);
}; // Output
On y retrouve aussi la fonction
parse qui fait le lien avec la fonction
parse de
SerialCommand, pour permettre au contrôleur de commander les pins de sortie de l'Arduino.
Les onglets Sensor.h et Sensor.cpp s'occupent des pins disponibles de l'Arduino qui peuvent être programmées en entrée pour lire des états de capteurs. Elles gèrent les structures :
struct SensorData {
int snum;
byte pin;
byte pullUp;
};
struct Sensor{
static Sensor *firstSensor;
SensorData data;
boolean active;
float signal;
Sensor *nextSensor;
static void load();
static void store();
static Sensor *create(int, int, int, int=0);
static Sensor* get(int);
static void remove(int);
static void show();
static void status();
static void parse(char *c);
static void check();
}; // Sensor
On y retrouve encore la fonction
parse qui fait le lien avec la fonction
parse de
SerialCommand, pour permettre au contrôleur de lire les états des capteurs de rétrosignalisation.
De plus, la fonction
check est scrutée par la loop de façon à informer le contrôleur au plus vite des changements d'état des capteurs.
Bien entendu je ne vais pas rentrer dans l'analyse des fonctions de ces 3 dernières familles, mais une série de conclusions d'imposent :
1) BaseStation est un logiciel très complet rédigé de façon propre et claire donc fiable (bien que je n'ai pas tout testé, loin s'en faut, mais cela fleure bon).
2) Sa modularité en fait un logiciel adaptable : on peut enlever et ajouter des fonctions, on peut réaliser certaines fonctions différemment, mais il convient de respecter le noyau principal qui est le moteur DCC.
3) BaseStation est principalement destiné à être piloté par un logiciel de contrôle sur PC. Gregg a écrit Controleur en Processing (version récente) : on peut l'apprécier ou pas. D'autres ont réalisé les adaptations dans JMRI (dernières versions seulement).
N'ayant pas pu installer de version récente dans mon MacBook, j'ai réalisé une petite manette perso que l'on peut voir en document joint.
4) Mais rien n'empêche d'ajouter d'autres fonctions, notamment un automate de gestion de circulation, une gestion de Bal, une gestion de signaux, etc..
Les modèles de programmation des onglets peuvent être une bonne base pour ces extensions. Un bel automate peut aussi trouver sa place dans la loop ou appelé depuis la loop.
Je vous invite maintenant à partager vos impressions et expériences sur DCC++.