Lesson illustration

Watchdog Timers for System Recovery

Hello! Welcome to the first lesson in our module on Designing for Industrial Reliability.

In the previous module, we built a complete data logging and visualization pipeline on a Raspberry Pi, taking data from an Arduino. This is a powerful setup, but what happens if the Arduino's software freezes? In a hobby project, you can just press the reset button. In an industrial setting, where a system might be inaccessible or control a critical process, a software freeze can be a major failure.

This lesson addresses the learning outcome: Implement watchdog timers to automatically recover from software freezes. We will explore the Watchdog Timer (WDT), a hardware-based fail-safe built into most microcontrollers, including the one on your Arduino. You'll learn what it is, why it's essential for any serious application, and how to implement it in your code to build robust, self-recovering systems.

1. The "Why": The Case for a Watchdog

Imagine you've deployed an Arduino-based monitoring system in a remote part of a factory. It runs flawlessly for months, but one day, due to a rare combination of inputs or a bug in a library, the software enters an infinite loop and hangs. The system stops reporting data, and no one knows until it's too late. This is a common failure mode that simple code testing might not catch.

{
  "type": "reading",
  "title": "Arduino Watchdog Timer (WDT) Example Code",
  "id": "[LINK](https://bigdanzblog.wordpress.com/2014/10/24/arduino-watchdog-timer-wdt-example-code/)",
  "url": "https://bigdanzblog.wordpress.com/2014/10/24/arduino-watchdog-timer-wdt-example-code/",
  "relevant_section_indices": [
    0,
    2
  ],
  "par_intro": "The blog post 'Arduino Watchdog Timer (WDT) Example Code' from BigDanzBlog provides a perfect real-world example of this exact problem. The author describes a long-running project that would hang intermittently.",
  "par_directions": "Please read the introduction (the first four paragraphs) and the comment from 'Dan TheMan' on January 29, 2018, which starts with 'Any ‘production’ program I write I use the WDT.' These sections highlight why the WDT is not just a feature, but a necessity for creating reliable, 'production-grade' systems.",
  "estimated_time": "4 minutes"
}

This is where the Watchdog Timer comes in.

2. What is a Watchdog Timer?

A Watchdog Timer is a piece of hardware—an independent timer on the microcontroller chip that runs separately from the main CPU and your code. Its job is to monitor the main program for signs of life.

The concept is simple:

  1. You enable the WDT and set a timeout period (e.g., 2 seconds).
  2. In your main program loop, you must periodically "pet the dog" by issuing a command to reset the WDT's timer back to zero.
  3. If your program freezes or gets stuck, it will fail to reset the timer.
  4. When the timer reaches the timeout period, it "barks"—triggering a hardware reset of the entire microcontroller, forcing your program to restart from the beginning.
{
  "type": "video",
  "title": "Tutorial: Using the Arduino Watchdog Timer",
  "id": "[LINK](https://www.youtube.com/watch?v=BDsu8YhYn8g)",
  "video_id": "BDsu8YhYn8g",
  "relevant_section_indices": [
    0
  ],
  "par_intro": "The video 'Tutorial: Using the Arduino Watchdog Timer' from the Make Course channel provides an excellent visual and conceptual explanation of how this independent clock works.",
  "par_directions": "Watch from the beginning to 03:11. Pay attention to the diagram showing the independent 128 kHz clock and how it drives the WDT, separate from the main 16 MHz CPU clock. This separation is what makes it a true fail-safe."
}

This mechanism ensures that if your software ever becomes unresponsive, the system can automatically recover itself without human intervention.

3. Basic Implementation in an Arduino Sketch

Implementing a WDT on an Arduino is straightforward using the built-in avr/wdt.h library. You only need to know three key functions:

  • wdt_enable(timeout): Enables the WDT with a specified timeout.
  • wdt_reset(): "Pets the dog" by resetting the WDT counter.
  • wdt_disable(): Turns the WDT off.

The timeout is specified using predefined constants, such as:

  • WDTO_15MS (15 milliseconds)
  • WDTO_1S (1 second)
  • WDTO_4S (4 seconds)
  • WDTO_8S (8 seconds, the maximum)

Here is the fundamental structure of a program using a WDT:

#include <avr/wdt.h> // Include the Watchdog Timer library

void setup() {
  Serial.begin(9600);
  Serial.println("System starting up...");
  
  // Enable the Watchdog Timer with a 2-second timeout.
  // If the system doesn't reset the WDT within 2 seconds, it will reboot.
  wdt_enable(WDTO_2S); 
  
  Serial.println("Watchdog enabled.");
}

void loop() {
  // Your main program logic goes here.
  // For example, read sensors, communicate, control actuators.
  Serial.println("Main loop running, all is well.");
  delay(1000); // Simulating some work

  // "Pet the dog" - reset the watchdog timer to prevent a reboot.
  // This tells the WDT that the program is still running correctly.
  wdt_reset(); 
}
Test your understanding!

