Sleepy Arduino

Hi,

I'm developing an Open Source solar powered arduino platform at Rachel's Electronics (Loading...), and have got some good results so far! This post is in regard to power saving techniques. My application is a sensor that does not have to run all the time, so I want the little bugger to sleep hard between readings. I've been working off of the almighty datasheet and these resources:
Embedded Systems: Sleeping Arduino - Part 1
http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/

My application of the sleepy arduino needs to be the most power conservative, so I focused on SLEEP_MODE_PWR_DOWN mode and would like to host a comprehensive sleepy-duino resource that addresses the other mode options in the future. So much of what we do with this thing is about waiting around, and it's always good to conserve energy :wink:

The code I'm posting here is called Sleepy Bones, and I'll follow with some notes and questions

/*
 Watchdog Sleepy Bones (the skeleton is the essential armature for any living thing)
 This is the basic structure that you need to Sleep_Mode_Pwr_Down/Watchdog interrupt wake
 Provided for general consumption and regurgitation by Rachel's Electronics http://www.rachelselectronics.com
 Under the CC licence here (http://creativecommons.org/licenses/by-sa/3.0/us/)
 Rachel's Electronics: Making the World Safe for Robots!
 
          Built in part from code posted by: 
           * KHM 2008 / Lab3/  Martin Nawrath nawrath@khm.de
           * Kunsthochschule fuer Medien Koeln
           * Academy of Media Arts Cologne 
             http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
*/


#include <avr/sleep.h>
#include <avr/wdt.h>
#define sleepTime  3                       //number of 8 second sleep cycles

volatile byte wdt=0;                       //used to count number of sleep cycles

void setup(){
  
  setup_watchdog();                        // set prescaler and enable interrupt                  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);     // sleep mode is set here Power Down uses the least current
                                           // system clock is turned off, so millis won't be reliable!
  delay(10);
}


void loop(){

         // do what you want to before you sleep here
           
    system_sleep();

         // do what you want to after you sleep here
}
 

void system_sleep() {
  byte dummy_0 = DDRB;                     // save port directions
  byte dummy_1 = DDRD;                     // save port directions
  byte dummy_2 = TWSR;                     // save TW Status Register (I2C)
  byte dummy_3 = TWBR;                     // save TW Bitrate Register (I2C)
  byte dummy_4 = SPCR;                     // save SPI Control Register
  byte dummy_5 = SPSR;                     // save SPI Status Register
  PRR = 0xEF;                              // disable peripherals
  ADCSRA |= (0<<ADEN);                     // disable ADC
  DDRB = 0x00;                             // set ports to intput to save power
  DDRD = 0x00;                             // set ports to intput to save power
  sleep_enable();                          // enable sleeping
  sleep_mode();                            // activate system sleep
  // sleeping ... 
  // first action after leaving WDT Interrupt Vector:
  if (wdt==sleepTime) {                   // sleep for this number times 8 seconds
    sleep_disable();                      // disable sleep     
    wdt=0;                                // reset watchdog counter  
    PRR = 0x00;                           // enable peripherals    
    ADCSRA |= (1<<ADEN);                  // enable ADC
    DDRB = dummy_0;                       // set ports to pre-sleep state
    DDRD = dummy_1;                       // set porst to pre-sleep state
    TWSR = dummy_2;                       // reset TW Status Register
    TWBR = dummy_3;                       // reset TW Bitrate Register
    SPCR = dummy_4;                       // reset SPI Control Register
    SPSR = dummy_5;                       // reset SPI Status Register
    }else{
    sleep_mode();                         // go back to sleep until it's time
  }
}


void setup_watchdog() {
  MCUSR = 0x00;  //clear all reset flags 
  //set WD_ChangeEnable and WD_resetEnable to alter the register
  WDTCSR |= (1<<WDCE) | (1<<WDE);   // this is a timed sequence to protect WDTCSR
  // set new watchdog timeout value to 1024K cycles (~8.0 sec)
  WDTCSR = (1<<WDP3) | (1<<WDP0);
  //enable watchdog interrupt
  WDTCSR |= (1<<WDIE);              
}

