11

Low-Power Arduino Using the Watchdog Timer

Posted November 17th, 2012. Filed under Arduino Electronics Guides

A project I am working on requires temperature data to be sent periodically (about every 5 minutes) from a sensor node to a data logger using an XBee radio. The project requires the sensor to operate using battery power for two weeks. To conserve power, I built a 3.3 V breadboard Arduino with a efficient switching DC to DC voltage regulator and put both the XBee and the ATmega328 to sleep most of the time. This post shows code to use the ATmega328 watchdog timer to keep the Arduino asleep most of the time only waking periodically to perform short tasks like read ing and sending temperature data.

#include <avr/sleep.h>
// This library contains functions to set various low-power 
// states for the ATmega328

// This variable is made volatile because it is changed inside
// an interrupt function
volatile int sleep_count = 0; // Keep track of how many sleep
// cycles have been completed.
const int interval = 5; // Interval in minutes between waking
// and doing tasks.
const int sleep_total = (interval*60)/8; // Approximate number 
// of sleep cycles needed before the interval defined above 
// elapses. Not that this does integer math.

void setup(void) {
watchdogOn(); // Turn on the watch dog timer.

// The following saves some extra power by disabling some 
// peripherals I am not using.

// Disable the ADC by setting the ADEN bit (bit 7) to zero.
ADCSRA = ADCSRA & B01111111;

// Disable the analog comparator by setting the ACD bit
// (bit 7) to one.
ACSR = B10000000;

// Disable digital input buffers on all analog input pins
// by setting bits 0-5 to one.
DIDR0 = DIDR0 | B00111111;
}

void loop(void) {
goToSleep(); // ATmega328 goes to sleep for about 8 seconds
// and continues to execute code when it wakes up

if (sleep_count == sleep_total) {

// CODE TO BE EXECUTED PERIODICALLY

}

}

void goToSleep()   
{
// The ATmega328 has five different sleep states.
// See the ATmega 328 datasheet for more information.
// SLEEP_MODE_IDLE -the least power savings 
// SLEEP_MODE_ADC
// SLEEP_MODE_PWR_SAVE
// SLEEP_MODE_STANDBY
// SLEEP_MODE_PWR_DOWN -the most power savings
// I am using the deepest sleep mode from which a
// watchdog timer interrupt can wake the ATMega328

set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode.
sleep_enable(); // Enable sleep mode.
sleep_mode(); // Enter sleep mode.
// After waking from watchdog interrupt the code continues
// to execute from this point.

sleep_disable(); // Disable sleep mode after waking.
                     
}

void watchdogOn() {
  
// Clear the reset flag, the WDRF bit (bit 3) of MCUSR.
MCUSR = MCUSR & B11110111;
  
// Set the WDCE bit (bit 4) and the WDE bit (bit 3) 
// of WDTCSR. The WDCE bit must be set in order to 
// change WDE or the watchdog prescalers. Setting the 
// WDCE bit will allow updtaes to the prescalers and 
// WDE for 4 clock cycles then it will be reset by 
// hardware.
WDTCSR = WDTCSR | B00011000; 

// Set the watchdog timeout prescaler value to 1024 K 
// which will yeild a time-out interval of about 8.0 s.
WDTCSR = B00100001;

// Enable the watchdog timer interupt.
WDTCSR = WDTCSR | B01000000;
MCUSR = MCUSR & B11110111;

}

ISR(WDT_vect)
{
sleep_count ++; // keep track of how many sleep cycles
// have been completed.
}


If you have enjoyed this entry. Please feel free to bookmark it using your favorite social bookmarking site

11 Responses so far

  1. Patrick says:

    Thanks for the great overview. I got it to work for my project and this was a big help. A couple things you might want to change are that the function called in the loop is goToSleep, while the one you define is sleepNow. I also had better success by changing ‘if (sleep_count == sleep_total)’ to ‘if (sleep_count > sleep_total)’ since for mysterious reasons sometimes the sleep_count would jump higher than the total so the loop wouldn’t exit. Lastly, you clear the MCUSR twice in watchdogOn(), is there a reason for this? Don’t mean to be nit picky, overall this works well.

    • Jeff says:

      I am glad you found the post useful. Thanks for pointing out the “goToSleep()” typo. I have fixed it. It is strange that you had trouble with (sleep_count==sleep_total). I have been using code like this in a project that has been operating continuously for over three months and never found the MCU to hang. Nevertheless, using (sleep_count > sleep_total) is a good way to be safe. As for the second time I clear the MCUSR flag, I seem to remember reading in the ATmega328 datasheet that turning on the interrupt could accidentally set this flag. I could be remembering incorrectly but I think I put this in there to be safe. Thanks for the feedback.

  2. Charles says:

    Thx a lot !!!
    Exactly what I was looking for. It will save me lot of time.
    Cheers

  3. Michael says:

    Thank you for this example. I don’t know why, but in my circuit, the sleep time is only 4 seconds (not 8 seconds). Although, I tried it in the same way as you. Maybe, you can give some hints?

    Curcuit:
    [atmega328p, 16Mhz, 3.3V, Bootloader(Arduino)]

    Best regards from germany

    • Jeff says:

      The eight second sleep time is an approximation. The watchdog timer continues running in the background even after the MCU wakes up so if you do four seconds of work then go back to sleep the MCU will only stay asleep for about four more seconds before the watchdog timer times out and wakes the MCU up again. Maybe this is what is happening.

  4. Test carte Rocket scream Mini Ultra + | Djynet says:

    [...] est de faire dormir le micro en plus du module Xbee. Je me suis inspire d’un article dispo ICI. Il sagit d’utiliser le WatchDog pour reveiller le micro toutes les 8s (on ne peut pas faire [...]

  5. kiwi says:

    Hi, Does it work with arduino uno?

  6. kiwi says:

    I just tried this on 2 Arduino Leonardos (wrong chips) and they’ve both gone caput. After the bootloader has loaded the board can no longer be recognized (checked in device manager). Any idea?

    • Jeff says:

      Wow, this is surprising. The ATmega32U4 on the Leonardo also has a programmable watchdog timer. I wouldn’t expect the program to brick the MCU. You may have to reload the bootloader using an AVR programmer. There are several resources (programmers and tutorials) on how to do this on sparkfun.com. I have bricked Unos a lot. I use the AVRISP MKII programmer by Atmel. Good luck.

    • Jan Willem says:

      I tried the same with same results :-(
      Board seems bricked.

      Problem with the Leonardo is that when it enters sleep mode, it loses the USB connection and it doesn’t come back. If you have bricked the board, the way to unbrick it is load another sketch (like the blink sketch). I haven’t managed to fix this on the Windows IDE, but on a Mac or Linux it works as follows: plug in the bricked leonardo board and slect the correct serial port (it will briefly appear in the list and then disappear), compile the sketch and upload. When the message Uploading… apperas press the reste button on the Leonardo and the sketch will upload and the board will be un-bricked.

Leave a Comment