「M5StickC」などのデバイスは、
- LCDディスプレイやバッテリーなどが搭載されている。
- GROVEポートを使えば、ハンダ付けなどをしなくても外付けセンサを接続できる。
といった理由で、IoTデバイスをお気軽に開発するのにうってつけです。
ただ、M5StickCを普通に動かすと、常時100mA近くの電流を消費する上、たとえディープスリープさせても数mAの電流を消費してしまうなど、低電力化という視点で見ると、あまり適切なデバイスとは言えません。
また、例えば一定期間毎に温度を測定するというような用途ならばディープスリープも活用できますが、例えば振動を常時監視し、揺れを検知したら通知するというような用途の場合は、デバイスをディープスリープさせることはできず、常に100mA近くの電流を消費してしまうことになります。
そんな訳で、ここでは、M5StickCをできるだけ低電力で動作させてみたいと思います。
M5StickCの低消費電力化
例として、以下のようなデバイスをつくることにします。
- M5StickC内蔵の加速度センサで、加速度データを常時監視する(加速度を常時監視するため、ディープスリープは使えません)。
- 20分に1回の間隔で、その期間の加速度データの最大値・最小値を「ambient」に送信する。
M5StickCの低電力化ノウハウについては こちら の記事を参考にさせていただきました。
実施する方策は、以下のとおりです。
- CPUの動作周波数を、デフォルトの「240MHz」から最低の「10MHz」に落とす。
- LCDディスプレイの輝度を、デフォルトの「12」から目視できる最低レベルの「8」に落とす。
- 5V出力用DCDCをOFFにする。
なお、Wi-Fi通信時にはCPUの動作周波数を「80MHz」以上にする必要があります。
今回は、データを「ambient」に送信する時(20分に1回)だけ動作周波数を「240MHz」にしてWi-Fi接続し、送信が完了したら速やかにWi-Fiを切断して「10MHz」に戻すことにしました。
作成したスケッチはこちらです。
#include <M5StickC.h>
#include "Ambient.h"
WiFiClient client;
Ambient ambient;
const char* ssid = "XXXXXXXX";
const char* password = "XXXXXXXX";
unsigned int channelId = XXXXX;
const char* writeKey = "XXXXXXXX";
float accX, accY, accZ;
float maxX = -99.9;
float minX = 99.9;
unsigned long measInterval = 10;
unsigned long sendInterval = 1200000;
unsigned long prevMeasTime = 0;
unsigned long prevSendTime = 0;
void setup() {
M5.begin();
M5.Axp.begin(false, false, false, false, true);
setCpuFrequencyMhz(10);
M5.Axp.ScreenBreath(8);
M5.Imu.Init();
M5.Lcd.setRotation(3);
}
void loop() {
M5.update();
unsigned long currentTime = millis();
if(currentTime-prevMeasTime >= measInterval) {
prevMeasTime = currentTime;
// 振動データ採取
M5.IMU.getAccelData(&accX, &accY, &accZ);
if(accX > maxX) maxX = accX;
if(accX < minX) minX = accX;
if(currentTime-prevSendTime >= sendInterval) {
prevSendTime = currentTime;
// 動作周波数を上げる
setCpuFrequencyMhz(240);
// Wi-Fi接続
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0, 0, 1);
M5.Lcd.printf("WIFI START : %s ", ssid);
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.printf(" WIFI READY\n");
// ambientにデータ送信
ambient.begin(channelId, writeKey, &client);
ambient.set(1, maxX);
ambient.set(2, minX);
ambient.send();
M5.Lcd.printf("[maxX] %.2f, [minX] %.2f\n", maxX, minX);
maxX = -99.9;
minX = 99.9;
// Wi-Fi切断
WiFi.disconnect(true);
M5.Lcd.printf("WIFI DISCONNECTED\n");
// 動作周波数を下げる
setCpuFrequencyMhz(10);
}
}
}
これにより、M5StickCの消費電流値は30mA程度、Wi-Fi通信時(20分のうち数秒間)のみ100mA超という低電力化を実現できました。
このスケッチをM5StickCに書き込み、内蔵バッテリーで動かしたところ、2時間40分にわたり連続稼働させることができました。
モバイルバッテリーを使ってM5StickCを長期稼働させる
ただ、M5StickCの内蔵バッテリーは容量が80mAhのため、ここまでの低電力化を行っても、前述のようにせいぜい2時間程度しか駆動できません。
電源のない場所で、より長期間動かしたい場合は、外付けバッテリーなどを使うことになります。
外付けバッテリーとして真っ先に思い浮かぶのは、スマホなどに充電するための「モバイルバッテリー」です。
さまざまな製品が販売されており、一般的にも広く使われているため、価格面でもこなれています。
ただ、現在販売されている大部分のモバイルバッテリーには「AUTO POWER OFF」という機能が搭載されています。
これは、モバイルバッテリーが給電していない時に、自動的にモバイルバッテリーの電源をオフにする機能で、一度オフになってしまうと、スイッチを押したりしない限り再度オンにすることはできません。
「AUTO POWER OFF」するかどうかは、モバイルバッテリーから給電している電流値で判定され、機種にもよると思いますが、概ね数十mAがしきい値になっているようです。
とある期間中、モバイルバッテリーから給電する電流値が数十mAを超えなかったら、モバイルバッテリーがオフになるという仕様です。
つまり、前述のスケッチを書き込んだM5StickCにモバイルバッテリーをつないでも、電流消費が30mA程度しかないため、バッテリーからは給電していないと見なされ、モバイルバッテリーはすぐにオフになってしまいます。
これではモバイルバッテリーからM5StickCに給電し続けることはできません。
モバイルバッテリーの「AUTO POWER OFF」をキャンセルするために、例えば こちら のような製品が販売されています。
モバイルバッテリーに定期的に負荷電流を流すことで、モバイルバッテリーの電源がオフにならないように制御するものです。
今回は、これと同様の機能を、M5StickCのスケッチで実現しようと思います。
使用するモバイルバッテリーは、Ankerの「PowerCore 10000」です。
このバッテリーは、120秒以内に一瞬90mA程度の電流を流すようにすれば、オフになりませんでした。
よって今回は、余裕をもって60秒毎に0.6秒間だけ、90mA程度の消費電流となるようなスケッチをつくります。
消費電流を増やすための方策は、以下のとおりです。
- CPUの動作周波数を最大の「240MHz」に上げる。
- LCDディスプレイの輝度を最大の「12」に上げる。
- CPUに負荷をかけるために「ハノイの塔」という処理を実行する(参考にさせていただいた記事は こちら)。
60秒毎に0.6秒間だけこれらを実施し、0.6秒が経過したら速やかに元の状態に戻します。
作成したスケッチはこちらです。
#include <M5StickC.h>
#include "Ambient.h"
WiFiClient client;
Ambient ambient;
const char* ssid = "XXXXXXXX";
const char* password = "XXXXXXXX";
unsigned int channelId = XXXXX;
const char* writeKey = "XXXXXXXX";
float accX, accY, accZ;
float maxX = -99.9;
float minX = 99.9;
boolean loadFlag = false;
unsigned long measInterval = 10;
unsigned long sendInterval = 1200000;
unsigned long loadInterval = 60000;
unsigned long loadPeriod = 600;
unsigned long prevMeasTime = 0;
unsigned long prevSendTime = 0;
unsigned long prevLoadTime = 0;
void hanoi(int n, char a, char b, char c) { // 消費電流値UPのための処理
if(n > 0) {
hanoi(n-1, a, c, b);
hanoi(n-1, c, b, a);
}
}
void task0(void* param) {
while(1) {
if(loadFlag) hanoi(20, 'a', 'b', 'c'); // loadFlag=trueの時、消費電流値UPのための処理を実行
vTaskDelay(1);
}
}
void setup() {
M5.begin();
M5.Axp.begin(false, false, false, false, true);
setCpuFrequencyMhz(10);
M5.Axp.ScreenBreath(8);
xTaskCreatePinnedToCore(task0, "Task0", 4096, NULL, 1, NULL, 0);
M5.Imu.Init();
M5.Lcd.setRotation(3);
}
void loop() {
M5.update();
unsigned long currentTime = millis();
if(currentTime-prevMeasTime >= measInterval) {
prevMeasTime = currentTime;
// 振動データ採取
M5.IMU.getAccelData(&accX, &accY, &accZ);
if(accX > maxX) maxX = accX;
if(accX < minX) minX = accX;
if(currentTime-prevLoadTime >= loadInterval) { // 消費電流値UPのための処理を開始
prevLoadTime = currentTime;
loadFlag = true;
setCpuFrequencyMhz(240);
M5.Axp.ScreenBreath(12);
M5.Lcd.fillRect(108, 69, 50, 10, RED);
}
if(loadFlag && (currentTime-prevLoadTime >= loadPeriod)) { // 消費電流値UPのための処理を終了
loadFlag = false;
setCpuFrequencyMhz(10);
M5.Axp.ScreenBreath(8);
M5.Lcd.fillRect(0, 69, 160, 10, BLACK);
}
if(currentTime-prevSendTime >= sendInterval) {
prevSendTime = currentTime;
// 動作周波数を上げる
setCpuFrequencyMhz(240);
// Wi-Fi接続
M5.Lcd.fillRect(0, 0, 160, 68, BLACK);
M5.Lcd.setCursor(0, 0, 1);
M5.Lcd.printf("WIFI START : %s ", ssid);
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.printf(" WIFI READY\n");
// ambientにデータ送信
ambient.begin(channelId, writeKey, &client);
ambient.set(1, maxX);
ambient.set(2, minX);
ambient.send();
M5.Lcd.printf("[maxX] %.2f, [minX] %.2f\n", maxX, minX);
maxX = -99.9;
minX = 99.9;
// Wi-Fi切断
WiFi.disconnect(true);
M5.Lcd.printf("WIFI DISCONNECTED\n");
// 動作周波数を下げる
setCpuFrequencyMhz(10);
}
}
}
このスケッチをM5StickCに書き込み、容量10000mAhのモバイルバッテリー(前述のAnker PowerCore 10000)で動かしたところ、約5日にわたり連続稼働させることができました。
5日という稼働期間が微妙ではありますが、例えば「屋外で電源のない場所」ではあるが、「定期的にバッテリー交換に行くことは可能」というようなケースであれば、このような構成でIoTデバイスをつくるのも「あり」ではないかな?と思います。
なお、私がM5Stack、M5StickCの使い方を習得するのにあたっては、以下の書籍を参考にさせていただきました。
ごく基本的なところから、かなり複雑なスケッチや、ネットワーク接続など、比較的高度なものまで、つまづかずに読み進めていけるような構成になっており、大変わかりやすい本です。