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 onlySLEEP_MODE_IDLE
,SLEEP_MODE_ADC
,SLEEP_MODE_PWR_SAVE
,SLEEP_MODE_STANDBY
orSLEEP_MODE_PWR_DOWN
sleep_mode()
- macro for switching to the sleep mode, while enabling theSE
(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 thesleep_mode()
macro enablesSE
bit (callsleep_enable()
); then puts microcontroller to sleep (callsleep_cpu()
); and after waking up theSE
bit is cleared (callsleep_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);
https://github.com/lowpowerlab/lowpower (vraj lepsia verzia?)
http://www.rocketscream.com/blog/2011/07/04/lightweight-low-power-arduino-library/