Récupérer automatiquement des redémarrages en boucle du ESP8266
2018-06-12
Un troisième temporisateur de surveillance du ESP8266, version simplifiée Un troisième temporisateur de surveillance du ESP8266, version finale

L'objectif est de gérer la situation quand le ESP8266 est pris dans une boucle de redémarrage. Voici comment cela se présente. Un nouveau micrologiciel est téléversé et le ESP8266 est mis en fonction. Après un certain temps, un bogue se manifeste. Une exception est soulevée et le ESP8266 redémarre. La source de l'exception est toujours présente et elle est de nouveau soulevée, d'où un nouveau redémarrage. Le cycle d'exceptions et démarrages se répète sans fin jusqu'à ce que le ESP8266 soit retiré de sa position pour téléverser une version corrigée du micrologiciel avec une connexion série. Malheureusement, cela est souvent difficile, le dispositif étant dans un endroit peu accessible.

On peut voir la cause du démarrage ou redémarrage d'un ESP8266 dans le moniteur série de l'EDI Arduino tant que le ESP8266 est branché par une connexion série. Il est bien plus utile de récupérer cette information dans le croquis Arduino pour gérer les problèmes récurrents associés à des exceptions de logiciels ou des morsures de chiens de garde examinés dans le billet précédent. Avec cette information on peut lancer un téléversement sans fil du micrologiciel pour revenir à une version qui fonctionnait. Voilà ce qui est proposé ci-dessous. Au préalable, le comportement du ESP8266 au démarrage est examiné.

