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.
#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.