← ESP32 PLC Hardware Watchdog for Unattended Machines
Hydraulic moving floor (BLE app)ESP32 PLC 38RGPIOResilience / OTA
ESP32 PLC Hardware Watchdog for Unattended Machines — full example
Make ESP32 PLC firmware self-healing with esp_task_wdt. A 20-second hardware watchdog reboots a hung controller automatically, with NVS diagnostics.
Complete, runnable program for the ESP32 PLC 38R (hardware-watchdog-38r.ino): wiring header, requirements and integration notes included.
Download the full project pack — freeThis example + the related ones + bill of materials
Read-only preview.
/*
* COMPLETE EXAMPLE — ESP32 hardware watchdog (esp_task_wdt)
*
* Hardware: ESP32 PLC 38R (Industrial Shields)
* Based on: hydraulic moving floor project, main.cpp (resilience)
*
* Wiring:
* I0_0 "Simulate hang" push button (blocks the loop on purpose)
* R0_1 Heartbeat LED (blinks while the firmware is healthy)
*
* Logic:
* - esp_task_wdt_init() arms a 20 s watchdog on the loop task.
* - Every loop iteration "feeds" it with esp_task_wdt_reset().
* - If the firmware hangs (deadlock, infinite loop, blocked BLE
* stack...), the watchdog reboots the PLC by itself: the machine is
* operational again without anyone climbing up to power-cycle it.
* - The reason of the last reboot and a watchdog reboot counter are
* stored in NVS for field diagnostics.
*
* Integration with other catalog modules:
* - The reboot counter uses Preferences, just like the cycle
* counters (see ejemplos/piso-movil/nvs-persistent-counters-38r.ino).
* - After the reboot the BLE server advertises again and the app
* reconnects by itself (see ejemplos/piso-movil/ble-mobile-app-control-38r.ino).
*/
#include
#include
#define BTN_HANG I0_0
#define LED_ALIVE R0_1
#define WDT_TIMEOUT 20 // seconds without feeding -> reboot
Preferences prefs;
int wdtResets = 0;
// Translation of the reset reason for the diagnostics log
const char* resetReasonText(esp_reset_reason_t r) {
switch (r) {
case ESP_RST_POWERON: return "power-on";
case ESP_RST_SW: return "software reboot";
case ESP_RST_TASK_WDT: return "WATCHDOG (firmware hung)";
case ESP_RST_BROWNOUT: return "brownout (power supply)";
default: return "other";
}
}
void setup() {
Serial.begin(115200);
pinMode(BTN_HANG, INPUT);
pinMode(LED_ALIVE, OUTPUT);
// 1. Diagnostics: why did we boot?
esp_reset_reason_t reason = esp_reset_reason();
Serial.print("Reason of the last reboot: ");
Serial.println(resetReasonText(reason));
// 2. Persistent counter of watchdog reboots (NVS pattern)
prefs.begin("nvdata", false);
wdtResets = prefs.getInt("wdt_resets", 0);
if (reason == ESP_RST_TASK_WDT) {
wdtResets++;
prefs.putInt("wdt_resets", wdtResets);
}
prefs.end();
Serial.println("Accumulated watchdog reboots: " + String(wdtResets));
// 3. Arm the watchdog: 20 s and panic=true (reboots instead of just warning)
esp_task_wdt_init(WDT_TIMEOUT, true);
esp_task_wdt_add(NULL); // watches the current task (loop)
Serial.println("Watchdog armed: " + String(WDT_TIMEOUT) + " s");
}
void loop() {
// Feed the watchdog: as long as the loop keeps spinning, nothing happens
esp_task_wdt_reset();
// Heartbeat LED at 1 Hz: at a glance you can see the firmware is healthy
digitalWrite(LED_ALIVE, (millis() / 500) % 2);
// Hang simulation: infinite loop WITHOUT feeding the watchdog.
// After 20 s the PLC reboots by itself and the NVS counter increments.
if (digitalRead(BTN_HANG)) {
Serial.println("Firmware 'hung' on purpose. Rebooting in ~20 s...");
while (true) {
// no esp_task_wdt_reset() and no delay(): a real hang
}
}
delay(50);
}
Download the full project pack — freeThis example + the related ones + bill of materials