Sleep.

How to let your Arduino go to sleep and wake up on an external event.

Preface

Sleep is commonly used to save power on Arduino boards. For some Arduino variants, however, there is not much benefit. For example, the Arduino serial and USB boards use a 7805 type of power regulator, which needs 10mA when the Atmega IC is in idle mode. Putting these boards to sleep will cut a few mA off the total power consumption however it will still be high.

If you bypass the inefficient regulator with your own power supply circuit, or use a board with a fairly efficient power supply, such as an Arduino Pro, then sleep can be very beneficial for reducing power and extending battery life. The regulator can even be removed altogether when using some Li-ion batteries.

Figure 1: a 220 Ohm resistor connects RX to pin 2

Global Principle

Sleep is assisted by interrupts. without them, only a reset can wake the Arduino up again. Fortunately interrupts are incorporated since the 0007 version of the Arduino IDE.

On the hardware front, the Arduino is equipped with two interrupt ports: digital pin 2 and 3. So the Arduino can sense those pins for an event to wake up and resume execution of code. It is even possible to execute special code depending on which pin triggered the wake up (the interrupt).

Events on the USART (the serial port) will also wake up the Arduino. In order for this to work, the Arduino must be in POWER_MODE_IDLE, the only power mode that doesn't disable the USART. Although this mode doesn't give great power savings you can use the functions provided in avr/power.h ( power_adc_disable(),power_spi_disable(),power_timer0_disable(), power_timer1_disable(),power_timer2_disable(),power_twi_disable()) to disable other hardware modules to achieve greater power savings. See this link for example code.

When using SLEEP_MODE_IDLE, care must be taken to ensure that the 8-bit timer is disabled if you're using the arduino layer. The timer can be disabled before entering sleep using the PRR = PRR | 0b00100000; statement and subsequently re-enabled once out of sleep using PRR = PRR & 0b00000000; . The PRR refers to the Power Reduction Register. One must note that if this timer is disabled, the millis(); cannot be relied upon anymore if you need reliable data comparison before and after a sleep command.

Because of the dominant way an interrupt breaks in in the execution of the main code, it is wise to make the code executed by an interrupt as short as possible. Maybe even just set a variable which will be handled in the main program. As long as the code for the interrupt runs, internal timers are waiting.

Level Interrupts

When the arduino is in SLEEP_MODE_PWR_DOWN the only way to wake it is with either a watchdog timer interrupt or a level interrupt on pins 2 or 3.

A level interrupt means that the pin has to be held in that state for a certain amount of time before the interrupt is triggered. In the interrupt service routine (ISR) for a level interrupt, the interrupt must be detached otherwise the interrupt will keep happening and the ISR will be repeatedly called until the pin changes state.

void pin2_isr()
{
  detachInterrupt(0);
  pin2_interrupt_flag = 1;
}

As a consequence, the interrupt must be re-enabled in the code once the pin has gone back to a normal state, i.e. for a low level interrupt, check that the pin has gone high before attaching the interrupt again.

Warning Code like this, which is a typical example of sleep code, can cause a problem if you are relying on the interrupt to wake you from sleep:

attachInterrupt(0, pin2_isr, LOW);
/* 0, 1, or many lines of code here */
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
cli();
sleep_enable();
sleep_bod_disable();
sei();
sleep_cpu();
/* wake up here */
sleep_disable();

The problem is if the interrupt occurs after attachInterrupt but before sleep_cpu(). The ISR will run, the interrupt will be detached, and then the CPU will enter sleep mode with no interrupt enabled. The MCU will never wake up this way. Fortunately there is a solution. The sleep enable bit can be cleared during the ISR and thus the MCU will not go to sleep. The sleep_enable() command also goes above the attachInterrupts function. e.g.

sleep_enable();
attachInterrupt(0, pin2_isr, LOW);
/* 0, 1, or many lines of code here */
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
cli();
sleep_bod_disable();
sei();
sleep_cpu();
/* wake up here */
sleep_disable();

void pin2_isr()
{
  sleep_disable();
  detachInterrupt(0);
  pin2_interrupt_flag = 1;
}

Example

This code makes use of the Serial port to receive commands. In parallel to that it will count 10 seconds before going into sleep mode. The 220 Ohm resistor keeps pin 2 HIGH (because RX is internally pulled-up to 5V when used as Serial port) until there is serial information coming in.

