Menu

Watchdog Timer su ESP32: perché il device si riavvia da solo

Task Watchdog, Interrupt Watchdog e RTC Watchdog spiegati

6 Luglio 2026 di Alessandro Colucci
Illustrazione Watchdog Timer ESP32

L'errore che ti ha portato qui

Se hai cercato questo articolo, probabilmente hai già visto uno di questi nel monitor seriale:

E (5231) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (5231) task_wdt: - loopTask (CPU 1)
E (5231) task_wdt: Tasks currently running:
E (5231) task_wdt: CPU 0: IDLE0
E (5231) task_wdt: CPU 1: loopTask
E (5231) task_wdt: Aborting.

O peggio: nessun errore, solo un riavvio silenzioso sul campo, segnalato da un cliente che dice che il device "si riavvia da solo" ogni tanto. Questo articolo copre entrambi i casi — come funziona davvero il watchdog su ESP32, i tre errori più comuni che causano riavvii indesiderati, e cosa dovrebbe fare davvero un firmware in produzione quando scatta.

Non esiste "il" watchdog, ce ne sono tre

Si parla del "watchdog" come se fosse una cosa sola. Su ESP32 ce ne sono tre, e confonderli è la prima fonte di errori:

  • Task Watchdog Timer (TWDT) — watchdog software gestito da FreeRTOS. Tiene sotto controllo task specifici (di default, i task idle su ogni core; su Arduino, anche loopTask) e si aspetta che ognuno "faccia il check-in" periodicamente. È quello dietro l'errore sopra.
  • Interrupt Watchdog Timer (IWDT) — un timer con supporto hardware che rileva specificamente quando gli interrupt restano disabilitati troppo a lungo (es. una sezione portDISABLE_INTERRUPTS() lunga, o un task che monopolizza un core senza cedere il controllo). Scatta con un panic "Guru Meditation Error: IWDT", non con il messaggio task_wdt di sopra.
  • RTC Watchdog — il watchdog hardware di livello più basso, completamente indipendente da FreeRTOS. È quello che recupera il chip se lo stack software è totalmente bloccato (es. incastrato in un loop di hard fault prima ancora che FreeRTOS possa schedulare un recupero).

Nel 90% dei casi di "il mio ESP32 si riavvia da solo" in un firmware di produzione, il colpevole è il Task Watchdog — è il focus del resto dell'articolo.

Configurare correttamente il Task Watchdog

In ESP-IDF, il task watchdog si configura tramite esp_task_wdt_config_t e si inizializza una volta sola:

#include "esp_task_wdt.h"

esp_task_wdt_config_t twdt_config = {
    .timeout_ms = 5000,
    .idle_core_mask = (1 << 0) | (1 << 1), // monitora entrambi i task idle
    .trigger_panic = true,
};
esp_task_wdt_init(&twdt_config);
esp_task_wdt_add(NULL); // iscrive il task corrente

Ogni task che iscrivi con esp_task_wdt_add() deve chiamare esp_task_wdt_reset() prima che scada il timeout, altrimenti viene segnalato. Sul core Arduino-ESP32, il loop task è iscritto automaticamente con un timeout di default (storicamente 5s, ma controlla la versione del tuo core — è cambiato tra le release), motivo per cui gli sketch Arduino semplici raramente vedono questo errore finché non aggiungono qualcosa di bloccante.

Errore #1: bloccare troppo a lungo senza "nutrire" il watchdog

È la causa più comune in assoluto. Ognuna di queste può bloccare abbastanza a lungo da far scattare il watchdog:

  • Un delay() o una lettura sensore bloccante più lunga del timeout, dentro un task iscritto al watchdog
  • Scrittura su flash (commit NVS, scrittura OTA, operazioni SPIFFS/LittleFS) — sono più lente di quanto ci si aspetti, specialmente su un bus flash condiviso con il WiFi
  • Una chiamata di rete bloccante (risoluzione DNS, connessione TCP senza timeout) dentro il loop principale
  • Un loop while(1) in polling che aspetta una condizione senza mai cedere il controllo allo scheduler

