趣味の開発ノート

ITの学習やプログラミング・ノーコードアプリ開発のことなど。

【M5Stack】基本のスケッチ例「PowerOFF」を読み解く

M5Stack開発について理解を深めたくて、Arduino IDEで見られるスケッチ例を読解していく。

前回「Hello World」のスケッチ例を題材に読み解いてみた。
そこではArduinoスケッチの基本構造と、M5Stackで画面に文字表示する基本のやり方を見てきた。

nouka-it.hatenablog.com

今回は、スケッチ例「PowerOFF」を読解して、M5Stackの電源周りに関係する「省電力モード」や「電源OFF」について整理していこうと思う。

スケッチ例「PowerOFF」

Arduino IDEのメニューから [ファイル] > [スケッチ例] > [M5Stack] > [Basics] > [PowerOFF] を選択。

f:id:massa_potato:20220228084228p:plain

M5Stackに書き込んでみる。
すると、最初にディスプレイ上に「After 5 seconds, the program entered light sleep」と表示される。
その言葉の通りに、待っていると5秒後に一度画面OFF(スリープ状態)になる。

f:id:massa_potato:20220228084951j:plain

そのまたすぐ5秒後に再びディスプレイ上に「press ButtonA: shutdown ...」と表示される。
今度はボタンA(3つあるうちの一番左のボタン)を押すとシャットダウン(電源OFF)になる。

f:id:massa_potato:20220228084952j:plain

というプログラム。

スケッチを読む

コメントなどは省略している。
また、これまでに学習してきた内容や、電源周りにあまり関係しない画面表示・ボタンなどは今回は省いてポイントを整理している。

▼スケッチ例「PowerOFF」

#include <M5Stack.h>

void setup(){
  M5.begin();
  M5.Power.begin();
  M5.Lcd.setTextSize(2);
  M5.Lcd.print("After 5 seconds, the program entered light sleep\n\n");
  delay(5000);
  M5.Power.lightSleep(SLEEP_SEC(5));
  M5.Lcd.print("press ButtonA: shutdown,  use power button to turn  back on");
}

void loop() {
  M5.update();
  if(M5.BtnA.wasPressed()) {
    M5.Power.powerOFF();
  }
}

Powerクラスの初期化

M5.Power.begin()
続いて、M5.Power.begin()として電源に関するクラスの初期化を行っている様子。
今回のような電源を扱うスケッチでも、この記述は省略しても問題なく動かすことができた。

やはりちょっと必要性がわからない一文だ。

時間を止める:delay

delay()
プログラムを引数に指定した時間だけ止める関数。
これはM5Stack固有ではなく、Arduinoスケッチで使える関数である。

引数に指定するのはミリ秒(1秒 = 1000ミリ秒)なので注意。

単純に、画面に表示させたまま数秒待機する、といった処理をしたい場合なんかはこの関数を使おう。

www.musashinodenpa.com

省電力モード:lightSleepとdeepSleep

M5.Power.lightSleep()
これはM5ライブラリ独自の関数で、「省電力モード」に移行する関数。
引数にはスリープさせたい秒数をSLEEP_SEC(秒)(uint64_t型)で指定している。

M5.Power.lightSleep(SLEEP_SEC(5))

ちなみに引数にdelay(1000)などと同じように直接数値で指定しても、省電力モードに移行せずに次のスケッチが実行されてしまった。
引数にはちゃんとSLEEP_SEC(秒)を使う必要があるらしい。(こちらは引数には秒数を指定する)

また引数を省略した場合は、省電力モードに入るとm5Stack側で復帰する方法が見当たらず、電源を再起動するしかなかった。
公式ドキュメントには「指定した時間、もしくはポートに変化があった場合に起動します。 」とのこと。何か方法があるのかな。

さて、スケッチ例ではlightSleep()しか使っていないが、似た「省電力モード」に移行する機能であるdeepSleep()も比較しながら見ていきたい。

まず、どちらも引数に関しては同じ。

  • 引数には復帰までの秒数を指定できる

違う点は2つ、「復帰後の処理」「省電力能力」とのこと。

  • 復帰後は、lightSleepの場合はスケッチの次の行から実行されるが、deepSleepではCPUが再起動されてスケッチの最初から実行される
  • 省電力能力は、deepSleepの方が高い

表にまとめた。

関数 書式 解説
lightSleep M5.Power.deepSleep(秒) 「省電力モード」に移行する。引数に指定した時間経過後に復帰し、スケッチの次の行から実行される
deepSleep M5.Power.deepSleep(秒) 「省電力モード」に移行する。引数に指定した時間経過後に復帰し、CPUが再起動される(スケッチの最初から実行される)

github.com

github.com

電源を切る:powerOFF

M5.Power.powerOFF()
M5Stack固有の、電源をオフにする関数。
if文を使って「Aボタンが押されたら、電源をオフにする」という処理をここで行なっている。

  if(M5.BtnA.wasPressed()) {
    M5.Power.powerOFF();
  }

こんな記述を見つけた。電源OFFは、単純な話ではなさそう。

こちらのpowerOFF()関数は、deepSleep()そのものということはなく、いろいろと下準備(WiFi止めたりとか)してから止まります。 (中略)しかし、最終的に止まる実体関数は、結局のところdeepSleep()と共通のようです。

(参考) jhalfmoon.com

で、このM5.Power.powerOFF()についてドキュメントを読むと、「強制的に本体の電源をOFFにしているわけではなく、回路に供給される電源をOFFにすることで擬似的に電源OFFを実現している、ということらしい。これがdeepSleepしている、ということなのかな?

github.com

なかなかこの「省電力モード」「電源OFF」については奥が深そうです。

バッテリー持ちを良くするためにはどうしたら良いの?

最後に、電源まわりの制御に関して。
結局、僕らの関心ごととしては「で、どうすればどれだけバッテリーを持たせることができるのか?」ということだと思う。

情報があまりまとまっている感じではなく、先人の皆さんも色々と電源周りで試行錯誤しているのかな、というのが調べると出てきます。

  • M5Stackの内部バッテリーだけを使うなら、

    • deepSleep()を使ったプログラムを書いてバッテリー消費を抑えるのが良さそう
  • 外部からの電源供給ができるなら、

    • プログラム側であまり気にすることはないかも。delayでもligthSleepでもdeepSleepでも
    • ただし電源供給が止まった時、一度シャットダウンしてしまうのでプログラムは最初からになるので注意する
    • 外部電源は繋ぎっぱなしでも問題はなさそう(M5Stackは過充電対策はされているらしい)
  • 外部からの電源供給(モバイルバッテリー)を使うなら、

    • deepSleepだと消費電力が少なすぎるため、モバイルバッテリー側の仕様により電源の供給がストップすることが多い
    • 少ない電力でも供給できるモバイルバッテリーを探すか、消費電力が増えるような負荷がかかるプログラムにするか、別の対策を考える必要がありそう

(参考)
thom.hateblo.jp

ambidata.io

www.facebook.com

japanese.engadget.com

まとめ

ちょっと結論が出ないけど、今日はここまで。

電源周りは引き続き研究課題かな、と思います。