以下のコードは、あなたが求めていることを達成します。
#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
Rxピンでピン変更割り込みを使用して、シリアルデータが到着したことを確認しました。このテストでは、5秒後にアクティビティがない場合、ボードはスリープ状態になります(「アウェイク」LEDが消灯します)。シリアルデータを受信すると、ピン変更割り込みによってボードがウェイクします。番号を探し、その回数だけ「緑色」のLEDを点滅させます。
測定電流
5 Vで動作し、スリープ状態で約120 nAの電流を測定しました(0.120 µA)。
覚醒メッセージ
ただし、問題は、シリアルハードウェアがRx(開始ビット)の立ち下がりレベルを予期しているという事実により、最初に到着したバイトが失われることです。
(geometrikalの答えのように)最初に「覚醒」メッセージを送信してから、少しの間休止することをお勧めします。一時停止は、ハードウェアが次のバイトをアウェイクメッセージの一部として解釈しないようにすることです。その後、正常に動作するはずです。
これはピン変更割り込みを使用するため、他のハードウェアは必要ありません。
SoftwareSerialを使用した修正版
以下のバージョンは、シリアルで受信した最初のバイトを正常に処理します。これを行うには:
コメントでFarOに触発され、これにより、プロセッサは6クロックサイクル(750 ns)で起動します。9600ボーでは、各ビット時間は1/9600(104.2 µs)であるため、余分な遅延はわずかです。
#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
スリープ時の消費電力は260 nA(0.260 µA)と測定されたため、不要な場合の消費電力は非常に低くなります。
ヒューズをそのように設定すると、プロセッサは8 MHzで動作することに注意してください。したがって、IDEにそのことを伝える必要があります(たとえば、ボードタイプとして「Lilypad」を選択します)。そうすれば、遅延とSoftwareSerialは正しい速度で動作します。