Utiliser le chien de garde indépendant
L’ensemble des sketchs de notre projet peuvent être téléchargés ici.
Le skech « independant_watchdog.ino » implémente le chien de garde indépendant (IDWG) dans « low_power.ino ». Pour le moment l’IDWG parait une complication inutile : on imagine mal pour quelle raison le firmware se planterait et resterait bloqué dans son chemin d’exécution. Mais nous allons ajouter la connexion au Wi-Fi juste après et il sera très utile pour redémarrer la station si, par exemple, le routeur Wi-Fi devient subitement muet à cause d’une défaillance du réseau téléphonique.
- Les modifications apportées aux déclarations globales sont :
082 // Bibliothèque de l'independant watchdog (IDWG)
083 #include <IWatchdog.h>
084
085 // Nécessaire pour tenir le décompte des software resets
086 uint32_t swrst_cnt __attribute__((__section__(".noinit")));
087 uint32_t first_run __attribute__((__section__(".noinit")));
Les lignes 86 et 87 déclarent deux variables, swrst_cnt et first_run, avec la directive attribute((__ section__(“.noinit”))). Celle-ci indique au compilateur que les variables en question devront être placées dans une zone de la mémoire volatile du microcontrôleur (une RAM) qui n’est pas effacée lorsque le celui-ci exécute un reset logiciel (ou « software reset » en anglais). Ces variables “noinit” serviront à compter le nombre de resets logiciels générés par l’IDWG depuis la mise sous tension de la station (voir cette référence).
- Dans setup, une seule instruction a été ajoutée, l’appel à la fonction Initialize_Watchdog :
127 // Initialise le watchdog
128 main_loop_delay = Initialize_Watchdog(&Serial);
- Voici le code de Initialize_Watchdog :
360 uint32_t Initialize_Watchdog(HardwareSerial *serial) {
361
362 // IWDG_TIMEOUT_MAX est la durée maximum du compte à rebours de l'IDWG (en µs)
363 serial->print("Watchdog timeout set to : ");
364 serial->print(IWDG_TIMEOUT_MAX / 1000000);
365 serial->println("s");
366
367 uint32_t MainLoopDelay;
368
369 // Si nécessaire, raccourcis la temporisation de la boucle principale pour
370 // qu'elle reste inférieure à la durée maximum du compte à rebours de l'IDWG
371 if ( 1000 * MAIN_LOOP_DELAY > IWDG_TIMEOUT_MAX) {
372 MainLoopDelay = (IWDG_TIMEOUT_MAX - 1000000) / 1000;
373 }
374 else {
375 MainLoopDelay = MAIN_LOOP_DELAY;
376 }
377
378 serial->print("Application time base set to : ");
379 serial->print(MainLoopDelay / 1000);
380 serial->println("s");
381 serial->print("Measurements period set to : ");
382 serial->print(MainLoopDelay * SET_REC_MOD / 1000);
383 serial->println("s");
384
385 // Initialise la variable swrst_cnt à la mise sous tension ou au
386 // premier démarrage après rechargement du firmware
387 if (first_run != 0xDEAD0000) {
388 swrst_cnt = 0;
389 Serial.println("First run after power-up");
390 first_run = 0xDEAD0000;
391 }
392
393 // Démarre l'IDWG
394 IWatchdog.begin(IWDG_TIMEOUT_MAX);
395 if (!IWatchdog.isEnabled()) {
396 serial->println("Watchdog enabled !");
397 }
398
399 // Si on vient juste de démarrer après un reset dû à l'IDWG
400 if (IWatchdog.isReset(true)) {
401 ++swrst_cnt; // on incrémente swrst_cnt (comptage des software resets)
402 serial->println("\nSystem rebooting from watchdog timeout.");
403 }
404
405 // Si swrst_cnt n'est pas égale à zéro
406 if (swrst_cnt) {
407 serial->println("Total software resets since power-on : " + String(swrst_cnt));
408 }
409
410 // Retourne la valeur potentiellement réduite de la temporisation de la boucle
411 // principale
412 return MainLoopDelay;
413 }
L’unité de temps du compte-à rebours de l’IDWG est la microseconde (µs) ; il y a 1000 µs dans une milliseconde (ms) et 1 000 000 µs dans une seconde (s). Ceci explique l’apparition de ces facteurs (lignes 364, 372 , 379, 382).
On commence par ajuster la temporisation de la boucle principale pour qu’elle soit toujours inférieure au compte à rebours de l’IDWG (lignes 371 à 376) dont la plus grande valeur possible est IWDG_TIMEOUT_MAX = 32s. C’est une précaution qui évitera, si vous modifiez incorrectement le sketch par la suite, de le placer dans une séquence sans fin de software resets.
Les lignes 387 à 391 gèrent les deux variables “noinit” à la mise sous tension de la carte NUCLEO. On donne alors à first_run la valeur arbitraire 0xDEAD0000 (ligne 387) et à swrst_cnt la valeur zéro (ligne 388). Par la suite swrst_cnt est incrémentée après chaque software reset dû à l’IDWG (ou à autre chose, lignes 400 à 403).
- Enfin, seule la ligne 177, a été rajoutée à la fonction loop. Elle « recharge » le compte à rebours de l’IDWG à chaque itération de la boucle principale :
139 void loop() {
140
141 if (rec_mod == SET_REC_MOD) { // Toutes les SET_REC_MOD itérations...
142
143 rec_mod = 0;
144
145 // Allume la LED utilisateur
146 digitalWrite(LED_BUILTIN, HIGH);
147
…
…
163
164 // Eteint la LED utilisateur
165 digitalWrite(LED_BUILTIN, LOW);
166
167 } // Clôture de if (rec_mod == SET_REC_MOD)
168
169 rec_mod++;
…
…
176 // Ré-arme l'IDWG
177 IWatchdog.reload();
178
179 // Mise en veille (mode "STOP") avant l'itération suivante
180 delay(5);
181 LowPower.deepSleep(main_loop_delay);
182 }