M5Stack CoreInkでできること 〜低電力モードをつかう

先日、「M5Stack CoreInk開発キット」用の環境構築(記事は こちら)、およびディスプレイに文字を表示する方法の確認(記事は こちら)を行いました。


「M5Stack CoreInk」をつかって、最終的には以下のようなIoTデバイスをつくりたいと思っています。

  • 「ENV IIIユニット」をつかって、一定時間毎に温度・湿度を測定する。
  • 測定した温度・湿度をディスプレイに表示する。また、次に温度・湿度を測定するまで、その表示内容を保持する。
  • 測定した温度・湿度をAmbientに送信する。
  • ESP32などの電源をOFFにするモード(ここではスタンバイモードと呼ぶことにします)を活用することで、内蔵バッテリーのみで長期間連続稼働させる(例えば10分毎の測定で数週間稼働させる、など)。
  • 可能であれば、外部から電源供給して、より長期間(例えば数ヶ月)連続稼働させる、もしくは小型ソーラーパネルなどを接続することで常時稼働させる。

今回は、スタンバイモードの使い方を確認してみます。

前回の記事(こちら)で、「ENV IIIユニット」で測定した温度・湿度をディスプレイに表示するスケッチを作成済みです。
まずは、ここで測定した温度・湿度をAmbientに送信するようにしてみます。
あわせて、内蔵バッテリー電圧も測定し、その値もディスプレイに表示、Ambientにも送信するようにしておきます。
内蔵バッテリー電圧の測定方法は、「ファイル」>「スケッチ例」>「M5-CoreInk」>「FactoryTest」に記載されています。

#include "M5CoreInk.h"
#include "UNIT_ENV.h"
#include "esp_adc_cal.h"
#include <WiFi.h>
#include "Ambient.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);

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;
  return BatVol;
}

void setup() {
  M5.begin(true, true, true); // InkEnable, wireEnable, SpeakerEnable
  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");
  }

  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");
}

void loop() {
  if(sht30.get()!=0) {
    return;
  }
  float temp = sht30.cTemp;
  float humi = sht30.humidity;
  float volt = getBatVoltage();
  char strTemp[6], strHumi[6], strVolt[6];
  sprintf(strTemp, "%.2f", temp);
  sprintf(strHumi, "%.2f", humi);
  sprintf(strVolt, "%.2f", volt);
  Serial.printf("Temperature: %.2f, Humidity: %.2f\n", temp, humi);
  InkPageSprite.clear();
  InkPageSprite.drawString(20,   0, "Temperature", &AsciiFont8x16);
  InkPageSprite.drawString(20,  16, strTemp, &AsciiFont24x48);
  InkPageSprite.drawString(20,  64, "Humidity", &AsciiFont8x16);
  InkPageSprite.drawString(20,  80, strHumi, &AsciiFont24x48);
  InkPageSprite.drawString(20, 128, "Bat Voltage", &AsciiFont8x16);
  InkPageSprite.drawString(20, 144, strVolt, &AsciiFont24x48);
  InkPageSprite.pushSprite();
  ambient.begin(channelId, writeKey, &client);
  ambient.set(1, temp);
  ambient.set(2, humi);
  ambient.set(3, volt);
  ambient.send();
  delay(30000);
}

これで、30秒毎に温度・湿度・内蔵バッテリー電圧を測定し、ディスプレイの表示内容が更新されるとともに、そのデータがAmbientに送信されるようになりました。

次に、データを送信してから次のデータを測定するまでの30秒間、スタンバイモードに移行するようにしてみます。

1回データを送るたびに再起動することになるので、全ての処理をsetup()の中に移動します。
その上で、処理の最後に「M5.shutdown()」を追加します。
ちなみに、「M5Stack CoreInk」にUSBで電源供給している場合は、「M5.shutdown()」でスタンバイモードに移行できません。そのため「M5.shutdown()」の後に、通常のディープスリープへの移行コマンドを追加しておきます。これで、内蔵バッテリーで動作している時はスタンバイモードに移行、USBで給電している場合はディープスリープに移行するようになるはずです。

#include "M5CoreInk.h"
#include "UNIT_ENV.h"
#include "esp_adc_cal.h"
#include <WiFi.h>
#include "Ambient.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);

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;
  return BatVol;
}

