wheatandcatの開発ブログ

技術系記事を投稿してます

ESP32C3で郵便受け開閉センサーを自作してみる②Deep Sleepの実装

概要

前回の記事では、ESP32C3とリードスイッチを使って郵便受けの開閉を検知し、Discordへ通知するところまで実装した。

www.wheatandcat.me

ただ、この実装には大きな課題があった。

前回のコードでは loop() で常にリードスイッチの状態を監視しているため、ESP32C3が常時起動したままになる。
この状態だと電池消費が大きく、郵便受けのような電源がない場所で運用するには厳しい。

そこで今回は、バッテリー問題への対策としてESP32C3の Deep Sleep 機能を実装したので紹介する。

Deep Sleepとは?

ESP32C3には、バッテリー消費を抑えるための Deep Sleep 機能が搭載されている。

docs.espressif.com

Deep Sleepを使うと、ESP32C3を低消費電力状態にできる。
今回作っている郵便受け開閉センサーでは、郵便受けが閉まっている時間の方が圧倒的に長い。

そのため、以下のように「普段はDeep Sleepで待機し、郵便受けが開いた時だけ起動する」構成にする。

普段 → Deep Sleep
開閉検知
↓
起動
↓
Wi-Fi接続
↓
Discord送信
↓
Deep Sleep

これにより、常時起動する方式と比べてバッテリー消費をかなり抑えられる想定。

実装

Deep Sleepは以下のコードで実装できる。

const int reedPin = D2;

void setup() {
  Serial.begin(115200);
  delay(3000);

  pinMode(reedPin, INPUT_PULLUP);

  // Deep Sleepから起床した理由を取得
  esp_sleep_wakeup_cause_t wakeupReason =
      esp_sleep_get_wakeup_cause();

  if (wakeupReason == ESP_SLEEP_WAKEUP_GPIO) {
    connectWiFi();
    sendDiscordMessage("📬 郵便受けが開きました");
  }

  // Deep Sleepから起床する条件を設定
  esp_deep_sleep_enable_gpio_wakeup(
      1ULL << GPIO_NUM_4,
      ESP_GPIO_WAKEUP_GPIO_HIGH);

  gpio_pullup_en(GPIO_NUM_4);
  gpio_hold_en(GPIO_NUM_4);
  gpio_deep_sleep_hold_en();

  // Deep Sleep開始
  esp_deep_sleep_start();
}

void loop() {
}

loopは空にする

Deep Sleepを使う場合は、前回のように loop() で常時監視する必要がない。

void loop() {
}

起動後に必要な処理を実行し、最後に esp_deep_sleep_start() でDeep Sleepへ移行する。
そのため、loop() は空で問題ない。

起床理由を取得する

以下のコードで、ESP32C3がDeep Sleepから起床した理由を取得できる。

esp_sleep_wakeup_cause_t wakeupReason =
    esp_sleep_get_wakeup_cause();

今回の実装では、GPIOによって起床した場合のみDiscord通知を送信する。

if (wakeupReason == ESP_SLEEP_WAKEUP_GPIO) {
  connectWiFi();
  sendDiscordMessage("📬 郵便受けが開きました");
}

これにより、初回起動時には通知せず、郵便受けが開いたタイミングでDeep Sleepから起床した場合だけ通知できる。

Deep Sleepから起床する条件を設定する

Deep Sleepから起床する条件は esp_deep_sleep_enable_gpio_wakeup で設定する。

esp_deep_sleep_enable_gpio_wakeup(
    1ULL << GPIO_NUM_4,
    ESP_GPIO_WAKEUP_GPIO_HIGH);

第一引数の 1ULL << GPIO_NUM_4 は、起床トリガーに使うピンを指定している。
今回はXIAO ESP32C3のD2ピンを使用しており、D2はGPIO4に対応しているため GPIO_NUM_4 を指定している。

第二引数の ESP_GPIO_WAKEUP_GPIO_HIGH は、GPIO4が HIGH になったらDeep Sleepから起床するという意味。

今回のリードスイッチの配線では、以下のような状態になる。

磁石あり
↓
LOW

磁石なし
↓
HIGH

つまり、郵便受けが開いて磁石がリードスイッチから離れるとGPIO4がHIGHになり、ESP32C3がDeep Sleepから起床する。

郵便受けが開く
↓
磁石が離れる
↓
GPIO4がHIGHになる
↓
Deep Sleepから起床
↓
Discord通知

内部プルアップ設定を保持する

以下のコードは、Deep Sleep中も内部プルアップの設定を保持するために記載している。

gpio_pullup_en(GPIO_NUM_4);
gpio_hold_en(GPIO_NUM_4);
gpio_deep_sleep_hold_en();

今回の検証ではこの設定で動作しているが、実運用では外付けの 10kΩ プルアップ抵抗 を付ける方が安定しそう。
外付け抵抗を使う場合、この内部プルアップ保持のコードは不要になる想定。

動作確認

上記の実装により、以下の流れで動作するようになった。

郵便受けが閉まっている
↓
ESP32C3はDeep Sleep状態
↓
郵便受けが開く
↓
リードスイッチから磁石が離れる
↓
GPIO4がHIGHになる
↓
ESP32C3が起床
↓
Wi-Fi接続
↓
Discord通知
↓
再びDeep Sleepへ移行

これで郵便受けが閉まっている間はDeep Sleepで待機し、開いた時だけ起動してDiscord通知を送信できるようになった。

まとめ

  • ESP32C3のDeep Sleepを使うことで、常時起動を避けられる
  • esp_deep_sleep_enable_gpio_wakeup を使うとGPIOの状態変化で起床できる
  • 今回はD2(GPIO4)がHIGHになった時に起床するように設定
  • 郵便受けが閉まっている間はDeep Sleep、開いた時だけ通知する構成にできた