/* =============== 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 #include #include #include // 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(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); }