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.

19 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. Could you comment on using the other interrupt wakeup modes (HIGH, CHANGE, RISING…) with sleep?

    On arduino.cc, the comment says we can only use LOW unless SLEEP_MODE_IDLE is used. Is this true? http://playground.arduino.cc/Learning/arduinoSleepCode

    * C Trigger mode of the interrupt pin. can be:
    * LOW a low level trigger
    * CHANGE a change in level trigger
    * RISING a rising edge of a level trigger
    * FALLING a falling edge of a level trigger
    *
    * In all but the IDLE sleep modes only LOW can be used.
    */

    • Hi Rahul,

      Technically, yes, you can use other interrupt modes to wake from sleep. Check out the datasheet for your processor for more information on wake sources during sleep modes, such as page 41 of this example datasheet: http://www.atmel.com/images/doc7674.pdf

      For the linked processors, it appears you can use a CHANGE interrupt to trigger wake from any mode.

      However, the constraints described in your link may be inherent in the Arduino environment. That example is using attachInterrupt() to setup their wake condition, which is an Arduino-specific function. That function may have limitations that the raw AVR sleep functions do not, preventing you from setting a different type of ISR.

      We haven’t tested all of the potential combinations ourselves, but you might try prototyping a simple circuit to test what happens when you use different interrupt types. You could try one scenario using the attachInterrupt() Arduino method, and another using the AVR functions. If you try it, let us know what you find out!

  6. Thanks for the amazing tutorial. I’ve got a question: based on the code I’m assuming if you hadn’t turned off the LEDs before calling sleep_mode() they would remain on when Arduino was idle. is that the same for deeper sleep modes?

    • Hi Marshmellow, that’s a great question. The LED’s do stay on for the basic sleep modes, and I believe that the output registers are left in their current state even for deeper sleep, but I’m not 100% sure. A good afternoon experiment! If anyone else has experience, we’d love for you to share it.

  7. This article has been really helpful – thanks! Forgive a naive newbie question, but I’m unclear as to why you set pullups on all the unused pins. The Arduino Playground article on Digital pines states that pins in this state “supply enough current to light an LED dimly”. So doesn’t this increase power consumption? Or does that current flow only if something is connected to the pin (i.e. the above should have said “pins in this state CAN supply enough current…”)? And if actually nothing is connected, why does it matter if the voltage state of an unused pin wanders, provided your true inputs have pullups or pulldowns connected?

    • Hi Snufkin, that’s a great question. If you truly have nothing connected to the pin, then you probably don’t have to worry about using a pullup – a floating pin usually won’t have any bad effects. However, there are several reasons it’s good practice to apply pullups to all input pins. First, it prevents “doh” mistakes – where you might connect something to a pin later, but lose time trying to figure out why it’s not behaving as you expect. Second, although rare, there are cases where a floating pin can cause problems even when not connected to an input – they can create capacitive coupling with nearby pins, potentially affecting other parts of your circuit.

      The Arduino Playground article is correct – pins will pass a small amount of current when the pullup is enabled. If you are rabidly trying to reduce power consumption, you could potentially take a look at the loss from using internal pullups. However, for most projects, it’s not a concern. Take the worst case mentioned in the article – let’s say a 20 k-Ohm pullup resistor on a 5V board. V = IR, so I = V/R or 5V/20000 Ohms, or about 0.2 mA! Even with all of your pullups enabled, you’re only talking about a few mA total. Chips with more pins like the Mega usually have higher resistance values for their pullups, so it evens out.

Leave a Reply

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