Watchdog Timers Explained for Embedded Projects

Make deployed devices recover from hangs on their own: how watchdog timers work, the ESP32's task/interrupt/RTC watchdogs, feeding strategies that prove real progress, interaction with deep sleep, reset-cause logging, graceful degradation, and the mistakes behind mysterious resets.

Written by Projectech8 min readPublished
For B.E./B.Tech Electronics, E&TC, IoT and Computer Science students whose embedded projects must recover from hangs and crashes without human intervention Topics: ESP32, Arduino, ESP-IDF, FreeRTOS, Embedded Systems
Illustration of a watchdog timer concept: a countdown timer beside a microcontroller, with a hand feeding it and a reset arrow showing automatic recovery from a hang.
Illustration generated for this guide.
In this guide

Your sensor node has been running for three weeks. Then it stops — not a clean failure, just silence. You drive to the site, press reset, and it works again. Two weeks later, silence again. The bug is a rare hang: a sensor that occasionally never responds, a deadlock that needs exactly the wrong timing, a memory corruption that takes days to manifest. You will never find it by testing, because it needs weeks of runtime to appear.

The watchdog timer is the embedded world's admission that software has bugs: an independent hardware timer that resets the chip unless your code proves, over and over, that it is still alive. This guide explains how watchdogs work, the ESP32's watchdog options, how to feed them correctly, the design patterns that make them actually useful, and the mistakes that turn a safety net into a nuisance.

The concept: a dead man's switch

A watchdog timer (WDT) is a countdown timer in hardware, independent of the CPU. Your code must reset ("feed" or "kick") the timer periodically. If the timer ever reaches zero — because your code hung, crashed, or got stuck — it resets the processor automatically.

The key property is independence: the watchdog keeps counting even if the CPU is locked in an infinite loop, because it is separate hardware (or, on multi-core chips, supervised separately). A software-only "reset myself if stuck" scheme cannot work — stuck software cannot reset itself.

Note: a watchdog converts hangs into resets. It does not fix the bug; it converts an unrecoverable state (needs a human) into a recoverable one (automatic reboot). For deployed devices, that conversion is everything.

What watchdogs catch (and what they do not)

Caught Not caught
Infinite loops Bugs that corrupt data but keep feeding the watchdog
Deadlocks (two tasks waiting on each other) Wrong-but-running behavior (bad sensor calibration)
A task that crashed or never runs Power supply failures (nothing to reset with)
Runaway code that never returns to the feed point Feeding from an interrupt while the main loop is dead
Stack overflows that halt execution Gradual memory leaks (until they finally hang — then caught)

The pattern: watchdogs catch liveness failures (the system stopped making progress), not correctness failures (the system confidently does the wrong thing). Design your feeding strategy around proving progress, not just proving the CPU is warm.

The ESP32's watchdog options

The ESP32 has several, and the distinction matters:

Watchdog Watches Typical timeout Notes
Task watchdog (TWDT) Individual FreeRTOS tasks Seconds (configurable) Each watched task must feed; catches single-task hangs
Interrupt watchdog Interrupt handlers ~300 ms default Catches ISRs that run too long
RTC watchdog Main system / RTC domain Configurable Can trigger reset even in some sleep states

In Arduino-ESP32, the task watchdog is enabled by default for the loop task — which is why beginners sometimes meet mysterious resets: a delay(10000) or a blocking sensor read longer than the watchdog timeout triggers it. Understanding this turns a confusing crash into a configured safety feature.

In ESP-IDF you configure the TWDT explicitly: which tasks are watched, the timeout, and the behavior on expiry (reset, or panic with debug info during development).

Feeding correctly: prove progress

The naive approach — feed the watchdog at the top of loop() — proves only that the loop iterates. If your sensor read hangs inside the loop iteration, the feed never happens and the watchdog fires: correct. But if you feed from a timer interrupt while the main task is deadlocked, the watchdog never fires: useless.