Imagine you have a function in your loop() called readCriticalSensor() that sometimes takes 3 seconds to complete, but is usually much faster. If you configured the WDT with wdt_enable(WDTO_2S);, what will happen? Where should the wdt_reset() call be placed in the loop() to ensure the program only resets if it truly freezes, not just when it's running a long but valid operation?

Show answer

If the readCriticalSensor() function takes 3 seconds, the wdt_reset() at the end of the loop() won't be called within the 2-second timeout period. The WDT will trigger a system reset, even though the program hasn't actually frozen.

The solution is to select a timeout period longer than the longest possible legitimate execution time of your loop. In this case, you should choose WDTO_4S or WDTO_8S.

The wdt_reset() call should generally be placed at the very end of the loop(). This ensures that all critical code within the loop has executed successfully before you "pet the dog." Placing it at the beginning could allow the program to freeze midway through the loop while the WDT remains satisfied.

4. Testing the Watchdog: Forcing a Failure

To be confident your WDT works, you need to see it in action. The best way to do this is to write a sketch that intentionally freezes, and watch the WDT bring it back to life.

{
  "type": "video",
  "title": "Tutorial: Using the Arduino Watchdog Timer",
  "id": "[LINK](https://www.youtube.com/watch?v=BDsu8YhYn8g)",
  "video_id": "BDsu8YhYn8g",
  "relevant_section_indices": [
    2,
    3
  ],
  "par_intro": "Let's return to the 'Tutorial: Using the Arduino Watchdog Timer' video. The author creates a brilliant example where the loop deliberately takes longer and longer to execute until it breaches the WDT timeout.",
  "par_directions": "Watch from 05:20 to 10:31. First, observe the code walkthrough, where a counter is used to increase a `delay()` inside the loop. Then, watch the live demonstration. You will see the program print to the Serial Monitor, run successfully a few times, and then suddenly print the 'Starting up...' message again. That's the WDT performing a reset."
}

This intentional failure and automatic recovery is the core purpose of the WDT.

5. Advanced Configuration: Interrupts

The WDT can do more than just a hard reset. It has two other modes that give you more control, which is particularly useful for complex industrial systems:

  1. Interrupt Mode: Instead of resetting, the WDT triggers an Interrupt Service Routine (ISR). This lets you run a special block of code (e.g., to log an error or put the system into a safe state) without resetting everything.
  2. Interrupt and Reset Mode: This is the most powerful mode. The WDT first triggers an ISR, giving you a chance to run some last-minute code, and then it performs a system reset.

Why would you use "Interrupt and Reset"? A common use case is to save critical data before the system reboots. For example, you could save an error code or the last known state of a machine to the Arduino's non-volatile EEPROM memory. When the system restarts, it can read this data from the EEPROM and know why it reset.

{
  "type": "video",
  "title": "The Watchdog Timer on Arduino",
  "id": "[LINK](https://www.youtube.com/watch?v=AzZBgH67mgE)",
  "video_id": "AzZBgH67mgE",
  "relevant_section_indices": [
    2,
    3,
    4
  ],
  "par_intro": "The video 'The Watchdog Timer on Arduino' from ForceTronics provides an excellent, in-depth explanation of these modes and demonstrates them with a comprehensive code example.",
  "par_directions": "Please watch these three segments:\n1. **Register Configuration (03:20 - 04:56):** This explains *how* the different modes (interrupt, reset, interrupt-and-reset) are configured by setting bits in a control register. As an engineer, you'll appreciate this look under the hood.\n2. **Code Walkthrough (05:26 - 11:29):** Study the code example. Pay close attention to the `ISR(WDT_vect)` function, which is the interrupt handler, and how it's used to write to EEPROM in mode 3 (interrupt-then-reset).\n3. **Live Demonstration (11:29 - 13:27):** Watch the demo of the 'interrupt-then-reset' mode. Notice how after the reset, the Serial Monitor displays an extra message indicating that an interrupt occurred first. This confirms the ISR ran before the reboot."
}

Conclusion

In this lesson, you've added a fundamental tool for reliability to your Arduino toolkit. The Watchdog Timer is a non-negotiable component for any system that needs to operate unattended or control critical processes.

Key Takeaways:

  • A Watchdog Timer (WDT) is an independent hardware timer that resets the microcontroller if the main software freezes.
  • It is essential for building robust, self-recovering systems, especially for industrial and remote applications.
  • The basic implementation involves enabling the timer in setup() with wdt_enable() and periodically calling wdt_reset() in your loop().
  • The timeout must be chosen carefully to be longer than your loop's longest legitimate execution time.
  • Advanced modes allow for an interrupt before a reset, giving you a chance to save state or log error information to EEPROM.

Preview of the Next Lesson:
We've now protected our system from internal software failures. But what about external threats? Industrial environments are often electrically "noisy," with power spikes and dips that can crash a microcontroller. In our next lesson, we will address this by learning how to select and implement input power filtering to protect against electrical noise.

Can't find a good explanation? Sign up and we'll make it for you