L’objectif de cet article est de proposer une solution simple à la réalisation d’une rétro-signalisation sur la base d’Arduino UNO/Nano et MEGA. La solution est un petit programme qui permet rapidement de créer des modules de détection à 16 entrées sur Arduino UNO/Nano ou 64 entrées sur Arduino MEGA. Chaque Arduino peut être relié à d’autres afin d’augmenter le nombre de modules. Cette solution est basée sur le projet de rudymodelrailway [1].
La rétro-signalisation sur Arduino
. Par :
. URL : https://www.locoduino.org/spip.php?article138La rétro-signalisation
La rétro-signalisation consiste à renvoyer des événements du réseau vers un TCO ou une centrale. Ces événements sont liés à des accessoires (position d’un aiguillage, d’une animation) ou de l’occupation d’une portion d’un réseau dans le cas d’un cantonnement. La norme DCC intègre uniquement la communication d’information de la centrale vers les détecteurs. Il n’existe pas de norme en ce qui concerne la rétro-signalisation. Plusieurs solutions existent, toutes propriétaires, qui sont devenus de fait des standards comme par exemple Loconet de Digitrax ou le bus S88 de Marklin. A noter une évolution avec l’apparition des transpondeurs qui permettent d’avoir une communication bidirectionnelle en DCC. Ce type de communication ne fait pas partie de la norme NMRA mais est plutôt une surcouche de cette norme. En particulier la firme Lenz a créé une technologie appelée Railcom qui commence à être adoptée par plusieurs constructeurs (Lenz, ESU, Uhlenbrock, Tams Elektronik). Il faut cependant que la centrale et le décodeur de la locomotive intègrent cette technologie. Dans le cadre de cet article nous ne parlerons que de la solution bus S88, pas forcément la meilleure mais la plus courante.
La détection
La détection d’un événement sur le réseau se fait par des capteurs (pédale de voie, ILS, consommation de courant, fin de course d’un aiguillage). Il faut donc d’une part un petit circuit capteur qui génère l’événement et un décodeur qui permet de renvoyer cet événement à la centrale ou au TCO. Nous prendrons comme exemple dans cet article un capteur par consommation de courant et un décodeur basé sur le bus S88. Le petit montage suivant, proposé par jacques Veillard sur le site train35 [2] est une bonne solution pour détecter la présence d’un convoi sur un canton et adapté la tension à celle acceptable par un Arduino (NB : prendre soin de bien relier le + et - du pont de diode).
Le bus S88
Le bus S88 consiste à relier par chaînage les modules entre eux. Ce n’est pas le meilleur bus mais il reste encore celui le plus utilisé. Il fonctionne sous une tension comprise entre 4,5 et 5v (donc tout à fait compatible avec nos Arduino). La longueur maximale entre deux modules ne doit pas dépasser 5m, encore faut-il veiller à ce que le câble du bus ne soit pas trop près d’environnements perturbateurs (courant de traction, 220v). Le nombre maximum de capteurs sur un bus S88 est limité à 512. L’adressage des capteurs est simple. Chaque module possède 8 ou 16 capteurs. On commence la numérotation par le premier module (valeur = 1) et son premier capteur (adresse 1:1) puis on incrémente de 1 à chaque capteur suivant dans le chaînage jusqu’à la valeur 16 (1:16) , puis on passe à 2 (2:1) et ainsi de suite modulo 16. La photo suivante provenant de LDT-infoCenter présente le principe de ce chaînage.
Le fonctionnement est simple. Chaque module est un registre à décalage de 8 ou 16 positions. La centrale génère un signal "PS" qui déclenche le comptage de l’horloge. Tous les modules reçoivent le signal "PS" et les fronts de l’horloge. Ils sont reliés entre eux par un câble de sortie et un d’entrée. On obtient ainsi un décalage de buffers à chaque top d’horloge.
Le bus S88 est un connecteur possédant 6 connexions dont la signification est la suivante :
- broche 1 : data (blanc) - sortie des données vers un autre module ou vers la centrale
- broche 2 : GND (marron) - GND
- broche 3 : clock (vert) – top de l’horloge
- broche 4 : PS (jaune) – signal PS démarrage de l’horloge
- broche 5 : Reset (violet) - reset du comptage
- broche 6 : V+ (rose) – alimentation 4,5 à 5V
Le bus S88 avec un Arduino
La solution proposée ici se base sur le travail de Ruud Boer [3]. Le principe est le suivant, chaque Arduino correspond à un module pouvant posséder 16 capteurs pour un UNO/Nano ou 64 capteurs pour un MEGA. Le chaînage des Arduino se fait de la manière suivante :
- Tous les Arduino doivent être connectés aux broches 2, 3,4 et 6 du bus S88
- Le premier Arduino connecte sa broche 1 à la broche 1 du bus S88
- Les autres Arduino connectent leur broche 1 à la broche 0 de l’Arduino précédent.
L’interface avec le bus S88 se fait sur les broches 0 à 3, Gnd et Vin de la manière suivante :
- broche 0 (rx) : entrée depuis un autre Arduino
- broche 1 (tx) : sortie vers la station de contrôle ou vers un autre Arduino
- broche 2 (int0) : interruption de l’horloge du bus S88 (sur tous les Arduino du chaînage)
- broche 3 (int1) : interruption du signal PS du bus S88 (sur tous les Arduino du chaînage)
- Gnd : masse commune (sur tous les Arduino du chaînage)
- Vin : alimentation S88 (sur tous les Arduino du chaînage)
Rétro-signalisation sur UNO/Nano
Le programme se limite à inclure la bibliothèque UNO_S88 et à faire un setup avec le nombre de détecteurs souhaités (8 ou 16). Les broches utilisées sont les suivantes :
- Broche 0 : entrée des états des autres Arduino chaînés
- Broche 1 : sortie des états vers la station de contrôle ou vers un autre Arduino
- Broche 2 : interruption de l’horloge S88
- Broche 3 : interruption PS du S88
- Pour 8 détecteurs les broches 4 à 11 sont utilisées
- Pour 16 détecteurs toutes les broches sont utilisées (4 à A5)
Le programme Arduino UNO/Nano
#include <UNO_S88.h> // la librairie S88 pour les UNO/Nano
void setup() {
S88_Setup(16); // on donne le nombre capteur 8 ou 16
}
void loop() {
S88_Loop(); // boucle sur les capteurs
}
et la bibliothèque associée
Le fichier entête UNO_S88.h
#include <Arduino.h>
void S88_Setup(int nb_sensors);
void S88_Loop();
et le fichier UNO_S88.cpp
#include "UNO_S88.h"
#include <Arduino.h>
const byte clockS88 = 2; // horloge du bus S88 pin = 2
int clockCounter=0; // compteur de tops horloge
const byte PSS88 = 3; // signal PS du bus S88 pin = 3
long loopCounter=0; // reset proper à l’ECOS
const byte dataIn=0; // entrée des données depuis un autre Arduino dans
// la chaîne S88 pin = 0
const byte dataOut=1; // sortie des données vers un autre Arduino dans
// la chaîne ou vers la centrale pin=1
unsigned int sensors=0; // tampon de 16 bits pour les capteurs
unsigned int data=0xffff; // le registre à décalage
int nbsensors; // nombre de capteurs 8 or 16
int beginPin = 4; // première broche utilisée pour les capteurs
int endPin8 = 12; // dernière broche pour 8 capteurs
int endPin16 = 21; // dernière broche pour 16 capteurs
int endPin; // variable locale
// routine d’interruption du signal PS
// (déclenchement d’un nouveau cycle d’horloge)
void PS() {
clockCounter=0; // on remet le compteur à zéro
data=sensors; // on vide le tampon des capteurs dans le
// registre à décalage
sensors=0; // on remet à zéro le tampon des capteurs
loopCounter++; // on incrémente le nombre de top d’horloge
}
// routine d’interruption de l’horloge S88
void clock() {
digitalWrite(dataOut,bitRead(data,clockCounter)); // on décale 1 bit en sortie
delayMicroseconds(16); // délai pour le décalage
bitWrite(data,clockCounter,digitalRead(dataIn)); // on décale 1 bit en entrée
clockCounter =(clockCounter +1) % nbsensors; // modulo le nombre de capteurs
// (8 ou 16)
}
// le setup S88
void S88_Setup(int nb_sensors) {
nbsensors = nb_sensors; // nombre de capteurs désirés (8 ou 16)
if (nbsensors == 8) { // MAJ des broches concernées
endPin = endPin8;
}
else {
endPin = endPin16;
}
pinMode(clockS88, INPUT_PULLUP); // init de la broche pour l’horloge
attachInterrupt(0,clock,RISING); // horloge sur int 0 sur la broche 2
pinMode(PSS88, INPUT_PULLUP); // init de la broche du signal PS
attachInterrupt(1,PS,RISING); // PS sur int1 sur la broche 3
pinMode(dataIn,INPUT_PULLUP); // pin 0 = entrée des données depuis un
// autre Arduino
pinMode(dataOut, OUTPUT); // pin 1 = sortie des données vers la
// centrale ou vers un autre Arduino
// dans le chaînage S88
for (int i = beginPin; i< endPin;i++) {
pinMode(i,INPUT_PULLUP); // init des broches des capteurs
}
}
// la boucle
void S88_Loop() {
if (loopCounter==20) {
bitSet(sensors,0); // reset des tampons des capteurs pour l’ECOS
}
for (int i = 4; i<endPin; i++) { // MAJ des capteurs
if (!digitalRead(i)) {
bitSet(sensors,i-4);
}
}
}
Rétro-signalisation sur MEGA
La différence avec les cartes UNO/Nano est que le MEGA représente à lui seul 4 modules S88 de 16 capteurs en interne. Le grand avantage est de réduire considérablement le câblage, par quatre par rapport à un UNO et limite grandement la demande en courant depuis le bus S88. Le chaînage de ces quatre modules devient interne au MEGA. Le module 1 est le premier et le module 4 le dernier, un MEGA à lui seul peut donc intégrer 64 capteurs sur quatre modules internes. Cela n’empêche pas d’avoir des chaînages externes avec d’autres Arduino (UNO, Nano ou MEGA). La limite étant de 512 capteurs pour un bus S88, il suffit donc de quatre MEGA pour remplir le bus.
Tout comme précédemment le programme se limite à inclure la bibliothèque MEGA_S88 et à faire un setup.
Les broches utilisées sont les suivantes :
- Broche 0 : entrée des états des autres Arduino chaînés
- Broche 1 : sortie des états vers la station de contrôle ou vers un autre Arduino
- Broche 2 : interruption de l’horloge S88
- Broche 3 : interruption PS du S88
- Les broches 20 et 21 sont volontairement inutilisées pour des extensions avec le DCC (interruption sur broche 20 et led de contrôle sur broche 21)
Les modules sont regroupés en quatre groupes de 16 détecteurs dont les broches sont les suivantes :
- Module1 = 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19
- Module2 = 22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37
- Module3 = 38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53
- Module4 = 54(A0),55(A1),56(A2),57(A3),58(A4),59(A5),60(A6),61(A7),62(A8),63(A9),64(A10),
- 65(A11), 66(A12), 67(A13), 68(A14), 69(A15)
Le programme permet d’instancier 1 à 4 modules de 16 détecteurs dans un ordre séquentiel partant du module 1 jusqu’au module 4.
Le programme Arduino MEGA
#include <MEGA_S88.h> // librairie S88 pour le MEGA
void setup()
{
S88_Setup(2); // on donne le nombre de modules souhaités
}
void loop()
{
S88_Loop(); // on boucle
}
et la petite bibliothèque associée
Le fichier d’entête MEGA_S88.h
#include "MEGA_S88.h"
#include <Arduino.h>
// Allocation des broches
const byte dataIn = 0; // entrée des données depuis d’autres Arduino
const byte dataOut = 1; // sortie vers la centrale ou vers d’autres
// Arduino
const byte clockS88 = 2; // int0 pour l’horloge sur la broche 2
const byte PSS88 = 3; // int1 pour le signal PS sur la broche 3
int clockCounter = 0; // compteur des tops horloge
long loopCounter = 0; // reset propre à l’ECOS
// nombre de modules de 16 capteurs
int nbBlocs = 0; // le nombre de module souhaité
// registres à décalage des différents modules
unsigned int data1 = 0xFFFF; // register du module 1
unsigned int data2 = 0xFFFF; // register du module 2
unsigned int data3 = 0xFFFF; // register du module 3
unsigned int data4 = 0xFFFF; // register du module 4
// tampon des capteurs
unsigned int sensors1 = 0; // 16 capteurs du module 1
unsigned int sensors2 = 0; // 16 capteurs du module 2
unsigned int sensors3 = 0; // 16 capteurs du module 3
unsigned int sensors4 = 0; // 16 capteurs du module 4
// internal variable
int i,j;
// routine d’interruption du signal PS
void PS()
{
clockCounter = 0; // RAZ du compteur de top horloge
// il y a au moins un module, le premier le module 1
data1 = sensors1; // on vide le tampon des capteurs
// dans le registre à décalage
sensors1 = 0; // RAZ du tampon des capteurs
switch (nbBlocs)
{
case 2 : { // si deux modules
data2 = sensors2; sensors2 = 0;
break;
}
case 3 : { // si 3
data2 = sensors2; sensors2 = 0;
data3 = sensors3; sensors3 = 0;
break;
}
case 4 : { // si 4
data2 = sensors2; sensors2 = 0;
data3 = sensors3; sensors3 = 0;
data4 = sensors4; sensors4 = 0;
break;
}
}
loopCounter++; // for reset management
}
// clock signal from S88
void clock()
{
// exit bits to next S88 in chain
digitalWrite(dataOut, bitRead(data1,clockCounter));
switch (nbBlocs) // data buffer shift
{
case 1 : {
bitWrite(data1, clockCounter, digitalRead(dataIn));
break;
}
case 2 : {
bitWrite(data1, clockCounter, bitRead(data2,clockCounter));
bitWrite(data2, clockCounter, digitalRead(dataIn));
break;
}
case 3 : {
bitWrite(data1, clockCounter, bitRead(data2,clockCounter));
bitWrite(data2, clockCounter, bitRead(data3,clockCounter));
bitWrite(data3, clockCounter, digitalRead(dataIn));
break;
}
case 4 : {
bitWrite(data1, clockCounter, bitRead(data2,clockCounter));
bitWrite(data2, clockCounter, bitRead(data3,clockCounter));
bitWrite(data3, clockCounter, bitRead(data4,clockCounter));
bitWrite(data4, clockCounter, digitalRead(dataIn));
break;
}
}
clockCounter = (clockCounter + 1) % 16; // bits 0 to 15
}
// setup
void S88_Setup(int NbBlocs)
{
nbBlocs = NbBlocs;
for (i=4; i<20; i++) { // the first bloc
pinMode(i,INPUT_PULLUP);
}
switch (nbBlocs)
{
case 2 : { // the second
for (i=22; i<38; i++) {
pinMode(i,INPUT_PULLUP);
}
break;
}
case 3 : { // the third
for (i=22; i<54; i++) {
pinMode(i,INPUT_PULLUP);
}
break;
}
case 4 : { // the fourth
for (i=22; i<70; i++) {
pinMode(i,INPUT_PULLUP);
}
break;
}
}
pinMode(clockS88,INPUT_PULLUP); // S88 clock in input
attachInterrupt(0,clock,RISING); // interrupt int0
pinMode(PSS88,INPUT_PULLUP); // S88 PS in input
attachInterrupt(1,PS,RISING); // interrupt int1
pinMode(dataOut, OUTPUT); // init data out to other Arduino
// or control board
digitalWrite(dataOut,LOW); // led off
pinMode(dataIn,INPUT_PULLUP); // data in from other Arduino
}
// loop
void S88_Loop()
{
// reset management for ECOS
if (loopCounter == 20) {
bitSet(sensors1,0);
switch (nbBlocs)
{
case 2 : {
bitSet(sensors2,0);
break;
}
case 3 : {
bitSet(sensors3,0);
break;
}
case 4 : {
bitSet(sensors4,0);
break;
}
}
}
// update all sensors by group
for (i=4; i<20; i++) {
if (!digitalRead(i)) {
bitSet(sensors1,i-4);
}
}
switch (nbBlocs)
{
case 2 : {
for (i=22; i<38; i++) {
if (!digitalRead(i)) {
bitSet(sensors2,i-22);
}
}
break;
}
case 3 : {
for (i=22; i<38; i++) {
if (!digitalRead(i)) {
bitSet(sensors2,i-22);
}
}
for (i=38; i<54; i++) {
if (!digitalRead(i)) {
bitSet(sensors3,i-38);
}
}
break;
}
case 4 : {
for (i=22; i<38; i++) {
if (!digitalRead(i)) {
bitSet(sensors2,i-22);
}
}
for (i=38; i<54; i++) {
if (!digitalRead(i)) {
bitSet(sensors3,i-38);
}
}
for (i=54; i<70; i++) {
if (!digitalRead(i)) {
bitSet(sensors4,i-54);
}
}
break;
}
}
}
Conclusion
En espérant que ma petite contribution répondra aux besoins de certains, cela permet pour tous ceux qui utilisent le bus S88 et qui ne souhaitent pas rentrer dans la complexité du codage de réaliser, de façon simple, rapide et peu chère, une rétro-signalisation avec un grand nombre de capteurs. J’ai testé sur mon ECOS et cela marche très bien et permet à mon logiciel RRTC de prendre les bonnes décisions sur le roulement des trains.
La bibliothèque pour les Nano/Uno
et celle pour les MEGA