Go Down

Topic: Extend watchdog timer beyond 8 seconds (Read 5991 times) previous topic - next topic

DaveEvans

Jun 21, 2014, 05:57 am Last Edit: Jun 22, 2014, 05:45 am by DaveEvans Reason: 1
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!

Code: [Select]

#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
 }
}

LarryD

#1
Jun 21, 2014, 06:18 am Last Edit: Jun 21, 2014, 06:20 am by LarryD Reason: 1
I've used the narcoleptic library for longer delays,  see: https://www.inkling.com/read/arduino-cookbook-michael-margolis-2nd/chapter-18/recipe-18-10
See longdelay() in above.
No technical PMs.
The last thing you did is where you should start looking.

DaveEvans

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

westfw

Quote
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!

Quote
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.)

PeterH


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.
I only provide help via the forum - please do not contact me for private consultancy.

DaveEvans

#5
Jun 22, 2014, 06:05 am Last Edit: Jun 22, 2014, 06:16 am by DaveEvans Reason: 1

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.

westfw

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

Most obviously: failures that disable interrupts.


PeterH


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).
I only provide help via the forum - please do not contact me for private consultancy.

DaveEvans

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

retrolefty

#9
Jun 23, 2014, 03:27 am Last Edit: Jun 23, 2014, 03:29 am by retrolefty Reason: 1
Quote

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.






juan3211

#10
Jan 25, 2015, 08:08 am Last Edit: Jan 25, 2015, 08:15 am by juan3211
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.

Code: [Select]


/* 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);      
    }
}


juan3211


juan3211


Nick Gammon

Before I answer, what happens when you test? Does it do what you expect?
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

juan3211

#14
Feb 01, 2015, 12:06 am Last Edit: Feb 01, 2015, 12:08 am by juan3211
Before I answer, what happens when you test? Does it do what you expect?
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

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy