Ruben Laguna's blog

Oct 15, 2008 - 4 minute read - arduino atmega168 electronics serial sleep usart

Arduino sleep mode - Waking up when receiving data on the USART

I’ve been playing with the Arduino sleep modes and i wanted to be able to wake up from the sleep when receiving data on the serial port. Mainly, because in my project I’m using the XBee in the API mode and the tricks exposed in http://www.arduino.cc/playground/Learning/ArduinoSleepCode and http://www.libelium.com/squidbee/index.php?title=Save_power_in_SquidBee_-_sleep_mode involve putting Arduino in SLEEP_MODE_PWR_DOWN and using an extra pin on the arduino to monitor the RX pin and detecting LOW.

I didn’t like that much that solution so I started to look into other ways of doing it without using an extra pin and without risk of losing data in the serial interface. Because as I understood it using SLEEP_MODE_PWR_DOWN requires to send first a burst of data to the serial interface in order to wake up the arduino. And it takes a while for the Arduino to become fully functional so that means that you will lose/miss data in the serial interface. That was something that didn’t fit my project.

In order to be able to sleep but without missing serial data I used POWER_MODE_IDLE, a power saving mode that leaves the USART on and then using the functions defined in power.h (you have to use arduino-12 to get power.h) I disabled all other modules that I don’t need to cut down the power consumption. When any data is received in the USART the Arduino will be brought back to normal power mode (USART uses interrupts and any interrupt makes the ATmega168 to exit the power saving mode).

See the actual code below (or in gist)

/* Sleep Demo Serial
 * -----------------
 * Example code to demonstrate the sleep functions in a Arduino. Arduino will wake up
 * when new data is received in the serial port USART
 * Based on Sleep Demo Serial from http://www.arduino.cc/playground/Learning/ArduinoSleepCode
 *
 * Copyright © 2006 MacSimski 2006-12-30
 * Copyright © 2007 D. Cuartielles 2007-07-08 - Mexico DF
 *
 * With modifications from Ruben Laguna 2008-10-15
 *
 * 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/>.
 *
 */

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

int sleepStatus = 0; // variable to store a request for sleep
int count = 0; // counter

void setup()
{

Serial.begin(9600);
}

void sleepNow()
{
/* 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
 *
 * the power reduction management <avr/power.h> is described in
 * http://www.nongnu.org/avr-libc/user-manual/group_avr_power.html
 */

set_sleep_mode(SLEEP_MODE_IDLE); // sleep mode is set here

sleep_enable(); // enables the sleep bit in the mcucr register
// so sleep is possible. just a safety pin

power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();

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...

power_all_enable();

}

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 asleep 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
}
}