// Watchdog Interrupt Service Routine. 
// Very first thing after sleep wakes with WDT Interrupt
ISR(WDT_vect) {
  wdt++;  // increment the watchdog timer
}

NOTES:
You have to follow the instructions in the almighty datasheet when you mess around with the WDTCSR. Any alteration to this register is a two part process. First, set the Change_Enable bit and the Reset_Enable bit. Then you can do whatever you want as long as you clear the Change_Enable bit in your next move.
Using PWR_DOWN will stop the system clock (duh) and so any attempt to use millis() will fail.
My application is a solar sensor, and so I need long sleep times. I set the watchdog prescaler to the longest time (~8 seconds) and increment a counter to maximize sleep for longer periods. You may want to change this for your application. I am using the arduino board as a programmer, and dropping the ATmega into my breadboarded prototype circuit. Check out Rachel's blog above for future posts that have diagrams and stuph.
QUESTIONS:
What happens inside the sleep_mode() command? I have gone ahead and placed all my pins as inputs, and saved the peripheral fuses with dummy bytes, and put the extra clocks into shutdown, but does this already happen for, say, the SPI clock since I'm using PWR_DOWN option? I'm trying to keep this skeleton as light as possible. Right now it weighs in at 732 bytes compiling to the Duemilanove w/328.

I appreciate your comments and suggestions. See you at the Open Source Hardware Summit!

I appreciate your comments and suggestions

You asked for it... :wink:

You have to follow the instructions in the almighty datasheet when you mess around with the WDTCSR

Which you haven't done. You need to save+disable interrupts before mucking with the WDT configuration and restore interrupts afterwards.

DDRB = 0x00; // set ports to intput to save power
DDRD = 0x00; // set ports to intput to save power

The datasheet and my testing are contradictory on this matter.

By my testing, a digital input is the worst choice. Without a pull-up (or pull-down) resistor, the input can (and does) float. Relatively speakinga, a floating pin is a power sponge...
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1243213127/2

My understanding from the ATtiny84 datasheet is that Power Down Sleep disables pins that have not been configured to perform "special" functions. If this is true then it is not necessary to make any changes before sleeping.

The best course is to connect a long wire to an unused pin, put the processor to sleep, and measure the current while waving your hand near the wire. If the current changes, the pin is floating and is going to be a power drain.

What happens inside the sleep_mode() command?

A sleep instruction is executed. I think the latest version: enables sleep, sleeps, disables sleep. But that's about it.

I have gone ahead and placed all my pins as inputs

INPUT w/ PULLUP, OUTPUT, or INPUT w/ external pull-whatever are better choices. Unless you want to turn off all digital outputs, you only need to worry about UNUSED pins.

and saved the peripheral fuses with dummy bytes

This is probably not necessary.

put the extra clocks into shutdown

This is definately not necessary. Power Down Sleep shuts off ALL clocks.

Finally, THANK YOU for publishing your work! :sunglasses: :slight_smile:

Lively Coding Badly!

Which you haven't done. You need to save+disable interrupts before mucking with the WDT configuration and restore interrupts afterwards.

so I will code it thusly:

cli(); //disable global interrupts
MCUSR = 0x00; //clear all reset flags
//set WD_ChangeEnable and WD_resetEnable to alter the register
WDTCSR |= (1<<WDCE) | (1<<WDE); // this is a timed sequence to protect WDTCSR
// set new watchdog timeout value to 1024K cycles (~8.0 sec)
WDTCSR = (1<<WDP3) | (1<<WDP0);
//enable watchdog interrupt
WDTCSR |= (1<<WDIE);
sei(); //enable global interrupts

Thankyou: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1197886952
also: http://www.arduino.cc/playground/Main/AVR Thankyou, theGoogle

By my testing, a digital input is the worst choice. Without a pull-up (or pull-down) resistor, the input can (and does) float.

Very well. In order to keep to the Port Manipulation theme:

DDRB = 0x00; // set ports to intput to save power
DDRD = 0x00; // set ports to intput to save power
PORTB = 0xFF; // enable pullups on portB
PORTC = 0xFF; // enable pullups on portC
PORTD = 0xFF; // enable pullups on portD

