Hush little microprocessor… AVR and Arduino sleep mode basics

Using low power and sleep modes on the Arduino can drastically cut energy use.

Sleep and power saving modes are popular topics in the various AVR and Arduino communities.  How do I put my device to sleep?  How can I wake it up?  How can I control what does or doesn’t get turned off?  It took me a while to round up answers to all of these questions during my own hacking journey, so this post is an attempt to compile the basics in one place.

What do I need to know before putting my precious Arduino to sleep?

Putting your Arduino to sleep is not as drastic as, say, doing the same to the family pet, but there are a few things you should be aware of before you start.  First, if you’re using an Arduino board and not a custom AVR setup, keep in mind that the onboard voltage regulator will consume a small amount of power (anywhere from negligible to ~10mA) even if the chip itself is powered down.  This will limit your ability to consume truly miniscule amounts of juice.  If you’re trying to build a device that runs for months using a single coin cell battery, you may want to look at a custom solution.

Next, before you dive into the intricacies of sleep, you’ll need to learn about interrupts.  Interrupts are events that interrupt the main program flow and cause the processor to do something else.  All sleep modes depend on various interrupts to wake the device up from sleep.  If you have interrupts disabled, or don’t set up the right ones before putting your chip to sleep, it will simply stay asleep forever.  Some interrupts are triggered by internal events, such as timer overflows, and some are external, triggered by changes in pin state.  Check out our Arduino interrupt tutorial for more information on configuring interrupts.

What kind of sweet, sweet power savings can I expect to see?

That depends on your particular device, but in general, a lot of power can be saved.  In my own informal testing, a standard 5V Arduino Pro from Sparkfun draws about 15mA while sitting there running code.  In Idle Mode with a few peripherals turned off, that drops to ~5mA.  People have reported power draw of fractions of a milliamp using more extreme sleep modes.  Theoretically, the ATMega328 chip by itself draws only 6uA when fully powered down.

Ok, enough already.  How do I do it?

I thought you’d never ask.  Let’s walk through some code, with explanations of why we’re taking each step:

Setup

First, since we’re moving beyond the training wheels of the Arduino API, we need to include a few AVR libraries in our sketch.  Here’s what we’ll need:

#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>

We’ll also include the AVR IO library so we can manipulate pins and ports directly if we want to.  This step is optional but can come in handy:

#include <avr/io.h>

Now let’s move onto our Arduino setup() routine.  First, let’s do a bit of housekeeping.  We don’t want rogue connections in our circuit to draw extra power from the AVR.  Often, any devices that are connected to output pins will draw small amounts of power even when not in use. Any input pins that are floating (not using a pullup or pulldown resistor) will also consume power. Below are some sample commands to manipulate ports and pins  on the Arduino.  For this example, we’ll set all pins besides TX (pin 0) to input mode and enable pullup resistors, since there’s nothing connected right now.

void setup()
{
	DDRD &= B00000011;       // set Arduino pins 2 to 7 as inputs, leaves 0 & 1 (RX & TX) as is
	DDRB = B00000000;        // set pins 8 to 13 as inputs
	PORTD |= B11111100;      // enable pullups on pins 2 to 7, leave pins 0 and 1 alone
	PORTB |= B11111111;      // enable pullups on pins 8 to 13
	pinMode(13,OUTPUT);      // set pin 13 as an output so we can use LED to monitor
}

There’s more information on working with ports at this Arduino reference page.

Sleep Modes

There are 5 sleep modes available on standard 8-bit AVRs:

  • SLEEP_MODE_IDLE                   – least power savings
  • SLEEP_MODE_ADC
  • SLEEP_MODE_PWR_SAVE
  • SLEEP_MODE_STANDBY
  • SLEEP_MODE_PWR_DOWN    – most power savings

SLEEP_MODE_IDLE provides the least power savings but also retains the most functionality.  SLEEP_MODE_PWR_DOWN uses the least power but turns almost everything off, so your options for wake interrupts and the like are limited.  Power reduction management methods are contained in <avr/power.h> , and are described in more detail on the avr-libc documentation page

Let’s implement one of these modes and write a function that actually puts the device to sleep. We start by setting a preferred sleep mode from the list given above. Once we do that, we enable sleep by setting the sleep enable (SE) bit in the AVR MCUCR register, This bit is essentially a safety switch. Finally, we put the device to sleep with the sleep_mode() method. When we hit this point in our code, we’ll need to rely on the interrupt we set earlier to wake things up.

Once an interrupt is triggered and the device wakes up, the sketch resumes from right where it left off. When it does, we’ll first disable sleep, and then we can end our function or do anything else that needs doing. Here’s how the basics look in code:

void sleepNow()
{
	// Choose our preferred sleep mode:
	set_sleep_mode(SLEEP_MODE_IDLE);

	// Set sleep enable (SE) bit:
	sleep_enable();

	// Put the device to sleep:
	sleep_mode();

	// Upon waking up, sketch continues from this point.
	sleep_disable();
}

Waking the Arduino Up

There are several ways we can wake up after going to sleep:

  • external interrupt (change in pin state)
  • hardware UART (serial interface)
  • internal timer or watchdog timer

Keep an eye out for an in-depth discussion of all of these waking methods in a future article. For now, we’ll use the first method, and rely on an input pin to provide our wake interrupt. In a real project, this change in pin state could be provided by a button press, a switch, or by reading a value from an attached sensor.

First, we’ll use Arduino’s attachInterrupt() function to attach an interrupt to our desired pin. We’ll do this in sleepNow(), right before we put the device to sleep:

