Note : cette page ne décrit pas un système complet, prêt à l’emploi, mais présente seulement quelques solutions, accompagnées de leurs tests de faisabilité.
Ces solutions s’appliquent aux réseaux de chemin de fer miniature digitalisés, mais aussi aux réseaux de véhicules routiers du genre Car-System Faller.
Principe de la solution :
Avec les systèmes classiques comme le Railcom, ou même avec la solution évoquée précédemment (solution 1), il faut un bus de rétrosignalisation pour faire remonter les informations depuis les détecteurs situés le long de la voie jusqu’à un organe central de prise de décision: centrale, PC, ou autre.
La solution décrite ici permet non seulement de ne pas avoir à sectionner la voie, mais aussi de se passer de ce bus de rétrosignalisation.
On verra plus loin comment ce système peut être utilisé en complément d’un système digital, sans modifier celui-ci.
Réalisation :
L’idée centrale consiste à inverser la logique habituelle, où c’est la voie qui détecte le passage d’un train et l’identifie.
Ici, c’est le train qui détecte son passage en un point du réseau (sur une balise) et rapporte cet évènement à l’ordinateur de gestion.
Pour cela, il est fait usage de petits modules WiFi très à la mode: la famille ESP8266.
On trouve sur internet quantité d’informations sur ces modules, et la façon de les programmer via l’environnement de développement (IDE) d’Arduino. Aussi nous n’y reviendrons pas.
Les balises :
Comme indiqué précédemment, vu les difficultés rencontrées avec le RFID (bien qu’il semble à première vue plus satisfaisant pour l’esprit), on utilise ici des balises actives émettant un signal infra-rouge.
Les avantages de ces balises sur les solutions traditionnelles sont les suivantes:
- elles ne nécessitent pas de coupures dans la voie, seulement un trou de 3mm pour la LED
- elles sont bien plus simples et donc bien moins chères qu’un module de détection
- et le câblage en est bien plus simple également.
De ce fait, les balises peuvent être installées ultérieurement, sur une réseau « non cantonné » à l’origine, et sans abimer une voie déjà peinte et ballastée.
Une balise de base se compose d’un microcontrôleur quelconque ( pourvu qu’il ait un port série), d’une LED infra-rouge et d’une résistance. Le microcontrôleur envoie des octets sur la LED via le port série.
La vitesse de signalisation est limitée par les performances de la LED et du photodétecteur. 19200 bps semble être la limite raisonnable avec les composants utilisés ici.
Un microcontrôleur peut commander plusieurs LEDs, donc plusieurs balises, ce qui réduit la quantité de matériel nécessaire.
Chaque LED est commandée à tour de rôle pour envoyer un octet.
Ci-contre, schéma d’une balise octuple réalisée à partir d’un Arduino nano.
L’Arduino met chaque point de commande (D2, D3…) à l’état haut pendant la durée d’émission d’un octet. Pendant ce temps les autres points de commande sont à l’état bas, aussi les LEDs correspondantes sont inactives.
On peut commander 8 LEDs par microcontrôleur avec une vitesse de signalisation de 19200 bps. Chaque LED émet alors un octet toutes les 4ms.
De nombreux paramétrages différents sont possibles.
Les microcontrôleurs doivent être alimentés électriquement. Un simple feeder 5V courant le long du réseau constitue la solution la plus simple.
Sur un réseau digital on peut envisager d’alimenter les microcontrôleurs depuis la voie, via une électronique ad-hoc.
Les détecteurs-transmetteurs embarqués:
Ils peuvent être réalisés comme un montage autonome, pour installation dans un engin moteur, à coté du décodeur DCC traditionnel, ou bien logés dans un véhicule auxiliaire.
On peut aussi y intégrer la commande de l’engin moteur. Cette possibilité, utile pour les engins fonctionnant sur batterie, n’est pas décrite ici.
Le détecteur-transmetteur minimum est basé sur un ESP-01, le plus petit module de la famille des ESP8266.
(Note : non, le phototransistor n’est pas monté à l’envers!).
Pour les tests, nos détecteurs-transmetteurs sont alimentés par batterie.
programme de test d’un détecteur embarqué
Les ESP-01 sont assez petits d’origine, mais il est possible d’en diminuer encore l’encombrement. Voir ici.
Performances :
Avec le phototransistor du wagon de test à 13 mm au dessus de la LED infrarouge, la détection fonctionne sur une longueur de 1,5 cm.
Une balise émet un octet toutes les 4ms. La vitesse du train doit donc être inférieure à 3,75 m/s pour avoir le temps de détecter un octet.
Mais comme le détecteur attend d’avoir reçu trois octets identiques avant de les transmettre, la vitesse maximum n’est que de 1,25 m/s, soit seulement 390 km/h à l’échelle HO.
Tous les paramètres peuvent être modifiés pour obtenir des performances différentes.
Exemples d’applications :
- annonce d’entrée en gare d’un train (sans action sur le train)
- déclenchement de l’avertisseur ou du sifflet lors du passage devant la pancarte S (action sur le train)
- allumage de l’éclairage à l’entrée d’un tunnel, extinction à la sortie (action sur le train)
- obéissance aux signaux, bloc-système simplifié (action sur le train)
- exploitation plus avancée d’un réseau (action sur le train).
Déclenchement d’une action au passage d’un train :
Le dispositif de commande peut être constitué e n’importe quel module de la famille ESP8266, mais pour simplifier l’alimentation électrique et la programmation, nous utilisons un NodeMCU, petit module qui comprend un ESP12 et les circuits nécessaires pour se connecter à un port USB.
Le détecteur embarqué lui envoie un message UDP indiquant le numéro de la balise qui vient d’être franchie. L’identité du train peut être ajoutée dans le message, ou bien déduite de l’adresse IP du détecteur.
Exemple : annonce d’entrée en gare. Il n’y a pas d’action sur le train.
Le programme qui déclenche l’action tourne sur le NodeMCU.
Il n’y a pas de dispositif d’annonce dans nos tests, ce programme affiche simplement les informations nécessaires via l’IDE Arduino.
Déclenchement d’une action simple sur un train :
Pour agir sur les trains, il faut envoyer à la centrale les commandes DCC appropriées, en mentionnant l’adresse de l’engin moteur.
Chaque modèle de centrale a ses interfaces, avec les protocoles correspondants.
Pour rester dans le domaine du sans fil, nous utilisons une centrale compatible Z21: on peut la commander en lui envoyant des ordres sous forme de paquets USB, en WiFi. Il suffit de respecter le protocole Roco Z21. La centrale reçoit les commandes comme s’ils provenaient d’un smartphone ou d’une tablette équipés de l’appli Z21.
Exemple : déclenchement du sifflet ou autre accessoire embarqué (éclairage, etc).
Le programme qui déclenche l’action tourne sur un NodeMCU.
Ce module fonctionne comme un point d’accès pour les détecteurs embarqués, et comme une station vis-à-vis de la centrale.
La commande des trains depuis d’autres manettes n’est pas impactée.
Les commandes Z21 nécessaires se limitent ici à la commande des fonctions (avertisseurs, éclairages).
Un « réseau » de test tout simple :
Pour tester ces quelques solutions sans mettre en oeuvre un grand réseau, on peut se contenter d’une voie d’essai, moins encombrante :
Les commandes Z21 nécessaires sont les commandes de vitesse et de fonctions des locos.
Gestion d’un réseau réel :
La gestion d’un vrai réseau est une tache complexe, et un NodeMCU n’est pas le processeur idéal pour l’assurer.
C’est pourquoi nous l’envisageons dans un PC pour plus de confort et de performances.
Il y a de multiples façons de faire communiquer un PC à la fois avec les véhicules et avec la centrale. Mais la centrale que nous utilisons pour les tests n’a pas d’interface réseau, et ne peut fonctionner qu’en WiFi et en mode point d’accès, aussi l’éventail des possibilités est limité.
En pratique la solution adoptée est très semblable à la précedente, sauf que le NodeMCU qui récupère les messages de localisation en provenance des mobiles et envoie ses ordres à la centrale, le fait sous contrôle du PC.
Ici les commandes Z21 nécessaires se limitent à la commande de vitesse des locos, afin d’obéir aux signaux. Un petit protocole est à établir entre le PC et le NodeMCU.
// balise 19200bps // émission d’un octet sur 8 ports à tour de rôle
void setup() { // put your setup code here, to run once:
Serial.begin(19200);
for (int i=2; i<=9; i++) { // ports 2 à 9
pinMode(i,OUTPUT); digitalWrite(i, LOW);
}
}
void loop() { // put your main code here, to run repeatedly:
envoi(2,55); // 0x37
envoi(3,113); // 0x71
envoi(4,126); // 0x1A
envoi(5,200); // 0xC8
envoi(6,56); // 0x38
envoi(7,114); // 0x72
envoi(8,127); // 0x7F
envoi(9,201); // 0xC9
}
void envoi(int port, int octet) {
digitalWrite(port,HIGH);
Serial.write(octet);
Serial.flush();
digitalWrite(port,LOW);
}
/* =============== Module Infrarouge de LOcalisation Universel V5 (;-)) ===============
Ce module tente de se connecter au réseau RASCAR s’il est disponible (mode fonctionnement normal)
sinon il se connecte à la box du réseau local (mode maintenance)
En mode fonctionnement normal: il lit des octets sur le port série à 19200 bps, les traite,
et retransmet des paquets UDP sur le réseau « rascar » à l’adresse 192.168.4.1 port 50000
En mode maintenance il communique avec un PC à l’adresse 192.168.1.10 port 50000
Il peut alors recevoir des commandes de paramétrage en UDP, ou charger un nouveau programme en OTA.
(Pour OTA, une taille mémoire de 1Mo est nécessaire).
https://www.aranacorp.com/fr/utilisation-de-leeprom-avec-lesp8266/
https://github.com/esp8266/Arduino/blob/master/libraries/EEPROM/EEPROM.h
*///============================================================================
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <EEPROM.h>
// informations remontées au PC pour affichage
#define sp Serial.print
#define spl Serial.println
// personnalisation du module
#define EEPROM_SIZE 4096
int EEPROMaddress = 0; // un seul octet à mémoriser…
byte AdresseDCC; // …c’est l’adresse de l’engin moteur associé
// divers
const byte LED = LED_BUILTIN; // LED externe (impossible de commander la LED interne de l’ESP-01)
char NbEssais = 20; // 20 essais de connexion à 500ms
char Reseau = 0; // réseau auquel on est connecté: Rascar ou local
// gestion des octets reçus sur le port série
byte DernierOctet, compte, precedente;
unsigned long previousMillis = 0;
const long interval = 1000;
bool EnableTransmission = true;
// transmission des paquets UDP vers Rascar
char PaquetUDP[16]; // paquets transmis vers Rascar
// récupération des paquets UDP provenant du PC
const uint16_t PORT = 50000; // Port d’écoute UDP
const uint16_t BUFFER_SIZE = 32; // Taille du tampon de réception
char buffer[BUFFER_SIZE]; // Tampon de réception
uint16_t len = 0;// Taille du paquet reçu;
// transmission des paquets au PC
char reponse[10] = « Milou5 # « ; // paquet – à compléter – transmis au PC
// instanciation du serveur UDP
WiFiUDP udp;
// ==================================================================================================== SETUP
void setup() {
pinMode (LED, OUTPUT);
digitalWrite (LED, HIGH); // Led connectée au plus
Serial.begin(19200); // on démarre le port série
EEPROM.begin(EEPROM_SIZE); // eeprom
AdresseDCC = EEPROM.read(0); // lecture de l’adresse du module
delay(10); // On attend « un peu » que le buffer soit prêt
// connexion au réseau rascar, ou sinon au réseau local
NbEssais = 20; setup_sta1(); // tentative de connexion au réseau rascar (mode opérationnel)
if (NbEssais == 0) { // rascar indisponible (non activé)
NbEssais = 20; setup_sta2(); // tentative de connexion au réseau local (pour test et mise au point)
}
if(WiFi.status() == WL_CONNECTED){
udp.begin(PORT); // démarrage de l’écoute
sp(« à l’écoute sur le port « ); spl(PORT);
delay(100);
if (Reseau == 2){ // connexion au PC
reponse[8] = AdresseDCC;
sendPacket ( reponse, 9, {192,168,1,10},{50000});
} // envoi au PC / Packet Sender
}
else {sp( » aucune connection! »);} // problème!
} // fin de setup
void setup_sta1(){ // =============================================== connexion au gestionnaire des messages
const char sta1_ssid[] = « rascar »;
const char sta1_password[] = « 12345678 »;
// demande d’une adresse IP précise ou utilisation des valeurs par défaut
//WiFi.config({192,168,4,100},{192,168,4,1},{192,168,4,1},{255,255,255,0}); // IP, DNS, gateway, subnet
// Initialisation de la connection WiFi
WiFi.begin(sta1_ssid, sta1_password);
// attente de connexion
spl(« »);sp(« tentative de connexion à « );sp(sta1_ssid);sp( » « );
//while (WiFi.status() != WL_CONNECTED && NbEssais > 0 ){ delay(500); sp(NbEssais,DEC); NbEssais–;}
while (WiFi.status() != WL_CONNECTED && NbEssais > 0 ){ delay(500); sp(« . »); NbEssais–;}
if(WiFi.status() == WL_CONNECTED){ // si connecté:
Reseau = 1; // réseau rascar
// Affichage des informations
spl(« »); sp(« connecté à « ); sp(sta1_ssid); // <<<<<<<<<< prendre la vraie valeur
sp( » avec l’ip « ); spl(WiFi.localIP());
delay(100);
// cligner 3 fois
for (char I=1; I<=3; I++){digitalWrite(LED,LOW); delay(200); digitalWrite(LED,HIGH); delay(200);}
}
else {spl(« »);sp(« échec de la connection à « );sp(sta1_ssid);}
delay(500);
}
void setup_sta2(){ // ============================================= connexion à la box en mode station
const char sta2_ssid[] = « Livebox-9052 »;
const char sta2_password[] = « MotDePasse »;
// pour simplifier les test, utilisation de la même adresse MAC (sinon bloquée par la box)
const uint8_t mac[6] = {0x2C, 0x3A, 0xE8, 0x26, 0xA3, 0x54}; // « 2c:3a:e8:26:a3:54 »
// changement de l’adresse MAC cf: https://circuits4you.com/2017/12/31/how-to-change-esp8266-mac-address/
spl(« »); sp(« ancienne adresse MAC: « ); sp(WiFi.macAddress()); // adresse MAC en cours
wifi_set_macaddr(0, const_cast<uint8*>(mac)); //changement MAC address pour Livebox
sp( » nouvelle adresse MAC: « ); spl(WiFi.macAddress());
// demande d’une adresse IP précise
WiFi.config({192,168,1,100},{192,168,1,1},{192,168,1,1},{255,255,255,0}); // IP, DNS, gateway, subnet
// Initialisation de la connection WiFi
WiFi.begin(sta2_ssid, sta2_password);
sp(« tentative de connexion à « ); sp(sta2_ssid);sp( » « );
// attente de connexion
//while (WiFi.status() != WL_CONNECTED && NbEssais >0 ){ delay(500);sp(NbEssais,DEC); NbEssais–;}
while (WiFi.status() != WL_CONNECTED && NbEssais >0 ){ delay(500);sp(« . »); NbEssais–;}
//spl(NbEssais,DEC); // test
if(WiFi.status() == WL_CONNECTED){ // si connecté:
Reseau = 2; // réseau local
// pour OTA
ArduinoOTA.setHostname(« milou5 »); // nom du module
ArduinoOTA.begin(); // initialisation de l’OTA
// Affichage des informations
spl(« »);sp(« Connecté à « ); sp(sta2_ssid); // <<<<<<<<<< prendre la vraie valeur
sp( » avec l’ip « ); spl(WiFi.localIP());
delay(500);
// cligner 6fois
for (char I=1; I<=6; I++){digitalWrite(LED,LOW); delay(200); digitalWrite(LED,HIGH); delay(200);}
}
else {spl(« »);sp(« échec de la connection à « );sp(sta2_ssid);}
}
// ===================================================================================================== LOOP
void loop() {
// gestion de l’OTA
ArduinoOTA.handle();
// réception des paquets UDP pour test et paramétrage
if (udp.parsePacket() > 0) { readPacket(); }
// réception des octets balise sur port série
if (Serial.available() > 0) {ReceptionSerie();}
//else { while(Serial.available () > 0){Serial.read();} } //vidage du buffer de réception série
// timer de blocage des re-transmissions
if (millis() – previousMillis >= interval) { // ré-autoriser la lecture après 1s
EnableTransmission = true;
digitalWrite (LED, HIGH); // extinction mouchard
}
}
// ================================================================ récupération et affichage d’un paquet UDP
void readPacket() { // lecture d’un paquet en provenance du PC
len = udp.available();
udp.read(buffer, len); // Mise en tampon du paquet
TraitementMessagePC(); // traitement du paquet
}
// ==================================================================================== envoi d’un paquet UDP
void sendPacket(const char content[], char nbchar, IPAddress ip, uint16_t port) { // avec longueur
//void sendPacket(char content[], IPAddress ip, uint16_t port) { // avec zéro à la fin
if (Reseau == 2) {
sp(« envoi de « );sp(nbchar,DEC);sp( » octets à « );sp(ip);sp(« : »);spl(port); delay(10);
}
udp.beginPacket(ip, port);
udp.write(content, nbchar);
udp.endPacket();
}
// ===================================================================================== sous programmes
void ReceptionSerie(){ // ——————————– réception des octets un par un sur port série
byte OctetBalise, vidage;
OctetBalise = Serial.read(); //réception d’un caractère
// attente de recevoir 3 octets identiques à la suite
if (OctetBalise != DernierOctet) { // nouvel octet
DernierOctet = OctetBalise;
compte = 1;
}
else { // même octet
compte++;
if ( compte == 3 ) { // transmission si N octets successifs identiques
// mais ne transmettre le résultat qu’une seule fois par seconde (train arrêté sur balise)
// sauf si c’est une autre balise (balises rapprochées)
if (EnableTransmission || (OctetBalise != precedente)) { // transmission
PaquetUDP[0] = AdresseDCC;
PaquetUDP[1] = OctetBalise; // numéro de la balise
if (Reseau == 1){sendPacket(PaquetUDP, 2, {192,168,4,1},{50000});}// envoi à rascar
if (Reseau == 2){sendPacket(PaquetUDP, 2, {192,168,1,10},{50000});}// envoi au PC
EnableTransmission = false;
digitalWrite (LED, LOW); // allumage LED pour test
precedente = OctetBalise; // mémorisation de la balise
}
else { while(Serial.available () > 0){vidage = Serial.read();} } //vidage du buffer de réception
previousMillis = millis(); // relancement tempo
DernierOctet = 255; // réinitialisation
}
}
}
// ============================================================== traitement des messages en provenance du PC
void TraitementMessagePC(){ // messages de paramétrage du module
// deux types de messages sont actuellement possibles:
// – deux octets « >N » où N est l’adresse DCC de la locomotive associée
// – n’importe quoi d’autre provoque la relecture et l’envoi de l’adresse
//AffichageMessage(); // test
if ((buffer[0] == ‘>’) && (len == 2)) { // message de changement d’addresse DCC
AdresseDCC = buffer[1];
EEPROM.write(EEPROMaddress, AdresseDCC); // ou: EEPROM.put(EEPROMaddress, AdresseDCC);
EEPROM.commit();
// relecture de l’adresse DCC
AdresseDCC = EEPROM.read(0); // ou EEPROM.get(EEPROMaddress, AdresseDCC);
}
// dans les deux cas, retransmission de l’adresse
reponse[8] = AdresseDCC;
if (Reseau == 2){sendPacket ( reponse, 9, {192,168,1,10},{50000});} // envoi au PC / Packet Sender
}
// =================================================================================== affichage d’un message
void AffichageMessage() {
sp(« reçu « ); sp(len); sp( » octets de « ); sp(udp.remoteIP()); // <<< remote IP irrelevant
sp(« : »); sp(udp.remotePort());
sp( » soit: « );
for(int i=1; i<=len; i++) {sp(buffer[i-1]); sp( » « );} spl(« »);
}
// ======================================================================= impression en hexadécimal amélioré
void PrtHex(char X) {
if (X<16){sp(« 0 »);} sp(X,HEX);
}
// ========================== programme Rascar_annonce ==============================
// Ce NodeMCU /ESP-12 fonctionne en mode point d’accès pour les détecteurs embarqués,
// et en même temps mode station vis-à-vis de la centrale DCC.
// Le programme reçoit les paquets UDP envoyés par les détecteurs embarqués,
// les traite, puis déclenche une action (par exemple: annnonce d’entrée en gare)
// ou pour tests, au PC avec Packet Sender
// ==================================================================================
#include <ESP8266WiFi.h>
#include <WiFiUDP.h>
// informations remontées au PC pour affichage
#define sp Serial.print
#define spl Serial.println
// commandes reçues sur port série
int NbMots;
String Mots[5];
// mode point d’accès pour les détecteurs embarqués
#define AP_SSID « rascar »
#define AP_PASS « 12345678 »
// mode station vers la centrale. PC + Packet Sender pour test
#define WIFI_SSID « Livebox-xxxx »
#define WIFI_PASS « abcdefg »
// pour récupération des paquets UDP
const uint16_t PORT = 50000; // Port d’écoute UDP
const uint16_t BUFFER_SIZE = 512; // Taille du tampon de réception
char buffer[BUFFER_SIZE]; // Tampon de réception
uint16_t len = 0;// Taille du paquet reçu;
// instanciation du serveur UDP
WiFiUDP udp;
void setup() { // =================================================================================== setup
Serial.begin(19200);
spl();
// —————————————————————— démarrage du point d’accès
WiFi.mode(WIFI_AP_STA);
IPAddress local_IP(192,168,4,1); // adresse IP du point d’accès
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);
WiFi.softAPConfig(local_IP, gateway, subnet); // fixe l’adresse IP du point d’accès
//WiFi.softAPConfig({192,168,4,1},{192,168,4,1},{255,255,255,0}); // ou directement
WiFi.softAP(AP_SSID, AP_PASS); // démarrage du point d’accès
sp(« adresse IP du réseau « ); sp(AP_SSID); sp( » : « ); spl(WiFi.softAPIP());
// ———————————————————— mode station sur la centrale DCC
// pour simplifier, utilisation de la même adresse MAC (sinon bloquée par la box)
const uint8_t mac[6] = {0x2C, 0x3A, 0xE8, 0x26, 0xA3, 0x54}; // « 2c:3a:e8:26:a3:54 »
// changement de l’adresse MAC cf: https://circuits4you.com/2017/12/31/how-to-change-esp8266-mac-address/
spl(« »); sp(« ancienne adresse MAC: « ); sp(WiFi.macAddress()); // adresse MAC en cours
wifi_set_macaddr(0, const_cast<uint8*>(mac)); //changement MAC address pour Livebox
sp( » nouvelle adresse MAC: « ); spl(WiFi.macAddress());
// ————————————————————————- connexion au PC
WiFi.begin(WIFI_SSID, WIFI_PASS); // connexion au réseau local domestique
sp(« connection à « ); spl(WIFI_SSID);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
sp(« . »);
}
udp.begin(50000); // démarre l’écoute sur port 50000
spl(); spl(« Connecté! »);
sp(« adresse IP sur réseau « ); sp(AP_SSID); sp( » : « ); spl(WiFi.softAPIP());
sp(« adresse IP sur réseau « ); sp(WIFI_SSID); sp( » : « ); spl(WiFi.localIP());
spl(« »);
}
void loop() { // =================================================================================== loop
if (udp.parsePacket() > 0) { readPacket(); } // réception des paquets UDP pour test
if (Serial.available() > 0) { ReceptionSerie(); } // réception des commandes sur port série
}
// ===================================================================================== sous programmes
void ReceptionSerie(){ // —————— réception des octets un par un sur port série, pour tests
char X;
X = Serial.read(); //réception d’un caractère
switch (X){ // ********************** trois cas: space, line feed, ou autre caractère
case (32):{++NbMots;} break; // ********************************************* espace
case 10: { // **************************************************** LF = fin de message
if (Mots[0] == « ? ») { // ——————————————— commande ?
spl(« commande ? »);
sp(« adresse IP sur réseau « ); sp(AP_SSID); sp( » : « ); spl(WiFi.softAPIP());
sp(« adresse IP sur réseau « ); sp(WIFI_SSID); sp( » : « ); spl(WiFi.localIP());
sp(« »);
}
if(Mots[0] == « ! ») { // ———————- tests de communication – commande !
spl(« commande ! »);
// sendPacket(« test », 4, {192,168,4,100},50000); // test
sendPacket(« test », 4, {192,168,1,10},21105); // envoi au PC / Packet Sender
}
// ————————————————————- fin de commande, reset
Mots[0]= » »; Mots[1]= » »; Mots[2]= » »; Mots[3]= » »;
NbMots = 0;
break; // fin de LF
}
default:{ // ****************************************** autre caractère: concaténation
Mots[NbMots] = Mots[NbMots] + X;};
break;
}
}
// ==================================================================== récupération et affichage d’un paquet
void readPacket() {
len = udp.available();
udp.read(buffer, len); // Mise en tampon du paquet
TraitementMessage(); // traitement du paquet
}
// ==================================================================================== envoi d’un paquet UDP
void sendPacket(const char content[], int longueur, IPAddress ip, uint16_t port) { // avec longueur en INT
//void sendPacket(char content[], IPAddress ip, uint16_t port) { // avec zéro à la fin
sp(« envoi de « ); sp(longueur); sp( » octets à « ); sp(ip); sp(« : »); spl(port); delay(10);
udp.beginPacket(ip, port);
udp.write(content, longueur);
udp.endPacket();
}
// ================================================================================= traitement des messages
void TraitementMessage(){
AffichageMessage(); // pour test
// le numéro du train peut être calculé d’après l’adresse IP du détecteur, ou contenue dans le message
//sp(« le train « ); spl(udp.remoteIP()[3]); spl(« »); // d’après l’adresse IP du détecteur
sp(« train « ); sp(buffer[0],DEC); sp ( » balise « ); spl (buffer[1],DEC); spl(« »); // d’après le message
// yapuka activer le dispositif d’annonce!
spl(« retransmission… »); delay(10);
sendPacket(« retransmission », 14, {192,168,1,10},50000); // envoi au PC / Packet Sender pour test
delay(10);
}
// ================================================================================== affichage d’un message
void AffichageMessage() {
sp(« reçu « ); sp(len); sp( » octets de « ); sp(udp.remoteIP());
sp(« : »); sp(udp.remotePort());
sp( » soit: « );
for(int i=1; i<=len; i++) {PrtHex(buffer[i-1]); sp( » « );} spl(« »);
}
// ======================================================================= affichage en hexadécimal amélioré
void PrtHex(char X) {
if (X<16){sp(« 0 »);} sp(X,HEX);
}
// ============================ programme Rascar_sifflet ==============================
// Ce NodeMCU /ESP-12 fonctionne en mode point d’accès pour les détecteurs embarqués,
// et en même temps mode station vis-à-vis de la centrale DCC.
// Le programme reçoit les paquets UDP envoyés par les détecteurs embarqués,
// les traite, puis envoie les commandes UDP appropriées à la centrale DCC (par exemple: siffler).
// ou pour tests, au PC avec Packet Sender
// Seule la partie traitement du message diffère de Rascar_annonce
// ==================================================================================
#include <ESP8266WiFi.h>
#include <WiFiUDP.h>
// informations remontées au PC pour affichage
#define sp Serial.print
#define spl Serial.println
const byte LED = LED_BUILTIN; // NodeMCU
//const byte LED = 2; // LED externe ESP-01 (impossible de commander la LED interne)
char NbEssais; // vérification de la connexion toutes les 500ms
char Reseau = 0; // réseau auquel on est connecté
// commandes reçues sur port série
int NbMots;
String Mots[5];
// mode point d’accès pour les détecteurs embarqués
#define AP_SSID « rascar »
#define AP_PASS « 12345678 »
// mode station vers la centrale MiniDCC
// #define WIFI_SSID1 « MiniDCC »
// #define WIFI_PASS1 « amfn2019 »
// #define IP_CENTRALE 192,168,1,1
// mode station vers la centrale DR5000
#define WIFI_SSID1 « DR5000-B0000763 »
#define WIFI_PASS1 « 12345678 »
#define IP_CENTRALE 192,168,16,254
// ou mode station vers PC + Packet Sender pour test
#define WIFI_SSID2 « Livebox-xxxx »
#define WIFI_PASS2 « abcdefg »
// #define IP_CENTRALE 192,168,1,10
// pour récupération des paquets UDP
const uint16_t PORT = 50000; // Port d’écoute UDP
const uint16_t BUFFER_SIZE = 512; // Taille du tampon de réception
char buffer[BUFFER_SIZE]; // Tampon de réception
uint16_t len = 0;// Taille du paquet reçu;
// pour envoi des messages à la centrale
char MessageUDP[32];
// instanciation du serveur UDP
WiFiUDP udp;
void setup() { // =================================================================================== setup
Serial.begin(19200);
spl();
// —————————————————————— démarrage du point d’accès
WiFi.mode(WIFI_AP_STA);
IPAddress local_IP(192,168,4,1); // adresse IP du point d’accès
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);
WiFi.softAPConfig(local_IP, gateway, subnet); // fixe l’adresse IP du point d’accès
//WiFi.softAPConfig({192,168,4,1},{192,168,4,1},{255,255,255,0}); // ou directement
WiFi.softAP(AP_SSID, AP_PASS); // démarrage du point d’accès
sp(« adresse IP du réseau « ); sp(AP_SSID); sp( » : « ); spl(WiFi.softAPIP());
// ———————————————- connexion en mode station à la centrale DCC
sp(« tentative de connection à « ); spl(WIFI_SSID1); // tentative de connexion à la centrale
WiFi.begin(WIFI_SSID1, WIFI_PASS1);
NbEssais = 20;
while (WiFi.status() != WL_CONNECTED && NbEssais > 0 ){ delay(500); sp(NbEssais,DEC); NbEssais–;}
// ————————————————————– si connecté à la centrale
if(WiFi.status() == WL_CONNECTED){ // si connecté:
Reseau = 1; // réseau rascar
spl(« »); sp(« connecté à « ); sp(WIFI_SSID1); // <<<<<<<<<< prendre la vraie valeur
sp( » avec l’ip « ); spl(WiFi.localIP());
delay(100);
// cligner 5 fois
for (char I=1; I<=5; I++){digitalWrite(LED,LOW); delay(200); digitalWrite(LED,HIGH); delay(200);}
}
// —————————- si pas de connexion à la centrale, se connecter au réseau local
else {
spl( » pas de connexion à la centrale »);
sp(« tentative de connection à « ); spl(WIFI_SSID2); delay(100);
// pour simplifier, utilisation de la même adresse MAC (sinon bloquée par la box)
const uint8_t mac[6] = {0x2C, 0x3A, 0xE8, 0x26, 0xA3, 0x54}; // « 2c:3a:e8:26:a3:54 »
// changement de l’adresse MAC cf: https://circuits4you.com/2017/12/31/how-to-change-esp8266-mac-address/
sp(« ancienne adresse MAC: « ); sp(WiFi.macAddress()); // adresse MAC en cours
wifi_set_macaddr(0, const_cast<uint8*>(mac)); //changement MAC address pour Livebox
sp( » nouvelle adresse MAC: « ); spl(WiFi.macAddress());
// tentative de connexion au réseau local
WiFi.begin(WIFI_SSID2, WIFI_PASS2);
NbEssais = 20;
while (WiFi.status() != WL_CONNECTED && NbEssais > 0 ){ delay(500); sp(NbEssais,DEC); NbEssais–;}
if (WiFi.status() == WL_CONNECTED){
Reseau = 2;
spl(« »); sp(« connecté à « ); sp(WIFI_SSID2); // <<<<<<<<<< prendre la vraie valeur
sp( » avec l’ip « ); spl(WiFi.localIP());
delay(100);
// cligner 10 fois
for (char I=1; I<=10; I++){digitalWrite(LED,LOW); delay(200); digitalWrite(LED,HIGH); delay(200);}
}
else {sp( » aucune connection! »);} // problème!
}
// ————————————————————————- connexion au PC
udp.begin(50000); // démarre l’écoute sur port 50000 (messages des milous)
//spl(« Prêt! »);
//sp(« adresse IP sur réseau « ); sp(AP_SSID); sp( » : « ); spl(WiFi.softAPIP());
//sp(« adresse IP sur réseau « ); sp(WIFI_SSID); sp( » : « ); spl(WiFi.localIP());
//spl(« »);
}
void loop() { // =================================================================================== loop
if (udp.parsePacket() > 0) { readPacket(); } // réception des paquets UDP pour test
if (Serial.available() > 0) { ReceptionSerie(); } // réception des commandes sur port série
}
// ===================================================================================== sous programmes
void ReceptionSerie(){ // —————— réception des octets un par un sur port série, pour tests
char X;
X = Serial.read(); //réception d’un caractère
switch (X){ // ********************** trois cas: space, line feed, ou autre caractère
case (32):{++NbMots;} break; // ********************************************* espace
case 10: { // **************************************************** LF = fin de message
if (Mots[0] == « ? ») { // ——————————————— commande ?
spl(« commande ? »);
spl(« programme Rascar-sifflet »);
//sp(« adresse IP sur réseau « ); sp(AP_SSID); sp( » : « ); spl(WiFi.softAPIP());
//sp(« adresse IP sur réseau « ); sp(WIFI_SSID); sp( » : « ); spl(WiFi.localIP());
sp(« »);
}
if(Mots[0] == « ! ») { // ———————- tests de communication – commande !
spl(« commande ! »);
// sendPacket(« test », 4, {192,168,4,100},50000); // test
sendPacket(« test », 4, {192,168,1,10},21105); // envoi au PC / Packet Sender
}
// ————————————————————- fin de commande, reset
Mots[0]= » »; Mots[1]= » »; Mots[2]= » »; Mots[3]= » »;
NbMots = 0;
break; // fin de LF
}
default:{ // ****************************************** autre caractère: concaténation
Mots[NbMots] = Mots[NbMots] + X;};
break;
}
}
// ==================================================================== récupération et affichage d’un paquet
void readPacket() {
len = udp.available();
udp.read(buffer, len); // Mise en tampon du paquet
//ne garder que les paquets provenant des milous, et pas de la centrale
if (udp.remotePort() == 50000) {TraitementMessage();} // traitement du paquet
}
// ==================================================================================== envoi d’un paquet UDP
void sendPacket(const char content[], int longueur, IPAddress ip, uint16_t port) { // avec longueur en INT
//void sendPacket(char content[], IPAddress ip, uint16_t port) { // avec zéro à la fin
sp(« envoi de « ); sp(longueur); sp( » octets à « ); sp(ip); sp(« : »); spl(port); delay(10);
udp.beginPacket(ip, port);
udp.write(content, longueur);
udp.endPacket();
}
// ================================================================================= traitement des messages
void TraitementMessage(){
AffichageMessage(); // pour test
// le numéro du train peut être calculé d’après l’adresse IP du détecteur, ou contenue dans le message
//sp(« le train « ); spl(udp.remoteIP()[3]); spl(« »); // d’après l’adresse IP du détecteur
sp(« Milou « ); sp(buffer[0],DEC); sp ( » balise « ); spl (buffer[1],DEC); // d’après le message
// envoi d’une commande à la centrale en UDP
if (buffer[0] == 70 && buffer[1] == 201) { // milou 70 => loco 67
spl(« envoi d’une commande F11 à la loco 67 »);
CommandeFonction(67 , 11 , 0 ); // loco, fonction, état
delay(200);
CommandeFonction(67 , 11 , 1 ); // loco, fonction, état
}
}
// ================================================================================== affichage d’un message
void AffichageMessage() {
sp(« reçu « ); sp(len); sp( » octets de « ); sp(udp.remoteIP());
sp(« : »); sp(udp.remotePort());
sp( » soit: « );
for(int i=1; i<=len; i++) {PrtHex(buffer[i-1]); sp( » « );} spl(« »);
}
// ==================================================================================== envoi d’un paquet UDP
void sendPacketL(const char content[], char longueur, IPAddress ip, uint16_t port) { // avec longueur
udp.beginPacket(ip, port);
udp.write(content, longueur);
udp.endPacket();
}
void sendPacketZ(char content[], IPAddress ip, uint16_t port) { // avec zéro à la fin
udp.beginPacket(ip, port);
udp.write(content);
udp.endPacket();
}
// =========================================================== envoi d’un message à la centrale au format Z21
void CommandeFonction(word loco, char fonction, bool etat){
char CheckSum;
MessageUDP[0] = 10; // longueur LSB = 0x0A
MessageUDP[1] = 0x00; // longueur MSB
MessageUDP[2] = 0x40; // header LSB(0x40)
MessageUDP[3] = 0x00; // header MSB
MessageUDP[4] = 0xE4; CheckSum = 0xE4; // X-header
MessageUDP[5] = 0xF8; CheckSum = CheckSum ^ MessageUDP[5]; // commande de fonction
MessageUDP[6] = loco >> 8; CheckSum = CheckSum ^ MessageUDP[6]; // adresse DCC MSB
MessageUDP[7] = loco & 127; CheckSum = CheckSum ^ MessageUDP[7]; // adresse DCC LSB
// DB3 = TTNNNNNN où TT = off/on/toggle/n.a. NNNNNN = fonction
MessageUDP[8] = fonction; // 00NNNNNNN
if (etat != 0) {MessageUDP[8] = MessageUDP[8] + 64; } // toggle traité comme on
CheckSum = CheckSum ^ MessageUDP[8];
//ajout de la checksum
MessageUDP[9] = CheckSum;
//sendPacketL(MessageUDP, 10, {192,168,1,10},{50000}); // envoi au PC / Packet Sender
//sendPacketL(MessageUDP, 10, {192,168,1,1},{21105}); // envoi à la centrale
sendPacketL(MessageUDP, 10, {IP_CENTRALE},{21105}); // envoi à la centrale
}
// ======================================================================= affichage en hexadécimal amélioré
void PrtHex(char X) {
if (X<16){sp(« 0 »);} sp(X,HEX);
}
// =========================================================
// lit sur le port série les commandes reçues de RegleurZ21
// et renvoie les paquets UDP appropriés
// =========================================================
// tester le nombre de stations connectées:
// https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/soft-access-point-examples.html
// https://www.tala-informatique.fr/wiki/index.php/Esp8266_wifi
// https://arduino-esp8266.readthedocs.io/en/latest/reference.html
// https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/soft-access-point-class.html?highlight=mac%20address#mac-as-a-string
#include <ESP8266WiFi.h>
#include <WiFiUDP.h>
#include <ArduinoOTA.h> // pour OTA
// pour affichage:
#define sp Serial.print
#define spl Serial.println
// entrées-sorties
const byte LED = 2; // LED interne
// pour récupération des paquets UDP
const uint16_t PORT = 21105; // 50000; // Port d’écoute UDP
const uint16_t BUFFER_SIZE = 64; // Taille du tampon de réception
char buffer[BUFFER_SIZE]; // Tampon de réception
uint16_t len = 0;// Taille du paquet reçu;
// pour envoi UDP
char MessageUDP[32];
// commandes reçues sur port série
int NbMots;
String Mots[5];
int X; byte Y; int xxx;
// mode point d’accès pour les détecteurs embarqués
#define AP_SSID « rascar »
#define AP_PASS « 12345678 »
// mode station vers le PC + Packet Sender pour test
// #define WIFI_SSID « Livebox-xxxx »
// #define WIFI_PASS « abcdefg »
// #define IP_CENTRALE 192,168,1,10
// mode station vers la centrale MiniDCC
// #define WIFI_SSID « MiniDCC »
// #define WIFI_PASS « amfn2019 »
// #define IP_CENTRALE 192,168,1,1
// mode station vers la centrale DR5000
#define WIFI_SSID « DR5000-B0000763 »
#define WIFI_PASS « 12345678 »
#define IP_CENTRALE {192,168,16,254}
char NbEssais = 20; // 10 essais à 500ms de connexion au réseau domestique
// instanciation du serveur UDP
WiFiUDP udp;
void setup() { // =================================================================================== setup
Serial.begin(19200);
spl();
// —————————————————————— démarrage du point d’accès
WiFi.mode(WIFI_AP_STA);
IPAddress local_IP(192,168,4,1); // adresse IP du point d’accès
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);
WiFi.softAPConfig(local_IP, gateway, subnet); // fixe l’adresse IP du point d’accès
//WiFi.softAPConfig({192,168,4,1},{192,168,4,1},{255,255,255,0}); // ou directement
WiFi.softAP(AP_SSID, AP_PASS); // démarrage du point d’accès
sp(« adresse IP du réseau « ); sp(AP_SSID); sp( » : « ); spl(WiFi.softAPIP());
// ———————————————————— mode station sur la centrale DCC
// pour simplifier, utilisation de la même adresse MAC (sinon bloquée par la box)
const uint8_t mac[6] = {0x2C, 0x3A, 0xE8, 0x26, 0xA3, 0x54}; // « 2c:3a:e8:26:a3:54 »
// changement de l’adresse MAC cf: https://circuits4you.com/2017/12/31/how-to-change-esp8266-mac-address/
spl(« »); sp(« ancienne adresse MAC: « ); sp(WiFi.macAddress()); // adresse MAC en cours
wifi_set_macaddr(0, const_cast<uint8*>(mac)); //changement MAC address pour Livebox
sp( » nouvelle adresse MAC: « ); spl(WiFi.macAddress());
// ————————————————————————- connexion au réseau
WiFi.begin(WIFI_SSID, WIFI_PASS); // connexion au réseau local domestique
sp(« connection à « ); spl(WIFI_SSID);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
sp(« . »);
}
//udp.begin(50000); // démarre l’écoute sur port 50000
udp.begin(21105); // démarre l’écoute sur port 21105 (Z21)
spl(); spl(« Connecté! »);
sp(« adresse IP sur réseau « ); sp(AP_SSID); sp( » : « ); spl(WiFi.softAPIP());
sp(« adresse IP sur réseau « ); sp(WIFI_SSID); sp( » : « ); spl(WiFi.localIP());
spl(« »);
}
// ===================================================================================================== LOOP
void loop() {
ArduinoOTA.handle(); // gestion OTA
if (udp.parsePacket() > 0) { readPacket(); } // réception des paquets
if (Serial.available() > 0) {ReceptionCommande();};
}
// ==================================================================== récupération et affichage d’un paquet
void readPacket() {
len = udp.available();
udp.read(buffer, len);
TraitementMessage(); // traitement du paquet UDP
}
// ==================================================================================== envoi d’un paquet UDP
void sendPacketL(const char content[], char longueur, IPAddress ip, uint16_t port) { // avec longueur
udp.beginPacket(ip, port);
udp.write(content, longueur);
udp.endPacket();
}
void sendPacketZ(char content[], IPAddress ip, uint16_t port) { // avec zéro à la fin
udp.beginPacket(ip, port);
udp.write(content);
udp.endPacket();
}
// ============================================================================== traitement des messages UDP
void TraitementMessage(){
// affichage du message en hexadécimal
sp(« reçu « ); sp(len); sp( » octets de « ); sp(udp.remoteIP());
sp(« : »); sp(udp.remotePort());
sp( » soit: « );
for(int i=1; i<=len; i++) {PrtHex(buffer[i-1]); sp( » « );} spl(« »);
// affichage en clair
sp(buffer);
// réponse
sendPacketZ(« message recu », udp.remoteIP(), udp.remotePort()); // envoi au PC / Packet Sender
}
// ============================================================================== réception des commandes
void ReceptionCommande(){ // décompose en mots séparés par un seul espace
char X, CheckSum, Port, I;
X = Serial.read(); //réception d’un caractère
switch (X){ // ********************** trois cas: space, line feed, ou autre caractère
case (32):{++NbMots;} break; // ********************************************* espace
case 10: { // ******************************************************* fin de message
if (Mots[0] == « ? ») { // ——————————————— commande ?
spl (« commandes (séparation par un seul espace): »); delay(10);
spl ( » udp valeur1 valeur2 valeur3 (envoi d’octets en UDP) »); delay(10);
spl ( » vit adresse sens cran (message Z21) »); delay(10);
spl ( » fonc adresse fonction 0/1 (message Z21) »); delay(10);
spl ( » loco adresse (message Z21 get loco mode) »); delay(10);
spl ( » trackon (message Z21 set track power on) »); delay(10);
spl ( » trackoff (message Z21 set track power off) »); delay(10);
spl ( » test port (par clignotement) »); delay(10);
spl ( » xxx valeur (écriture dans la variable xxx) »); delay(10);
spl ( » nnn valeur (test numérique) »); delay(10);
}
if (Mots[0] == « trackon ») { // —————————– commande set track power on
spl (« commande set track power on »);
const char MessageUDP[] = {0x07,0x00,0x40,0x00,0x21,0x81,0xA0};
//sendPacketL(MessageUDP, 10, {192,168,1,1},{21105}); // envoi à la centrale
//sendPacketL(MessageUDP, 10, {192,168,1,1},{21105}); // envoi à la centrale
sendPacketL(MessageUDP, 7, IP_CENTRALE,{21105}); // envoi à la centrale
}
if (Mots[0] == « trackoff ») { // —————————– commande set track power off
spl (« commande set track power off »);
const char MessageUDP[] = {0x07,0x00,0x40,0x00,0x21,0x80,0xA1};
//const char MessageUDP[] = {0x06,0x00,0x40,0x00,0x80,0x80};
//sendPacketL(MessageUDP, 10, {192,168,1,1},{21105}); // envoi à la centrale
sendPacketL(MessageUDP, 7, IP_CENTRALE,{21105}); // envoi à la centrale
}
if (Mots[0] == « loco ») { // —————————– commande get loco mode
sp (« commande loco « );
spl (Mots[1].toInt(),DEC);
MessageUDP[0] = 6; // longueur LSB = 0x06
MessageUDP[1] = 0x00; // longueur MSB
MessageUDP[2] = 0x60; // header LSB(0x60)
MessageUDP[3] = 0x00; // header MSB
MessageUDP[4] = 0x00; // MSB adresse loco
MessageUDP[5] = Mots[1].toInt(); //LSB adresse loco
sendPacketL(MessageUDP, 6, {192,168,1,1},{21105}); // envoi à la centrale
}
if (Mots[0] == « vit ») { // —————————– commande de vitesse (loco, sens, cran)
sp (« vitesse loco « );
sp (Mots[1].toInt(),DEC);
sp ( » sens « );
sp (Mots[2].toInt(),DEC);
sp ( » cran « );
spl (Mots[3].toInt(),DEC);
spl(« envoi du paquet »);
CommandeVitesse(Mots[1].toInt() , Mots[2].toInt() , Mots[3].toInt() ); // loco, sens, cran
}
if (Mots[0] == « fonc ») { // —————————– commande des fonctions
sp (« fonction loco « ); sp (Mots[1].toInt(),DEC);
sp ( » F »); sp (Mots[2].toInt(),DEC);
sp ( » « );
spl (Mots[3].toInt(),DEC);
spl(« envoi du paquet »);
CommandeFonction(Mots[1].toInt() , Mots[2].toInt() , Mots[3].toInt() ); // loco, fonction, état
//’E4 F8 set loco function (commande fonctions)
// If (O(11) And 192) = 0 Then ‘TTNNNNNN
// Prt Z21.RTB4, vbBlack, (O(11) And 31) & » off »
// Else ‘par exemple 0B
// Prt Z21.RTB4, vbBlack, (O(11) And 31) & » on »
/*
MessageUDP[0] = 10; // longueur LSB = 0x0A
MessageUDP[1] = 0x00; // longueur MSB
MessageUDP[2] = 0x40; // header LSB(0x40)
MessageUDP[3] = 0x00; // header MSB
MessageUDP[4] = 0xE4; CheckSum = 0xE4; // X-header
MessageUDP[5] = 0xF8; CheckSum = CheckSum ^ MessageUDP[5]; // commande de fonction
MessageUDP[6] = 0x00; // adresse DCC MSB – CheckSum pas modifiée par 0
MessageUDP[7] = Mots[1].toInt(); CheckSum = CheckSum ^ MessageUDP[7]; // adresse DCC LSB
// DB3 = TTNNNNNN où TT = off/on/toggle/n.a. NNNNNN = fonction
MessageUDP[8] = Mots[2].toInt(); // 00NNNNNNN
if (Mots[3].toInt() != 0) {MessageUDP[8] = MessageUDP[8] + 64; } // toggle traité comme on
CheckSum = CheckSum ^ MessageUDP[8];
//ajout de la checksum
MessageUDP[9] = CheckSum;
//sendPacketL(MessageUDP, 10, {192,168,1,10},{50000}); // envoi au PC / Packet Sender
sendPacketL(MessageUDP, 10, {192,168,1,1},{21105}); // envoi à la centrale
*/
}
if (Mots[0] == « udp ») { // —————————– commande udp pour tests
spl (« commande udp… »);
MessageUDP[0] = Mots[1].toInt();
MessageUDP[1] = Mots[2].toInt();
MessageUDP[2] = Mots[3].toInt();
sendPacketL(MessageUDP, 3, {192,168,1,10},{50000}); // envoi au PC / Packet Sender
}
if (Mots[0] == « test ») { // commande test pour clignotement (recherche) d’un port
Port = Mots[1].toInt();
sp (« commande port « ); spl (Port,DEC);
pinMode(Port, OUTPUT);
for (I=1; I<=10; I++){digitalWrite(Port,HIGH); delay(200); digitalWrite(Port,LOW); delay(200);}
}
if (Mots[0] == « xxx ») { // ——————- d’affectation d’une valeur à une variable
if (Mots[1] == « ? ») { // lecture
sp(« valeur: « ); spl(xxx,DEC);
}
else { // écriture
xxx = Mots[1].toInt();spl(« ok »);
sp(« écrit « ); sp(xxx,DEC); sp( » dans xxx « );
}
}
if (Mots[0] == « nnn ») { // ——————————– commande nnn pour numérique
sp (« commande nnn »);
Y = Mots[1].toInt();
sp ( » paramètre: « ); sp (Y);
Y = 2 * Y;
sp (« . Vérif x 2 = « ); spl (Y,DEC);
}
// ————————————————————- fin de commande, reset
//for (NbMots=0; NbMots<=5; NbMots++){ Mots[NbMots]= » »; }; // plante !?
Mots[0]= » »; Mots[1]= » »; Mots[2]= » »; Mots[3]= » »;
NbMots = 0;
//spl(« ok »);
break; // fin de LF
}
default:{ // *************************************************************** concaténation
Mots[NbMots] = Mots[NbMots] + X;};
break;
}
}
void CommandeVitesse(word loco, char sens, char cran) {
char CheckSum;
MessageUDP[0] = 10; // longueur LSB = 0x0A
MessageUDP[1] = 0x00; // longueur MSB
MessageUDP[2] = 0x40; // header LSB(0x40)
MessageUDP[3] = 0x00; // header MSB
MessageUDP[4] = 0xE4; CheckSum = 0xE4; // X-header
MessageUDP[5] = 0x13; CheckSum = CheckSum ^ MessageUDP[5]; // 128 steps
MessageUDP[6] = loco >> 8; CheckSum = CheckSum ^ MessageUDP[6]; // adresse DCC MSB
MessageUDP[7] = loco & 127; CheckSum = CheckSum ^ MessageUDP[7]; // adresse DCC LSB
// DB3 = Cran, If SensAvant Then DB3 = DB3 + 128, Arrêt urgent non traité
MessageUDP[8] = cran + 128 * sens ; CheckSum = CheckSum ^ MessageUDP[8];
//ajout de la checksum
MessageUDP[9] = CheckSum;
//sendPacketL(MessageUDP, 10, {192,168,1,10},{50000}); // envoi au PC / Packet Sender
//sendPacketL(MessageUDP, 10, {192,168,1,1},{21105}); // envoi à la centrale
sendPacketL(MessageUDP, 10, IP_CENTRALE,{21105}); // envoi à la centrale
}
void CommandeFonction(char loco, char fonction, bool etat){
char CheckSum;
MessageUDP[0] = 10; // longueur LSB = 0x0A
MessageUDP[1] = 0x00; // longueur MSB
MessageUDP[2] = 0x40; // header LSB(0x40)
MessageUDP[3] = 0x00; // header MSB
MessageUDP[4] = 0xE4; CheckSum = 0xE4; // X-header
MessageUDP[5] = 0xF8; CheckSum = CheckSum ^ MessageUDP[5]; // commande de fonction
MessageUDP[6] = loco >> 8; CheckSum = CheckSum ^ MessageUDP[6]; // adresse DCC MSB
MessageUDP[7] = loco & 127; CheckSum = CheckSum ^ MessageUDP[7]; // adresse DCC LSB
// DB3 = TTNNNNNN où TT = off/on/toggle/n.a. NNNNNN = fonction
MessageUDP[8] = fonction; // 00NNNNNNN
if (etat != 0) {MessageUDP[8] = MessageUDP[8] + 64; } // toggle traité comme on
CheckSum = CheckSum ^ MessageUDP[8];
//ajout de la checksum
MessageUDP[9] = CheckSum;
//sendPacketL(MessageUDP, 10, {192,168,1,10},{50000}); // envoi au PC / Packet Sender
//sendPacketL(MessageUDP, 10, {192,168,1,1},{21105}); // envoi à la centrale
sendPacketL(MessageUDP, 10, IP_CENTRALE,{21105}); // envoi à la centrale
}
// ======================================================================= impression en hexadécimal amélioré
void PrtHex(char X) {
if (X<16){sp(« 0 »);} sp(X,HEX);
}
0 commentaires