Extend watchdog timer beyond 8 seconds

Here’s a way I discovered to use the watchdog timer to get reset times arbitrarily longer than 8 seconds.

This topic was covered by Nick Gammon for sleeping/waking here: http://forum.arduino.cc/index.php/topic,173850.0.html, but the code posted below is different - this is for “running” sketches that might occasionally hang.

I wrote this for a sketch that uses the Adafruit cc3000 wi-fi board. That board randomly hangs at nearly all of the dozen or so commands needed to connect to an access point and upload data. Before I wrote the code below, I wrapped each cc3000 command in the wdt_enable(WDT0_8S)/wdt_reset() pair, and sometimes 8 seconds wasn’t long enough. That wasn’t too bad, but it seemed messy and the timer probably expired a few times before it should have. This new code allows me to wrap the entire process (essentially all of loop()) in one pair of timer enable/disable commands (as in the sketch below).

This uses the watchdog timer’s “interrupt-only” mode for a user-defined number of loops, and then changes to the “reset-only” mode.

I would appreciate if anyone can find problems with this. It seems to work for me!

#include <avr/wdt.h> // for watchdog timer

volatile int counter;      // Count number of times ISR is called.
volatile int countmax = 3; // Arbitrarily selected 3 for this example.
                           // Timer expires after about 24 secs if
                           // 8 sec interval is selected below.

void setup()
{  
  // Initialize
  Serial.begin(115200);
}

void loop()
{
  Serial.println("**** starting loop ****");  // should see this approx every xx secs

  watchdogEnable(); // set up watchdog timer in interrupt-only mode

  while (true); // Whoops...in an infinite loop!  For testing only.
                // For production, this would be useful
                // code that normally takes less than xx sec to process
                // but occasionally hangs.

  wdt_disable();

}

void watchdogEnable()
{
  counter=0;
  cli();                              // disable interrupts

  MCUSR = 0;                          // reset status register flags

                                      // Put timer in interrupt-only mode:                                        
  WDTCSR |= 0b00011000;               // Set WDCE (5th from left) and WDE (4th from left) to enter config mode,
                                      // using bitwise OR assignment (leaves other bits unchanged).
  WDTCSR =  0b01000000 | 0b100001;    // set WDIE (interrupt enable...7th from left, on left side of bar)
                                      // clr WDE (reset enable...4th from left)
                                      // and set delay interval (right side of bar) to 8 seconds,
                                      // using bitwise OR operator.

  sei();                              // re-enable interrupts
  //wdt_reset();                      // this is not needed...timer starts without it

  // delay interval patterns:
  //  16 ms:     0b000000
  //  500 ms:    0b000101
  //  1 second:  0b000110
  //  2 seconds: 0b000111
  //  4 seconds: 0b100000
  //  8 seconds: 0b100001

}

ISR(WDT_vect) // watchdog timer interrupt service routine
{
  counter+=1;

  if (counter < countmax)
  {
    wdt_reset(); // start timer again (still in interrupt-only mode)
  }
  else             // then change timer to reset-only mode with short (16 ms) fuse
  {
    
    MCUSR = 0;                          // reset flags

                                        // Put timer in reset-only mode:
    WDTCSR |= 0b00011000;               // Enter config mode.
    WDTCSR =  0b00001000 | 0b000000;    // clr WDIE (interrupt enable...7th from left)
                                        // set WDE (reset enable...4th from left), and set delay interval
                                        // reset system in 16 ms...
                                        // unless wdt_disable() in loop() is reached first
 
    //wdt_reset(); // not needed
  }
}

I’ve used the narcoleptic library for longer delays, see: Inkling
See longdelay() in above.

Different purpose, but nice to know...thx...

This uses the watchdog timer's "interrupt-only" mode for a user-defined number of loops, and then changes to the "reset-only" mode.

That's a clever way to handle the requirement!

Uses about 2k of memory.

