O código abaixo alcança o que você está perguntando:
#include <avr/sleep.h>
#include <avr/power.h>
const byte AWAKE_LED = 8;
const byte GREEN_LED = 9;
const unsigned long WAIT_TIME = 5000;
ISR (PCINT2_vect)
{
// handle pin change interrupt for D0 to D7 here
} // end of PCINT2_vect
void setup()
{
pinMode (GREEN_LED, OUTPUT);
pinMode (AWAKE_LED, OUTPUT);
digitalWrite (AWAKE_LED, HIGH);
Serial.begin (9600);
} // end of setup
unsigned long lastSleep;
void loop()
{
if (millis () - lastSleep >= WAIT_TIME)
{
lastSleep = millis ();
noInterrupts ();
byte old_ADCSRA = ADCSRA;
// disable ADC
ADCSRA = 0;
// pin change interrupt (example for D0)
PCMSK2 |= bit (PCINT16); // want pin 0
PCIFR |= bit (PCIF2); // clear any outstanding interrupts
PCICR |= bit (PCIE2); // enable pin change interrupts for D0 to D7
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();
UCSR0B &= ~bit (RXEN0); // disable receiver
UCSR0B &= ~bit (TXEN0); // disable transmitter
sleep_enable();
digitalWrite (AWAKE_LED, LOW);
interrupts ();
sleep_cpu ();
digitalWrite (AWAKE_LED, HIGH);
sleep_disable();
power_all_enable();
ADCSRA = old_ADCSRA;
PCICR &= ~bit (PCIE2); // disable pin change interrupts for D0 to D7
UCSR0B |= bit (RXEN0); // enable receiver
UCSR0B |= bit (TXEN0); // enable transmitter
} // end of time to sleep
if (Serial.available () > 0)
{
byte flashes = Serial.read () - '0';
if (flashes > 0 && flashes < 10)
{
// flash LED x times
for (byte i = 0; i < flashes; i++)
{
digitalWrite (GREEN_LED, HIGH);
delay (200);
digitalWrite (GREEN_LED, LOW);
delay (200);
}
}
} // end of if
} // end of loop
Usei uma interrupção de troca de pinos no pino Rx para perceber quando os dados seriais chegam. Neste teste, a placa entra em suspensão se não houver atividade após 5 segundos (o LED "acordado" se apaga). Os dados seriais recebidos fazem com que a interrupção da troca de pinos ative a placa. Ele procura um número e pisca o LED "verde" esse número de vezes.
Corrente medida
Correndo a 5 V, medi cerca de 120 nA de corrente quando estava dormindo (0,120 µA).
Mensagem de despertar
Um problema, no entanto, é que o primeiro byte que chega é perdido devido ao fato de o hardware serial esperar um nível decrescente no Rx (o bit de inicialização) que já chegou no momento em que está totalmente acordado.
Sugiro (como na resposta da geometrikal) que você primeiro envie uma mensagem "acordada" e faça uma pausa por um curto período de tempo. A pausa é garantir que o hardware não interprete o próximo byte como parte da mensagem de ativação. Depois disso, deve funcionar bem.
Como isso usa uma interrupção de troca de pinos, nenhum outro hardware é necessário.
Versão alterada usando SoftwareSerial
A versão abaixo processa com êxito o primeiro byte recebido em série. Faz isso da seguinte maneira:
Usando SoftwareSerial, que usa interrupções de alteração de pinos. A interrupção causada pelo bit inicial do primeiro byte serial também ativa o processador.
Configurando os fusíveis para que possamos usar:
- Oscilador RC interno
- BOD desativado
- Os fusíveis eram: Baixo: 0xD2, Alto: 0xDF, Estendido: 0xFF
Inspirado pelo FarO em um comentário, isso permite que o processador seja ativado em 6 ciclos de clock (750 ns). A 9600 bauds, cada vez que o bit é 1/9600 (104,2 µs), portanto o atraso extra é insignificante.
#include <avr/sleep.h>
#include <avr/power.h>
#include <SoftwareSerial.h>
const byte AWAKE_LED = 8;
const byte GREEN_LED = 9;
const unsigned long WAIT_TIME = 5000;
const byte RX_PIN = 4;
const byte TX_PIN = 5;
SoftwareSerial mySerial(RX_PIN, TX_PIN); // RX, TX
void setup()
{
pinMode (GREEN_LED, OUTPUT);
pinMode (AWAKE_LED, OUTPUT);
digitalWrite (AWAKE_LED, HIGH);
mySerial.begin(9600);
} // end of setup
unsigned long lastSleep;
void loop()
{
if (millis () - lastSleep >= WAIT_TIME)
{
lastSleep = millis ();
noInterrupts ();
byte old_ADCSRA = ADCSRA;
// disable ADC
ADCSRA = 0;
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();
sleep_enable();
digitalWrite (AWAKE_LED, LOW);
interrupts ();
sleep_cpu ();
digitalWrite (AWAKE_LED, HIGH);
sleep_disable();
power_all_enable();
ADCSRA = old_ADCSRA;
} // end of time to sleep
if (mySerial.available () > 0)
{
byte flashes = mySerial.read () - '0';
if (flashes > 0 && flashes < 10)
{
// flash LED x times
for (byte i = 0; i < flashes; i++)
{
digitalWrite (GREEN_LED, HIGH);
delay (200);
digitalWrite (GREEN_LED, LOW);
delay (200);
}
}
} // end of if
} // end of loop
O consumo de energia durante o sono foi medido em 260 nA (0,260 µA), de modo que o consumo é muito baixo quando não é necessário.
Observe que, com os fusíveis configurados assim, o processador funciona a 8 MHz. Portanto, você precisa informar o IDE sobre isso (por exemplo, selecione "Lilypad" como o tipo de placa). Dessa forma, os atrasos e o SoftwareSerial funcionarão na velocidade correta.