Notes on putting Arduino to sleep

 

I’ve been challenged recently with the need to minimize the power drain on my battery-powered Arduino project. It seems I’m not alone and I found a bunch of great ideas on the web, and the solution I settled on involves the use of the AVR watchdog timer, and AVR sleep mode. The functions needed are available if you include the following header files in your Arduino code:

#include <avr/sleep.h>

and

#include <avr/wdt.h>

Before going any further, special thanks to these bloggers for providing the code, so the credit for the difficult works belongs here and here and here.

I’ve gotten about 3-5 days out of 6 rechargeable AA batteries powering an XBEE Pro, and a couple of sensors using this technique, and 5-7 days with a couple of solar cells for recharging.

Also, the AVR 8-bit instruction set documentation can be downloaded here for those who want to dig in.

After the includes, add these defines (used for clearing and setting bits, “sfr” is a special function register):

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

Add a variable for use with the watchdog timer:

volatile boolean f_wdt=1;

Add an interrupt handler function:

// Watchdog Interrupt Service / is executed when  watchdog timed out
ISR(WDT_vect) {
 f_wdt=1;  // set global flag
}

Add a function to configure the watchdog timer:

//****************************************************************
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {
 byte bb;
 int ww;
 if (ii > 9 ) ii=9;
 bb=ii & 7;
 if (ii > 7) bb|= (1<<5);
 bb|= (1<<WDCE);
 ww=bb;
 MCUSR &= ~(1<<WDRF);
 // start timed sequence
 WDTCSR |= (1<<WDCE) | (1<<WDE);
 // set new watchdog timeout value
 WDTCSR = bb;
 WDTCSR |= _BV(WDIE);
}

Add the system_sleep() function:

//****************************************************************  
// set system into the sleep state
// system wakes up when wtchdog is timed out
void system_sleep() {
 cbi(ADCSRA,ADEN);  // switch Analog to Digitalconverter OFF
 set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
 sleep_enable();
 sleep_mode();                        // System sleeps here
 sleep_disable();              // System continues execution here when watchdog timed out
 sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON
}

In your Arduino setup() function add a call to the setup_watchdog() and cbi/sbi macros shown above:

void setup() {
// other stuff you want to setup
 cbi( SMCR,SE );      // sleep enable, power down mode
 cbi( SMCR,SM0 );     // power down mode
 sbi( SMCR,SM1 );     // power down mode
 cbi( SMCR,SM2 );     // power down mode
 setup_watchdog(9);
}

Now, in your Arduino loop function you can perform some work, and “power down” periodically and hopefully save some battery power:

void loop() {
 if (f_wdt==1) // wait for timed out watchdog
   f_wdt=0;   
 // do some stuff
 system_sleep(); // when we wake up, we’ll return to the top of the loop
}

That’s all for now. I wanted to capture all of this in one place, hopefully others will find it useful as well.









 

Another post about Arduino sleep

I’ve written up my experience with putting Arduino to sleep and using the Watchdog Timer in “interrupt and system restart” mode. Hope this helps.

RE:

Thanks! I like that you’ve taken the time to step through and describe what your code is doing…