No it doesn't. That's the full size of your sample sketch, which includes a bunch of arduino overhead stuff. Your code looks like significantly less than 200 bytes (the size of the sketch's .o file, without arduino overhead linked in.)

DaveEvans: This uses the watchdog timer's "interrupt-only" mode for a user-defined number of loops, and then changes to the "reset-only" mode.

Doesn't this assume that the board remains capable of handling watchdog interrupts? There are failure modes where that would not be the case, so as a hard recovery option this would be less robust than using a watchdog to trigger a reset directly.

PeterH: Doesn't this assume that the board remains capable of handling watchdog interrupts? There are failure modes where that would not be the case, so as a hard recovery option this would be less robust than using a watchdog to trigger a reset directly.

FWIW, according to the datasheet, using the watchdog interrupt can be useful for things like saving essential data before reset. So apparently someone at Amtel thought that watchdog interrupts would be at least somewhat reliable.

What failure modes might cause the board to not be able to handle watchdog interrupts? Is there a failure mode that might corrupt the watchdog status registers and control bits, too, thus killing the dog and its reset capability, too?

Using external components to reset after inactivity would be the most reliable approach. I don't need that for this project.

What failure modes might cause the board to not be able to handle watchdog interrupts?

Most obviously: failures that disable interrupts.

DaveEvans: What failure modes might cause the board to not be able to handle watchdog interrupts?

Any failure that occurred while interrupts were disabled, or within an interrupt handler (which amounts to the same thing).

Now that you say that, it seems obvious! :) Thanks for the education.

What failure modes might cause the board to not be able to handle watchdog interrupts?

Pretty simple to simulate such a failure, just put a while(1) {} statement inside any ISR function and the that will lock up the chip with no way out other then reset or power down/up. There are have been several high level white papers on critical fail safe system design and bottom line is while the WDT is useful it should never be used to try and detect/protect from failures of the chip it is running on. What happen if the crystal resonator fails? External WDT chips are available and is the go to method for industrial fail-safe system design. So as you don't require fail-safe, just validating that all your ISR functions are proper and reliable should allow you to use the WDT with confidence. Note that the older arduino 328P boards had a bootloader bug such that the chip would hang up from a WDT interrupt. Current boards are OK.

Hi, thanks a lot. What to do you think of this sketch that JOINS the extended watchdogtimer and LowPower library for sleep the board ?

Thanks a lot for your comments. Any comments will be very usefull for me and for everybody

Hope your answer.

/* Try to use watchdogtimer for:
    1. Use LowPower library for sleep the ATMEGA (link: http://www.rocketscream.com/blog/2011/07/04/lightweight-low-power-arduino-library/)
    2. Use the auto reset feature of ATMEGA in case a long loop or problems in the sketch. In this case, the time will be much more than 8 seconds.
    
For utility 2. I have to use ISR (WDT_vect) and use a counter to increase the WatchDogTimer enough to use it for 60 or 120 seconds or even more.

The sketch will be in a BOARD that not need to be active all time (It sleeps most of the time), but it needs an autoreset feature (just in case) because it uses a SIM900 shield (that usually has problems)

For utility 1. and 2. I need to change the ISR (WDT_vect) of the LowPower library. I have to delete it and rename library as LowPowerLong.h and redefine ISR (WDT_vect) in 
this sketch. 

ISR(WDT_vect) from LowPower.h is:
  ISR (WDT_vect)
  {
 // WDIE & WDIF is cleared in hardware upon entering this ISR
 wdt_disable();
  }  

I aslo need a boolean variable to know insie ISR that I came from LowPowerLong interrupt or from Long autoreset feature.

To provoque a long loop so the board will reset, just put a jumper between A0 and a 5V pin. To normal function, put hte jumper between A0 and GND pin. (see loop)

*/

#include "LowPowerLong.h"   //It is the same as LowPower.h but deleting ISR(WDT_vect) function.
#include "avr/wdt.h"

volatile int counter;  
volatile boolean counterEnabled = false;
#define counterMax 2  //Number of times of ISR(WDT_vect) to autoreset the board. I will autoreset the board after 8 secondes x counterMax

ISR (WDT_vect)
{
    if (counterEnabled) {
        counter += 1;
        if (counter < counterMax - 1) {
            wdt_reset(); // Reset timer, still in interrupt mode
        } else {
            MCUSR = 0;
            WDTCSR |= 0b00011000;    //WDCE y WDE = 1 --> config mode
            WDTCSR = 0b00001000 | 0b100001;    //clear WDIE (interrupt mode disabled), set WDE (reset mode enabled) and set interval to 8 seconds
                                               //Arduino post's author put here 64 ms, but, I am a noob, so I prefer to repeat the loop again just in case. Finally I get what I want.
        }
    } else {
        // It is only for sleep, so I copy source code from LowPower.h
 // WDIE & WDIF is cleared in hardware upon entering this ISR
 wdt_disable();        
    }
}

