前回記事でESP32のスイッチ入力によりMicroPythonへ割り込みを掛ける方法を試した。
しかし、この記事で試したirqメソッドで設定する割り込みハンドラではスリープ解除が出来ない。
ピン入力でスリープ解除を行うためにはESP32専用関数を使う必要がある。
MicroPythonで使えるスリープ関数は2種類
lightsleep関数とdeepsleep関数の2種類がある。
- lightsleep RAMが保持されるのでスリープからの復帰時はプログラムの続きから再開
- deepsleep RAMが保持されずスリープ復帰時はリセット状態から再開。deepsleepからの復帰かどうかはreset_cause関数で確認。
詳細は公式ドキュメント参照。
MicroPythonのスリープを解除するためのESP32専用関数
lightsleep関数もしくはdeepsleep関数をタイムアウト引数なしで呼ぶとMicroPythonは無期限スリープに入る。そこからピン入力で復帰させるためにはwake_on_ext0関数もしくはwake_on_ext1関数のいずれかのesp32専用関数を使う。これら2つの関数の書式はほぼ同じ。
wake_on_ext?(pin, level)
下の表にまとめた通り引数pinに指定出来るピン数やlevelに指定するトリガ条件などが少し違う。
関数 | 指定出来るピン数 | level=WAKEUP_ANY_HIGHの時 | level=WAKEUP_ALL_LOWの時 |
---|---|---|---|
wake_on_ext0 | 1本 | 指定ピンがHレベル | 指定ピンがLレベル |
wake_on_ext1 | 複数 | 指定ピンのいずれかがHレベル | 指定ピンの全てがLレベル |
こちらも詳細は公式ドキュメント参照のこと。
スリープ中に入力変化を検出可能なESP32のIOピン
どのIOピンでもスリープ解除が可能なわけではなく、使えないピンを初期化時に指定すると”ValueError: invalid pin”とのメッセージが出てエラーとなる。少なくとも静電容量タッチで使える以下のピンであれば問題なく指定可能なようだ。
0, 2, 4, 12, 13 14, 15, 27, 32, 33
ピン入力でスリープから復帰するMicroPythonプログラム
上記の通り、lightsleepはプログラムの続きから再開され、deepsleepはリセット状態から再開される違いがある。それぞれ具体的にどんな風にプログラムを書けばよいか見ていこう。動作確認にはIO2をプルアップし、スイッチを押すとGNDに落ちる回路を使った。
lightsleepを使った例
lightsleepを使ったMicroPythonプログラムの例は以下のリストの通り。プログラムはampy runで実行後シリアルコンソールを繋ぐか、シリアルコンソール上のREPLからCtrl-Eを押し貼付けモードでプログラムを貼り付けて実行しても良いだろう。
from machine import Pin, lightsleep
from esp32 import wake_on_ext0, WAKEUP_ALL_LOW
def keywait():
print('*** Enterning Sleep... ***')
lightsleep()
print('*** Backed from lightsleep() ***')
if __name__ == '__main__':
key = Pin(2, Pin.IN)
wake_on_ext0(key, level=WAKEUP_ALL_LOW)
keywait()
プログラムを実行すると、以下のような動作をする。
- keywait()関数が呼ばれる(12行目)
- keywait()関数の中でlightsleep()が呼ばれ、スリープに入る(6行目)
- プッシュスイッチを押すとwake_on_ext0が発動してスリープが解除される
スリープが解除されると7行目からプログラムが再開され、制御がREPLに戻る。REPLからkeywait()関数を実行すれば、再度スリープさせプッシュスイッチで起こす、を繰り返す事が出来る。
deepsleepを使った例
deepsleepは一度呼ぶと復帰時にリセット状態に戻ってしまうため、lightsleepのプログラムと同じ書き方をすると無限にdeepsleepが呼ばれる状態になってしまう。そこで以下のプログラムでは、プッシュボタンを一回押すと割り込みハンドラが起動し、もう一回押すとスリープに入るようにした。
from machine import Pin, deepsleep, reset_cause, DEEPSLEEP
from esp32 import wake_on_ext0, WAKEUP_ALL_LOW
def keyhandler(pin):
while not(pin.value()):
pass
print('*** Enterning Sleep... ***')
deepsleep()
if __name__ == '__main__':
if reset_cause() == DEEPSLEEP:
print('*** Backed from deepsleep() ***')
else:
print('*** Power on reset ***')
key = Pin(2, Pin.IN)
key.irq(handler=keyhandler, trigger=Pin.IRQ_FALLING)
wake_on_ext0(key, level=WAKEUP_ALL_LOW)
deepsleep実行によってRAMにロードしたプログラムが消えてしまうため、このプログラムはampy runやREPLの貼り付けモードだとうまく実行出来ない。boot.pyやmain.pyなどのファイル名でESP32のフラッシュメモリに書き込んで自動実行する必要がある。
このプログラムをboot.pyやmain.pyで自動実行させた後はREPLが起動してインタープリタが使える状態になるが、割り込みハンドラが設定されているのでプッシュスイッチを押すことで以下のような動きをする。
- プッシュスイッチを一回押すとPin2の立ち下がりによって割り込みハンドラkeyhandlerが呼ばれる
- 割り込みハンドラの中でプッシュスイッチが離されるのを待つ(5〜6行目)
- deepsleepに入る(8行目)
- 再度プッシュスイッチを押すとwake_on_ext0が発動してスリープが解除されリブートされる
上記lightsleepとdeepsleepのそれぞれのプログラムで’Backed from …’のメッセージを出しているprint文の位置を比較すると、スリープ解除後に復帰するプログラム位置の違いがよく分かると思う。
スリープ時のマイコン消費電流を測定してみる
ESP32の開発キットだとUSBシリアル変換チップにも電流が流れてしまってマイコンの消費電流だけを測定することが出来ない。そこで、以前の記事で取り上げたESP32単体モジュールを線出ししてブレッドボードに配線した物を使った。
測定結果は以下の表の通り。
lightsleep | deepsleep | |
---|---|---|
電流 | 約1.6mA | 124μA |
deepsleepなら消費電力をかなり抑えられる。lightsleepは一部回路が動作しているせいか1.6mAを挟んで±0.1mA程度の幅で電流が変動する。
まとめ
ESP32のMicroPythonをスリープで寝かせた後、ピン入力で起こす方法を試してみた。
特にdeepsleepを使うと消費電力を大幅に抑えられることが確認出来たが、起床後にリブートが掛かってしまうため、実際に使うには少々工夫が必要かもしれない。とはいえバッテリーでESP32を動作させる場合は消費電力を抑える事はとても重要なので、スリープを上手に活用していきたいものだ。
コメント