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
}
}
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?
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);
}
}
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, ...)
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 .
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.