Note to the author: there is no need for a resistor between RX and pin 2 and the above statement is rather confusing. In fact RX and pin 2 can be connected directly as RX is already connected to the output of the USB to serial converter and a serial line when in idle state is at logic HIGH (in TTL that means 5V). So pin 2 is effectively already pulled up at a HIGH state. A resistor may only be useful to limit contention between two outputs if one programs pin 2 as output by mistake (pins are input by default anyway), though current is already internally limited to 40mA.

Note to the author #2: The attachInterrupt function is being called in the setup routine (line 68) and the sleepNow function (line 118). From the comments in the sleepNow function, I suspect that the call in the setup routine should be removed.

  1. #include <avr/sleep.h>
  2.  
  3. /* Sleep Demo Serial
  4.  * -----------------
  5.  * Example code to demonstrate the sleep functions in an Arduino.
  6.  *
  7.  * use a resistor between RX and pin2. By default RX is pulled up to 5V
  8.  * therefore, we can use a sequence of Serial data forcing RX to 0, what
  9.  * will make pin2 go LOW activating INT0 external interrupt, bringing
  10.  * the MCU back to life
  11.  *
  12.  * there is also a time counter that will put the MCU to sleep after 10 secs
  13.  *
  14.  * NOTE: when coming back from POWER-DOWN mode, it takes a bit
  15.  *       until the system is functional at 100%!! (typically <1sec)
  16.  *
  17.  * Copyright (C) 2006 MacSimski 2006-12-30
  18.  * Copyright (C) 2007 D. Cuartielles 2007-07-08 - Mexico DF
  19.  *
  20.  *  This program is free software: you can redistribute it and/or modify
  21.  *  it under the terms of the GNU General Public License as published by
  22.  *  the Free Software Foundation, either version 3 of the License, or
  23.  *  (at your option) any later version.
  24.  *
  25.  *  This program is distributed in the hope that it will be useful,
  26.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  27.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  28.  *  GNU General Public License for more details.
  29.  *
  30.  *  You should have received a copy of the GNU General Public License
  31.  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  32.  *
  33.  */
  34.  
  35. int wakePin = 2;                 // pin used for waking up
  36. int sleepStatus = 0;             // variable to store a request for sleep
  37. int count = 0;                   // counter
  38.  
  39. void wakeUpNow()        // here the interrupt is handled after wakeup
  40. {
  41.   // execute code here after wake-up before returning to the loop() function
  42.   // timers and code using timers (serial.print and more...) will not work here.
  43.   // we don't really need to execute any special functions here, since we
  44.   // just want the thing to wake up
  45. }
  46.  
  47. void setup()
  48. {
  49.   pinMode(wakePin, INPUT);
  50.  
  51.   Serial.begin(9600);
  52.  
  53.   /* Now it is time to enable an interrupt. In the function call
  54.    * attachInterrupt(A, B, C)
  55.    * A   can be either 0 or 1 for interrupts on pin 2 or 3.  
  56.    *
  57.    * B   Name of a function you want to execute while in interrupt A.
  58.    *
  59.    * C   Trigger mode of the interrupt pin. can be:
  60.    *             LOW        a low level trigger
  61.    *             CHANGE     a change in level trigger
  62.    *             RISING     a rising edge of a level trigger
  63.    *             FALLING    a falling edge of a level trigger
  64.    *
  65.    * In all but the IDLE sleep modes only LOW can be used.
  66.    */
  67.  
  68.   attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
  69.                                       // wakeUpNow when pin 2 gets LOW
  70. }
  71.  
  72. void sleepNow()         // here we put the arduino to sleep
  73. {
  74.     /* Now is the time to set the sleep mode. In the Atmega8 datasheet
  75.      * http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
  76.      * there is a list of sleep modes which explains which clocks and
  77.      * wake up sources are available in which sleep mode.
  78.      *
  79.      * In the avr/sleep.h file, the call names of these sleep modes are to be found:
  80.      *
  81.      * The 5 different modes are:
  82.      *     SLEEP_MODE_IDLE         -the least power savings
  83.      *     SLEEP_MODE_ADC
  84.      *     SLEEP_MODE_PWR_SAVE
  85.      *     SLEEP_MODE_STANDBY
  86.      *     SLEEP_MODE_PWR_DOWN     -the most power savings
  87.      *
  88.      * For now, we want as much power savings as possible, so we
  89.      * choose the according
  90.      * sleep mode: SLEEP_MODE_PWR_DOWN
  91.      *
  92.      */  
  93.     set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
  94.  
  95.     sleep_enable();          // enables the sleep bit in the mcucr register
  96.                              // so sleep is possible. just a safety pin
  97.  
  98.     /* Now it is time to enable an interrupt. We do it here so an
  99.      * accidentally pushed interrupt button doesn't interrupt
  100.      * our running program. if you want to be able to run
  101.      * interrupt code besides the sleep function, place it in
  102.      * setup() for example.
  103.      *
  104.      * In the function call attachInterrupt(A, B, C)
  105.      * A   can be either 0 or 1 for interrupts on pin 2 or 3.  
  106.      *
  107.      * B   Name of a function you want to execute at interrupt for A.
  108.      *
  109.      * C   Trigger mode of the interrupt pin. can be:
  110.      *             LOW        a low level triggers
  111.      *             CHANGE     a change in level triggers
  112.      *             RISING     a rising edge of a level triggers
  113.      *             FALLING    a falling edge of a level triggers
  114.      *
  115.      * In all but the IDLE sleep modes only LOW can be used.
  116.      */
  117.  
  118.     attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
  119.                                        // wakeUpNow when pin 2 gets LOW
  120.  
  121.     sleep_mode();            // here the device is actually put to sleep!!
  122.                              // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
  123.  
  124.     sleep_disable();         // first thing after waking from sleep:
  125.                              // disable sleep...
  126.     detachInterrupt(0);      // disables interrupt 0 on pin 2 so the
  127.                              // wakeUpNow code will not be executed
  128.                              // during normal running time.
  129.  
  130. }
  131.  
  132. void loop()
  133. {
  134.   // display information about the counter
  135.   Serial.print("Awake for ");
  136.   Serial.print(count);
  137.   Serial.println("sec");
  138.   count++;
  139.   delay(1000);                           // waits for a second
  140.  
  141.   // compute the serial input
  142.   if (Serial.available()) {
  143.     int val = Serial.read();
  144.     if (val == 'S') {
  145.       Serial.println("Serial: Entering Sleep mode");
  146.       delay(100);     // this delay is needed, the sleep
  147.                       //function will provoke a Serial error otherwise!!
  148.       count = 0;
  149.       sleepNow();     // sleep function called here
  150.     }
  151.     if (val == 'A') {
  152.       Serial.println("Hola Caracola"); // classic dummy message
  153.     }
  154.   }
  155.  
  156.   // check if it should go to sleep because of time
  157.   if (count >= 10) {
  158.       Serial.println("Timer: Entering Sleep mode");
  159.       delay(100);     // this delay is needed, the sleep
  160.                       //function will provoke a Serial error otherwise!!
  161.       count = 0;
  162.       sleepNow();     // sleep function called here
  163.   }
  164. }
  165.  
  166.  