void setup()
{
    wdt_disable();
    
    // No setup is required for this library
    pinMode(13,OUTPUT);
    pinMode(A0,INPUT_PULLUP);
    for (byte j = 0; j < 99; j++) {  //In order to know the board has reset. Flash led lots of times.
      digitalWrite(13, j % 2);
      delay(50);
    }
}

void wdt_long_enable()
{
    counter = 0;
    counterEnabled = true;
    cli();    //disabled ints
    MCUSR = 0;   //clear reset status
    WDTCSR |= 0b00011000;    //WDCE y WDE = 1 --> config mode
    WDTCSR = 0b01000000 | 0b100001;    //set WDIE (interrupt mode enabled), clear WDE (reset mode disabled) and set interval to 8 seconds
    sei();   //enable ints
}

void wdt_long_disable()
{
    counterEnabled = false;
    wdt_disable();
}

void loop() 
{
    wdt_long_enable();
    
    //do stufff
    
    if (analogRead(A0)>700) {
      //A0 connected to 5V pin
      delay(30000);  //Let's go to reset the board. We will wait here more than 8 seconds * counterMax
    } else {
      //A0 connected to ground pin
    }

    wdt_long_disable();
    for (unsigned int i = 0; i < 5; i++) {
      LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);    //Here I could sleep for much more time than autoreset-time as wdt_long_disable was called.
      //Flash slow three times so I know that your are sleeping. (normally nothing will be here)
      digitalWrite(13,HIGH);
      delay(100);
      digitalWrite(13,LOW);
      delay(100);
      digitalWrite(13,HIGH);
      delay(100);
      digitalWrite(13,LOW);
      delay(100);
      digitalWrite(13,HIGH);
      delay(100);
      digitalWrite(13,LOW);
      delay(100);      
    }
}

anybody ?

cheers

anybody ?

best regards,

Before I answer, what happens when you test? Does it do what you expect?

[quote author=Nick Gammon link=msg=2069810 date=1422744376] Before I answer, what happens when you test? Does it do what you expect? [/quote]

Hi Nick, thanks a lot.

My main objetive is to have a watchdog timer as an autoreset feaute (just in case) but also sleep some time to consume less power, so I need to activate and deactivate autoreset feature when I deactive or activate sleep mode.

Yes, I think it works well and what I expect, but I don't know if there is any register config to do, or anything that you could think that it is not safe or could be risky to do (not to reset wdt after any line of code, or something similar).

And what about the change of the LowPower library from rocketscream ISR (WDT_vect) function ?

I think that my example could be very usefull for other peoplo (for example when using a GSM shield that usually some of the functions could take more than 8 seconds, a very laxative autoreset function after more than 8 seconds, ...)

NOTE: Sorry for my english.

Best regards

I don't see anything obviously wrong with it, and if it seems to work, well and good.

[quote author=Nick Gammon link=msg=2069989 date=1422755812] I don't see anything obviously wrong with it, and if it seems to work, well and good. [/quote]

Thanks a lot for your help (your aproval ;) )

I think it will be usefull for more people.

This dies not work on a mega although the registers are the same. Any hint anyone?

hello all ,

Sorry if i commented on old topic.

I am working on project which i want to enter the avr on full sleep_mode with watchdog interrupt over 5 minutes it is working well for some hours . if i leave it for more than one day it is hanging . I am wondering if that due to extending the watchdog timer over 5 minutes . Thanks in advance .

Amir Ezz

DaveEvans: ``` void loop() {     watchdogEnable(); // set up watchdog timer in interrupt-only mode

  while (true); // Whoops...in an infinite loop!  For testing only.                 // For production, this would be useful                 // code that normally takes less than xx sec to process                 // but occasionally hangs.

  wdt_disable();

}

What's the idea behind disabling the watchdog at the end of the loop and rightthereafter reenabling it with the first instruction of the loop function?

Also I recommend to add a wdt_disable(); as the first instruction in the setup to avoid nasty hang-ups with faulty boot loaders.