UNO R3: Studying the Three Operating Modes of the Watchdog Timer (WDT)

A:

For readers who are interested in studying the behavior of the Watchdog Timer (WDT) in its three operating modes (WDT Interrupt Mode, System Reset Mode, and WDT Interrupt + System Reset Mode), I have provided below three separate sketches tested in UNO R3.

I chose to configure the WDT mode of operation using register-level code because the wdt_enable(timeout) function interferes with my implementation of the WDT Interrupt + System Reset Mode. The wdt_enable(timeout) function of interrupt/aver.h Library is hardcoded to configure the WDT in its System Reset Mode.

I have not used the while(true) {} infinite-loop approach to keep the MCU occupied during the 8-second timeout period. Since returning from the WDT ISR causes the MCU to resume execution within the same infinite loop, it becomes difficult to observe and evaluate the experimental results. Instead, I decided to put the MCU into sleep mode by sending command 12 + New Line from the Serial Monitor. After the expiry of 8 s timeout period, the WDT wakesup the MCU from sleep.

To indicate that the MCU is active and running normally, the onboard LED continues to blink at 200 ms intervals except sleeping time.

Feedback is highly appreciated.

1.
Sketch for only WDT Interrupt Mode (8 sec timeout):
At the expiration of each 8 m\s WDT timeout period, the MCU enters the ISR and then returns to the loop() function, where it prints the message "WDT Interrupt happened". It then continues with the main program flow, which includes blinking the onboard LED.

#include <avr/wdt.h>
#include <avr/sleep.h>

volatile bool flag = false;
char myData[20];
byte ledState = HIGH;
unsigned long prMillis = millis();
int y;

void setup() {
  Serial.begin(9600);
  Serial.println("Sytem Reset");
  pinMode(13, OUTPUT);
  SMCR |= (1 << SM1); //power down mode

  noInterrupts();
  WDTCSR |= (1 << WDCE) | (1 << WDE);
  WDTCSR = (1 << WDIE) | (0 << WDE)
           | (1 << WDP3) | (0 << WDP2)  //1001 = 8 sec
           | (0 << WDP1) | (1 << WDP0); //WDT Interrupt Mode
  interrupts();
}

void loop() 
{
  if(flag == true)
  {
    flag = false;
    Serial.println("WDT Interrupt happened.");
  }
  
  byte n = Serial.available();
  if (n != 0) 
  {
    byte m = Serial.readBytesUntil('\n', myData, sizeof myData - 1);
    myData[m] = '\0';
    y = atoi(myData);
    if (y == 12) 
    {
      Serial.println("Entering into sleep until WDT timeout.");
      Serial.flush();  //to get all the above charcaters onto Serial Monitor
      wdt_reset();
      sleep_enable();//while(true){;}
      sleep_cpu();
      sleep_disable();
      Serial.println("WDT Interrupt happened; wakeup from sleep.");
      flag = false;
      wdt_reset();
    } 
  }
  else 
  {
    digitalWrite(13, ledState);
    if (millis() - prMillis > 200) 
    {
      ledState = !ledState;
      prMillis = millis();
    }
  }
}

ISR(WDT_vect) 
{
   flag = true;
}

Output:

21:18:07.632 -> Sytem Reset
21:18:15.922 -> WDT Interrupt happened.
21:18:24.292 -> WDT Interrupt happened.
21:18:28.261 -> Entering into sleep until WDT timeout.
21:18:36.612 -> WDT Interrupt happened; wakeup from sleep.
21:18:44.916 -> WDT Interrupt happened.
21:18:53.217 -> WDT Interrupt happened.

2.
Sketch for only System Reset Mode (8 sec imeout): (I have kept the ISR(WDT_vect){} related to code so show that they are not called upon in System Reset Mode.)

At the expiration of each 8 s WDT timeout period, the MCU does not enter the ISR. Instead, it undergoes a system reset and executes the bootloader. After that, it returns to the setup() function, where it prints the message "System Reset", and then proceeds to the loop() function to blink the onboard LED.

#include <avr/wdt.h>
#include <avr/sleep.h>

volatile bool flag = false;
char myData[20];
byte ledState = HIGH;
unsigned long prMillis = millis();
int y;

void setup() {
  Serial.begin(9600);
  Serial.println("Sytem Reset");
  pinMode(13, OUTPUT);
  SMCR |= (1 << SM1); //power down mode

  noInterrupts();
  WDTCSR |= (1 << WDCE) | (1 << WDE);
  WDTCSR = (0 << WDIE) | (1 << WDE)
           | (1 << WDP3) | (0 << WDP2)  //1001 = 8 sec
           | (0 << WDP1) | (1 << WDP0);  //Reset Mode
  interrupts();
}

void loop() 
{
  if(flag == true)
  {
    flag = false;
    Serial.println("WDT Interrupt happened.");
  }
  
  byte n = Serial.available();
  if (n != 0) 
  {
    byte m = Serial.readBytesUntil('\n', myData, sizeof myData - 1);
    myData[m] = '\0';
    y = atoi(myData);
    if (y == 12) 
    {
      Serial.println("Entering into sleep until WDT timeout.");
      Serial.flush();  //to get all the above charcaters onto Serial Monitor
      wdt_reset();
      sleep_enable();//while(true){;}
      sleep_cpu();
      sleep_disable();
      Serial.println("WDT Interrupt happened; wakeup from sleep.");
      flag = false;
      wdt_reset();
    } 
  }
  else 
  {
    digitalWrite(13, ledState);
    if (millis() - prMillis > 200) 
    {
      ledState = !ledState;
      prMillis = millis();
    }
  }
}