Rules for meaningful feeding:

  1. Feed from the task whose liveness matters, after it completes its real work — not from interrupts, not from a separate "feeder" task.
  2. Feed at the end of a successful work cycle: read sensors → process → transmit → feed → sleep. The feed means "I completed a full cycle."
  3. Timeout longer than the worst legitimate cycle, with margin: if a normal cycle takes up to 5 seconds (slow sensor + WiFi), a 10-second timeout with a 30-second panic threshold is sane. Too short = false resets during normal slow operations.
  4. One watchdog per critical task in FreeRTOS designs: the sensor task feeds its own watch, the network task feeds its own. A hang in either resets the system.
#include "esp_task_wdt.h"

void setup() {
  // Watch the current task with a 15-second timeout
  esp_task_wdt_init(15, true);   // true = reset on expiry
  esp_task_wdt_add(NULL);        // NULL = current task
}

void loop() {
  readSensors();       // if this hangs forever, no feed -> reset
  transmitReadings();  // slow WiFi is fine, within timeout
  esp_task_wdt_reset();  // full cycle completed: prove it
  enterDeepSleep();
}

Watchdogs and sleep: an important interaction

Deep sleep stops the CPUs — and the task watchdog with them. The standard battery-node pattern (wake → work → feed → sleep) is naturally compatible: the watchdog only supervises the awake window. But two gotchas:

  • Feeding before sleep is pointless but harmless; the meaningful feed is after the work completes.
  • The RTC watchdog can supervise sleep itself — if the chip should wake every 10 minutes but does not (a deeper hardware hang), the RTC watchdog can force a reset. Consider it for nodes that must never stay dark.

Design patterns that make watchdogs useful

Log the reset cause. On boot, read the reset reason: if it was a watchdog reset, log it (to flash, or transmit it with the next telemetry). A node that watchdog-resets weekly has a bug you should know about — the watchdog is a safety net, not a fix. Telemetry like {"resets": 3, "last_reset": "watchdog"} turns invisible hangs into visible data.

Degrade gracefully after watchdog resets. Keep a reset counter in RTC memory: if the device has watchdog-reset three times in an hour, something is systematically wrong — fall back to a safe mode (minimal functionality, longer sleeps) instead of reset-looping forever. A device that resets every 15 seconds is arguably worse than one that stays down; it can spam your backend and drain its battery.

Watchdog during development vs deployment. During development, configure the watchdog to panic with a backtrace instead of silently resetting — you want the debugger's information. Switch to reset behavior for deployment builds.

Testing that the watchdog actually works

A safety net you have never tested is a rumor. Deliberate fault injection during development:

  1. The infinite-loop test: add a debug command (a serial input or a special MQTT message) that enters an endless loop. The watchdog should reset the chip within the configured timeout. If it does not, your feeding strategy has a hole — often a feed happening in an interrupt or a second task keeping the system alive.
  2. The slow-operation test: simulate your worst-case legitimate timing (slowest sensor read, longest WiFi connect, biggest OTA download) and confirm the watchdog does NOT fire. This validates the timeout margin.
  3. The deadlock test: in FreeRTOS builds, have two tasks take two mutexes in opposite order under a debug trigger. The task watchdog should catch the resulting hang.
  4. The reset-cause check: after each test, verify the boot code correctly identifies the reset as watchdog-caused and logs it. A watchdog that resets but is misreported as a power-on reset hides the data you need.

Run these on the bench, then remove the debug triggers for deployment — or guard them behind a physical button press, since a remotely triggerable hang is a self-inflicted denial of service. Document the test results in your report: evaluated claims about reliability, backed by a repeatable procedure, are what separate engineering from assertion.

Common mistakes checklist

  • Feeding from an interrupt. Proves the interrupt runs, not that the work completes.
  • Timeout shorter than legitimate slow operations. OTA downloads and first WiFi connects are slow — size the timeout for the worst case.
  • Disabling the watchdog to "fix" resets. The resets were the symptom; find the hang.
  • No reset-cause logging. Invisible watchdog resets hide the bug forever.
  • No degradation policy. Reset-looping forever drains batteries and spams backends.
  • One feed point for a multi-task system. A deadlocked secondary task goes unnoticed.
  • Blocking sensor reads with no timeout. The hang the watchdog catches should ideally never happen — add timeouts to I2C/SPI reads too.

Where to go from here

More project guides

More in IoT & Embedded