Dernièrement, j'ai corrigé une erreur grossière dans un croquis Arduino qui réalisait un temporisateur de surveillance du ESP8266. Cela m'a permis d'examiner mon code avec plus de recul pour me rendre compte qu'il était souhaitable de l'améliorer. J'ai aussi décidé qu'il était temps de traduire les trois billets en anglais sur ce sujet qui datent d'août et septembre de l'année dernière. Encore là, le recul apporte conseil, et il me semble préférable de réécrire plutôt que traduire dans l'espoir qu'une présentation en deux étapes du logiciel sera plus digeste. Pour continuer avec cette métaphore alimentaire, j'espère que mes programmes et billets sont comme ma sauce spaghetti; meilleurs réchauffés.
Alors que les temporisateurs de surveillance de nature matérielle et logiciel du ESP8266 sont essentiels, ils ne sont pas suffisants pour assurer le niveau de fiabilité nécessaire dans un dispositif de l'Internet des objets. Ci-dessous, je propose un troisième chien de garde pour améliorer la fiabilité des appareils basés sur ce circuit intégré fabriqué par Espressif. Il s'agit d'une version simplifiée du chien de garde, la version complète sera présentée dans un billet ultérieur.
Table des matières
- Qu'est-ce qu'un chien de garde informatique ?
- Les chiens de garde du ESP8266
- Le chien de garde loop
- Deux variables
lwdTime
etlwdTimeout
sont modifiées parlwdFeed
. Comme ça il devient plus improbable qu'un dérangement périodique quelconque puisse déjouer le mécanisme de vérification. - La variable
lwdWhere
où est stocké l'identificateur (de 2 octets) du module en exécution par la fonctionlwdStamp
est en fait de 4 octets dont les deux premiers doivent toujours être 0xABBA. Si le gestionnairelwdtcb()
constate que les deux premiers octets delwdWhere
ont changé de valeur, il remplace le contenu de la variable avec un identificateur spécialLWD_OVERRIDEN
et redémarre le ESP8266 aveclwdRestart()
. - À la fin de la boucle
loop()
, un autre identificateur spécialLOOP_START
est placé danslwdWhere
avec la fonctionlwdStamp()
. Comme ça, si le chien de garde mord avant d'exécuter le premier module à l'itération suivante de la boucle, on saura que le problème s'est produit lors des tâches d'arrière-plan fait au début ou à la fin de la boucleloop()
et non pas dans le dernier module. - Conclusion
If the man stops kicking the dog, the dog will take advantage of the
hesitation and bite the man.
Traduction libre: si l'homme cesse d'assaillir à coups de pied le chien, ce dernier profitera de l'hésitation et mordra l'homme.
Niall Murphy, Watchdog Timers.
En principe un temporisateur de surveillance doit réagir rapidement à un dysfonctionnement matériel ou logiciel d'un dispositif en rétablissant son fonctionnement normal, le plus souvent, en effectuant une réinitialisation. Et comment vérifier que le dispositif fonctionne correctement ? Ce dernier doit confirmer à intervalles réguliers que la situation est normale. Si ce signalement n'est pas reçu dans un délai prévu, le système de surveillance passe à l'action.
Le temporisateur de surveillance est communément appelé un chien de garde (watchdog timer en anglais, en abrégé WDT), car il y a une analogie assez forte comme révélée dans l'encadré ci-dessus. Si asséner de coups de pieds un chien virtuel est pénible, on peut remplacer la métaphore avec «nourrir le chien» ce qui est encore plus éloquent à mon avis.
Un chien de garde peut être vu comme un compteur qui est régulièrement décrémenté. Il « mord » quand la valeur du compteur atteint zéro. Il est « alimenté » quand le compteur est remis à sa valeur initiale. Tant que le logiciel d'un dispositif comme un micro contrôleur alimente le chien de garde avant que la valeur critique du compteur ne soit atteinte, le logiciel peut continuer avec ses autres tâches normales. Si une erreur de programmation, une surtension transitoire, ou un rayonnement cosmique à la limite, écarte le logiciel de son fonctionnement prévu, vraisemblablement ce dernier n'alimentera plus le chien de garde et conséquemment l'appareil sera réinitialisé.
Le ESP8266 possède deux chiens de garde: l'un matériel et l'autre logiciel. Le chien de garde matériel est associé à l'unique temporisateur matériel de la puce. Voici un croquis qui provoque la morsure du chien de garde logiciel.
Parce que le processeur est engagé dans la boucle while
qui
est vide et qui ne se termine jamais, le chien de garde logiciel n'est pas
nourri et il mordra en quelques secondes. Si la boucle while
avait été insérée dans la fonction loop()
, le résultat aurait
été le même.
Le croquis suivant provoque la morsure du chien de garde matériel.
Le chien de garde matériel est plus patient que le chien de garde
logiciel. On ne verrait pas la morsure du premier si le second réinitialise
le ESP8266 bien avant que soient écoulées les 6 secondes qu'attend le chien de
garde matériel pour repartir le ESP s'il n'est pas nourri. Voilà la raison
d'être de l'instruction ESP.wdtDisable();
qui stoppe le
fonctionnement du chien de garde logiciel.
Quand un croquis est téléversé puis immédiatement exécuté, le système
se bloque s'il est redémarré par un des chiens de garde, ou par une
exception, avec la fonction restart()
et ainsi de suite. C'est
un problème bien connu qui ne se produit qu'immédiatement après le
téléchargement d'un micrologiciel par liaison série (voir esp8266/Arduino FAQ). Après une réinitialisation manuelle
ou l'arrêt momentané de son alimentation, l'ESP redémarrera sans
faute à chaque fois qu'un chien de garde mord ou qu'une exception se
produise et ainsi de suite.
Comment nourrir les chiens de garde ? En fait c'est rarement
nécessaire d'invoquer explicitement la méthode ESP.wdtFeed()
(ESP
est une instance de la classe EspClass
). Les
deux chiens sont alimentés automatiquement à chaque itération de la procédure
loop()
du croquis, à chaque invocation de la fonction
yield()
et systématiquement pendant l'exécution de la fonction
delay()
.
Dans la version originale en anglais de ce billet, il y a une longue discussion, avec croquis à l'appuie, sur comment mesurer la période des deux temporisateurs de surveillance. Je voulais comprendre pourquoi certains disaient que le chien de garde logiciel mordait après 3,2 secondes et d'autres affirmaient que la période est d'environ 1,5 secondes. Pour faire court, les deux camps ont raison, tout dépend comment le chien de garde logiciel est initialisé. Conséquemment, il est plus prudent de miser sur un délai de 1,5 secondes maximum avant que ne morde le chien de garde logiciel et de 6,2 secondes pour le chien de garde matériel.
Parce qu'il est au cœur du fonctionnement du système, on recommande souvent de ne pas interférer avec le fonctionnement du chien de garde matériel. Aussi bien étendre cette recommandation au chien de garde logiciel à mon avis. Or d'après ce qu'on peut lire sur divers forums, plusieurs cherchent à arrêter le fonctionnement des chiens de garde qui interrompent le micrologiciel qu'ils développent. Il me semble que ce raisonnement est fautif. Le fabricant n'a certainement pas créé ces temporisateurs de surveillance pour des raisons frivoles. Je penche dans l'autre direction et je rajoute un troisième chien de garde à mes micrologiciels. Le croquis suivant explique mon raisonnement.
La boucle while
dans le module badModule
représente une action qui prendra beaucoup de temps. Alors on y invoque
delay()
pour permettre au ESP8266 de compléter ses opérations en
arrière plan dont la gestion des connexions WiFi et les communications par
UART. Malheureusement, les deux chiens de garde du ESP8266 sont
systématiquement nourris et ils ne captent pas le fait que l'on ne revient
jamais à la boucle principale loop()
. Voici ce que l'on
verra avec le moniteur série de l'EDI Arduino.
Simple Loop Watchdog Example GPIO LED pin intialized bad count: 4 bad count: 3 bad count: 2 bad count: 1 bye bye...
À partir de là, plus rien ne se produit. Les seules façons d'interrompre
la boucle sans fin dans laquelle le ESP8266 est engagé sont d'activer le
signal RESET
de la puce ou d'arrêter l'alimentation.
Malheureusement, après sa réinitialisation, l'ESP recommencera à tourner en
rond. C'est à cause de ce genre de problème qu'il faut ajouter un autre
temporisateur de surveillance que je nomme loop watchdog
.
Le troisième chien de garde sera nourri uniquement au début de chaque itération de la boucle loop() du croquis Arduino. C'est ainsi qu'on peut s'assurer que le ESP8266 continue d'exécuter cette boucle au complet. Non seulement le chien de garde loop mord quand la boucle principale du croquis ne s'éxécute plus, mais il indique aussi où le problème s'est produit sans qu'il soit nécessaire de le compliquer outrageusement.
Le temps qu'on accorde au chien de garde loop (lwdt pour faire plus court) est critique. Le délai d'attente du lwdt doit être supérieur à celui des chiens de garde ESP8266 intégrés et supérieur au scénario le plus défavorable pour l'exécution de la boucle du programme. C'est une bonne pratique d'ajouter du temps supplémentaire. En revanche, un délai trop long pourrait avoir de fâcheuses conséquences si le ESP8266 est affecté à un contrôle ou au suivi d'un processus physique en temps réel.
Dans l'exemple ci-dessous, l'attente de 12 secondes, le double du délai
du chien de garde matériel, semblaient être une période raisonnable.
Cet exemple reprend celui de la section précédente pour montrer comment
lwdt révélera le problème qui se produit dans le module badModule
.
Le fonctionnement du chien de garde est simple. Au début de l'exécution
de la boucle loop()
, le chien de garde est nourri
(lwdFeed()
) ce qui consiste principalement à actualiser la
variable lwdTime
avec la valeur de l'horloge
millis()
. Cette dernière mesure le temps écoulé en millisecondes
depuis le dernier démarrage du ESP8266. Le début de chaque module est marqué
en appelant la fonction lwdStamp()
avec l'identificateur unique du
module. Ainsi le chien de garde peut suivre le cheminement du croquis. Un
gestionnaire d'interruption (lwdtcb
), invoqué régulièrement par
une minuterie de type Ticker
, vérifie régulièrement si le temps
écoulé depuis la dernière actualisation de lwdTime
dépasse le
délai du chien de garde. Le cas échéant, le gestionnaire passe le contrôle à
la fonction lwdRestart()
qui transmet un message d'information
sur le UART de la puce et puis qui redémarre le ESP8266. L'avertissement
ressemblera à ceci:
Simple Loop Watchdog Example GPIO LED pin intialized bad count: 4 bad count: 3 bad count: 2 bad count: 1 bye bye... Loop watch dog timed out in routine bad module ets Jan 8 2013,rst cause:2, boot mode:(3,6) load 0x4010f000, len 1384, room 16 tail 8 chksum 0x2d csum 0x2d v614f7c32 ~ld Simple Loop Watchdog Example GPIO LED pin intialized bad count: 4 ...
Le code est en fait un peu plus complexe que ce qui est dit ci-dessus, car j'ai incorporé des idées de Jack Ganssle et al pour améliorer la fiabilité du chien de garde.
Le chien de garde présenté ci-dessus constitue un outil que j'ose croire
utile dans le développement d'un croquis moindrement complexe.
Cependant, il faut un peu de discipline lors de la création
du croquis pour tirer avantage de cette fonctionnalité. En particulier, il
faut morceler toutes les tâches à accomplir pendant chaque itération
de la boucle du programme en modules chacun ne faisant qu'une chose. C'est
ainsi que la fonction lwdStamp()
peut être utilisée à bon escient.
Il est clair que tous ne partagent pas mon enthousiasme pour l'approche
modulaire qui est utilisée ici. On peut trouver de nombreux exemples de
croquis Arduino ne contenant que la fonction setup()
suivie
d'une longue fonction loop()
. Les tenants de cette façon de
faire peuvent définir des identificateurs uniques et les placer
stratégiquement dans la fonction loop()
avec la fonction
lwdStamp()
. À chacun son style, le résultat sera sensiblement le
même.
Le chien de garde loop souffre d'une lacune partagée avec les deux chiens de garde intégrés et le mécanisme d'exceptions. Puisque le ESP8266 est souvent utilisé dans des dispositifs de type IdO difficilement accessible, il faut prévoir une façon de récupérer si l'appareil est pris dans une boucle de redémarrage continue. Cette question est examinée dans le prochain billet de cette série.
References:
Barr, Michael (2001), Introduction to Watchdog Timers.
Murphy, Niall (2000), Watchdog Timers.
Ganssle, Jack (2016), Great Watchdog Timers for Embedded Systems.
Santos, Nuno (2017), ESP8266: Watchdog functions.
Markus (Links2003) (2016), Watchdog managed by sketch only.
Ryabkov, Deomid (2017), Reverse engineering of the ESP8266 watchdog timer.
Téléchargements:
lwdt_needed.ino Croquis Arduino illustrant le besoin d'un troisième chien de garde.lwdt_simple.ino Croquis Arduino contenant un troisième chien de garde simplifié de type loop.