Table des matières

  1. Démarrage du ESP8266
  2. Récupération de la raison du démarrage du ESP8266
  3. Quelques questions
  4. Gérer le bogue du mode UART
  5. Gérer les cycles de démarrage
  6. Mise en garde importante
  7. Téléchargements

  1. Démarrage du ESP8266
  2. Selon un document de Espressif ESP8266 Reset Causes and Common Fatal Exception Causes, « À chaque redémarrage de l'ESP8266, le code ROM affichera un nombre correspondant à la cause de la réinitialisation ». Il imprime également quelque chose appelé le mode de démarrage (boot mode) qui indique d'où le code a été pris. Cela est visible sur les captures d'écran de la fenêtre du moniteur série de l'EDI Arduino.

       ets Jan 8 2013, rst cause:2, boot mode: (1,6)
    ou
       ets Jan 8 2013, rst cause:4, boot mode: (3,6)

    Le 2 à la droite de rst cause, dénote que le chien de garde logiciel a réinitialisé le dispositif alors que le 4 dans le second exemple dénote que le chien de garde matériel est la cause de la réinitialisation. Quand le premier chiffre du mode de démarrage est égal à 1 cela veut dire que le code actuellement exécuté vient d'être téléchargé sur l'ESP8266. Un 3, comme dans le second exemple, indique que le code provient de la mémoire flash SPI.

    L'explication du mode de démarrage provient d'un document de Max Filippov intitulé Boot Process. Selon ce texte, le nombre affiché à droite de rst cause est la valeur de trois broches du circuit intégré qui contrôlent le comportement de celui-ci à son démarrage. On sait que l'on doit fixer correctement les valeurs logiques des broches GPIO0, GPIO2 et GPIO15 au démarrage du ESP8266 pour le mettre dans le mode désiré. Bien qu'il y ait huit combinaisons possibles, seulement trois sont définies dans le document et seulement deux sont utilisées en pratique.

    GPIO15GPIO0GPIO2Dec.ModeDescription
    0011UARTTélécharger le code du UART (programmation)
    0113FlashCode de la mémoire SPI Flash (usage normal)
    1xx4-7SDIOpas utilisé (voir encadré ci-dessous)

    En situation normale, on désire que le ESP8266 exécute le code déjà contenu dans la mémoire flash lorsqu'il est mis sous tension. Pour cela il faut que la broche GPIO0 soit tirée vers Vcc (3,3 volts). En revanche, quand on veut téléverser un nouveau micrologiciel vers le ESP8266, la broche GPIO0 doit être mise à terre quand la puce est placée sous tension. Une fois le téléversement complété, le code téléversé sera immédiatement exécuté. La broches GPIO15 doit toujours être mise à terre et GPIO2 doit être tirée vers Vcc. Les petits systèmes de développement WeMos D1 mini, pro etc. et NodeMCU gèrent les valeurs de ces trois broches automatiquement et une intervention physique n'est pas nécessaire pour téléverser un micrologiciel vers leur ESP8266. Ce n'est pas le cas pour le commutateur sans fil Sonoff; on doit appuyer sur son bouton-poussoir en appliquant la tension pour téléverser un nouveau micrologiciel. On s'en doute, ce bouton-poussoir raccorde GPIO0 à la terre lorsque le contact est fermé.

    Le mode SDIO. Le tableau fait mention du mode Secure Digital Input Output activé si la broche GPIO15 est tirée vers 3,3 volts qu'importent les valeurs de GPIO0 et GPIO2. Si je comprends correctement, ce mode ne veut pas dire que le ESP8266 peut démarrer à partir de code stocké dans une carte SD à laquelle il serait relié d'une façon quelconque. C'est plutôt le contraire, le ESP8266 pourrait servir de dispositif WiFi pour un portable auquel il serait branché comme une carte SD. Cela me fait penser à un périphérique WiFi qui se branchait à un vieux portable IBM par le truchement du connecteur pour la mémoire de type CF (compact flash, un prédécesseur des cartes SD). C'est un mode ésotérique que je n'anticipe jamais utiliser. Pour les curieux, voici deux références: Nava Whiteford, Nava (2016) The esp8266 and SD Cards et ajlitt (2015) RPi WiFi, Fast RPi WiFi without USB.

    Il est utile pour le programmeur de voir cette information dans le moniteur série de l'EDI Arduino lorsqu'il élabore une nouvelle version de micrologiciel du ESP8266. Cependant, l'information doit être récupérée par programme si le croquis doit l'utiliser.

  3. Récupération de la raison du démarrage du ESP8266
  4. La classe EspClass contient une méthode qui retourne la cause de la réinitialisation du ESP8266 en tant que chaîne: getResetReason(). Il y a une autre fonction, nommée getResetInfoPtr(), qui renvoie un pointeur vers une structure avec des informations au sujet de la réinitialisation du système, y compris la raison de celle-ci. Cette dernière est plus utile pour ce qui suit.

    J'ai écrit un croquis Arduino pour redémarrer le ESP8266 de différentes façons et après afficher l'information recueillie sur la cause du redémarrage. Voici le premier écran affiché dans le moniteur série de l'EDI Arduino après le téléversement du croquis.

    On note l'avertissement qu'il faut appuyer sur le bouton de réinitialisation du dispositif après avoir téléversé ce nouveau micrologiciel vers le ESP8266. Si on ne le fait pas, le ESP ne redémarra pas. S'il n'y a pas de bouton de réinitialisation, on peut simplement couper puis rétablir l'alimentation.

    Si l'on repart le ESP8266 avec la fonction restart() ou reset(), la valeur dans ESP.getResetInfoPtr()->reason est REASON_SOFT_RESTART(=4):

    Redémarrage avec ESP.restart() ou Redémarrage avec ESP.reset() ets Jan 8 2013,rst cause:2, boot mode:(3,6) ... Raison du démarrage: (4) getResetReason: Software/System restart Fonction restart() ou reset() invoquée.

    Si l'on provoque la morsure des chiens de garde materiel puis logiciel du ESP8266, la raison du redémarrage sont REASON_WDT_RST(=1) et REASON_SOFT_WDT_RST(=3) respectivement.

    Si le dispositif est réinitialisé en activant la broche RESET, en appuyant sur le bouton-poussoir nommé RESET de système de développement WeMos D1 mini d'un NodeMCU par exemple, alors la raison est REASON_EXT_SYS_RST(=6).

    Si une exception est soulevée pendant l'exécution du micrologiciel, le ESP8266 est reparti et la raison donnée est REASON_EXCEPTION_RST(=2). On peut également récupérer l'identité de l'exception dans la structure d'information de redémarrage: getResetInfoPtr()->exccause.

    Il y a d'autres raisons pour le redémarrage du ESP8266 mais celles-ci ne sont pas directement accessibles à partir du croquis. Si l'on met la puce dans un «sommeil profond», pour diminuer au maximum l'énergie consommée, alors à son réveil, qui se fait en activant une broche de la puce, le ESP est redémarré et la raison de ce départ est REASON_DEEP_SLEEP_AWAKE(=5). Il y a aussi REASON_DEFAULT_RST(=0) qui correspond à la mise sous tension du ESP8266.

  5. Quelques questions
  6. La démarche précédente soulève des questions, dont les suivantes.

    Pourquoi faut-il appuyer sur le bouton RESET après avoir téléversé le micrologiciel ?
    Ivan Grokhotkov donne une description détaillée de ce bogue. En gros, quand le ESP8266 est en mode UART il reste dans ce mode même après avoir été redémarré avec ESP.restart() ou une exception etc. Il ne vérifie pas l'état des 3 broches GPIO15, GPIO2 et GPIO0. Donc, au redémarrage, il ne fait que vérifier l'entrée série en attente d'un téléversement d'un nouveau micrologiciel. C'est ce qui nous porte à croire que le dispositif est en panne.
    Y a-t-il une façon de redémarrer le ESP8266 pour contourner ce bogue ?
    Non. Seule une réinitialisation manuelle de ESP8266 arrive à lui faire quitter le mode UART après un téléversement. Une approche un peu radicale à ce problème fait l'objet de la section suivante.
    Pourquoi la raison POWER_ON n'est pas affichée par le WeMos D1 mini ou NodeMCU alors qu'on l'observe quand on met un Sonoff en marche ?
    Il y a une discussion sur ce sujet sur le Wiki de ESP8266-Arduino. La conclusion qui semble s'en dégager est que cette différence est causée par la conception des circuits entourant le ESP8266. J'ai comparé les schématiques du WeMos D1 et du Sonoff en examinant de près les connexions à la broche 32 (RST) de la puce. Chez le Sonoff, la broche est directement reliée à Vcc (3,3V) en passant par une résistance de 10K ohms probablement pour limiter le courant. Donc dès que l'appareil est mis sous tension, RST a une valeur logique de 1 (HIGH) et est inactif. Pour ce qui est du D1 mini, la broche est connectée à Vcc à travers une résistance de 12K ohms et aussi à la terre à travers un condensateur de 100 nF. Il s'agit d'un circuit R-C classique dont une utilisation courante est de ralentir l'augmentation de la tension. Il faut un certain temps pour que le condensateur n'atteigne sa pleine charge. Selon une calculatrice il faudra 1,2 ms avant que la tension entre les bornes du condensateur atteigne 2,08V. C'est comme si l'on appuyait sur le bouton-poussoir RESET quand on met sous tension ce dispositif pour le relâcher plus tard. Ce qui expliquerait pourquoi la raison donnée pour le démarrage est REASON_EXT_SYS_RST(=6).

    Logiquement, on devrait observer deux démarrages quand le WeMos D1 mini est mis sous tension: REASON_DEFAULT_RST suivi très rapidement de REASON_EXT_SYS_RST. Il me semble qu'il serait possible de tester cela, mais je suis trop paresseux pour essayer et je préfère me contenter de mon explication ci-dessus, qui pourrait être complètement fausse.

    En passant, on peut définir la directive MULTI_ANNOUNCEMENT dans le croquis pour faire afficher la cause initiale du démarrage du ESP8266. Malheureusement, ce petit truc ne permet pas de vérifier s'il y a deux démarrages en succession rapide.

    Quelle est la différence entre ESP.restart() et ESP.reset() ?
    Elle n'est pas claire en ce qui me concerne. Selon Links2004, ESP.reset() correspond à activer la broche RST du ESP8266 alors que ESP.restart() effectuerait un redémarrage plus propre. J'utilise toujours cette dernière instruction depuis que j'ai lu ce commentaire au tout début de mes recherches sur la puce. Je n'ai donc aucune expérience avec ESP.reset() ce qui m'enlève toute possibilité d'en faire la comparaison.

  7. Gérer le bogue du mode UART
  8. Le bogue du mode UART, si je peux utiliser ce terme, a été mentionné à quelques reprises ici et dans le billet précédent. Il importe de souligner que ce bogue est pratiquement sans importance. Il ne m'est jamais arrivé de téléverser un nouveau micrologiciel par connexion série vers un ESP8266 et d'avoir utilisé ce dernier sans le débrancher.

    En fait, ce bogue est nuisible aux néophytes qui en sont à leurs premiers essaies et qui sont facilement découragés quand ils ont l'impression que le micrologiciel fraîchement téléversé ne fonctionne plus pour une raison insaisissable alors qu'il fonctionnait il y avait à peine quelque secondes.

    Ceci étant dit, il m'arrivait de perdre du temps à cause de lui parce que j'avais oublié que je venais de téléverser un croquis. Cela arrive le plus souvent dans le feu de l'action après une longue séquence de téléversements pour essayer de comprendre ou corriger une erreur qui n'est nullement liée au bogue. Depuis assez longtemps, j'ai opté pour une approche un peu radicale. Mes croquis sont toujours en panne après un téléversement du micrologiciel. Donc impossible de tester ce dernier avant de repartir manuellement le ESP8266.

    Pour autant que je sache, il n'y a pas de méthode dans la classe EspClass pour récupérer le mode d'opération. Mais un contributeur au forum de la communauté ESP8266, dont le sobriquet est Off, a élaboré une routine en assembleur pour le faire. Voici comment je l'utilise au début de la fonction setup() de mes croquis Arduino pour ESP8266.

    extern "C" { #include "user_interface.h" } int getBootDevice(void) { int bootmode; asm ( "movi %0, 0x60000200\n\t" "l32i %0, %0, 0x118\n\t" : "+r" (bootmode) /* Output */ : /* Inputs (none) */ : "memory" /* Clobbered */ ); return ((bootmode >> 0x10) & 0x7); } void setup() { Serial.begin(115200); if ( getBootDevice() == 1 ) { Serial.println("\n\nLe ESP8266 est encore en mode UART et les redémarrages"); Serial.println("ne fonctionneront pas correctement. Appuyez sur le bouton"); Serial.println("RESET ou interrompez temporairement l'alimentation pour"); Serial.println("sortir de ce mode."); while (1) { yield(); } } } ...

  9. Gérer les cycles de démarrage
  10. Il y a trois causes de cycles de démarrage du ESP8266.

    ValeurRaisonSource
    1REASON_WDT_RSTChien de garde matériel
    3REASON_SOFT_WDT_RSTChien de garde logiciel
    2REASON_EXCEPTION_RSTException

    Si le ESP8266 est démarré pour l'une de ces trois raisons, on peut tenter un téléversement sans fil (OTA - over the air) du micrologiciel pour en installer une version correcte. Heureusement, tout le travail pour mettre en oeuvre cette opération est déjà codé dans la bibliothèque ESP8266httpUpdate. Celle-ci est automatiquement installée dans l'EDI Arduino avec les autres bibliothèque du ESP8266. J'ai modifié quelque peu le croquis le plus simple des deux exemples fournis avec la bibliothèque. Voir le menu Fichier/Exemples/ESP8266httpUpdate de l'EDI Arduino.

    #include <ESP8266WiFi.h> #include <ESP8266httpUpdate.h> // include one and only one of the following //#include "esp_boot_ota_en.h" #include "esp_boot_ota_fr.h" extern "C" { #include "user_interface.h" } #define VERSION 0x000100 // version 0.1.0 #define SSID_SZ 33 // maximum size of SSID #define PWD_SZ 65 // maximum size of password minimum 8 /* * Fallback WiFi network credentials */ #define NET_SSID "" #define NET_PSK "" /* * WiFi network credentials for the http server that can serve firmware OTA. */ #define OTA_SSID "" #define OTA_PSK "" /* * URL of the firmware to upload OTA if necessary */ #define AUTO_URL "http://192.168.1.22/ota/test.bin" /* * getBootDevice returns 1 if the code being executed has * been downloaded over the UART */ int getBootDevice(void) { int bootmode = 0; asm ( "movi %0, 0x60000200\n\t" "l32i %0, %0, 0x118\n\t" : "+r" (bootmode) /* Output */ : /* Inputs (none) */ : "memory" /* Clobbered */ ); return ((bootmode >> 0x10) & 0x7); } /* * Stops ESP when firmware has just been downloaded over UART. */ void stopUartMode(void) { if (getBootDevice() == 1) { for (int i = 0; i < 3; i++) { Serial.println(stopUartModeString[i]); } while (1) { yield(); } } } /* * Define the time allowed for establishing a Wi-Fi connection * in 100's ms (1/10 seconds) */ #define CONNECT_TIME 1200 // 1200*100 = 120 000 ms = 2 minutes for WiFi connection /* * Connects to a Wi-Fi network with the specified SSID and password (which can * be empty). Used to connect to working WiFi network and OTA WiFi network. */ boolean connectWiFi(char * ssid, char * pswd, int connectTime = CONNECT_TIME) { if ((strlen(ssid) == 0) && (WiFi.SSID().length() == 0)) { Serial.println(S_NET_SSID_NOT_DEFINED); return false; } if ( WiFi.isConnected() && ( !strlen(ssid) || (!strcmp(ssid, WiFi.SSID().c_str())) ) ) { Serial.printf(S_ALREADY_CONNECTED, ssid); return true; } for (int i=0; i < 4; i++) { if (WiFi.mode(WIFI_STA)) break; if (i >= 3) { Serial.println(S_UNABLE_TO_SET_WIFI_STA); return false; } delay(100); } if ( strlen(ssid) ) { Serial.printf(S_ATTEMPT_CONNECT, ssid); WiFi.begin(ssid, pswd); } else { Serial.printf(S_ATTEMPT_CONNECT, WiFi.SSID().c_str()); WiFi.begin(); } /* * This is similar to WiFi.waitForConnectResult() */ for (int i = 0; i < connectTime; i++) { delay(100); // switch (WiFi.status()) { case WL_CONNECTED: Serial.printf(S_CONNECTED_WIFI, WiFi.SSID().c_str()); return true; break; case WL_NO_SSID_AVAIL: Serial.printf(S_NET_NOT_FOUND, WiFi.SSID().c_str()); return false; break; case WL_CONNECT_FAILED: Serial.println(S_BAD_PASSWORD); return false; break; } } Serial.println(S_NET_CONNECT_TIMEOUT); return false; } boolean performOtaUpdate(const char *url) { ESPhttpUpdate.rebootOnUpdate(false); // we will take care of reboot after some housekeeping switch(ESPhttpUpdate.update(url)) { case HTTP_UPDATE_FAILED: Serial.printf(S_HTTP_UPDATE_FAILED, ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); return false; break; case HTTP_UPDATE_NO_UPDATES: Serial.println(S_HTTP_UPDATE_NO_UPDATES); return false; break; case HTTP_UPDATE_OK: Serial.println(S_HTTP_UPDATE_OK); return true; break; } } void otaUpdate(void) { char currentSsid[SSID_SZ] = {0}; char currentPsk[PWD_SZ] = {0}; char otaSsid[SSID_SZ] = {0}; char otaPsk[PWD_SZ] = {0}; boolean needRestart = false; Serial.println(S_ATTEMPT_OTA); if (!strlen(AUTO_URL)) { Serial.println(S_NO_OTA_URL); return; } // begin by saving the current WiFi credentials if (WiFi.SSID().length()) { strlcpy(currentSsid, WiFi.SSID().c_str(), SSID_SZ); if (WiFi.psk().length()) { strlcpy(currentPsk, WiFi.psk().c_str(), PWD_SZ); } } if (strlen(OTA_SSID)) { strlcpy(otaSsid, OTA_SSID, SSID_SZ); strlcpy(otaPsk, OTA_PSK, PWD_SZ); } else if (strlen(currentSsid)) { // WiFi.SSID strlcpy(otaSsid, currentSsid, SSID_SZ); strlcpy(otaPsk, currentPsk, PWD_SZ); } else if (strlen(NET_SSID)) { strlcpy(otaSsid, OTA_SSID, SSID_SZ); strlcpy(otaPsk, OTA_PSK, PWD_SZ); } if (!strlen(otaSsid)) { Serial.println(S_NO_OTA_SSID); return; } if (connectWiFi(otaSsid, otaPsk)) { needRestart = performOtaUpdate(AUTO_URL); } // Restore the initial WiFi network credentials if needed. if (strcmp(currentSsid, otaSsid)) { WiFi.disconnect(); Serial.println(WiFi.SSID()); if (strlen(currentSsid)) { connectWiFi(currentSsid, currentPsk); } } // And now restart the ESP if (needRestart) { Serial.println(S_RESTARTING_ESP); Serial.flush(); ESP.restart(); } } void setup() { Serial.begin(115200); delay(100); Serial.printf("\n\nesp_restart_ota (version %d.%d.%d)\n", (VERSION >> 16) & 0xFF, (VERSION >> 8) & 0xFF, VERSION & 0xFF); stopUartMode(); delay(5000); // enough time for WiFi autoconnect ? boolean needUpdate = false; switch (ESP.getResetInfoPtr()->reason) { case REASON_WDT_RST: Serial.println(S_HARDWARE_WD); needUpdate = true; break; case REASON_EXCEPTION_RST: Serial.printf(S_EXCEPTION_OCCURED, ESP.getResetInfoPtr()->exccause); needUpdate = true; break; case REASON_SOFT_WDT_RST: Serial.println(S_SOFTWARE_WD); needUpdate = true; break; } if (needUpdate) { otaUpdate(); } if (WiFi.status() != WL_CONNECTED) { connectWiFi(NET_SSID, NET_PSK); } Serial.println(S_SETUP_DONE); } int count = 3; // execute loop() for 3x10 seconds before doing a watch dog timeout void loop() { Serial.print("loop() "); if (WiFi.status() == WL_CONNECTED) { Serial.printf (S_WIFI_SSID_IP, WiFi.SSID().c_str(), WiFi.localIP().toString().c_str()); } else { Serial.println(S_WIFI_NOT_CONNECTED); } // wait 10 seconds delay(10000); count -= 1; if (count <= 0) { Serial.printf("loop() %s\n", S_TESTING_WITH_WD); while(1) { } // WDT timeout! } }

    La seule complication qu'on retrouve dans cet exemple découle du fait que le réseau Wi-Fi où se trouve le serveur HTTP pour la mise à jour sans fil pourrait être que celui utilisé par le ESP8266. Normalement, le ESP se rebranche automatiquement au dernier réseau Wi-Fi utilisé. Si l'on veut que la connexion au réseau de travail se fasse après une mise à jour du micrologiciel sans fil, il faut rétablir la connexion originale avant de repartir le ESP. Voilà pourquoi on retrouve l'instruction ESPhttpUpdate.rebootOnUpdate(false); au début de la fonction performOtaUpdate(). Sans cela, cette dernière ne retourne pas la valeur true quand la mise à jour fonctionne parce qu'elle redémarre l'ESP avec la fonction ESP.restart().

  11. Mise en garde importante
  12. Je ne propose pas que l'on adopte la stratégie présentée ci-dessus. C'est une ébauche présentée à des fins « pédagogiques ». Il y a deux complications qu'il faut souligner.

    Premièrement, la mise à jour sans fil automatique, qui dans les faits est un retour vers une version antérieure du micrologiciel, se fait trop promptement. Une bonne proportion des problèmes rencontrés avec notre système de domotique est liée à des coupures de tension ou peut-être des surtensions transitoires quand le courant est rétabli. Il n'y a pas de faute de programmation; la plupart du temps, le dispositif fonctionne correctement après un simple redémarrage.

    Deuxièmement, le mécanisme proposé ci-dessus ne fonctionne pas avec le chien de garde loop décrit dans le billet précédent. On se souviendra que la morsure de celui-ci se termine par un redémarrage du ESP avec l'instruction ESP.restart(). Si l'on ajoutait REASON_SOFT_RESTART comme raison pour faire une mise à jour sans fil, il s'en suivrait un cycle sans fin de mise à jour automatique puisque cette opération se termine elle aussi avec un ESP.restart().

    Il y a des façons de contourner en partie ces problèmes. On pourrait mettre en place le fichier binaire contenant la version « sure » du micrologiciel sur le serveur HTTP qu'à des moments stratégiques. Il est probable que les problèmes récurrents se manifesteront après l'installation d'un nouveau micrologiciel par exemple. La morsure du chien de garde loop pourrait se terminer avec while (1) {} qui déclencherait une morsure du chien de garde logiciel.

    Ces solutions de contournement ne me satisfont pas. Je préfère compter le nombre de fois qu'une même raison est la cause de redémarrages consécutifs du ESP et procéder à un téléchargement d'une version « sure » du micrologiciel seulement quand ce compte atteint une valeur critique. Ceci exige que l'on conserve en mémoire la cause du précédent redémarrage. Du coup, il devient possible de distinguer la morsure du chien de garde loop des autres raisons de redémarrage.

    Voilà ce qui sera abordé dans le prochain billet. Tout cela est déjà disponible depuis quelques mois, mais je vais réécrire le code pour qu'il soit plus clair que le mécanisme que j'avais proposé est indépendant du troisième chien de garde. Il sera tout aussi utile pour contourner le premier problème soulevé ci-dessus dans un croquis qui n'utilise pas ce chien de garde. simple de s'en servir.

  13. Téléchargements
  14. esp_boot_reason.zip
    Contient le croquis esp_boot_reason.ino qui signale la cause d'un redémarrage du ESP et qui permet à l'utilisateur de sélectionner la raison du démarrage suivante.
    esp_restart_ota.zip
    Contient le croquis esp_restart_ota.ino montrant comment récupérer automatiquement quand le ESP s'engage dans une boucle de redémarrages.

    Chacun de ces croquis est accompagné de deux fichiers d'en-tête contenant toutes les chaînes du programme en anglais et en français. Un seul de ces fichiers doit être inclue dans le croquis. On est loin du mécanisme GNU gettext.

Un troisième temporisateur de surveillance du ESP8266, version simplifiée Un troisième temporisateur de surveillance du ESP8266, version finale