Sunday, 27 November 2011

Sleeping Arduino - Part 5 Wake Up Via The Watchdog Timer

Overview
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:


  1. 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.
  2. System Reset - A watchdog time-out reset will occur, i.e. the micro-controller will be restarted. For use in handling code lockups.
  3. 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);
  
  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...");
  
  /*** 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.");
}



/***************************************************
 *  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:



References

  • Martin's excellent tutorial here.
  • Darius's  excellent blog here.

8 comments:

  1. Hi, thanks for your articles first.
    Does it work only for Arduino Diecimila?
    I'm trying to run it on my Arduino UNO but the sketch returns me some strange characters
    C¡ªËë šÙ•±¥½…<0> instead of a simple test print i wrote.

    void loop()
    {
    if(f_wdt == 1)
    {
    /* Toggle the LED */
    Serial.println("Hello!");
    /* Don't forget to clear the flag. */
    f_wdt = 0;

    /* Re-enter sleep mode. */
    enterSleep();
    }
    else
    {
    /* Do nothing. */
    }
    }

    Thanks

    ReplyDelete
  2. Hi NicolaG,
    Yes, the code should work on your UNO. The strange characters could be because of your serial port settings aren't correct.
    Check the baud rade that you have set in the Arduino pc software and check the value you have set in code using the Serial.begin(...) function.
    Donal

    ReplyDelete
  3. First of all thanks a lot for the article, I'm newbe but i understand a lot of thinks... i think :-)
    I just want to ask ... is possible to put the arduino in sleep mode and also use an interrupt pulse counter ?
    But what i need is a timer that tell me how much time
    is between pulses...
    Thanks a lot again
    Denis

    ReplyDelete
  4. Hi Denis,
    You probably will be able to do this a sleep mode, but not the lowest power sleep mode (power-down).
    If you have a look at the excerpt from the datasheet here:
    http://2.bp.blogspot.com/_9WOJMofzxU0/S9PyagiuISI/AAAAAAAAHY8/tGlOUPRmano/s400/Picture+1.png
    You can see the different sleep modes and what hardware modules are supported within it.
    I think you will need to wake the board from sleep using the external interrupt(count the pulses) You could then use one of the timers to check the time between interrupt pulses.
    Hope this helps.
    Donal

    ReplyDelete
  5. Thanks a lot Donal,
    great think all this timers on the tabel.

    I think i'll try using an external crystal (32MHz)

    This weekend i'll play :-)

    Thanks again
    Denis

    ReplyDelete
  6. Thanks for this, very good.
    How about if I just want to wake the arduino every hour?
    How can I set that?

    Thanks, great article!
    Joao, Portugal

    ReplyDelete
  7. Hi Joao,
    Thank you for the feedback. If you want a very long sleep time: several minutes or hours, I'd suggest using an external real-time clock, such as:
    http://proto-pic.co.uk/deadon-rtc-ds3234-breakout/
    Something like this could generate an external interrupt to wakeup the Arduino.

    Best Regards,
    Donal

    ReplyDelete