Mise à jour: 2018-06-27
Le billet précédent présentait un mécanisme de récupération des démarrages en catastrophe du ESP8266 trop prompt à tenter un téléversement sans fil d'un nouveau micrologiciel. Il se peut que le ESP redémarre à cause d'une exception elle même causé par un problème passager du réseau électrique. Il est inutile de recharger le micrologiciel du ESP, il n'est pas fautif.
Ci-dessous une méthode pour garder la trace de la raison des démarrages du ESP8266 est présentée. En gros, il s'agit de conserver la raison du dernier démarrage ainsi que le nombre de démarrages consécutifs pour cette raison dans un enregistrement dans la mémoire de l'horloge en temps réel (Real Time Clock ou RTC en anglais).
Table des matières
- Mémoire RTC
- Alignement des variables
- Compter le nombre de démarrages successifs pour une même raison
- Compteur de démarrages et chien de garde loop
- Mise à jour du 27 juin 2018
- Les valeurs des raisons pour démarrage ont été
changées. Le tableau les énumérant ci-dessus dans le paragraphe Compteur de démarrages et
chien de garde loop est à jour.
La seule raison pour ce changement est qu'il me semblait plus facile de gérer les raisons si une seule comparaison permettait d'identifier les trois causes de redémarrage associées au chien de garde loop:
if (restartReason >= REASON_LWD_RST) { // nécessairement l'un de REASON_LWD_RST, REASON_LWD_LOOP_RST ou REASON_LWD_OVW_RST // et restartData est l'identificateur du dernier module vu ... } - La fonction
getResetReasonEX()a été ajoutée. Comme la fonctionESP.getResetReason()le résultat est de typeString. Elle fournit de l'information supplémentaire comme le nombre de démarrages consécutifs et l'identificateur du dernier module vu dans le cas des démarrages causés par le chien de garde loop ou la valeur du paramètre pour les fonctionsuserReset()etuserRestart().Étant dubitatif quant à la valeur réelle de cette fonction, je ne l'ai pas incluse dans les bibliothèques. Cependant elle l'est dans les exemples
esp_boot_lwdtetesp_boot_lwdt_sf. L'inclusion de la fonction est contrôlée par uneINCLUDE_GETRESETREASONEXdans le fichiermdEspRestart.houmdEspRestartSF.h. - Une coquille a été corrigée dans les fichiers
keywords.txtdes bibliothèques. - Conclusion
- Téléchargements
Les informations au sujet des redémarrages sont enregistrées dans la mémoire non volatile de l'horloge temps réel de l'ESP8266. La capacité de cette mémoire est relativement limitée; elle ne compte que 768 octets dont les 256 premiers octets sont réservés pour le système. Il ne reste que 512 octets accessibles à l'utilisateur. Or cette ressource est précieuse, car son contenu n'est pas effacé quand le ESP8266 est placé en mode de veille profonde alors que les données stockées dans la mémoire vive sont perdues.
Il n'y a pas d'accès direct à la mémoire RTC. Celle-ci est divisée en 192 tranches de 4 octets. Pour lire le contenu du 6e octet, il faut lire la deuxième tranche de 4 octets puis récupérer les 8 bits désirés dans ces 32 bits.

Les 64 premières tranches sont réservées pour le système. Donc
l'utilisateur est libre d'utiliser les tranches dont l'adresse est entre 64
et 191. Il est plus pratique de les numéroter de 0 à 127. C'est ce que font
les fonctions rtcUserMemoryRead() et
rtcUserMemoryWrite() de la classe EspClass. De
façon semblable, la fonction
déclarée dans mdEspRestart.h est utilisée pour spécifier le
numéro de tranche de 0 à 127 où l'information au sujet du dernier démarrage
sera stockée. Si l'on spécifie la valeur par défaut
DEFAULT_ADDRESS ou si la fonction
setRestartRtcAddress n'est pas invoquée, l'information est
conservée à la toute fin de la mémoire RTC.
L'information est dans une structure nommée restart. Le
préprocesseur « calcule » l'adresse par défaut qui est affectée à
la variable globale rtcAddress. RESTART_BUCKET_SIZE
est le plus petit multiple de 4 plus grand ou égal à la taille de
restart. Si l'on préfère, RESTART_BUCKET_SIZE/4 est
le nombre minimum de tranches pouvant contenir la structure
restart.
Comme on peut voir, la fonction vérifie que l'adresse spécifiée
ne dépasse pas la dernière tranche possible étant donnée la taille de
restart. Autrement la fonction renvoie la valeur logique
faux (false).
Puisque la validation de l'adresse RTC est faite avec
setRestartRtcAddress, je n'utiliserai pas les fonctions de
lecture et d'écriture de la classe EspClass qui refont cette
validation à chaque étape. D'ailleurs, il y a une erreur dans cette étape.
Voici l'une des fonctions.
Le paramètre offset spécifie la tranche dans l'espace utilisateur
et elle doit donc avoir une valeur entre 0 et 127. Le pointeur
data est l'adresse de la variable devant accueillir les données
lues et size est le nombre d'octets à lire. Il est donc possible
de lire plus de 4 octets à la fois. Clairement, le test devrait être
(size + (64 + offset)*4 > 768) ou (size + offset*4 > 512)L'erreur a été soulignée et devrait être corrigée bientôt.
Voulant diminuer le plus possible la taille de l'enregistrement conservé dans la mémoire RTC, j'ai créé une structure de 4 octets.
Un octet est utilisé pour sauvegarder la raison du démarrage
(reason) et le compte du nombre de redémarrages consécutifs pour
cette même raison est conservé dans deux octets (count).
L'octet entre (data) contiendra de l'information supplémentaire
dont il sera question plus tard.
L'ordre des éléments d'un struct peut être important.
Initialement l'ordre était différent.
Cette structure occupe 5 octets.
| adresse | variable | taille |
| 24 | reason | 1 |
| 25 | remplissage | 1 |
| 26 | data | 2 |
| 28 | count | 1 |
La raison est que le compilateur rajoute un octet de remplissage après
reason pour que la variable de deux octets data
ait une adresse qui est un multiple de deux ce qui permet un accès plus
rapide.
On peut éliminer cette action du compilateur avec un attribut.
Alors la structure occupera 4 octets.
| adresse | variable | taille |
| 24 | reason | 1 |
| 25 | data | 2 |
| 27 | count | 1 |
En revanche, l'accès à ses membres sera plus lent. Il est préférable de s'en tenir à la structure proposée ci-dessus.
Le SDK de Espressif contient deux fonctions pour accéder à la mémoire RTC dont on a déjà vu une en action ci-dessus. Voici leur déclaration.
Il s'avère que l'alignement de la destination (des_addr) et
de la source src_addr) est important. Il faut que ces variables
soient alignées sur une limite de 32 bits, en d'autres mots, leur adresse doit
être un multiple de 4. Sinon, les fonctions ne font rien, sauf retourner la
valeur faux (false). La documentation de Espressif est très claire à ce sujet,
Espressif, (2018/05) ESP8266 Non-OS SDK API Reference, Version 2.2.1, "3.3.23 system_rtc_mem_write" p. 19.
https://www.espressif.com/sites/default/files/documentation/2c-esp8266_non_os_sdk_api_reference_en.pdf
Malheureusement, je n'avais pas lu la documentation et c'est presque par
hasard que j'ai enfin compris que le problème provenait de l'alignement de
restart. Le compte des démarrages consécutifs pouvait être bon
puis après quelques ajouts mineurs au croquis, le compte restait fixé à 1
qu'importe le nombre de fois que je redémarrais l'ESP d'une façon donnée. La
raison était l'alignement de restart sur une limite de 16 bits,
car le plus gros membre de la structure est de 16 bits seulement. Selon la
taille des autres variables du croquis, l'adresse de restart,
toujours un multiple de 2, pouvait aussi être un multiple de 4 et alors tout
fonctionnait ou ne pas l'être et alors les données sauvegardées en mémoire
RTC n'étaient copiées dans restart par
system_rtc_mem_read. Heureusement, on peut obtenir l'alignement
désiré avec l'attribut aligned. La déclaration de la structure
restart dans le fichier mdEspStart.cpp garantit
qu'elle pourra être lue de la mémoire RTC.
Mentionnons un autre piège associé aux fonctions
system_rtc_mem_write et system_rtc_mem_read. Les
paramètres load_size et save_size spécifient le
nombre d'octets à lire ou à écrire sur la mémoire RTC mais en fait, le nombre
réel d'octets lus ou écrits est arrondi au plus petit multiple de 4 plus
grand ou égal à la taille spécifiée. J'insiste sur ce fait, si
restart faisait 5 octets alors
system_rtc_mem_read(64, &restart, 5) lirait deux
tranches de mémoire RTC, les numéros 64 et 65, et les trois derniers octets
des huit lus empiéteraient sur les variables qui suivent
restart.
Les fonctions rtcUserMemoryRead() et
rtcUserMemoryWrite() de la classe EspClass sont
aussi assujetties à ces deux contraintes évidemment.
Voici comment tenir compte des causes des redémarrages de l'ESP au
début du croquis dans la fonction setup(). On commence en
récupérant de la mémoire RTC l'information au sujet du démarrage précédent.
Puis on compare la cause de ce dernier avec la cause du démarrage en
cours. Si c'est la même, la valeur du compteur de démarrages consécutifs
count est augmentée, sinon on remet le compteur à 1. Puis on
sauvegarde l'information du démarrage en cours dans la mémoire RTC.
Après on examine la cause du démarrage courant pour décider de l'action à prendre. Comme dans le billet précédent, on pourrait remplacer le micrologiciel en place avec une version antérieure, mais en plus on peut faire ce geste un peu drastique seulement si le compteur de démarrages consécutifs a atteint une valeur critique. J'utilise 3 comme valeur critique pour les redémarrages en boucle causés par les chiens de garde et les exceptions.
Voilà pour les grandes lignes. Comme toujours, il y a des complications. Toutes les exceptions sont traitées comme si elles étaient une même cause de démarrage. C'est peut-être la bonne stratégie, mais j'ai décidé de traiter différentes exceptions comme étant différentes causes de démarrage. Plus précisément, la cause reste une exception, mais le compteur est remis à un si une deuxième exception est différente de l'exception qui aurait causé le démarrage précédent.
Comme on peut voir, cela est fait avec un test un peu plus complexe pour
décider si la cause du démarrage actuel est la même que celle du démarrage
antérieur. Pour vérifier si une même exception s'est produite deux fois de
suite, on sauvegarde l'identité de l'exception dans data qui est
un membre de la structure restart, .
On peut se demander comment le processus commence; d'où vient
l'information au sujet d'un démarrage précédent qui ne peut pas exister lors
du tout premier démarrage ? J'ai décidé d'inclure un identificateur dans
l'enregistrement sauvegarder dans la mémoire RTC. Si cet identificateur n'est
pas présent dans les données provenant de la mémoire RTC, les valeurs de la
structure restart sont remises à zéro en quelque sorte.
Puisque restart est déjà de la taille d'une tranche complète
de mémoire RTC, ajouter un membre pour agir comme identificateur doublerait
la taille qu'il occupe dans la mémoire RTC. Or il n'y a que 7 causes de
redémarrage alors il n'est pas nécessaire de consacrer un octet en entier pour
sauvegarder cette information. Donc l'identificateur sera une valeur
particulière 0b1011 = 0xB stockée dans les 4 bits supérieurs du
membre reason de restart.
Avec ces définitions il est facile de récupérer soit l'identificateur, soit la raison ou enregistrer une raison pour un démarrage.
restart.reason & REASON_MASK |
la cause du démarrage |
restart.reason & MARKER_MASK |
l'identificateur; si égal à RESTART_MARKER il s'agit
d'un enregistrement valide. |
RESET_MARKER | (aReason & REASON_MASK) |
valeur à mettre dans restart.reason pour enregistrer la
cause aReason et inclure l'identificateur d'un
enregistrement. |
Maintenant que la structure restart est correctement définie
et que l'adresse de la tranche de mémoire RTC à utiliser est fixée, il
est très facile de lire et d'écrire l'enregistrement restart.
Voici les fonctions de mdEspRestart.cpp qui prennent en charge
ces opérations.
On peut voir que la vérification de la validité de l'enregistrement lu de la mémoire RTC est un peu plus poussée que ce qui a été décrit ci-dessus. En effet en plus de confirmer la présence de l'identificateur, il faut que la raison de démarrage soit valide aussi.
Comme d'habitude, je rends disponible le code source du croquis
ESP8266 Arduino dont des éléments apparaissent ci-dessus. Notez qu'il s'agit
d'une étape vers la version finale qui sera présentée dans la section
suivante. Conséquemment, mdEspRestart.cpp est truffé de
Serial.print qui m'aidaient à développer le programme. En outre,
la fonction, dumpInfo() vérifie la taille et l'alignement de la
structure restart. Voici un exemple de ce qu'affiche le
programme sur le moniteur série quand on choisi l'option 'C' deux fois de
suite pour redémarrer l'ESP8266.
On peut combiner le chien de garde loop qui a été l'objet du premier billet de cette série (Un troisième temporisateur de surveillance du ESP8266, version simplifiée) avec le mécanisme de sauvegarde de l'information au sujet de la cause du redémarrage du ESP8266 pour obtenir une bibliothèque qui est encore plus simple d'utilisation.
Voici le fichier en-tête de la bibliothèque amputé de ses commentaires.
Le fonctionnement du chien de garde loop est presque entièrement
opaque, caché dans le fichier mdEspRestart.cpp. Il faut quand
même initialiser le chien de garde ce qu'on fait avec la fonction
lwdtInit() vers la fin de la fonction setup() du
croquis. Cette fonction à un paramètre optionnel, le temps d'attente avant
que le chien de garde morde s'il n'est pas nourri. La valeur par défaut est
LWD_TIMEOUT fixée à 12 secondes qui est le double du chien de
garde matériel du ESP.
Le chien de garde doit être nourri au début de la fonction
loop() avec la fonction lwdtFeed(). C'est le seul
endroit où devrait apparaître cette fonction. La fonction
lwdtStamp() (ou lwdtStamp(LOOP_END)) doit être la
dernière instruction de la fonction loop().
À chaque étape importante, le progrès est marqué avec la fonction
lwdtStamp(id) où id est une valeur
numérique unique entre 0 et 2^32-2 = 4 294 967 294. J'ai
appelé cette valeur l'identificateur de module parce que d'habitude dans mes
croquis chaque tâche accomplie dans loop() est faite par une
fonction que j'appelle un module. Si le chien de garde est la métaphore pour
le temporisateur de surveillance, alors la fonction lwdtStamp
est comme le pointage à chaque station que doit faire le gardien de nuit
pendant ses rondes de surveillance.
La fonction getRestartReason() est utilisée vers le début de
la fonction setup() pour gérer le cycle de démarrage. Elle
renvoie la cause du démarrage actuelle et le nombre de fois consécutives que
le démarrage a été fait pour cette même raison dans la variable
count. En plus des sept raisons déjà définies dans le SDK de
Espressif, la bibliothèque en rajoute cinq.
| Raison | Valeur | Description |
| REASON_DEFAULT_RST | 0 | démarrage normal à la mise sous tension |
| REASON_WDT_RST | 1 | redémarrage du chien de garde matériel |
| REASON_EXCEPTION_RST | 2 | redémarrage à cause d'une exception (1) |
| REASON_SOFT_WDT_RST | 3 | redémarrage du chien de garde logiciel |
| REASON_SOFT_RESTART | 4 | redémarrage par programmation (ESP.reset() ou ESP.restart()) |
| REASON_DEEP_SLEEP_AWAKE | 5 | réveil après un sommeil profond |
| REASON_EXT_SYS_RST | 6 | réinitialisation externe du système |
| REASON_USER_RESET | 7 | redémarrage avec userReset() (3) |
| REASON_USER_RESTART | 8 | redémarrage avec userRestart() (3) |
| REASON_LWD_RST | 9 | redémarrage du chien de garde loop |
| REASON_LWD_LOOP_RST | 10 | redémarrage du chien de garde loop, la fonction loop() n'a pas été complétée (2) |
| REASON_LWD_OVW_RST | 11 | redémarrage du chien de garde loop, ses variables ont été écrasées (2) |
Notes
| (1) | La variable data contient le numéro de l'exception. | |
| (2) | La variable data contient l'identificateur du dernier module marqué avec lwdtStamp(). | |
| (3) | La variable data contient la valeur du paramètre de userReset() ou userRestart(). |
Il y a trois nouvelles raisons de démarrage associées au chien de garde loop. Ces raisons ont déjà été examinée (voir Le chien de garde loop dans Un troisième temporisateur de surveillance du ESP8266, version simplifiée.
Il y a aussi les fonctions userReset() et
userRestart() qui peuvent être utilisé à la place de
ESP.reset() et ESP.restart()
respectivement si c'est utile de pouvoir distinguer leur utilisation
au démarrage du croquis. On peut aussi utiliser ces fonctions pour
sauvegarder une valeur de 32 bits (un int par exemple)
en mémoire RTC pour y avoir accès après le démarrage.
Comme avant, la cause du démarrage précédent est conservée dans une
structure restart enregistrée dans la mémoire RTC. Cependant,
j'ai opté pour une structure qui prend 8 octets de mémoire.
Je préférais que le membre data soit assez grand pour
contenir un entier (le type int occupe 4 octets, car le
microprocesseur du ESP8266 est un Tensilica L106 Diamond est de 32 bits). Il
faut aussi un membre booléen de plus à la structure restart pour
gérer les démarrages causés par le temporisateur de surveillance
supplémentaire ou par l'utilisateur. Le champ flag peut à la
fois identifier la validité de l'enregistrement et servir d'identificateur de
démarrage pour raison supplémentaire en lui affectant l'une de deux
valeurs.
RESTART_MARKER dénote un redémarrage pour l'une des sept
raisons habituelles du ESP8266 alors que LWD_USR_MARKER dénote
l'une des cinq nouvelles raisons pour un redémarrage du ESP8266. Toute autre
valeur est une indication que la structure est corrompue. Comme on peut voir,
ces deux valeurs ne diffèrent que d'un bit, c'est le champ booléen
supplémentaire dont il était question ci-dessus. Le masque
MARKER_MASK sert à vérifier que restart.flag
contient l'une des deux valeurs RESTART_MARKER et
LWD_USR_MARKER.
Comme pour le chien de garde, la détermination de la cause du
redémarrage et le calcul du nombre de démarrages consécutifs pour la
même raison sont fait de façon opaque dans mdEspRestart.cpp.
La fonction getRestarReason() renvoi la raison, le compte et
le contenu de data. Avant d'examiner le code, voici sa
logique.

Les étapes de la démarche plus simple d'avant sont indiquées par le fond
beige. Si le redémarrage est fait pour une des raisons standard, il n'y
a pas de grand changement. En revanche si le redémarrage est fait
automatiquement par le chien de garde loop ou par l'utilisateur
en invoquant les fonctions userRestart() ou userReset()
alors il y a des étapes supplémentaire. Premièrement ces fonctions doivent
modifier le contenu de restart pour 1) identifier la raison du
démarrage qui s'en vient, et pour 2) ajouter un indicateur de démarrage
pour raison supplémentaire. On sait déjà que cela est fait en changeant la
valeur de restart.flag à LWD_USR_MARKER.
Deuxièmement, il faut les fonctions doivent sauvegarder le contenu modifié
de restart dans la mémoire RTC. Enfin, elle redémarre le ESP
avec la fonction ESP.restart().
C'est en vérifiant la valeur de restart.flag que
getRestartReason() peut distinguer un démarrage pour l'une des
raisons supplémentaires d'un démarrage cause par ESP.restart()
ou ESP.reset(). A peine deux ou trois lignes de code
additionnelles sont nécessaire dans getRestartReason
pour gérer le démarrage pour raisons supplémentaires. Elles sont en gras
ci-dessous
Préparer restart avant un redémarrage pour raison
supplémentaire n'est pas complexe non plus.
Avec cette bibliothèque, on peut mettre en œuvre la technique de récupération en cas de redémarrages inopportuns décrite dans le billet précédent Récupérer automatiquement des redémarrages en boucle du ESP8266 en tenant compte de la raison et du nombre consécutif de démarrages pour cette raison.
De petites modifications ont été faites à la bibliothèque depuis la première version publique.
Par rapport à la première version du troisième chien de garde que j'ai publiée il y a presque 10 mois, cette nouvelle version est une amélioration. Premièrement, la présentation sous forme de bibliothèque simplifie considérablement l'utilisation. Et deuxièmement, le code est plus clair, du moins je l'espère.
L'ancienne version permettait d'utiliser la mémoire EEPROM plutôt que la
mémoire RTC. J'ai opté pour l'élimination de cette possibilité pour deux
raisons. Avec la création d'une bibliothèque, il fallait ajouter un paramètre
à la fonction setRestartRtcAddress pour spécifier quelle mémoire
non volatile utiliser. Cela voulait dire que la bibliothèque EEPROM était
toujours importée et je voulais éviter cette dépendance. En plus, avec la
prise en charge du compte de démarrage successif, le contenu de
restart est copié sur la mémoire non volatile au moins une fois
à chaque démarrage et deux fois quand celui-ci est à cause d'une raison
supplémentaire. L'usure de la mémoire flash est à éviter.
Il est impossible de créer un programme qui agisse comme chien de garde
sans faille. Supposons que le croquis s'emballe et systématiquement efface le
contenu de la mémoire RTC. Alors le chien de garde loop ne fonctionnera plus,
car la fonction loadRestart() réinitialisera la structure
restart à chaque démarrage.
Un chien de garde matériel, une puce spécialisée, est préférable pour plus de certitude. Cependant, comment garantir que cette puce fonctionne correctement ? Il faudrait un autre puce comme chien de garde du chien de garde. Et une autre après. Impossible de se sortir de cette régression sans fin à moins d'accepter la possibilité d'un bris non anticipé.
Ceci étant dit, j'utiliserai la bibliothèque mdEspRestart
quand j'élaborerai des croquis avec l'espoir qu'elle m'aidera à identifier
des problèmes de programmation.
Auparavant j'avais mentionné l'idée de créer une classe C++ pour encapsuler cette fonctionnalité. Maintenant que je suis « expert » en la matière avec deux classes C++ à mon actif, je pense que je pourrais le faire. Cependant, je doute de l'intérêt de la chose. L'objet serait nécessairement un « singleton » (un des patrons de conception de la Gang of Four). Or je trouve aberrant de créer une classe dont la raison d'être est le polymorphisme et la possibilité de créer plusieurs instances pour après s'assurer qu'il n'y aura qu'une seule instance de l'objet créée et qu'aucune autre classe héritera du singleton. Avec Borland Pascal, Delphi et maintenant Free Pascal, je préfère créer une unité séparée qui n'affiche que les fonctions et attributs publics dans la partie interface et qui cache les détails dans la partie implémentation de l'unité. De point de vue syntaxique, le résultat est presque identique à la création du singleton. Alors j'ai choisi de faire la même chose en C++. Malheureusement, mes connaissances encore très limitées du C/C++, malgré mon expertise auto proclamée ci-dessus, ne me permettent pas de juger du bien-fondé de cette décision. Commentaires ? Conseils ? Il y a un lien vers mon courriel au bas de la page.
On peut télécharger la bibliothèque mdEspRestart.zip qui
s'intègre à l'EDI Arduino avec le gestionnaire de bibliothèque (menu:
Croquis/Inclure une bibliothèque/Ajouter bibliothèque .ZIP...)
Si l'on préfère tester celle-ci avant de l'installer dans l'EDI, télécharger l'exemple esp_boot_lwdt.zip.
Pour les situations où la mémoire RTC est presque toute utilisée à
d'autres fins, il y a une version allégée de la bibliothèque, mdEspRestartSF.zip, qui
n'accapare que 4 octets. L'exemple esp_boot_lwdt_sf.zip
permet de test avant d'installer dans l'EDI. Pour réduire la taille de
l'enregistrement, le type restartCount_t est d'un seul octet et
le type restartData_t occupe deux octets seulement. Enfin,
seulement trois bits de restart.reason servent à confirmer que
restart est valide, le quatrième est utilisé comme indicateur de
raison de démarrage supplémentaire.
getRestartReason() interprète les données lues de la mémoire
RTC comme valide alors qu'elles ne le sont pas quand restart
occupe 8 octets sont très faible, un peu moins de quatre centièmes de 1%
((2/256)×(12/256)). Dans mdEspRestartSF ce type d'erreur
est bien plus probable, presque 9,4% ((1/8)×(12/16)). Il y a une façon
de diminuer cette probabilité à un peu moins de 6% ((1/16)×(15/16))
mais en perdant l'habileté de distinguer userReset() et
userRestart(). Pour ce faire, on garde 4 bits de
restart.reason pour vérifier la validité des données. On change
les codes des raisons de démarrage.
| Raison | Valeur | Description |
| REASON_DEFAULT_RST | 0 | démarrage normal à la mise sous tension |
| REASON_WDT_RST | 1 | redémarrage du chien de garde matériel |
| REASON_EXCEPTION_RST | 2 | redémarrage à cause d'une exception (1) |
| REASON_SOFT_WDT_RST | 3 | redémarrage du chien de garde logiciel |
| REASON_SOFT_RESTART | 4 | redémarrage par programmation (ESP.reset() ou ESP.restart()) |
| REASON_DEEP_SLEEP_AWAKE | 5 | réveil après un sommeil profond |
| REASON_EXT_SYS_RST | 6 | réinitialisation externe du système |
| REASON_USER_RST | 7 | redémarrage avec userReset() ou userRestart() |
| REASON_LWD_RST | 8 | redémarrage du chien de garde loop |
| REASON_LWD_LOOP_RST | 9 | redémarrage du chien de garde loop, la fonction loop() n'a pas été complétée (2) |
| REASON_LWD_OVW_RST | 10 | redémarrage du chien de garde loop, ses variables ont été écrasées (2) |
| REASON_USER_X | 11 | marqueur pour REASON_USER_RST |
| REASON_LWD_X | 12 | marqueur pour REASON_LWD_RST |
| REASON_LWD_LOOP_X | 13 | marqueur pour REASON_LWD_RST |
| REASON_LWD_OVW_X | 14 | marqueur pour REASON_LWD_OVW_RST |
Au lieu de vérifier s'il y a un indicateur de démarrage pour une des raisons additionnelles,
getRestartReason() vérifie si
restart.reason contient l'un des marqueurs (..._X) et, le
cas échéant, choisit comme raison de démarrage la raison supplémentaire
correspondante. Je n'ai pas encore assez d'expérience pour savoir si cette
complication vaut la peine, car pour l'instant j'utilise la version 8
octets.