ISR(WDT_vect) 
{
   flag = true;
}

Output:

21:20:29.369 -> Sytem Reset
21:20:37.710 -> Sytem Reset
21:20:46.087 -> Sytem Reset
21:20:46.873 -> Entering into sleep until WDT timeout.
21:20:55.290 -> Sytem Reset
21:21:03.634 -> Sytem Reset

3.
Sketch for WDT Interrupt + System Reset Mode (8 sec timeout):

At the expiration of the first 8 s WDT timeout period, the MCU enters the WDT ISR and then returns to the loop() function, where it prints the message "WDT Interrupt happened".

At the expiration of the next 8 s timeout period, the MCU passes through the bootloader and then restarts execution from the setup() function, where it prints the message "System Reset". It then enters the loop() function and resumes blinking the onboard LED.

#include <avr/wdt.h>
#include <avr/sleep.h>

volatile bool flag = false;
char myData[20];
byte ledState = HIGH;
unsigned long prMillis = millis();
int y;

void setup() {
  Serial.begin(9600);
  Serial.println("Sytem Reset");
  pinMode(13, OUTPUT);
  SMCR |= (1 << SM1); //power down mode

  noInterrupts();
  WDTCSR |= (1 << WDCE) | (1 << WDE);
  WDTCSR = (1 << WDIE) | (1 << WDE)
           | (1 << WDP3) | (0 << WDP2)  //1001 = 8 sec
           | (0 << WDP1) | (1 << WDP0);  //WDT Interrupt and Reset Mode
  interrupts();
}

void loop() 
{
  if(flag == true)
  {
    flag = false;
    Serial.println("WDT Interrupt happened.");
  }
  
  byte n = Serial.available();
  if (n != 0) 
  {
    byte m = Serial.readBytesUntil('\n', myData, sizeof myData - 1);
    myData[m] = '\0';
    y = atoi(myData);
    if (y == 12) 
    {
      Serial.println("Entering into sleep until WDT timeout.");
      Serial.flush();  //to get all the above charcaters onto Serial Monitor
      wdt_reset();
      sleep_enable();//while(true){;}
      sleep_cpu();
      sleep_disable();
      Serial.println("WDT Interrupt happened; wakeup from sleep.");
      flag = false;
      wdt_reset();
    } 
  }
  else 
  {
    digitalWrite(13, ledState);
    if (millis() - prMillis > 200) 
    {
      ledState = !ledState;
      prMillis = millis();
    }
  }
}

ISR(WDT_vect) 
{
   flag = true;
}

Output:

21:26:56.729 -> Sytem Reset
21:27:05.065 -> WDT Interrupt happened.
21:27:13.423 -> Sytem Reset
21:27:21.741 -> WDT Interrupt happened.
21:27:25.387 -> Entering into loop until WDT timeout.
21:27:33.755 -> Sytem Reset
21:27:42.059 -> WDT Interrupt happened.
21:27:50.440 -> Sytem Reset

B:

Design of a timeout sketch using the Watchdog Timer (WDT), where the MCU waits for a 60-second period to check whether a fault condition is cleared. If the fault persists, the MCU is forced to reset after the next 15 ms WDT timeout period.

The original contributor of this sketch is @InquisitiveMind. I am sharing it here with slight moderation for readers.

In this implementation, the MCU continuously blinks the onboard LED at 200 ms intervals. A fault condition is simulated by pulling a digital pin to GND using a button (Fig-1). If the button is released before the 60-second expiration, the system returns to normal operation and continues blinking the LED. Otherwise, after the next 15 ms WDT timeout period, the MCU is reset.


Figure-1:

Sketch:

#include <avr/wdt.h>
#include <avr/interrupt.h>

volatile unsigned long watchdogTicks = 0;    // 1 tick = 1 ms
const unsigned long timeoutTicks = 60000UL;  // reset after approx. 60 seconds

unsigned long prMillis = millis();
byte ledState = HIGH;

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  pinMode(13, OUTPUT);
  Serial.println("System Reset");

  wdt_disable();  // Disable watchdog after startup
  setupTimer2();  // Start Timer2-based software watchdog timing
}

void loop() 
{
  watchdogTicks = 0;

  if (digitalRead(2) == LOW) 
  {
    while (true) 
    {
      if(digitalRead(2) == HIGH)
      {
        break;
      }
    }
  } 
  else 
  {
    digitalWrite(13, ledState);
    if (millis() - prMillis > 200) 
    {
      ledState = !ledState;
      prMillis = millis();
    }
  }
}

void setupTimer2() {
  cli();  // Disable interrupts

  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2 = 0;

  OCR2A = 249;              // 1 ms at 16 MHz with prescaler 64
  TCCR2A |= (1 << WGM21);   // CTC mode
  TCCR2B |= (1 << CS22);    // Prescaler 64
  TIMSK2 |= (1 << OCIE2A);  // Enable compare match interrupt

  sei();  // Enable interrupts
}

ISR(TIMER2_COMPA_vect) 
{
  watchdogTicks++;

  if (watchdogTicks >= timeoutTicks) {
    wdt_enable(WDTO_15MS);  // Enable watchdog with 15 ms timeout
    while (true)
      ;  
  }
}

C:

WDT Timer Hardwae

.... in progress