M5StampC3(ESP32C3)をSleepさせる

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と同じ感覚で使える感じです!やったね!

ドキュメント

docs.espressif.com