Watchdog
A watchdog timer resets the device if the firmware stops responding. It is the minimum viable recovery mechanism for any deployed device. If your firmware hangs in the field, the watchdog wakes it up.
Rule: never ship firmware without a watchdog.
Hardware watchdog (WDT)
The ESP32 has a hardware watchdog peripheral. Zephyr exposes it through the wdt driver API:
prj.conf
CONFIG_WATCHDOG=y
src/watchdog.c
#include <zephyr/drivers/watchdog.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(watchdog);
static const struct device *wdt = DEVICE_DT_GET(DT_ALIAS(watchdog0));
static int wdt_channel_id;
int watchdog_init(void)
{
struct wdt_timeout_cfg wdt_config = {
/* Reset SoC if watchdog fires */
.flags = WDT_FLAG_RESET_SOC,
/* Timeout: 5 seconds */
.window.min = 0U,
.window.max = 5000U,
};
if (!device_is_ready(wdt)) {
LOG_ERR("WDT not ready");
return -ENODEV;
}
wdt_channel_id = wdt_install_timeout(wdt, &wdt_config);
if (wdt_channel_id < 0) {
LOG_ERR("Cannot install WDT timeout: %d", wdt_channel_id);
return wdt_channel_id;
}
wdt_setup(wdt, WDT_OPT_PAUSE_HALTED_BY_DBG);
LOG_INF("Watchdog initialized, timeout 5s");
return 0;
}
/* Call this periodically — at least once every 5 seconds */
void watchdog_feed(void)
{
wdt_feed(wdt, wdt_channel_id);
}
Feeding the watchdog from your main loop
int main(void)
{
watchdog_init();
while (1) {
do_work(); /* your application logic */
watchdog_feed(); /* tell the WDT we're alive */
k_sleep(K_SECONDS(1));
}
}
Devicetree alias
Add the watchdog alias to your overlay if it's not in the board DTS:
/ {
aliases {
watchdog0 = &wdt0;
};
};
&wdt0 {
status = "okay";
};
What happens when the watchdog fires
On ESP32 with WDT_FLAG_RESET_SOC:
- MCU resets
- Firmware restarts from the beginning of
main() - The reset reason is stored in the
RESETREASregister — readable viahwinfo_get_reset_cause()
#include <zephyr/drivers/hwinfo.h>
uint32_t reason;
hwinfo_get_reset_cause(&reason);
if (reason & RESET_WATCHDOG) {
LOG_WRN("Device reset by watchdog — check for hang");
}
hwinfo_clear_reset_cause();
warning
WDT_OPT_PAUSE_HALTED_BY_DBG pauses the watchdog when the debugger halts the CPU. Without this flag, the watchdog fires every time you hit a breakpoint.
Production watchdog pattern
For production firmware with multiple threads, each thread feeds its own watchdog channel, and a supervisor thread checks they all reported in:
/* Each thread reports to a bitmask */
#define THREAD_SENSOR_BIT BIT(0)
#define THREAD_BLE_BIT BIT(1)
#define ALL_THREADS_ALIVE (THREAD_SENSOR_BIT | THREAD_BLE_BIT)
static atomic_t alive_mask = ATOMIC_INIT(0);
/* Each thread sets its bit before sleeping */
void sensor_thread(void *a, void *b, void *c) {
while (1) {
atomic_or(&alive_mask, THREAD_SENSOR_BIT);
k_sleep(K_SECONDS(2));
}
}
/* Supervisor: feed WDT only if all threads reported */
void watchdog_supervisor(void *a, void *b, void *c) {
while (1) {
if ((atomic_get(&alive_mask) & ALL_THREADS_ALIVE) == ALL_THREADS_ALIVE) {
atomic_set(&alive_mask, 0);
watchdog_feed();
}
k_sleep(K_SECONDS(1));
}
}