OLD: example code for 0007

This code assumes Arduino-0007. It uses calls to the included interrupts.c file in the distribution. If you find a way to make it run in older versions of the IDE, please attach the needed alterations underneath this page.

  1. #include <avr/interrupt.h>
  2. #include <avr/sleep.h>
  3.  
  4. /* Sleep Demo
  5.  * ------------
  6.  * Example code to demonstrate the sleep functions in a Arduino.
  7.  *
  8.  * use a pull up resistor on pin 2 and 12 to 5V.
  9.  * attach a led with resistor to gnd on pin 10.
  10.  * ground pin 12 momentary to put the Arduino to sleep
  11.  * and ground pin 2 momentary to wake it up again.
  12.  *
  13.  * When awake, the arduino will run the led_blink code
  14.  * from the example sketchbook, Created 1 June 2005
  15.  * by DojoDave <http://www.0j0.org>
  16.  * http://arduino.berlios.de
  17.  * who based it on an orginal by H. Barragan for the Wiring i/o board
  18.  *
  19.  * Hacked together by MacSimski 30-12-2006.
  20.  */
  21.  
  22. int ledPin = 13;            // LED connected to digital pin 13
  23. int sleepPin = 12;          // active LOW, ground this pin momentary to sleep
  24. int interruptPin = 10;      // LED to show the action of a interrupt
  25. int wakePin = 2;            // active LOW, ground this pin momentary to wake up
  26. int sleepStatus = 0;        // variable to store a request for sleep
  27.  
  28. void setup()
  29. {
  30.   pinMode(ledPin, OUTPUT);         // sets the digital pin as output
  31.   pinMode(interruptPin, OUTPUT);   //
  32.   pinMode(sleepPin, INPUT);        // sets the digital pin as input
  33.   pinMode(wakePin, INPUT);
  34.  
  35.   /* Now is time to enable a interrupt. In the function call
  36.    * attachInterrupt(A, B, C)
  37.    * A   can be either 0 or 1 for interrupts on pin 2 or 3.  
  38.    *
  39.    * B   Name of a function you want to execute while in interrupt A.
  40.    *
  41.    * C   Trigger mode of the interrupt pin. can be:
  42.    *             LOW        a low level trigger
  43.    *             CHANGE     a change in level trigger
  44.    *             RISING     a rising edge of a level trigger
  45.    *             FALLING    a falling edge of a level trigger
  46.    *
  47.    * In all but the IDLE sleep modes only LOW can be used.
  48.    */
  49.  
  50.   attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
  51.                                       // wakeUpNow when pin 2 gets LOW
  52. }
  53.  
  54.  
  55.  
  56. void sleepNow()         // here we put the arduino to sleep
  57. {
  58.     /* Now is the time to set the sleep mode. In the Atmega8 datasheet
  59.      * http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
  60.      * there is a list of sleep modes which explains which clocks and
  61.      * wake up sources are available in which sleep modus.
  62.      *
  63.      * In the avr/sleep.h file, the call names of these sleep modus are to be found:
  64.      *
  65.      * The 5 different modes are:
  66.      *     SLEEP_MODE_IDLE         -the least power savings
  67.      *     SLEEP_MODE_ADC
  68.      *     SLEEP_MODE_PWR_SAVE
  69.      *     SLEEP_MODE_STANDBY
  70.      *     SLEEP_MODE_PWR_DOWN     -the most power savings
  71.      *
  72.      * For now, we want as much power savings as possible,
  73.      * so we choose the according sleep modus: SLEEP_MODE_PWR_DOWN
  74.      *
  75.      */  
  76.     set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
  77.  
  78.     sleep_enable();              // enables the sleep bit in the mcucr register
  79.                                  // so sleep is possible. just a safety pin
  80.  
  81.     /* Now is time to enable a interrupt. we do it here so an
  82.      * accidentally pushed interrupt button doesn't interrupt
  83.      * our running program. if you want to be able to run
  84.      * interrupt code besides the sleep function, place it in
  85.      * setup() for example.
  86.      *
  87.      * In the function call attachInterrupt(A, B, C)
  88.      * A   can be either 0 or 1 for interrupts on pin 2 or 3.  
  89.      *
  90.      * B   Name of a function you want to execute at interrupt for A.
  91.      *
  92.      * C   Trigger mode of the interrupt pin. can be:
  93.      *             LOW        a low level triggers
  94.      *             CHANGE     a change in level triggers
  95.      *             RISING     a rising edge of a level triggers
  96.      *             FALLING    a falling edge of a level triggers
  97.      *
  98.      * In all but the IDLE sleep modes only LOW can be used.
  99.      */
  100.  
  101.     attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
  102.                                        // wakeUpNow when pin 2 gets LOW
  103.  
  104.     sleep_mode();                // here the device is actually put to sleep!!
  105.                                  //
  106.  
  107.     sleep_disable();             // first thing after waking from sleep:
  108.                                  // disable sleep...
  109.     detachInterrupt(0);          // disables interrupt 0 on pin 2 so the
  110.                                  // wakeUpNow code will not be executed
  111.                                  // during normal running time.
  112.     delay(1000);                 // wat 2 sec. so humans can notice the
  113.                                  // interrupt.
  114.                                  // LED to show the interrupt is handled
  115.     digitalWrite (interruptPin, LOW);      // turn off the interrupt LED
  116.  
  117. }
  118.  
  119.  
  120.  
  121. void wakeUpNow()        // here the interrupt is handled after wakeup
  122. {
  123.   //execute code here after wake-up before returning to the loop() function
  124.   // timers and code using timers (serial.print and more...) will not work here.
  125.   digitalWrite(interruptPin, HIGH);
  126. }
  127.  
  128.  
  129.  
  130.  
  131. void loop()
  132. {
  133.   digitalWrite(ledPin, HIGH);            // sets the LED on
  134.   delay(1000);                           // waits for a second
  135.   digitalWrite(ledPin, LOW);             // sets the LED off
  136.   delay(1000);                           // waits for a second
  137.   sleepStatus = digitalRead(sleepPin);   // read sleep pin here. only active
  138.                                          //when blink led is off.
  139.   if (sleepStatus == LOW) {            // start to put the device in sleep
  140.       sleepNow();                      // sleep function called here
  141.       }
  142. }

Share