今回は、猫型ロボットの消費電力を最適化するために、スリープモードをどのように選択し、実装していくかについて見ていく。こちらの記事で、Lipoによる給電にも対応できるようにしたので、USB-C接続で常に電源供給されている状態とは異なり、ESP32の消費電力も考慮する必要が出てきた。
ESP32のスリープモード概要
ESP32は高度な省電力機能を備えており、その中心となるのがスリープモード。主にDeep-sleep modeとLight-sleep modeの2つのスリープモードがある。
実際には、細かく分けると5つのモードがあるが、今回は割愛。
公式詳細記事はこちら
Light-sleep mode
- デジタル周辺機器、ほとんどのRAM、CPUは一時停止状態(clock-gated)になる。しかし、完全オフではなく一時停止状態のため、復帰時には、プログラムはスリープ前の状態から再開されるため、迅速な復帰が可能
- Light-sleepは、比較的短時間のアイドル状態や、迅速なレスポンスが求められる場合に適している。
Deep-sleep mode
- デジタル周辺機器、ほとんどのRAM、CPUは一時停止ではなく電源オフになる。そのおかげで低い電力消費を実現する。
- チップの中で電源が維持されるのは、RTCコントローラ(時刻やアラームの維持)、ULPコプロセッサ(簡単な計算やセンサーデータの監視)、RTC FASTメモリ、RTC SLOWメモリ(データの保存)のみ。
- メモリの内容は保持されず、復帰時にはリセットが行われ、プログラムは最初から再開される。
- Deep-sleepは長期間のアイドル状態や、非常に限られた機能しか必要としないアプリケーションに適している
ちなみに各modeでの消費電力はこちら
- アクティブモード(通常時):95-240mA
- Light-sleep mode:〜0.8mA(800μA)
- Deep-sleep mode:10〜150μA
特筆すべきは、Light-sleep modeとDeep-sleep modeで消費電力がアクティブモートの時と比較すると、ほとんど誤差みたいな差であること。
また、Wi-Fi/Bluetooth機能は、Light/DeepいずれのSleep modeでも、デフォルトでOffになり、Wi-fi/Bluetooth機能でワイヤレスにESP32と接続されている機器は通信を絶たれる。ちなみに、例外的にONに保っておくこともコードを書けばできる。
ねこ型ロボット「ミーア」の特性と要件
このプロジェクトでは、ESP32ベースの猫型ロボットを開発している。このロボットは、ST7735S LCDディスプレイを目として使用し、MP3ファイルの再生機能と、TTP223タッチセンサーによるインタラクション機能(目が動き、音声を再生する)を持っている。また、Wi-Fiを通じてMP3ファイルをアプリからダウンロードする機能もある。
なぜLight-sleep modeが適切か
今回は、下記理由から、Deep-sleep modeよりLight-sleep modeの方が適していると判断。
迅速なレスポンス
- ユーザーがTTP223タッチセンサーを操作した際、すぐにロボットが反応し、目を動かし、音声を再生する必要がある。Light-sleep modeでは、Deeep-sleepmodeより復帰が迅速で、ユーザーのインタラクションに対するレスポンスが速い。
外部割り込みによる復帰
- TTP223タッチセンサーからの割り込みにより、Light-sleepから迅速に復帰することができる。
- Deep-sleep modeでは、ESP32はGPIO割り込みを利用して復帰することができるが、この機能は限られたピンにのみ対応。また、復帰後、ESP32は完全にリセットされ、プログラムの実行は最初から開始される。今回だと、目のLCDディスプレイが電源オフの状態から電源ONになるのがUX的に再起動的な印象を与えて良くないかなと
Light-sleep modeの実装
Light-sleep modeへの移行条件と復帰要件
Light-sleep modeへの移行条件:
- ユーザーの最後のアクション(頭に取り付けたTTP223タッチセンサーを触れる / アプリからの操作するなど)から一定時刻過ぎた場合、ESP32はライトスリープモードに移行する。
- この移行前に、ロボットが「おやすみ」といったフレーズを発して、お休みの目の画像を表示することで、ユーザーにスリープモードへの移行を知らせる。
Light-sleep modeからの復帰:
- ユーザーが猫の頭を撫でる(TTP223タッチセンサーを触れる)と、ロボットはライトスリープモードから復帰。
- 復帰時には、「もう起こしたの?」などのフレーズを発することで、自然なインタラクションの演出を試みる。
実際のコード
こちらのgithubに、light-sleep modeの実装例が記載してある。
wake upの方法には、Timer, Touchpad, GPIOピン, UARTなどがあるが、今回はGPIO wake upで行う。現状、ttp223外部センサーをGPIO 14に接続しているため。
実際のコードがこちら(main.cpp)。espressifのスリープモード周りのライブラリが充実しているので、シンプル。
#include "esp_sleep.h”
#define HEAD_BUTTON_PIN 14
void setup(){
// GPIOウェイクアップ機能を有効にする
gpio_wakeup_enable((gpio_num_t)HEAD_BUTTON_PIN, GPIO_INTR_HIGH_LEVEL);
}
unsigned long lastActionTime = 0; // 前回のアクション実行時刻を記録
void loop(){
// 現在時刻
const unsigned long currentTime = millis();
// スリープモードへの移行判定
if ((currentTime - lastActionTime) > 15 * 1000)
{
Serial.println("スリープモードに入ります");
delay(1000);
// TODO:
esp_sleep_enable_gpio_wakeup(); // GPIOウェイクアップを有効にする
esp_light_sleep_start(); // ライトスリープモードに入る
Serial.println("Active modeになります。");
// TODO:
eyeMovement();
lastActionTime = millis();
}
// 音声が再生中ではない場合
if (!mp3->isRunning())
{
eyeMovement();
}
}
実行ファイルで、#include “esp_sleep.h” が必要。
esp_sleep_enable_gpio_wakeup() 関数
まず、esp_sleep_enable_gpio_wakeup()
関数を使用して、GPIOによるウェイクアップを有効にしておく。
今回の外部タッチセンサーも、esp32と接続している周辺機器に該当し、ライトスリープモードに入ると、CPUから周辺機器への通信接続が一時停止になるので、デフォルトだと、その後外部タッチセンサーをタッチしても命令を受け付けない。なので、あらかじめ、このGPIOピンは、ライトスリープモードに入ったとしても、例外的に通信接続を保ちますよと宣言しておくためのもの。
esp_light_sleep_start() 関数
至ってシンプルで、この関数を呼び出せば、ライトスリープモードに入る。
今回は、現在時刻をmillis()
で取得し、テスト的に最後のアクションから15秒以上経過した場合にスリープモードに入るようにした。
gpio_wakeup_enable() 関数
最後に、gpio_wakeup_enable()
関数で、ttp223センサーの状態変化を検出して、ライトスリープモードからデバイスをウェイクアップする。この関数は下記の2つの引数を持つ。
- gpio_num_t gpio_num: ウェイクアップに使用するGPIOピン番号。gpio_num_t型なので、int型でGPIOピンを宣言している場合にはキャストする必要がある。もしくは、#define HEAD_BUTTON_PIN GPIO_NUM_14 などと、gpio_num_t型で宣言するか。
- gpio_int_type_t intr_type: ウェイクアップのトリガーとなる条件を指定。指定できる条件は
GPIO_INTR_LOW_LEVEL
とGPIO_INTR_HIGH_LEVEL
の2つのみ。GPIO_INTR_LOW_LEVEL
を指定すると、ピンが低電圧レベルになった時にウェイクアップし、GPIO_INTR_HIGH_LEVEL
を指定すると、ピンが高電圧レベルになった時にウェイクアップする。
TTP223センサーはデフォルトでactive HIGHなので(下記記事参照)、今回は、gpio_wakeup_enable(HEAD_BUTTON_PIN, GPIO_INTR_HIGH_LEVEL); となる。
ちなみにeyeMovement()関数は、st7735s TFT LCDディスプレイで目を動かし続けるための関数。
完成!
静止画なので分かりづらいが、無事最後のタッチ操作から15秒が経過すると、その時の最後の目の状態で一時停止状態になってスリープモードに入り
スリープモード状態で、タッチセンサーを押すと、Active modeに戻る。
残タスク:よりインタラクティブにするために
Light-sleep modeに入る前に、スリープ用のアニメーション画像と音声「おやすみ〜 / 眠くなってきたにゃぁ など」を表示する。
また、スリープ中から、ユーザーに頭をタッチされてawakeになる時に、eyeMovementではなく、寝起きのアニメーション画像と音声「起こしたん? / 寝とったのに、もう など」を表示することで、よりインタラクティブな要素を演出できるようにする。
コメント