Putting Your Device to Sleep

What was achieved using the interrupts is the elimination of the code from the main program loop. It might seem that the empty loop is saves the energy. However, this is assumption is wrong. The microcontroller is still active and continues to call the loop() function, even when its content is empty. Just try and observe the output if you write a the following script to the command line interpreter on your computer:

while true; do
    # nothing to do in here
    :
done

If you notice the usage of the processor then you will see that it is (in case of multicore system, it is only one core) utilized to 100%. This happens even though that code snippet does nothing.

How do you conserve energy if the contents of loop() is empty and the microcontroller does “nothing”? The answer is put the microcontroller to the sleep mode.

What is Sleep Mode?

Sleep mode is a special microcontroller mode, which microcontroller enters in the time of its inactivity. In this mode, the microcontroller switches to the low power mode to conserve the electricity. How much is saved is actually dependent on components that remain powered even after microcontroller enters the sleep mode. The microcontroller in this mode doesn’t lose its state, because it remains in its memory.

The way how it operates is analogous to your home appliance, e.g. TV, DVD player or set-top box, you turn it off using the remote control. The device doesn’t turn off completely because it waits for a remote-control signal to wake it up (turn on) and put it into the full operating mode. Its power consumption, during the sleep mode, is minimal compared to the amount of electricity it receives during the full operation mode. Many devices represent this mode using a red LED diode.

Sleep mode doesn’t work without utilization of the interrupts. Once the microcontroller is put to sleep mode, it can be only woken by reset (which is also an interrupt). In this case, the interrupt works as an alarm – it wakes up microcontroller, which automatically handles the interrupt using the appropriate IRS function. The microcontroller may then switch back to sleep mode or start to execute the main program.

Putting Arduino to Sleep

The amount of saved energy is depend on type of used Arduino prototyping board. Various Arduino versions have different components that also consume some energy. For example, if you use Arduino Uno, it will have a power consumption of 19 mA in the sleep mode, while in normal operation it will have a power consumption ranging from 30 to 40 mA. However, if Arduino Pro Mini is utilized, its consumption during sleep mode is only 0.57 mA and in normal operation 25 mA. The difference is thus considerable.

Support of sleep modes with a particular microcontroller should be always verified in the documentation. In case of the ATmega328P microcontroller, which is the heart of Arduino Uno, are 6 modes, of which only 5 are available in the avr/sleep.h header file:

  • Idle (SLEEP_MODE_IDLE)
  • ADC Noise Reduction (SLEEP_MODE_ADC)
  • Power-save (SLEEP_MODE_PWR_SAVE)
  • Standby (SLEEP_MODE_STANDBY)
  • Power-down (SLEEP_MODE_PWR_DOWN)

The Idle mode is the least economical mode. It is also the default mode, thus if no other mode is explicitly selected when putting microcontroller to sleep mode, the Idle mode will be used. The microcontroller can wake up from this mode in almost any way.

The most effective power saving mode is the Power-down mode. The most parts of the microcontroller are disabled in this mode, it can be only woken up from the sleep by external interrupts.

Modes differ one from another depending on what components of the microcontroller are switched off, as well as how the microcontroller is awaken. Table XXX provides an overview of how the ATmega328P microcontroller can be woken from sleep mode. For a particular microcontroller it is always good practice to verify its documentation.

Wake-up Sources Idle ADC Noise Reduction Power-save Standby Power-down
INT1, INT0 and Pin Change X X X X X
TWI Address Match X X X X X
Timer2 X X X
SPM/EEPROM Ready X X
A/D Converter X X
Watchdog Timer X X X X X
Other I/O X

Bear in mind that the microcontroller can be always woken up from each level of sleep mode with RESET.

In order to utilize the sleep mode, the header file avr/sleep.h has to be loaded into the program. It contains all the necessary macros and functions for sleep mode. In general, utilization of the following is sufficient:

  • set_sleep_mode() - function to set the sleep mode, while the its parameters can be only SLEEP_MODE_IDLE, SLEEP_MODE_ADC, SLEEP_MODE_PWR_SAVE, SLEEP_MODE_STANDBY or SLEEP_MODE_PWR_DOWN
  • sleep_mode() - macro for switching to the sleep mode, while enabling the SE (Sleep Enable) bit, prior to entering entering the sleep mode, and disabling it after waking up

[!NOTE]

Various sources state the sequence of calls for these macros:

sleep_enable();
sleep_cpu();
sleep_disable();

Calling the sleep_mode() macro allows to replace these three macros at once. In fact the sleep_mode() macro enables SE bit (call sleep_enable()); then puts microcontroller to sleep (call sleep_cpu()); and after waking up the SE bit is cleared (call sleep_disable()).

A simple example for putting the microcontroller to the sleep mode is found in the code fragment below, it represents a modification of the standard Blink example. The built-in LED will illuminate for 500 ms after that the microcontroller to is set to the deep sleep (Power-down). However, since no manner to wake up was set before the sleep mode, the microcontroller will no longer wake up and the LED light won’t switch off. This means that the HIGH level on pin will remain unchanged even after the device is in sleep. The only way to re-awaken it is to press the RESET button.

#include <Arduino.h>
#include <avr/sleep.h>

