「M5Stack CoreInk開発キット」を使い、「ENV IIIユニット」で測定した温度・湿度をAmbientに送信するIoTデバイスをつくりました(記事は こちら)。
ESP32などの電源をOFFにするモード(ここではスタンバイモードと呼ぶことにします)を活用することで、10分毎にデータ測定する場合、内蔵バッテリーのみで10日近く連続稼働できる見込みです。
このデバイスを、既に数日にわたり稼働させています。
ここまで順調に稼働しており、Ambientのデータは10分毎に更新されています。
ただ、使っているうちに、現状のディスプレイ表示内容では「M5Stack CoreInk」が順調に稼働しているかどうかが分かりにくいことに気付きました。
「M5Stack CoreInk」のディスプレイは「電子ペーパー」なので、スタンバイ中だろうが電池がなくなろうが、表示内容は保持されます。データ送信ができなくても表示内容が変わらないので、「M5Stack CoreInk」が正常に稼働しているかどうかは、Ambientのデータが実際に更新されているかどうかを確認しなければ分かりません。
そのため、「M5Stack CoreInk」のディスプレイに、データ測定時刻を表示することにしました。
以前「M5StickC」で同様のことを実施した(記事は こちら)ので、同様の処理を追加します。
ついでに「バッテリー残量」と「WI-FiのRSSI値」もディスプレイに表示するようにしました。
「バッテリー残量」の見積もりには、こちら を使わせていただきました(バッテリー電圧より1次式で計算しており、正確ではありません)。
スケッチはこちらです。
#include "M5CoreInk.h"
#include "UNIT_ENV.h"
#include "esp_adc_cal.h"
#include <WiFi.h>
#include "Ambient.h"
#include "time.h"
WiFiClient client;
Ambient ambient;
SHT3X sht30;
unsigned int channelId = XXXXX;
const char* writeKey = "XXXXXXXX";
const char* ssid = "XXXXXXXX";
const char* password = "XXXXXXXX";
Ink_Sprite InkPageSprite(&M5.M5Ink);
int getBatCapacity() {
const float maxVoltage = 4.02;
const float minVoltage = 3.65;
int cap = (int)(100.0 * (getBatVoltage() - minVoltage) / (maxVoltage - minVoltage));
if(cap > 100) cap = 100;
if(cap < 0) cap = 0;
return cap;
}
float getBatVoltage() {
analogSetPinAttenuation(35, ADC_11db);
esp_adc_cal_characteristics_t *adc_chars = (esp_adc_cal_characteristics_t *)calloc(1, sizeof(esp_adc_cal_characteristics_t));
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 3600, adc_chars);
uint16_t ADCValue = analogRead(35);
uint32_t BatVolmV = esp_adc_cal_raw_to_voltage(ADCValue, adc_chars);
float BatVol = float(BatVolmV) * 25.1 / 5.1 / 1000;
free(adc_chars);
return BatVol;
}
void setup() {
M5.begin(true, true, true); // InkEnable, wireEnable, SpeakerEnable
// initialize display
if(!M5.M5Ink.isInit()) {
Serial.printf("Ink Init faild");
}
M5.M5Ink.clear();
if(InkPageSprite.creatSprite(0, 0, 200, 200, true)!=0) {
Serial.printf("Ink Sprite creat faild");
}
// connect wifi
Serial.printf("Connecting to %s\n", ssid);
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.printf("\nWiFi connected\n");
// get time
configTime(9 * 3600, 0, "ntp.nict.jp");
struct tm timeInfo;
getLocalTime(&timeInfo);
char now[20];
sprintf(now, "%04d/%02d/%02d %02d:%02d:%02d",
timeInfo.tm_year + 1900,
timeInfo.tm_mon + 1,
timeInfo.tm_mday,
timeInfo.tm_hour,
timeInfo.tm_min,
timeInfo.tm_sec
);
// get data
if(sht30.get()!=0) {
return;
}
float temp = sht30.cTemp;
float humi = sht30.humidity;
float volt = getBatVoltage();
int capa = getBatCapacity();
int rssi = WiFi.RSSI();
char strTemp[10], strHumi[10], strVolt[20], strCapa[20], strRssi[20];
sprintf(strTemp, "%.2fC", temp);
sprintf(strHumi, "%.2f%%", humi);
sprintf(strVolt, "Bat Voltage:%.2fV", volt);
sprintf(strCapa, "Bat Capacity:%d%%", capa);
sprintf(strRssi, "RSSI:%d", rssi);
// show info
Serial.printf("Temperature: %.2f, Humidity: %.2f\n", temp, humi);
InkPageSprite.clear();
InkPageSprite.drawString(10, 0, now, &AsciiFont8x16);
InkPageSprite.drawString(10, 20, "Temperature", &AsciiFont8x16);
InkPageSprite.drawString(10, 36, strTemp, &AsciiFont24x48);
InkPageSprite.drawString(10, 84, "Humidity", &AsciiFont8x16);
InkPageSprite.drawString(10, 100, strHumi, &AsciiFont24x48);
InkPageSprite.drawString(10, 152, strVolt, &AsciiFont8x16);
InkPageSprite.drawString(10, 168, strCapa, &AsciiFont8x16);
InkPageSprite.drawString(10, 184, strRssi, &AsciiFont8x16);
InkPageSprite.pushSprite();
// send data
ambient.begin(channelId, writeKey, &client);
ambient.set(1, temp);
ambient.set(2, humi);
ambient.set(3, volt);
ambient.set(4, capa);
ambient.set(5, rssi);
ambient.send();
// shutdown
M5.shutdown(600);
esp_sleep_enable_timer_wakeup(600*1000*1000);
esp_deep_sleep_start();
}
void loop() {
// shutdown
M5.shutdown(600);
esp_sleep_enable_timer_wakeup(600*1000*1000);
esp_deep_sleep_start();
}
「getBatVoltage()」関数の最後に「free(adc_chars);」を追加した方がよいとの記事を見つけたので、追加しています。
また、一応「loop()」の中にもスタンバイモードに移行する処理を入れておきました。
ディスプレイの表示内容は以下のとおりです。
これで、データがいつ測定されたのかが分かるようになりました。
これで所望のIoTデバイスが完成しました。
内蔵バッテリーのみで10日近く連続稼働できる他、単三型Ni-MH電池(2000mAh)4本を「EXT_5VI」につなげば、「2000mAh / 1.74mA = 1149.4h = 47.8日」と、50日近く連続稼働できる見込みです。
さらに、小型のソーラーパネルを追加すれば、電源のない場所でも常時稼働できそうです。
2022年5月4日追記
実際に、内蔵バッテリーでの稼働時間を調べてみました。
バッテリーを満充電状態にした「M5Stack CoreInk」に、上記のスケッチを書き込んで稼働させたところ、約442時間(約18.4日)にわたり連続稼働させることができました。
これだけ電池も持ちがよければ、電源のない場所でも、色々な用途で活用できそうです。
なお、私がM5Stack、M5StickCの使い方を習得するのにあたっては、以下の書籍を参考にさせていただきました。
ごく基本的なところから、かなり複雑なスケッチや、ネットワーク接続など、比較的高度なものまで、つまづかずに読み進めていけるような構成になっており、大変わかりやすい本です。