M5StampC3に搭載されているESP32-C3のスペックを見ていると、ESP8266の後継なのかなって感じます。
家庭内のセンシングに利用しているESP8266の置き換えを目指して、まずはスリープ周りの確認をしたいと思います。
Wakeup sources
スリープ復帰は以下のトリガで可能です。
- Timer
- UART (LightSleepのみ)
- GPIO
LightSleep
ライトスリープモードはクロックを止めて省電力モードに入ります。
スリープ解除後は開始した行の次から再開されます。また、メモリも保持されています。
GPIO
GPIOをライトスリープの復帰トリガにするには、下記の手順で準備します。
gpio_wakeup_enable(...)
でポートと論理を指定esp_sleep_enable_gpio_wakeup
でGPIOのスリープを有効化
その後スリープしたいタイミングでesp_light_sleep_start()
をコールすればライトスリープモードに入ります。
M5StampC3はG3にタクトスイッチがつながっていますので、G3がLOWに落ちたら復帰するサンプルを記述してみました。
ボタンを押すとesp_light_sleep_start()
の後から再開し、counter
の値もカウントアップされているので保持されていますね。
int counter = 0; void setup() { Serial.begin(115200); Serial.println("begin"); ::pinMode(3, INPUT_PULLUP); ::gpio_wakeup_enable(GPIO_NUM_3, GPIO_INTR_LOW_LEVEL); ::esp_sleep_enable_gpio_wakeup(); } void loop() { ::esp_light_sleep_start(); Serial.printf("Cause:%d\r\n", ::esp_sleep_get_wakeup_cause()); Serial.printf("Counter:%d\r\n", counter++); while (::digitalRead(3) == LOW) ::delay(1); }
Timer
Timerはesp_sleep_enable_timer_wakeup()
でタイムアウト時間の設定とスリープ機能の有効化をおこないます。
タイムアウト時間はusecで指定します。どのぐらい正確なのかは不明ですのでいつか測定してみようと思います。
int counter = 0; void setup() { Serial.begin(115200); Serial.println("begin"); const int wakeup_time_sec = 3; ::esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000); } void loop() { ::esp_light_sleep_start(); Serial.printf("Cause:%d\r\n", ::esp_sleep_get_wakeup_cause()); Serial.printf("Counter:%d\r\n", counter++); }
UART
esp_sleep_enable_uart_wakeup()
とuart_set_wakeup_threshold()
で設定できるはずです(未確認)
DeepSleep
ディープスリープモードではほとんどのブロックの電源をオフして省電力モードに入ります。 ライトスリープと異なり、復帰後はsetup()から始まります。RTC fast memory以外のメモリの内容も消えてしまいます。
RTC fast memoryに配置するにはRTC_DATA_ATTR
をつけて変数を宣言します。
GPIO
GPIOのうち、RTC機能をもつポート(G0〜G5)でディープスリープの復帰ができます。
esp_deep_sleep_enable_gpio_wakeup()
でポートと論理が指定できます。
esp_sleep_start()
でうまいことするらしいので、プルアップ・ダウンを気にする必要がないそうです。
レベルをLOWに指定する場合はプルアップすることを強くお勧めする
とのことです。
M5StampC3だとタクトスイッチ(G3)とGroveポートA(G0,G1)が使いやすいですね、
RTC_DATA_ATTR int counter = 0; void setup() { Serial.begin(115200); Serial.printf("Cause:%d\r\n", ::esp_sleep_get_wakeup_cause()); Serial.printf("Counter:%d\r\n", counter++); ::esp_deep_sleep_enable_gpio_wakeup( BIT(3), ESP_GPIO_WAKEUP_GPIO_LOW); ::esp_deep_sleep_start(); } void loop() { // never reach.. }
Timer
Timerはライトスリープと同様にesp_sleep_enable_timer_wakeup(...)
で指定した後esp_deep_sleep_start()
でスリープできます。
もしくは、esp_deep_sleep(...)
でタイムアウトを指定してスリープする方法も用意されています。
RTC_DATA_ATTR int counter = 0; void setup() { Serial.begin(115200); Serial.printf("Cause:%d\r\n", ::esp_sleep_get_wakeup_cause()); Serial.printf("Counter:%d\r\n", counter++); const int wakeup_time_sec = 3; ::esp_deep_sleep(wakeup_time_sec * 1000000); // こっちでもOK // ::esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000); // ::esp_deep_sleep_start(); } void loop() { // never reach.. }
まとめ
ペリフェラルの違いはあれど、ESP32と同じ感覚で使える感じです!やったね!