void setup(){
    pinMode(LED_BUILTIN, OUTPUT);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}

void loop(){
    // blink
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);

    // sleep
    sleep_mode();

    // unreachable code
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
}

[!WARNING]

Be careful! If the microcontroller is put to Power-down sleep mode without first defining interrupt procedure, the microcontroller will not wake up. The only way to wake it up is by provide a signal to the RESET pin. As a result of this situation, if you try to reprogram the microcontroller in sleep mode, you will not be successful. The tool for uploading the code to the microcontroller will attempt, in case of failure, to upload the code again. Rebooting the microcontroller in this moment is a suitable solution. As a result, the microcontroller wakes up and, during the startup, the microcontroller code is recorded.

Waking the Microcontroller with External Interrupts

We will modify the use case of the motion detection device. This time, the device will be put to sleep mode once it starts. As soon as the PIR sensor detects the motion, the device is to wake up and will start to flash the external diode. It is to keep blinking, until the key that resets the device is pressed, then the LED will be turned off and the device will return to the sleep mode.

The state diagram of the device will not be very different from the previous one. Again, the device will be able to get to one of three states. However, transitions between the states are changed.

The wiring diagram will also remain unchanged - it will be the same as on the picture XXX.

The source code for the solution is shown in Listing XXX. The most significant difference from the previous code is the sleep mode.

#include <Arduino.h>
#include <TimerOne.h>
#include <avr/sleep.h>

#define PIN_LED 8
#define PIN_PIR 2
#define PIN_BTN 3

volatile bool isMovement;
volatile byte countdown;

void idle();
void watch();
void alarm();

void idle(){
    isMovement = false;
    digitalWrite(PIN_LED, LOW);
    digitalWrite(LED_BUILTIN, LOW);

    // reatach interrupts
    detachInterrupt(digitalPinToInterrupt(PIN_BTN));
    attachInterrupt(digitalPinToInterrupt(PIN_PIR), watch, RISING);
    Timer1.detachInterrupt();

    // go to sleep
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    interrupts();
    sleep_mode();
}

void tick(){
    countdown--;

    if(countdown == 0){
        alarm();
    }

    // go to sleep
    interrupts();
    sleep_mode();
}

void watch(){
    // update state
    digitalWrite(PIN_LED, HIGH);

    // reatach interrupts
    set_sleep_mode(SLEEP_MODE_ADC);
    attachInterrupt(digitalPinToInterrupt(PIN_PIR), idle, FALLING);
    countdown = 10;
    Timer1.attachInterrupt(tick);
    Timer1.restart();

    // go to sleep
    interrupts();
    sleep_mode();
}

void alarm(){
    // update state
    digitalWrite(PIN_LED, HIGH);
    isMovement = true;

    // reatach interrupts
    Timer1.detachInterrupt();
    detachInterrupt(digitalPinToInterrupt(PIN_PIR));
    attachInterrupt(digitalPinToInterrupt(PIN_BTN), idle, LOW);
}

void setup(){
    // set pin modes
    pinMode(PIN_LED, OUTPUT);
    pinMode(PIN_PIR, INPUT);
    pinMode(PIN_BTN, INPUT_PULLUP);

    // setup timer and sleep mode
    Timer1.initialize(1 * 1000000);

    // enter idle state
    idle();
}

void loop(){
    if (isMovement == true){
        digitalWrite(PIN_LED, HIGH);
        delay(1000);
        digitalWrite(PIN_LED, LOW);
        delay(1000);
    }
}

This code shows the possible handling of the external interrupts. In particular level interrupt, which represents the button and pin change interrupt, which represents the PIR sensor.

Low-Power Library

The microcontroller often includes components that require special settings to reduce the energy consumption. These include:

  • A/D converter
  • Analog comparator
  • Brown-out detector
  • 3 timers
  • Watch-dog timer

These microcontroller components can be selectively switched on and off. This can be done using the microcontroller registers or using macro calls utilizing the avr/power.h library. For example, to disable the A/D converter, it is possible to call the macro power_adc_disable() and turn it back on using the macro power_adc_enable().

However, library called Low-Power may be utilized, it simplifies the sleep mode and allows to turn off the required components prior to sleep mode, in order to achieve higher energy savings. The library has the following features:

  • supports all the sleep modes of ATmega328P microcontroller,
  • allows to put the microcontroller to a sleep mode for 15 ms, 30 ms, 60 ms, 120 ms, 250 ms, 500 ms, 1 s, 2 s, 4 s, 8 s and to infinity,
  • allows to turn off the A/D converter,
  • allows to turn off the Brownout Detector,
  • in selected sleep modes, it allows to turn off all the timers, USART0, TWI, and SPI module.

The library is used through the LowPower class, which has several methods: idle(), adcNoiseReduction(), powerDown(), powerSave(), powerStandby() and powerExtStandby(). Each of these represents one of the microcontroller sleep modes. Using the parameters of these methods, it is possible to set the sleep mode duration, as well as the list of components to be switched on or off.

Line of code illustrated below puts the microcontroller to sleep mode for 8 seconds to the Power Down mode, having ADC and BOD modules switched off.

LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);

Measuring Power in Sleep Mode

results matching ""

    No results matching ""