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



References

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

54 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
  8. Hey, great tutorials, keep up the neat work.

    Do you know if it is possible to combine the wake from interrupt and the watchdog ? I'm asking because I would like to build a low power board that transmits temp data every 5 minutes, but should also wake up on a PIR interrupt.

    Thanks in advance,
    Eric

    ReplyDelete
  9. This is really great, thanks! I wasn't able to get anywhere with the use of sleep modes by following the threads on the Arduino forums, but you've made it much more clear. I'm going to be using these concepts in the code for a project I'm writing up for MAKE magazine.

    Can you tell me, is there a way to put an Arduino to seep so that it won't wake up until the power is cycled (or it is reset)?

    I have a gizmo that counts down for several minutes (or even hours) before it performs a single action, and then it doesn't do anything else until it is restarted. I would like it to essentially shut itself off after it performs the action, but the issue is complicated because I am using the watch dog timer with an 8s delay to preserve power during the initial count-down phase.

    Of course, after the action is complete, I can just make it go right back to sleep every time it wakes up, but I'm looking for a more elegant solution. Is there a way to switch from an 8s watch dog to _no_ watch dog once the code is already looping?

    Thanks again for this great site, and for any other suggestions you may be able to give me.

    ReplyDelete
  10. Hi Tom,
    Thank you for the feedback, always good to hear that people are finding the site helpful.

    I think it would be as straight forward as disabling the watchdog interrupt before entering the SLEEP_MODE_PWR_DOWN sleep mode.

    I don't have access to my Arduino at the moment to test this, so if you get to test this before I do, please let me know if it works :-)

    Cheers,
    Donal

    ReplyDelete
  11. Thanks for work on Power Save, this has saved me many hours.

    How do I change the test PIN from 13 to 4 ?
    Just changing #define LED_PIN (13) to (4) doesn't do the trick?

    I have a WiFiRedback and am trying very hard to teach it and me how to make it sleep since battery operation is essential.

    Any chance you have info on how to make the WiFi part (MRF24WBOMA) sleep using DTIM beacon, then though SPI from the Arduino (Nano) wake up the WiFi? I'm completly stumped.
    thanks,

    ReplyDelete
  12. Hi Jerry,
    I've plenty of experience with XBee modules, but unfortunately I have no experience with the MRF24WBOMA radio module or the WiFiRedback.
    Sorry I can't be of more help.
    Cheers,
    Donal

    ReplyDelete
    Replies
    1. How about how to change froom PIN 13 to PIN 4 ?
      Thanks, your work here is very helpful.

      Delete
    2. Hi Jerry,
      If you want to control an LED from PIN 4, you will need to configure the mode of the pin to be an output. You will need to do this in your setup() function.

      pinMode(LED_PIN, OUTPUT);

      Hope that helps,
      Cheers,
      Donal

      Delete
    3. Again, thanks! That did the trick. Hum didn't understand where PIN 13 was made an OUTPUT. Must have been in one of the headers.

      Delete
  13. Thanks for the article.
    When running the code it appears to put the arduino into a lower power mode for execution - ie the LED is only dimly lit. How would I modify the code so that the board runs at full power when not in sleep? I'm using an Uno (AtMega 328P), don't know if that may be causing problems?

    ReplyDelete
  14. What is the current consumption under SLEEP_MODE_PWR_DOWN mode for wdt enabled? and wdt disabled?

    ReplyDelete
  15. Hi I'm trying to use this code in my arduino uno, but it doesn't works, the serial show a different character "III" the baud rate is correct, the led doesn't change the state. Maybe you can help me.
    Thanks for now
    luis

    ReplyDelete
  16. Hi I have a similar problem that luis with arduino uno, it doesn't write anything in the serial monitor at (9600 bauds)and the led is always "on". Any suggestion?

    ReplyDelete
  17. Hi Guys,
    I will get my hands on an Arduino Uno and test the software.
    Donal

    ReplyDelete
  18. This comment has been removed by the author.

    ReplyDelete
  19. I solved the problem with arduino UNO: pinMode(LED_PIN, OUTPUT); into SETUP LOOP solves the led problem.
    the problem of communication is due to the microcontroller has a small delay in initialization, and it goes to sleep too faster before to finish the transmission (or something like that), a delay(50); solves this problem.

    Regards

    ReplyDelete
    Replies
    1. Excellent Emos,
      I'm due to receive my UNO tomorrow, I will update the source code during the weekend.
      Best Regards,
      Donal

      Delete
  20. Donal,

    could you explain how to use a RTC to sleep the arduino and wake it up? Thanks

    ReplyDelete
    Replies
    1. Does anyone use the watchdog timer on the Geogram One to extend the battery life?

      http://dsscircuits.com/geogram-one.html

      Delete
    2. Hi Woodmate,
      I have one of these Arduino data logging shields http://learn.adafruit.com/adafruit-data-logger-shield - which has a real time clock. Only problem is I can't find the shield! I'll search again and if I find it write a post on using it to wake the Arduino.
      Best Regards,
      Donal

      Delete
  21. I have tried code,sleeping-arduino-part-3-wake-up-via uart and this is works ! mm but If I want to awake arduino with serial data which I choose (ex: just send character "A" and then arduino wake up ) /not receiving any serial data ? I really2 confuse,, I start depressed because that, can you help me ?? plis T___T

    ReplyDelete
    Replies
    1. Hi yuliaRetno,
      I don't think it is possible to wake up via a particular character. As far as I know, the wake up is triggered on either a logical 1 or 0 on one of the serial uart lines.
      Cheers,
      Donal

      Delete
  22. hmm, ok sir, can you help me sir ?mm,I find a difficulty from my final project, particularly with wake up sleep arduino with tag rfid (mifare) ,my tag rfid communication with SPI,So I decided wake up with serial uart,
    Actually, it can wake up, but I want to wake up with particularly tag (not all of tag )I confuse in what way I must try to wake up ?
    Can I wake up with internal timer or watchdog ?

    void sleepNow(){

    uint32_t idc ;

    idc = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A);// triger

    from tag rfid ,,is like serial.available()


    set_sleep_mode(SLEEP_MODE_PWR_DOWN);

    sleep_enable();

    sleep_mode();

    sleep_disable();}

    ReplyDelete
    Replies
    1. Hi Yulia,
      I don't think you will be able to wake up via serial uart on a particular tag. What you could do is wake up the microcontroller on all tags, but check the tag immediately and if the tag in not the particular one you want, put the microcontroller back to sleep.
      Hope that helps,
      Donal

      Delete
  23. hi, this code seems not to work if i just look on the indicators (Serial and LED toggle).
    Put a delay of 100ms after each serial print and initialize the LED_PIn as output in the setup.
    Then its showing what it should (on arduino UNO).
    I was searching for a bug in the WDT and Sleep setup for hours.... until i found this simple bug of just initializing the pin.. ;-(..

    ReplyDelete
    Replies
    1. Hi saperlot,
      Cheers for finding that, I have updated the code.
      Donal

      Delete
  24. Hi in my case I'm not able to see the message "WDT Overrun!".
    I've also added the delay(100) after the Serial.println.
    In addition I've moved the MCU on a breadboard but the LED is always on (pin 13 arduino -> pin 19 atmega328p).

    ReplyDelete
    Replies
    1. Hi Anto,
      The "WDT Overrun!" is an error message that you will see if your application doesn't clear the f_wdt correctly.
      Was the LED flashing correctly if you run the code with your MCU on your Arduino board? If so, then perhaps it is an issue with your circuit on the breadboard? Have you checked you are wiring the LED correctly?
      Cheers,
      Donal

      Delete
  25. I've integrated this code with a ZigBee transmission and it doesn't work. The ZigBee transmission works without the watchdog timer and it requires some delay time (5 secs) to work properly. Is the program restarted from the last row executed before the WatchDog interrupt ?

    ReplyDelete
  26. Hi Anto,
    The code is designed to be a basic example that can be extended by the reader if necessary, not a piece of code that will work with all scenarios, so I'm not surprised that you are having issues.

    The first line of C code executed when the micro wakes from sleep is 'sleep_disable();' in the'void enterSleep(void)' function.

    Can you please post your code so I can have a better idea of what you are trying to do?

    Cheers,
    Donal

    ReplyDelete
  27. You're right. Thanks in advance for your help.
    This is the sketch: http://pastebin.com/Zn2htthL
    I've disabled the power saving options to be sure the problem wasn't there.

    ReplyDelete
  28. The transmission is now working. This is the sketch: pastebin.com/tFt2vJRm It's using some power saving options, the power hibernation of the xBee pin and the power_done mode. The problem is that I need to wait for a really long time (about 10 seconds) before I can send a packet. Where am i wrong?

    ReplyDelete
    Replies
    1. Hi Anto,
      Will have a look this evening when I get home from work.
      Donal

      Delete
    2. Hi Ando,
      I suspect it is taking 10 seconds for your xBee module to connect to the network once it has exited sleep.

      First of all disable the watchdog sleep stuff and test enabling & disabling the xbee sleep.

      Also, keep an eye on the Association LED connected to your xbee module. Does it take several seconds to illuminate after the xbee has been taken out of sleep?

      Donal

      Delete
  29. Hello,
    First, I'm sorry about my english, I'm french!
    I'm trying to do a datalogger with low power to economise the battery. You say before that if we want 15 minutes of sleeping, we have to use the RTC ds3234. I want to know if it's possible to do it with the DS1307 that I have yet or if I have to buy this one particular? And if it's possible do you some tutorial to do it?
    Thanks

    Cécile

    ReplyDelete
    Replies
    1. Hi Cecile,
      I don't think you can use the DS1307 to wake your microcontroller as it doesn't have an alarm feature, whereas the DS3224 does:
      DS3224 Features:
      ...
      * Two Time-of-Day Alarms
      ...
      "The clock provides two programmable time-of-day
      alarms and a programmable square-wave output. The
      INT/SQW pin either generates an interrupt due to alarm
      condition or outputs a square-wave signal and the
      selection is controlled by the bit INTCN."

      I recently got a DS3234 and will try to complete the tutorial for using it this weekend.

      Hope that helps!
      Donal


      Delete
    2. Thank you very much for the information!
      So I will follow with interest your next article!
      The informations you give on your diferents posts were very helpful to put my arduino in sleep mode! Now I have to try to reduce more the consumption with a RBBB instead of my arduino!
      thanks a lot!

      Cécile

      Delete
    3. Hi Cécile,

      You can find here Feature Comparison of the DS323x Real-Time Clocks.

      Delete
    4. Hi donal!
      do you know when you will publish your article about DS3234? I'm very interested on it! I'm trying to program it and I have some dificulties! I hope you can help me!
      thanks
      Cécile

      Delete
  30. Hi Donal,

    Thanks for the code that you provided. Very useful!
    I created a blog posing using your code, where I measure the current of an Arduino Uno R3 board. It's nice to see how current draw & power consumption changes as you put the microcontroller to sleep. Check out my post: http://www.dossant.com/projects/arduino-low-power/

    Thanks,
    Igor

    ReplyDelete
  31. Using the code from above, my arduino diecimila only seems to sleep for about 4 seconds, any idea why this might be?

    ReplyDelete
  32. Hi,
    I have discovered a possible major problem with - Part 5 Wake Up Via The Watchdog Timer.
    I used this code as a test on the Leonardo R3 and now cannot access the board.
    I had no problems installing the code to my board, but now whenever I connect the board to the PC the Port tab in Device manager disappears after the first 8 second sleep cycle and does not re-apear.
    I have no access to any port information in either the PC or Arduino 1.5.2 Sketchbook.
    I have tested my other Arduino boards and they work perfectly.
    My Leonardo usually access Ports 11 to 14, but all I have available with the Leonardo now is Ports 3 to 5 (which are already in use)

    Do you have any ideas why the sleep cycle cuts off communication to the PC ports (I am assuming it cuts USB communication)?

    Do you have any ideas on how I might be able to re-gain access to my Leonardo???

    Any help will be appreciated.
    Lance.

    ReplyDelete
    Replies
    1. Hi Lance,
      Can you hold down the reset button until the 'uploading sketch' message appears in the IDE then release it? Please let me know if you can program it that way.
      BR,
      Donal

      Delete
    2. Many thanks for that Donal.

      It took a couple of attempts, but it eventually allowed the IDE to sync with the correct Port and let me upload the blink sketch and has given me full control of the board again (I shall remember this trick for future similar lock ups).

      I like the Wakeup with Watchdog Timer, so it would be good to figure out why it severed USB control and find a code fix for this (plus maybe add a loop counter or interrupt to end the Timer).

      Thanks again for your great tutorial on Sleep and Wakeups, these have been very educational and useful. I will certainly be using these in my future projects to reduce power usage.

      I am soon to start using some ATtiny85 chips in my small projects. Do you foresee any conflicts with this code? (I have not used AT85 chips before. I have the Digispark Tiny 1.04 IDE).

      Lance.

      Delete
    3. Hi Lance,
      Glad you got your board back up and running again! I must get my hands on a Leonardo to see why is it causing a lock-up!
      Hmm, I've no experience with the ATtiny85 chips, please share if the code works.
      Cheers,
      Donal

      Delete
  33. Hi Donal,

    Thanks for the excellent article.
    I have a suggestion for an enhancement.
    Instead of delay(100) to wait for the serial transport to finish, you can also use:
    Serial.flush();
    Older arduino versions would clear the transmit buffers, but > 1.0 will really wait for the transmit to finish.

    ReplyDelete