Welcome to the fifth and final part of the "Sleeping Arduino" series, where we will cover how to wake the Arduino from sleep mode using the Watchdog Timer (WDT). When waking your Arduino from sleep, you could use one of the standard internal timers of an Arduino as I have detailed in Part 4, but if you are looking for the maximum sleep time and/or minimum sleep power consumption, you have the use the WDT;
As I have mentioned in this table, the WDT can give us a sleep time of 8 seconds, whereas the 'longest' 8/18bit timer will only give us a sleep time of ~4 seconds.
Watchdog Timer (WDT)
The Watchdog Timer on the Arduino's microprocessor only has one source to drive it: it's own separate internal 128kHz oscillator (as opposed to the 8/16bit internal timers, which can use either the 16Mhz system clock or an external clock). It is this separate oscillator that enables the WDT to function in the lowest power mode: SLEEP_MODE_PWR_DOWN (see here for more detailed info on the Arduino's power modes).
The WDT also has a prescaler, which is used to configure the timeout period. It supports timeout periods from 16ms to 8 seconds:
Also it has three modes of operation:
- Interrupt - The WDT_vect interrupt sub-routing will be called when the WDT times out. Can be used to wake the micro from sleep modes, including the lowest power sleep mode (SLEEP_MODE_PWR_DOWN), where other timers are not available.
- System Reset - A watchdog time-out reset will occur, i.e. the micro-controller will be restarted. For use in handling code lockups.
- Interrupt and System Reset - First the WDT_vect interrupt sub-routing will be called, when completed a watchdog time-out reset will occur.
Please refer to my post here for the other operation modes of the WDT. The post is for the Atmega1281, but the functionality is pretty much the same.
Code
So, we will be using interrupt mode in our code to wake the Arduino from sleep every 8 seconds to toggle the state of the LED:
/* * Sketch for testing sleep mode with wake up on WDT. * Donal Morrissey - 2011. * */ #include <avr/sleep.h> #include <avr/power.h> #include <avr/wdt.h> #define LED_PIN (13) volatile int f_wdt=1; /*************************************************** * Name: ISR(WDT_vect) * * Returns: Nothing. * * Parameters: None. * * Description: Watchdog Interrupt Service. This * is executed when watchdog timed out. * ***************************************************/ ISR(WDT_vect) { if(f_wdt == 0) { f_wdt=1; } else { Serial.println("WDT Overrun!!!"); } } /*************************************************** * Name: enterSleep * * Returns: Nothing. * * Parameters: None. * * Description: Enters the arduino into sleep mode. * ***************************************************/ void enterSleep(void) { set_sleep_mode(SLEEP_MODE_PWR_SAVE);
/* EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption. */
sleep_enable(); /* Now enter sleep mode. */ sleep_mode(); /* The program will continue from here after the WDT timeout*/ sleep_disable(); /* First thing to do is disable sleep. */ /* Re-enable the peripherals. */ power_all_enable(); } /*************************************************** * Name: setup * * Returns: Nothing. * * Parameters: None. * * Description: Setup for the serial comms and the * Watch dog timeout. * ***************************************************/ void setup() { Serial.begin(9600); Serial.println("Initialising..."); delay(100); //Allow for serial print to complete. pinMode(LED_PIN,OUTPUT); /*** Setup the WDT ***/ /* Clear the reset flag. */ MCUSR &= ~(1<<WDRF); /* In order to change WDE or the prescaler, we need to * set WDCE (This will allow updates for 4 clock cycles). */ WDTCSR |= (1<<WDCE) | (1<<WDE); /* set new watchdog timeout prescaler value */ WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */ /* Enable the WD interrupt (note no reset). */ WDTCSR |= _BV(WDIE); Serial.println("Initialisation complete."); delay(100); //Allow for serial print to complete. } /*************************************************** * Name: enterSleep * * Returns: Nothing. * * Parameters: None. * * Description: Main application loop. * ***************************************************/ void loop() { if(f_wdt == 1) { /* Toggle the LED */ digitalWrite(LED_PIN, !digitalRead(LED_PIN)); /* Don't forget to clear the flag. */ f_wdt = 0; /* Re-enter sleep mode. */ enterSleep(); } else { /* Do nothing. */ } }
All parts of this series:
- Part 1 Overview Of Arduino Sleep Modes
- Part 2 Wake Up Via An External Interrupt
- Part 3 Wake Up Via the UART
- Part 4 Wake Up Via Internal Timer
- Part 5 Wake Up Via The Watchdog Timer
References