Watchdog, come evitare i dead loop

Il watchdog è un sistema molto interessante impiegato per supervisionare il normale ciclo di istruzioni  eseguite dalla cpu.
L’idea è quella di monitorare l’esecuzione del programma per evitare blocchi o cicli infiniti (dead loop), dovuti a bug logici o situazioni esterne che portano il microcontrollore ad attender, per esempio, a input che non arriveranno mai.

In questo caso il watchdog interviene resettando il microcontrollore e riavviando automaticamente l’esecuzione del codice.

Questo meccanismo è solitamente implementato in hardware e, anche Arduino, basato sul microcontrollore ATMega328, supporta questa funzione

Arduino atmega328 watchdog timer

Se il programma che andiamo a scrivere per la board Arduino è progettato considerando tutti gli aspetti e le variabili che possono bloccare la cpu, non è necessario gestire il Watchdog, che di default è disabilitato.
Capita comunque che si verificano delle particolari condizioni che portano il programma a rimanere in attesa, ad esempio, di un particolare input che per cause esterne, non gestibili, potrebbe non arrivare mai.

Il watchdog quindi attende per un certo periodo e se, allo scadere di questo tempo l’input non è arrivato il Watchdog resetta la cpu facendola riavviare. In caso contrario, quando l’input arriva entro questo tempo, è fondamentale che il codice resetti il watchdog per evitare che questo resetti continuamente la cpu.

Il diagramma a blocchi seguente illustra in modo banale il meccanismo del watchdog

Schema esecuzione watchdog

Codice da utilizzare

Per utilizzare il Watchdog inseriamo nello sketch il riferimento alla libreria wdt.h

#include <avr/wdt.h>

I tempi di reset del Watchdog sono prestabiliti e possono essere scelti utilizzando i valori specificati nella tabella seguente

Tempo reset Parola Chiave
15mS WDTO_15MS
30mS WDTO_30MS
60mS WDTO_60MS
120mS WDTO_120MS
250mS WDTO_250MS
500mS WDTO_500MS
1S WDTO_1S
2S WDTO_2S
4S WDTO_4S
8S WDTO_8S

Nel blocco setup() decidiamo di abilitare il watchdog specificando il tempo di reset utilizzando la seguente funzione:

//abilito il watchdog e imposto come tempo di reser 2 secondi 
wdt_enable(WDTO_2S);

questo significa che se entro 2 secondi non resettiamo il conteggio del watchdog, esso eseguirà automaticamente il reset della cpu.
Per resettarlo impieghiamo la seguente funzione:

//resetto il watchdog 
wdt_reset();

Il codice seguente mostra meglio il funzionamento del sistema

#include <avr/wdt.h> 
void setup() {
  //attivo il watchdog e lo imposto 
  //per una soglia di tempo di 2 Secondi 
  wdt_enable(WDTO_2S);
} 

void loop() { 
  //eseguo qualcosa... 
  delay(500); 
  //resetto il watchdog 
  wdt_reset(); 
}

All’interno del blocco loop() eseguo un delay di 500 ms e poi resetto il watchdog, in questo caso il watchdog non resetterà mai la cpu poiché non arriverà mai a contare i due secondi preimpostati, dato che il reset avviene ogni 500 ms.
Vediamo invece questa situazione:

#include <avr/wdt.h>

void setup()
{
  //attivo il watchdog e lo imposto
  //per una soglia di tempo di 2 Secondi
  wdt_enable(WDTO_2S);

}

void loop()
{
  //eseguo qualcosa...
  delay(3000); //3 secondi

  //resetto il watchdog
  wdt_reset();
}

Il codice precedente mette in funzione il sistema di auto reset in quanto, il delay di 3 secondi, fa in modo che il watchdog arrivi a contare i 2 secondi preimpostati facendo resettare la cpu.

Di fatto l’istruzione wdt_reset() non verrà mai eseguita.

Consiglio di utilizzare questo meccanismo in tutte le situazioni critiche che richiedono una certezza che la cpu esegua costantemente il suo compito evitando blocchi che porterebbero all’interruzione del servizio.