void setup() {
  M5.begin(true, true, true); // InkEnable, wireEnable, SpeakerEnable
  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");
  }

  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");

  if(sht30.get()!=0) {
    return;
  }
  float temp = sht30.cTemp;
  float humi = sht30.humidity;
  float volt = getBatVoltage();
  char strTemp[6], strHumi[6], strVolt[6];
  sprintf(strTemp, "%.2f", temp);
  sprintf(strHumi, "%.2f", humi);
  sprintf(strVolt, "%.2f", volt);
  Serial.printf("Temperature: %.2f, Humidity: %.2f\n", temp, humi);
  InkPageSprite.clear();
  InkPageSprite.drawString(20,   0, "Temperature", &AsciiFont8x16);
  InkPageSprite.drawString(20,  16, strTemp, &AsciiFont24x48);
  InkPageSprite.drawString(20,  64, "Humidity", &AsciiFont8x16);
  InkPageSprite.drawString(20,  80, strHumi, &AsciiFont24x48);
  InkPageSprite.drawString(20, 128, "Bat Voltage", &AsciiFont8x16);
  InkPageSprite.drawString(20, 144, strVolt, &AsciiFont24x48);
  InkPageSprite.pushSprite();
  ambient.begin(channelId, writeKey, &client);
  ambient.set(1, temp);
  ambient.set(2, humi);
  ambient.set(3, volt);
  ambient.send();

  M5.shutdown(30);
  esp_sleep_enable_timer_wakeup(30*1000*1000);
  esp_deep_sleep_start();
}

void loop() {}

起動すると、本体上部の緑LEDが点灯し、各処理を実行します。
処理が終わるとLEDが消灯し、スタンバイモードに移行します。この間もディスプレイには温度、湿度、バッテリー電圧が表示され続けています。
なお、スタンバイモードから復帰する際には、処理が最初からスタートすることになるので、ディスプレイが一旦暗くなってから真っ白になり、その後に改めてデータが表示されます。

これで所望の処理ができた訳ですが、動作内容だけでは、本当にスタンバイモードに移行できているのかどうかが分かりません。
そのため、テスターを使って実際に消費電流値を確認してみたいと思います。

「M5Stack CoreInk」の回路図(こちら)を確認したところ、電源周りの接続は以下のようになっています(間違っているかもしれません)。

USB給電すると「CP2104」が動作してしまいますが、「EXT_5VI」に給電するのであれば、内蔵バッテリーで動作するのとほぼ同じ状態になるはずです。
そのため、単三型Ni-MH電池4本を「EXT_5VI」に給電し、その電源ライン上の電流値を測定することとします。

以下のようにテスターをつなぎ、内蔵バッテリーが満充電のとき(内蔵バッテリーに充電していないときには、本体下部の赤LEDが消灯します)の、動作中およびスタンバイ中の電流値を測定しました。

その結果、動作中は数十mA〜100mA程度の電流消費が数秒間継続し、スタンバイ中の電流消費は 0.07mA程度でした。
非常に低電力です。
ちなみに、前述の電源周りの回路図を見るとわかるとおり、GROVEポートの電源端子「EXT_5VO」も、スタンバイモードの間は電源遮断されています。よって、スタンバイ中は「ENV IIIユニット」も電流消費しません。

上記の結果より、もしも1回の動作で100mAの電流消費が10秒間継続すると仮定すると、10分毎にデータ測定する場合の平均消費電流値は、

100mA × 10sec / 600sec + 0.07mA = 1.74mA

となります。
内蔵バッテリー容量は 390mAh なので、

390mAh / 1.74mA = 224.1h = 9.3日

と、10日近く連続稼働できる計算になります。

ちなみに、同じ方法でUSBポートから給電したときの電流消費を調べたところ、動作中の電流消費は同等でしたが、ディープスリープ中の電流消費は 7.9mA 程度と、スタンバイ中の電流消費の100倍の値となりました。


なお、私がM5Stack、M5StickCの使い方を習得するのにあたっては、以下の書籍を参考にさせていただきました。


ごく基本的なところから、かなり複雑なスケッチや、ネットワーク接続など、比較的高度なものまで、つまづかずに読み進めていけるような構成になっており、大変わかりやすい本です。


このサイトで書いている、M5Stackシリーズ(M5Stack、M5StickCなど)に関するブログ記事を、「さとやまノート」という別のブログページに、あらためて整理してまとめました。

他のM5Stackシリーズの記事にも興味のある方は「さとやまノート」をご覧ください。