Un petit tour de l’avis des membres de mon club m’a fait découvrir une autre réalisation consistant à compter les tours d’essieu d’un wagon, avec un détecteur Hall et un compteur de vélo. Mais c’est trop gros pour du N !
Nous voulions quelque chose de discret, pas cher et précis à la fois, qui soit visible à coté de la console DCC (une ECOS) pour permettre l’étalonnage des trains.
J’ai pris du plaisir à réaliser ce petit montage et j’y ai ajouté quelques astuces qui devraient vous plaire...
Le principe
Comme dans l’article précité, la mesure sera basée sur la mesure du temps qui sépare les passages devant 2 capteurs de position. Le plus petit Arduino, le Pro-Mini suffit à s’acquitter de cette tâche et un afficheur à 4 digits à Led permet de lire la vitesse avec une bonne luminosité.
Les capteurs devront être placés de part et d’autre de la console DCC, là où le charcutage du décor est le moins critique. Il faudra donc un système de réglage de la distance prise en compte dans le logiciel pour le calcul de la vitesse, sans ajouter de clavier, ni obliger de brancher le PC/Mac. Vous allez voir comment !!
La liste des courses
J’ai commencé par une recherche sur eBay, auprès de mes vendeurs favoris (qui sont mentionnés dans l’article Où acheter ?.
Voici mes trouvailles :
Les capteurs de position
Ce circuit tout fait pour 1,41€ [1] se connecte par 3 fils (+5V, masse et signal DO, actif a l’état bas).
Il mesure 1,5 x 3,5 cm et le capteur fait 0,5 x 1 cm. Un trou de 1,5 x 1,5 cm permet de le mettre en place avec le seul capteur TCRT5000L qui dépasse pour se situer en face du bas de caisse des voitures, à environ 1,5 cm.
L’afficheur 4 digits
Ce circuit tout fait pour 3,25€ [2] est équipé d’un circuit TM1637 qui fait tout le travail pour nous. Il se connecte avec seulement 4 fils (+5V, masse, signaux CLK et DIO). Une bibliothèque existe. Elle est fournie avec le code en fin d’article.
L’Arduino Pro Mini
C’est le moins cher, pour quelques € [3] et le plus petit, facile à oublier dans un coin du réseau.
Attention, il y a plusieurs modèles : préférez ceux qui sont marqués "DTR" et "GND" dans les coins.
Il se programme comme les autres à condition de connecter un circuit d’interface USB série, de préférence à base de FT232 (FTDI) dont le driver est déjà présent dans votre système Mac ou PC, donc le port est facilement reconnu par l’IDE Arduino.
L’adaptateur USB série
Cet adaptateur peut servir à programmer des quantités d’Arduino Pro Mini. Un seul suffit, peu cher [4]
Les 6 broches de ce circuit coincident exactement avec les 6 broches sur le coté droit de l’Arduino. Il faudra veuiller à faire coincider la patte "DTR" du Pro Mini avec la patte "DTR" de l’adaptateur USB et les pattes "GND" des 2 cartes aussi.
Les fils de câblage
Pour 0,50€ le cable [5] que je coupe en deux pour relier les 2 capteurs, la prise à 3 contacts allant sur un capteur et les fils à l’autre extrémité sont soudés directement sur l’Arduino.
Au total, la facture s’élève avec peine à 8 € (sans l’adaptateur FT232 que vous devez avoir déjà dans vos stocks), soit environ 10€ avec les quelques fils, soudure, et outils nécessaires.
Le schéma de câblage
En respectant ce schéma, il est impossible de se tromper !
Les 2 détecteurs et l’afficheur utilisent le 5V de l’Arduino (donc à relier à VCC et GND) : j’ai mesuré une consommation totale d’environ 50 mA (bien inférieure au 150 mA maximum que supporte le Pro Mini).
Les sorties des détecteurs sont à relier aux Pins 8 et 9 (ou toutes autres qui vous conviendrait mieux à condition de modifier le programme en conséquence).
L’afficheur utilise les 2 Pins 2 et 3 pour les signaux CLK et DIO respectivement.
Pour l’alimentation, il existe plusieurs options possibles :
Alimenter le montage directement en 5 V avec un petit chargeur pour USB par exemple : On raccordera l’alimentation directement sur les pins 5V et GND de l’Arduino.
Alimenter le montage à partir d’une tension plus élevée, sans excéder 12V. Comme on trouve souvent du 12V (voir un peu plus) sur nos réseaux, notamment pour l’alimentation des accessoires, j’ai pris l’habitude, par méfiance, d’intercaler un régulateur 9V. Dans ce cas il faut se raccorder sur les pins RAW (+9V) et GND.
On trouve de petits régulateurs en kit pour moins de 2€ [6].
Avant d’installer le montage sur le réseau, j’ai réalisé le montage sous forme "volante" comme on dit, j’ai écrit et mis au point le programme et l’ai testé avec un simple batterie LiPo comme source d’énergie.
Cela m’a permis de tester la distance entre les capteurs et les trains, déterminer la distance par rapport à la voie, avant de faire les trous dans le réseau :)))
Remarques pratiques :J’ai pris soin de faire en sorte que tous les éléments soient connectés à l’aide de mini-connecteurs pour pouvoir passer les câbles dans les trous. Attention à bien respecter les couleurs des fils : rouge pour le +5V et noir pour le GND, les signaux pouvant avoir la couleur que vous voulez.
Le programme
Le programme est un automate qui doit se dérouler de façon totalement fiable, dans n’importe quel sens de circulation, selon plusieurs phases :
détecter le passage d’un convoi devant l’un ou l’autre détecteur et initialiser la mesure du temps en enregistrant la valeur du temps système avec millis() ;
détecter le passage du convoi devant l’autre détecteur et enregistrer à nouveau le temps système.
calculer le temps passé T entre les 2 détecteurs et en déduire la vitesse V du train selon la formule V = D / T où D est la distance entre les capteurs.
afficher le résultat sur l’afficheur pendant quelques secondes
retourner en attente d’une autre mesure.
Pour réaliser cet automate, on verra que j’ai utilisé une variable décrivant les différentes situations possibles. L’action qui en découle est conditionnée par un test de cette variable. C’est simple !
Pour définir et enregistrer la distance D exacte, j’ai évité de coder cette distance en dur dans le programme. Au contraire, j’ai imaginé une condition qui doit être réalisée à la mise sous tension pour appeler un sous-programme de réglage. Il utilise les 2 capteurs pour augmenter ou diminuer la valeur précédente (comme des boutons + et -) et pour enregistrer le résultat dans l’EEPROM de l’Arduino, sur 2 octets seulement (jusqu’à 65532 mm soit 65 m ce qui est largement suffisant, mais sans dépasser 65 cm toutefois dans le programme actuel à cause des types des variables de calcul)
Enfin, vous découvrirez quelques astuces qui vous seront peut-être utiles dans vos programmes.
TM1637Display qu’il faut télécharger [7] et installer dans l’IDE. Cette bibliothèque est fournie avec le programme dans l’archive jointe à la fin de l’article.
EEPROM est une bibliothèque intégrée à l’IDE
Déclaration des pattes de l’Arduino utilisées : 2 et 3 pour l’afficheur, 8 et 9 pour les capteurs.
Déclaration de l’afficheur avec ses 2 pins (création de l’objet "display").
Je n’ai pas trouvé nécessaire ni souhaitable d’ajouter d’autres périphériques (leds, boutons, ..). Je me suis dit qu’il faut faire avec les organes d’interface ci-dessus qui sont 2 entrées et une sortie. Nous verrons plus loin comment...
Définition des variables
uint8_t data[] = { 0xff, 0xff, 0xff, 0xff };
uint8_t blank[] = { 0, 0, 0, 0 };
uint8_t lumin = 0x0f;
unsigned long _time1, _time2, temps;
byte dt0, dt1, odt0, odt1;
bool debug = false;
bool done;
// etat des capteurs de proximité
// repos = tous les bits à 0 (etat=0)
// dt0 = 0 si détection sur DT0 => P0=1
// dt1 = 0 si détection sur DT1 => P1=2
// 1ere detection sur DT0 : DP0 = 4
// 1ere detection sur DT1 : DP1 = 8
// affiche pendant _time2 : AFF = 16
enum {Repos=0, P0=1, P1=2, DP0=4, DP1=8, AFF=16 };
byte etat=Repos;
// distance entre capteurs 500 mm
unsigned int distance=500;
unsigned int VCM, VKM;
unsigned int val = 500;// distance par défaut en mm
unsigned int delai = 5000;
unsigned int k=0;
byte V0,V1;
Nous avons maintenant les variables utilisées par le programme :
data[] et blank[] sont des affichages spéciaux ;
les variables de temps pour le calcul de vitesse, les instants de détection et des temporisations ;
les valeurs des capteurs,
des booleens debug pour des affichages sur la console (faux donc désactivé par défaut) et done pour sortir d’une boucle while dans la configuration de la distance ;
la variable d’état pour l’automate : elle est constituée de l’assemblage de bits correspondant aux divers événements. Les valeurs résultantes permettent de prendre les bonnes décisions ;
les variables pour le calcul de la distance ;
une valeur par défaut de la distance entre les capteurs : 50 cm ou 500 mm, la distance étant exprimée en mm pour une meilleure précision.
Remarque : la distance entre capteurs doit être inférieure à 655 mm pour ne pas dépasser la capacité des variables unsigned int VCM et VKM, à cause d’un facteur 100 utilisé pour augmenter la précision. Si ce devait être le cas, il faudrait changer leur type en unsigned long.
Le programme du setup
Il commence par l’ouverture du port série pour la console et l’envoi de la chaine de caractère VERSION. Cette pratique permet de savoir ce qu’il y a dans la mémoire de l’Arduino !
Ensuite il y a l’initialisation de l’afficheur avec un test d’affichage pendant 1 seconde (8888), l’initialisation des pins des détecteurs, la lecture des 2 premiers octets de l’EEPROM qui devraient contenir la distance D.
Si les valeurs lues sont 0xFF, cela signifie que l’EEPROM est vide : il faut donc ignorer cette lecture et prendre la valeur 500 définie plus tôt. Seule la procédure de réglage permettra d’écrire une valeur significative dans l’EEPROM. Alors, au démarrage suivant c’est cette valeur qui sera utilisée.
// ----------------------------
// SETUP
// ----------------------------
void setup() {
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println(VERSION);
display.setBrightness(0x0f); // All segments on
display.setSegments(data);
delay(1000);
Serial.println("init détecteurs");
// init Pins détecteurs
pinMode(DT0, INPUT_PULLUP);
pinMode(DT1, INPUT_PULLUP);
V0 = EEPROM.read(0);
V1 = EEPROM.read(1);
if ((V0!=0xFF)&&(V1!=0xFF)) {
val = V0;
val = val << 8;
val = val | V1;
}
display.showNumberDec(val, false, 4, 0);
distance=val;
Serial.print("distance = ");Serial.println(distance);
delay(2000);
odt0=digitalRead(DT0);
odt1=digitalRead(DT1);
if (!odt0 && !odt1) { // les 2 detecteurs sont excités
reglage(); // reglage distance
}
_time1=millis();
_time2=millis();
etat=AFF;
}
Pour déclencher la procédure de réglage, il faut placer un wagon devant chaque détecteur et mettre le montage sous tension. Voici ce que fait cette procédure :
L’affichage de la valeur initiale (soit 500, soit le contenu de l’EEPROM)
L’attente de la libération des 2 capteurs dans une boucle while qui bloque le programme en attendant
la lecture des 2 détecteurs, l’un pour augmenter, l’autre pour diminuer la valeur de la distance. Une tempo de 200 ms permet de rendre cette action "humaine".
Et si les 2 détecteurs sont à nouveau actifs en même temps, la nouvelle valeur est enregistrée en EEPROM.
Petite astuce nécessaire du fait que l’EEPROM ne peut stocker que des octets (byte) et que la distance est un entier (2 octets) : L’entier est recopié dans l’octet V1, perdant au passage les 8 bits de poids fort. Puis l’entier est décalé de 8 bits vers la droite, perdant ainsi ses 8 bits de poids faible et le résultat est copié dans V2. V1 et V2 sont ensuite stockés dans l’EEPROM avec la fonction update qui évite d’écrire si la valeur précédente était identique (cela augmente la durée de vie de l’EEPROM).
Une façon plus "moderne" de convertir 1 entier en 2 octets et 2 octets en 1 entier est d’utiliser les macros word(), lowByte() et highByte() :
// au lieu de val = V0; val = val << 8; val = val | V1;//
// tout simplement ://
val = word(V0,V1);
// au lieu de V1 = val; V0 = val >> 8;//
// tout simplement ://
V0 = highByte(val);
V1 = lowByte(val);
Le réglage de la distance entre les capteurs et l’enregistrement en EEPROM
Le coeur du programme étant le calcul de vitesse, nous y voici maintenant !
Après vérification que le temps n’est pas nul pour éviter une division par 0 qui planterait le programme, on calcule la vitesse réelle en millimetre/milliseconde (ou mètres/seconde) multipliée par 100 pour augmenter la précision (plus de chiffres significatifs).
Puis on applique le rapport 576/100 pour obtenir des km/h à l’échelle 1/160.
Rappel : on multiplie les m/s par 3600/1000 = 3,6 pour obtenir des km/h.
Puis on multiplie par 160 (échèle N), d’où le nombre 576 ci-dessus.
Le résultat est affiché pendant 5 secondes et la variable d’état devient égale à AFF pour que l’extinction soit effectuée plus loin dans la LOOP.
La LOOP permet de faire du multitâche simplifié dans la mesure où ces tâches ne s’exécutent que si une condition particulière est réalisée.
Ici chaque condition est une ou plusieurs valeurs de la variable d’état état.
Nous avons donc successivement :
la lecture du 1er capteur
la lecture du 2eme capteur
le test du port série, si la console est connectée
le maintien de l’affichage du résultat pendant 5 secondes
et une animation durant l’inter-détection.
Le traitement des capteurs est le plus important :
Il est conditionné par l’état Repos ou une détection préalable sur l’autre capteur seulement.
Une première détection permet de positionner les bits de la variable d’état qui décrivent le capteur actif.
Une deuxième détection lance la procédure de calcul et d’affichage.
Dans tous les autres cas les lectures des ports DT0 ou DT1 sont ignorées. Cela a l’énorme avantage d’ignorer les détections parasites, les rebonds, etc..
La seule précaution à prendre consiste à éviter d’inonder de lumière les capteurs, ce qui provoquerait des fausses mesures. Mais cela se verrait très vite.
// ----------------------------
// LOOP
// ----------------------------
void loop() {
// lecture des capteurs de proximité
//enum etat {Repos=0, P0=1, P1=2, DP0=4, DP1=8, AFF=16 };
dt0=digitalRead(DT0);
if (!dt0) { // detection sur DT0 (niveau LOW)
if (etat==Repos) { // =0 : premiere detection sur DT0
etat = P0+DP0; // 5
k=0;
temps = millis();
_time1=millis();
if (debug) {Serial.print("etat=");Serial.println(etat);}
} else { // sinon c'est une 2me detection
if (etat == P1+DP1) { // bonne seulement après DT1
temps = millis()-temps;
if (debug) {Serial.print(" Temps=");Serial.print(temps); }
if (debug) {Serial.print(", Distance=");Serial.print(distance);}
calcul();
}
}
}
//enum etat {Repos=0, P0=1, P1=2, DP0=4, DP1=8, AFF=16 };
dt1=digitalRead(DT1);
if (!dt1) { // detection sur DT1 (niveau LOW)
if (etat==Repos) { // =0 : premiere detection sur DT0
etat = P1+DP1; // 10
k=0;
temps = millis();
_time1=millis();
if (debug) {Serial.print("etat=");Serial.println(etat);}
} else { // sinon c'est une 2me detection
if (etat == P0+DP0) { // bonne seulement après DT0
temps = millis()-temps;
if (debug) {Serial.print(" Temps=");Serial.print(temps); }
if (debug) {Serial.print(", Distance=");Serial.print(distance);}
calcul();
}
}
}
Le test de la console via le port série est très sommaire :
On lit un nombre entier et on le teste.
Si c’est une valeur positive non nulle, on considère que c’est la distance entre les capteurs en mm et on l’écrit dans l’EEPROM.
Si la valeur est égale à 0, comme il est interdit de diviser par 0 dans le calcul de vitesse, on utilise ce cas particulier pour permuter le booléen debug. S’il est vrai, quantité d’affichages apparaissent alors sur la console.
while (Serial.available() > 0)
{
val = Serial.parseInt(); // lecture entier
if (Serial.read() == '\n')
{
if (val==0) {debug = !debug;}
else {
V1 = val;
V0 = val >> 8;
EEPROM.update(0, V0);
EEPROM.update(1, V1);
Serial.print("distance entre capteurs : ");
Serial.println(val);
distance = val;
}
}
}
La variable delai est initialisée à 5000 (5 secondes). Au bout de 5 secondes l’affichage est éteint et l’état Repos autorise une nouvelle mesure.
if (_time2+delai > millis()) {
if (etat==AFF) {
etat=Repos;
if (debug) {Serial.print("etat=");Serial.println(etat);}
display.setSegments(blank);
}
}
Animation entre les détections du 1er et du 2ème détecteur
C’est juste pour faire joli : la condition dans l’if représente ce cas particulier. Tant que cette condition existe, on affiche un compteur k qui est incrémenté à chaque loop. Cela permet de se rendre compte de la vitesse d’exécution de l’Arduino !
if ((etat==P1+DP1)||(etat==P0+DP0)) {
display.showNumberDec(k, false, 4, 0);
k++;
}
} // fin de LOOP
Le programme ne pèse que 6202 octets, soit 20% de la flash du Pro Mini, qui n’est donc pas surchargé !
Vous pouvez le télécharger ici (l’archive contient aussi la bibliothèque TM1637) :
L’installation sur le réseau
J’ai commencé par percer des trous carrés de 1,5 cm de coté aux emplacements des capteurs, sur une ligne droite passant devant la console DCC. Dans chaque trou, j’ai glissé un capteur avec les diodes IR placées vers la voie, à un bon centimètre au dessus du sol pour viser sur le bas de la caisse des wagons, voitures et locos.
Ensuite un 3ème trou devant la console DCC est percé pour installer l’afficheur. celui-ci sera maintenu à environ 45° pour être bien orienté vers les yeux du conducteur.
Le câblage est ensuite réalisé sous le réseau, simplement en branchant les câbles (vérifiez plusieurs fois avant de brancher et avant de mettre sous tension que les connecteurs sont dans le bon sens et sur les bonnes pins, le montage "en l’air" ayant pour but des marquer les câbles avec des repères).
Après mise sous tension et l’affichage de "8888" puis "500", je teste le système en faisant passer un wagon ou une loco. Ca marche !
Il ne reste plus qu’à étalonner la distance en plaçant un wagon devant chaque détecteur et en redémarrant le système. On trouve très vite le capteur qui augmente et celui qui diminue ! Quand la valeur est bonne on place les 2 wagons devant les 2 capteurs en même temps et l’initialisation est terminée.
L’intégration dans le décor se fait en installant une cabane sur chaque capteur, quelques buissons et c’est beau comme sur les photos ci-dessus.
Merci d’avance pour vos commentaires et suggestions, ainsi que votre propre expérience (plutôt sur le Forum).