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 ).

  • 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 }