How to let your Arduino go to sleep and wake up on an external event.
Although it may seem like a good idea to put your Arduino to sleep, it is not ideal. The Arduino serial and USB boards use a 7805 type of power regulator, which needs a mere 10mA in idle mode. But if you plan to use the Atmega8 chip on its own board after programming on the Arduino, you can choose any regulator you like, even none if you use Li-ion accus, for example.
Figure 1: a 220 Ohm resistor connects RX to pin 2
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.
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.
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.
#include <avr/sleep.h>
/* Sleep Demo Serial
* -----------------
* Example code to demonstrate the sleep functions in an Arduino.
*
* use a resistor between RX and pin2. By default RX is pulled up to 5V
* therefore, we can use a sequence of Serial data forcing RX to 0, what
* will make pin2 go LOW activating INT0 external interrupt, bringing
* the MCU back to life
*
* there is also a time counter that will put the MCU to sleep after 10 secs
*
* NOTE: when coming back from POWER-DOWN mode, it takes a bit
* until the system is functional at 100%!! (typically <1sec)
*
* Copyright (C) 2006 MacSimski 2006-12-30
* Copyright (C) 2007 D. Cuartielles 2007-07-08 - Mexico DF
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
int wakePin = 2; // pin used for waking up
int sleepStatus = 0; // variable to store a request for sleep
int count = 0; // counter
void wakeUpNow() // here the interrupt is handled after wakeup
{
// execute code here after wake-up before returning to the loop() function
// timers and code using timers (serial.print and more...) will not work here.
// we don't really need to execute any special functions here, since we
// just want the thing to wake up
}
void setup()
{
pinMode(wakePin, INPUT);
Serial.begin(9600);
/* Now it is time to enable an interrupt. In the function call
* attachInterrupt(A, B, C)
* A can be either 0 or 1 for interrupts on pin 2 or 3.
*
* B Name of a function you want to execute while in interrupt A.
*
* 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.
*/
attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
// wakeUpNow when pin 2 gets LOW
}
void sleepNow() // here we put the arduino to sleep
{
/* Now is the time to set the sleep mode. In the Atmega8 datasheet
* http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
* there is a list of sleep modes which explains which clocks and
* wake up sources are available in which sleep mode.
*
* In the avr/sleep.h file, the call names of these sleep modes are to be found:
*
* The 5 different modes are:
* SLEEP_MODE_IDLE -the least power savings
* SLEEP_MODE_ADC
* SLEEP_MODE_PWR_SAVE
* SLEEP_MODE_STANDBY
* SLEEP_MODE_PWR_DOWN -the most power savings
*
* For now, we want as much power savings as possible, so we
* choose the according
* sleep mode: SLEEP_MODE_PWR_DOWN
*
*/
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_enable(); // enables the sleep bit in the mcucr register
// so sleep is possible. just a safety pin
/* Now it is time to enable an interrupt. We do it here so an
* accidentally pushed interrupt button doesn't interrupt
* our running program. if you want to be able to run
* interrupt code besides the sleep function, place it in
* setup() for example.
*
* In the function call attachInterrupt(A, B, C)
* A can be either 0 or 1 for interrupts on pin 2 or 3.
*
* B Name of a function you want to execute at interrupt for A.
*
* C Trigger mode of the interrupt pin. can be:
* LOW a low level triggers
* CHANGE a change in level triggers
* RISING a rising edge of a level triggers
* FALLING a falling edge of a level triggers
*
* In all but the IDLE sleep modes only LOW can be used.
*/
attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
// wakeUpNow when pin 2 gets LOW
sleep_mode(); // here the device is actually put to sleep!!
// THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
sleep_disable(); // first thing after waking from sleep:
// disable sleep...
detachInterrupt(0); // disables interrupt 0 on pin 2 so the
// wakeUpNow code will not be executed
// during normal running time.
}
void loop()
{
// display information about the counter
Serial.print("Awake for ");
Serial.print(count);
Serial.println("sec");
count++;
delay(1000); // waits for a second
// compute the serial input
if (Serial.available()) {
int val = Serial.read();
if (val == 'S') {
Serial.println("Serial: Entering Sleep mode");
delay(100); // this delay is needed, the sleep
//function will provoke a Serial error otherwise!!
count = 0;
sleepNow(); // sleep function called here
}
if (val == 'A') {
Serial.println("Hola Caracola"); // classic dummy message
}
}
// check if it should go to sleep because of time
if (count >= 10) {
Serial.println("Timer: Entering Sleep mode");
delay(100); // this delay is needed, the sleep
//function will provoke a Serial error otherwise!!
count = 0;
sleepNow(); // sleep function called here
}
}
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.
#include <interrupt.h>
#include <avr/sleep.h>
/* Sleep Demo
* ------------
* Example code to demonstrate the sleep functions in a Arduino.
*
* use a pull up resistor on pin 2 and 12 to 5V.
* attach a led with resistor to gnd on pin 10.
* ground pin 12 momentary to put the Arduino to sleep
* and ground pin 2 momentary to wake it up again.
*
* When awake, the arduino will run the led_blink code
* from the example sketchbook, Created 1 June 2005
* by DojoDave <http://www.0j0.org>
* http://arduino.berlios.de
* who based it on an orginal by H. Barragan for the Wiring i/o board
*
* Hacked together by MacSimski 30-12-2006.
*/
int ledPin = 13; // LED connected to digital pin 13
int sleepPin = 12; // active LOW, ground this pin momentary to sleep
int interruptPin = 10; // LED to show the action of a interrupt
int wakePin = 2; // active LOW, ground this pin momentary to wake up
int sleepStatus = 0; // variable to store a request for sleep
void setup()
{
pinMode(ledPin, OUTPUT); // sets the digital pin as output
pinMode(interruptPin, OUTPUT); //
pinMode(sleepPin, INPUT); // sets the digital pin as input
pinMode(wakePin, INPUT);
/* Now is time to enable a interrupt. In the function call
* attachInterrupt(A, B, C)
* A can be either 0 or 1 for interrupts on pin 2 or 3.
*
* B Name of a function you want to execute while in interrupt A.
*
* 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.
*/
attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
// wakeUpNow when pin 2 gets LOW
}
void sleepNow() // here we put the arduino to sleep
{
/* Now is the time to set the sleep mode. In the Atmega8 datasheet
* http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
* there is a list of sleep modes which explains which clocks and
* wake up sources are available in which sleep modus.
*
* In the avr/sleep.h file, the call names of these sleep modus are to be found:
*
* The 5 different modes are:
* SLEEP_MODE_IDLE -the least power savings
* SLEEP_MODE_ADC
* SLEEP_MODE_PWR_SAVE
* SLEEP_MODE_STANDBY
* SLEEP_MODE_PWR_DOWN -the most power savings
*
* For now, we want as much power savings as possible,
* so we choose the according sleep modus: SLEEP_MODE_PWR_DOWN
*
*/
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_enable(); // enables the sleep bit in the mcucr register
// so sleep is possible. just a safety pin
/* Now is time to enable a interrupt. we do it here so an
* accidentally pushed interrupt button doesn't interrupt
* our running program. if you want to be able to run
* interrupt code besides the sleep function, place it in
* setup() for example.
*
* In the function call attachInterrupt(A, B, C)
* A can be either 0 or 1 for interrupts on pin 2 or 3.
*
* B Name of a function you want to execute at interrupt for A.
*
* C Trigger mode of the interrupt pin. can be:
* LOW a low level triggers
* CHANGE a change in level triggers
* RISING a rising edge of a level triggers
* FALLING a falling edge of a level triggers
*
* In all but the IDLE sleep modes only LOW can be used.
*/
attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
// wakeUpNow when pin 2 gets LOW
sleep_mode(); // here the device is actually put to sleep!!
//
sleep_disable(); // first thing after waking from sleep:
// disable sleep...
detachInterrupt(0); // disables interrupt 0 on pin 2 so the
// wakeUpNow code will not be executed
// during normal running time.
delay(1000); // wat 2 sec. so humans can notice the
// interrupt.
// LED to show the interrupt is handled
digitalWrite (interruptPin, LOW); // turn off the interrupt LED
}
void wakeUpNow() // here the interrupt is handled after wakeup
{
//execute code here after wake-up before returning to the loop() function
// timers and code using timers (serial.print and more...) will not work here.
digitalWrite(interruptPin, HIGH);
}
void loop()
{
digitalWrite(ledPin, HIGH); // sets the LED on
delay(1000); // waits for a second
digitalWrite(ledPin, LOW); // sets the LED off
delay(1000); // waits for a second
sleepStatus = digitalRead(sleepPin); // read sleep pin here. only active
//when blink led is off.
if (sleepStatus == LOW) { // start to put the device in sleep
sleepNow(); // sleep function called here
}
}