void sleepNow(void)
{
	// Set pin 2 as interrupt and attach handler:
	attachInterrupt(0, pinInterrupt, LOW);
	delay(100);

	// Choose our preferred sleep mode:
	set_sleep_mode(SLEEP_MODE_IDLE);
	...
	...

This addition says that when pin 2 goes from high to low, the chip will wake up and execute our pinInterrupt function. In pinInterrupt(), we’ll want to detach the interrupt so that it doesn’t keep firing after wakeup:

void pinInterrupt(void)
{
	detachInterrupt(0);
}

Now we have the basics of a sketch. All we have to do is write our loop() function to put the device to sleep when required. Let’s also add a visual indicator to tell us whether the Arduino is sleeping or not. We can do this by turning on the built-in LED on pin 13 during setup(), then toggling it off and on within our sleepNow() function. Such an indicator can be very useful while debugging a project, and can be removed once you’ve confirmed everything is working.

When we put everything together, it looks like this:

#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/io.h>
				//
void setup(void)
{
	DDRD &= B00000011;       // set Arduino pins 2 to 7 as inputs, leaves 0 & 1 (RX & TX) as is
	DDRB = B00000000;        // set pins 8 to 13 as inputs
	PORTD |= B11111100;      // enable pullups on pins 2 to 7
	PORTB |= B11111111;      // enable pullups on pins 8 to 13
	pinMode(13,OUTPUT);      // set pin 13 as an output so we can use LED to monitor
	digitalWrite(13,HIGH);   // turn pin 13 LED on
}
				//
void loop(void)
{
	// Stay awake for 1 second, then sleep.
	// LED turns off when sleeping, then back on upon wake.
	delay(1000);
	sleepNow();
}
				//
void sleepNow(void)
{
	// Set pin 2 as interrupt and attach handler:
	attachInterrupt(0, pinInterrupt, LOW);
	delay(100);
	//
	// Choose our preferred sleep mode:
	set_sleep_mode(SLEEP_MODE_IDLE);
	//
	// Set sleep enable (SE) bit:
	sleep_enable();
	//
	// Put the device to sleep:
	digitalWrite(13,LOW);   // turn LED off to indicate sleep
	sleep_mode();
	//
	// Upon waking up, sketch continues from this point.
	sleep_disable();
	digitalWrite(13,HIGH);   // turn LED on to indicate awake
}
				//
void pinInterrupt(void)
{
    detachInterrupt(0);
}

Feel free to save or copy the above sketch for use in your own projects (double click anywhere in the code to copy+paste). Have fun extending your battery life!

If you liked this article or want to be notified when we do more updates on power saving and wake methods, make sure to subscribe to our newsletter below.

12 thoughts on “Hush little microprocessor… AVR and Arduino sleep mode basics

  1. Thanks for the article.  I have been curious about this for a while but have had no need to investigate.

    Can you clear up one question:  I am surprised by the final “digitalWrite(13,HIGH)” in the main loop.  Is this necessary?  I expect that the next time through loop() would turn on the LED.

    Cheers
    H

    • Hi Howard,

      Thanks for the feedback! You’re correct about the 2nd digitalWrite() being extraneous. I’ve moved the original digitalWrite() to setup() and put the LED toggling into sleepNow(), which makes more sense and avoids code repetition if you use sleepNow() in multiple places.

  2. Great tutorial. It is always nice to see AVR tidbits thrown in with Arduino write ups – it makes transitioning to standalone AVR C much easier. 

    For clarity sake I would note that the DDR macro also accepts hexadecimal representations of the pin IO assignment. The only reason I would add this is due to hexadecimal representation being used much more frequently. 

  3. I tried this on a WiFiRedback. The WiFiReback has a Nano on it. Any chance anyone can see why the code will not work on the Nano? thanks.

  4. Great tutorial, thanks. Do all the Arduino boards have a regulator that uses about 10mA, even the Pro Mini?

    You set the pins as inputs and turn on pullup resistors with this code:
    DDRD &= B00000011;
    PORTD |= B11111100;
    Is there an advantage of doing it that way verses pinmode(). You code is more compact, but also more obscure.

    • Hi Scott,

      Thanks, glad you liked it. If I remember correctly, all Arduino boards have a linear regulator, but not all regulators are created equally, nor do you have to use it on all models. The Pro Mini, for example, runs through the regulator if you power it through V_IN, but it is bypassed if you feed power through the RAW pin. However, the Pro Mini consumes far less power than boards like the Uno, because it’s not running other peripherals. As long as you are inputting a voltage close to designated operating voltage, the regulator itself is probably only burning a few uA. Often, it’s a good idea not to bypass it, because the AVR chip can be sensitive to power fluctuations.

      Setting pins by modifying the ports directly is more efficient, and will take less CPU cycles. But you’re correct, it’s a bit harder to read if you’re not used to it. In most cases, it shouldn’t matter much unless you’re developing an application that is computationally intensive or relies on precise timing.

  5. Thanks for the excellent tutorial. Works on my JeeNode. Since JeeNodes have no LEDs, you can put one between the JeeNode SCK pin and any + pin and it will work just like Arduino’s pin 13. i.e. you do not have to change the above code.

    Looking forward to more but I cannot figure out how to subscribe to the news letter ;-(

  6. Hi Butch,

    You can sign up for the newsletter using the box in the upper right of our sidebar – did it give you any problems? You can also stay up to date by subscribing to our RSS feed, by clicking the orange RSS icon in the upper right of the sidebar as well. Thanks for the props and tip!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>