variation on this post http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1243235304/5
if setting the ports equal to 0x00 does not turn off the internal pullups, i will use a better method on wake from sleep.

edit< no better method http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1277985118/15

Regarding my question about peripherals: maintaining their values, and shutting down their clocks, you said

This is definately not necessary. Power Down Sleep shuts off ALL clocks.

The PRR register description (9.11.3 in Atmel DS) recommends that the fuses be kept track of if you're shutting them down. I'm happy if the clocks get turned off. (had some question about the TWI clock, because there is a TWI address recognition wake cue, but it looks like you're right about all the generated clocks getting shut down.) I'll play with the peripheral fuse settings and run my I2C Digital Potentiometer (MCP4551) to see if I get a crash.

CHEERS!
Joel

"...you are at Liberty to communicate this Paper to whom you please;
it being of more Importance that Knowledge should increase,
than that your Friend should be thought an accurate Philosopher"
-Ben Franklin

I've modified the above code and made some nice discoveries through empirical testing.
When the Atmel goes into Power Down sleep mode, it shuts down all the asynchronous clocks, and turns off the system clock (crystal oscillator stops, millis() won't be reliable), but it's important to disable the Analog to Digital Converter to squeeze out the most power saving. I'm running off a rechargeable battery that has a solar charging circuit, so I need the Atmel to sleep very deeply. It's also very important (see above) to keep track of what your pins are doing. If enough pins are not accounted for, the circuit can draw more current during sleep than when the MCU is operating! I'm using a P-channel MOSFET (BS250) as a power switch for the board. The Atmel +V pins are connected on the high side of the MOSFET, so it's always getting juice, but just before sleeping, the gate of the MOSFET is pulled HIGH (it's got a pullup to boot) and the board is shutdown. In this case, it's very important not to have any of the internal pullups enabled! that's where the power loss would come from in my setup: pulled up pins connected to GND will draw current. Happily, I'm getting really good results.

Besides the Atmel328, I've got the following chips on board:
MCP4551 digital potentiometer (I2C)
S-80835 voltage detector
Photo resistor
TNC75A Temperature sensor (I2C)

Here's the current draw specs:
MCU operating, reading sensors ~ 6.8mA
MCU in RESET (pin 1 LOW) ~ 1.2mA
MCU in Power Down (rest of board off with MOSFET) ~ 0.21mA
(tested within a curly hair on VellemanVC140 and RadioShack22-81)

Here's an app note that helped me out figuring the proper Power MOSFET to use
http://www.latticesemi.com/lit/docs/appnotes/pac/an6048.pdf?jsessionid=f03091fa05970c3c49b01838131a11167b40
On the PCB prototype I will use NDT456P MOSFET. It's got better stats.

/*
 Watchdog Sleepy Bones (the skeleton is the essential armature for any living thing)
 This is the basic structure that you need to Sleep_Mode_Pwr_Down/Watchdog interrupt wake
 Provided for general consumption by Rachel's Electronics http://www.rachelselectronics.com
 Under the CC licence here (http://creativecommons.org/licenses/by-sa/3.0/us/)
 Rachel's Electronics: Making the World Safe for Robots! -Joel Murphy joel@joelmurphy.net
 
          Built in part from code posted by: 
           * KHM 2008 / Lab3/  Martin Nawrath nawrath@khm.de
           * Kunsthochschule fuer Medien Koeln
           * Academy of Media Arts Cologne 
             http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
*/


#include <avr/sleep.h>
#include <avr/wdt.h>
#define sleepTime  3                       //number of 8 second sleep cycles

volatile byte wdt=0;                       //used to cound number of sleep cycles

void setup(){
  
  setup_watchdog();                        // set prescaller and enable interrupt                  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);     // sleep mode is set here Power Down uses the least current
                                           // system clock is turned off, so millis won't be reliable!
  delay(10);
}


void loop(){

         // do what you want to before you sleep here
           
    system_sleep();

         // do what you want to after you sleep here
}
 

void system_sleep() {
  ADCSRA |= (0<<ADEN);                     // disable ADC
  sleep_enable();                          // enable sleeping
  sleep_mode();                            // activate system sleep
  // sleeping ... 
  // first action after leaving WDT Interrupt Vector:
  if (wdt == sleepTime) {                  // sleep for this number times 8 seconds
    sleep_disable();                       // disable sleep  
    ADCSRA |= (1<<ADEN);                   // switch ADC on    
    wdt = 0;                               // reset watchdog counter   
    }else{
    system_sleep();                        // go back to sleep until it's time
  }
}


void setup_watchdog() {
cli(); //disable global interrupts
  MCUSR = 0x00;  //clear all reset flags 
  //set WD_ChangeEnable and WD_resetEnable to alter the register
  WDTCSR |= (1<<WDCE) | (1<<WDE);   // this is a timed sequence to protect WDTCSR
  // set new watchdog timeout value to 1024K cycles (~8.0 sec)
  WDTCSR = (1<<WDP3) | (1<<WDP0);
  //enable watchdog interrupt
  WDTCSR |= (1<<WDIE);    
sei(); //enable global interrupts
}

// Watchdog Interrupt Service Routine. 
// Very first thing after sleep wakes with WDT Interrupt
ISR(WDT_vect) {
  wdt++;  // increment the watchdog timer
}

Just a couple of other things.
In the code posted above, I said 'sleep_mode();' after the }else{ in system_sleep(). Well, that obviously caused me no end of trouble... except that the trouble did end. The program will sleep for any multiple of 8 seconds that you desire by adjusting the sleepTime value.
Also, the datasheet notes on PRR register says that you need to keep track of your settings if you're going to turn off the clocks to save power. I can only assume that the good folks at arduino are using this register to kill the clocks, and that they are also keeping track of the associated settings, because my I2C business picks right back up without any coding on my end. I did test manually flipping the PRR, and it didn't make a damn bit of difference in anything.
Finally, if you want to communicate (I2C,SPI,UART,etc) you must delay for 2 or 5 mS after waking up or else you will lose bits.

I will post a link on this thread when I get my pictures and bits all sorted out.
I hope this helps!
Joel

I suggest removing the highlighted line of code...

void system_sleep() {
  ADCSRA |= (0<<ADEN);                     // disable ADC
  sleep_enable();                          // enable sleeping
  sleep_mode();                            // activate system sleep
  // sleeping ...
  // first action after leaving WDT Interrupt Vector:
  if (wdt == sleepTime) {                  // sleep for this number times 8 seconds
    sleep_disable();                       // disable sleep  
    ADCSRA |= (1<<ADEN);                   // switch ADC on    
    wdt = 0;                               // reset watchdog counter  
    }else{
    [glow]system_sleep();                        // go back to sleep until it's time[/glow]
  }
}
  1. The call is not necessary.

  2. The call results in a recursion. If the sleep time is long enough, the nested recursive calls will eat through the stack crashing your application.

Calling system_sleep() from inside system_sleep() a few times works, but i see what you're saying about recursion.
I'll put the sleep_mode(); inside of a while loop:

void system_sleep() {
  ADCSRA |= (0<<ADEN);                     // disable ADC
  sleep_enable();                          // enable sleeping
  while (wdt < sleepTime){                 // sleep for sleepTime * 8sec
    sleep_mode();                          // activate system sleep
    // sleeping ... 
  }
  sleep_disable();                         // disable sleep  
  ADCSRA |= (1<<ADEN);                     // switch ADC on 
  wdt = 0;                                 // reset wdt for next cycle
}

Works great for 75 cycles which gets me close enough to a 10 minute low power sleep.
Thanks for your help!

Just posted on Rachel's Electronics blog about the SolArduino V1 Open Source Hardware project that will be on display at Maker Faire NYC this weekend! The Sleepy Arduino code posted here is an important aspect of power management. I will have a limited amount of first run prototypes (kitted) for sale if you are in town and want to get on board. We've been focusing mostly on environmental sensing. Next major move with this is in the direction of low power robotics. Aquatic? Tree climbing? ...

http://www.rachelselectronics.com/blog

All mankind is divided into three classes: those that are immovable, those that are movable, and those that move.

  • Benjamin Franklin