La soluzione non è sempre "nutrirlo più spesso" — spesso la vera soluzione è spostare del tutto il lavoro bloccante fuori dal task monitorato (un task FreeRTOS dedicato non iscritto al watchdog, o un'API asincrona/a callback invece che bloccante).

Errore #2: iscrivere il task sbagliato, o dimenticare un core

Se configuri manualmente idle_core_mask e monitori solo il core 0 mentre il tuo codice bloccante gira sul core 1 (o viceversa, a seconda di come hai assegnato i task con xTaskCreatePinnedToCore), il watchdog semplicemente non vede mai il problema — finché non lo vede, in un modo diverso e più difficile da riprodurre. Fai coincidere la configurazione del watchdog con l'assegnazione reale dei task ai core, non con un default copiato e incollato.

Errore #3: timeout troppo corto per operazioni lente ma legittime

Scritture OTA, commit NVS voluminosi e handshake TLS sono tutti operazioni legittimamente lente — a volte più lente di un timeout impostato in modo conservativo. Invece di disabilitare il watchdog per queste operazioni (il che ne vanifica lo scopo), il pattern usato in produzione è chiamare esp_task_wdt_reset() in checkpoint sicuri dentro l'operazione lenta stessa, oppure spostarla su un task non monitorato se è rara e ben isolata.

Cosa fare davvero quando scatta

Prevenire i riavvii è solo metà del lavoro — un device che arriva dal cliente deve sopravviverne uno con eleganza e dirti perché. All'avvio, controlla il motivo del reset:

#include "esp_system.h"

esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_TASK_WDT || reason == ESP_RST_INT_WDT || reason == ESP_RST_WDT) {
    // Loggalo, su Serial, in NVS per recuperarlo dopo, o direttamente sul
    // tuo backend se hai connettività.
}

E assicurati che un watchdog che scatta ripetutamente non diventi un loop di riavvio infinito — se il device si riavvia per il watchdog, si riconnette al WiFi, e rifà subito la stessa chiamata bloccante che ha causato il primo riavvio, hai costruito un mattone che si riavvia ogni 5 secondi per sempre. Una guardia semplice (conta i riavvii in una finestra breve via RTC memory, passa a una modalità sicura/degradata dopo N tentativi) evita questo scenario.

Checklist per la produzione

  • Sai quale dei tre watchdog stai effettivamente guardando (messaggio task_wdt vs Guru Meditation IWDT vs un reset hardware duro senza nessun log)
  • idle_core_mask corrisponde alla reale assegnazione dei task ai core
  • Ogni task iscritto fa il check-in ben entro il timeout, oppure non è iscritto affatto
  • Le operazioni lente ma legittime (OTA, NVS, TLS) hanno un piano — reset a checkpoint o isolamento, non una disabilitazione generica
  • Il motivo del reset viene loggato da qualche parte recuperabile davvero quando il device è sul campo
  • Esiste una guardia contro il loop di riavvio, così un trigger ricorrente degrada in modo controllato invece di riavviarsi all'infinito

Niente di esotico — è il tipo di boilerplate che ogni firmware ESP32 di produzione finisce per aggiungere, e che ogni progetto reinventa leggermente diverso. Se preferisci specificare il comportamento e ottenere codice funzionante invece di ricablare tutto questo a mano un'altra volta, è esattamente lo scopo di PleaseDontCode.

Torna al Blog Costruisci il tuo primo dispositivo

Vuoi realizzare questo progetto?

Hai appena letto Watchdog Timer su ESP32: perché il device si riavvia da solo.
E se potessi trasformarlo in un progetto funzionante in pochi minuti?

Con PleaseDontCode descrivi in linguaggio naturale cosa deve fare il tuo dispositivo: l'intelligenza artificiale genera il firmware completo, pronto da caricare su oltre 100 schede ESP32 e compatibili.

Scegli i sensori, attuatori e protocolli di comunicazione che ti servono — al codice ci pensiamo noi. Non serve esperienza di programmazione.

Costruisci il tuo primo dispositivo Gratis, senza carta di credito
Raggiungici su WhatsApp