🔌

M5StickC Plus2の省電力化

に公開

やったこと

M5StickC Plus2の省電力化を目的に以下を実施して、電源が切れるまでの時間を調査した。

検証

常時Wi-Fiに接続している状態で毎秒mqttでjsonを送信し、バッテリーがなくなるまで秒数を記録した。
今回は4つのパターンでバッテリーの持ち時間を確認した。

  1. 何も行わない(バックライトの輝度=50)
  2. バックライトの輝度=0
  3. LCDをスリープ
  4. CPUを80MHz(Wi-Fiが使える最低の周波数) + 5V出力を無効化

検証結果

1.の条件で輝度を50にしてしまったことが原因である可能性はあるが、バックライトのみを変更しても結果に差はなかった。一方で、3.と4.の有効性は確認することができた。
各モードにおけるバッテリー持続時間

検証用のコード

Aボタンを押すごとにモードが切り替わる仕様にした。

#include <Arduino.h>
#include <M5Unified.h>
#include <WiFi.h>
#include <PubSubClient.h>

// ======== Wi-Fi設定 ========
const char* WIFI_SSID     = "ssid";
const char* WIFI_PASSWORD = "password";

// ======== MQTT設定 ========
const char* MQTT_HOST = "192.168.1.108";       // ←ブローカのIPに変更
const uint16_t MQTT_PORT = 1883;
const char* MQTT_CLIENT_ID = "M5StickCPlus2_BatteryTest";
const char* MQTT_TOPIC = "test/m5stickcplus2/battery";

// ======== グローバル ========
WiFiClient espClient;
PubSubClient mqttClient(espClient);

unsigned long lastPublishMs = 0;
unsigned long publishIntervalMs = 1000;  // 1秒間隔
uint32_t seq = 0;                        // 送信カウンタ

// ecoMode: 0=通常, 1=バックライトのみOFF, 2=LCD OFF, 3=CPU80MHz+5V出力無効化
int ecoMode = 0;
const int ECO_MODE_NORMAL        = 0;
const int ECO_MODE_BACKLIGHT_OFF = 1;
const int ECO_MODE_LCD_OFF       = 2;
const int ECO_CPU                = 3;

uint8_t normalBrightness = 50;   // 通常モード時の明るさ
uint8_t ecoBrightness    = 5;    // (使いたければ省エネ時の明るさに利用)

// ---------- Wi-Fi接続 ----------
void connectWiFi() {
  M5_LOGI("Connecting to WiFi: %s", WIFI_SSID);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    M5_LOGI("WiFi connecting...");
  }

  M5_LOGI("WiFi connected: %s", WiFi.localIP().toString().c_str());
}

// ---------- MQTT接続 ----------
void connectMQTT() {
  mqttClient.setServer(MQTT_HOST, MQTT_PORT);

  while (!mqttClient.connected()) {
    M5_LOGI("Connecting to MQTT...");

    if (mqttClient.connect(MQTT_CLIENT_ID)) {
      M5_LOGI("MQTT connected");
    } else {
      M5_LOGW("Failed MQTT connect, rc=%d. Retry in 2s", mqttClient.state());
      delay(2000);
    }
  }
}

// ---------- 画面表示 ----------
void updateDisplay() {
  // LCD OFF モードのときは描画しない(見えないため)
  if (ecoMode == ECO_MODE_LCD_OFF) {
    return;
  }

  M5.Display.fillScreen(BLACK);
  M5.Display.setCursor(0, 0);
  M5.Display.setTextSize(1);

  M5.Display.println("Battery test");
  M5.Display.println("1 msg / second");
  M5.Display.println("");

  if (ecoMode == ECO_MODE_NORMAL) {
    M5.Display.println("Mode: NORMAL (0)");
  } else if (ecoMode == ECO_MODE_BACKLIGHT_OFF) {
    M5.Display.println("Mode: BACKLIGHT OFF (1)");
  } else if (ecoMode == ECO_MODE_LCD_OFF) {
    M5.Display.println("Mode: LCD OFF (2)");
  } else if (ecoMode == ECO_CPU) {
    M5.Display.println("Mode: CPU80MHz+Not 5V OUT (3)");
  }

  M5.Display.println("");
  M5.Display.println("Btn A: change mode");
}

// ---------- モード適用 ----------
void applyMode() {
  switch (ecoMode) {
    case ECO_MODE_NORMAL:
      // LCDの電源ON・バックライトON
      M5.Display.wakeup();        // LCDは有効
      M5.Display.setBrightness(normalBrightness);
      updateDisplay();
      break;

    case ECO_MODE_BACKLIGHT_OFF:
      // LCDは動かすが、バックライトのみOFF
      M5.Display.wakeup();        // LCDは有効
      M5.Display.setBrightness(0);           // 見た目は真っ暗
      // 明るさ戻したとき用に中身は描いておく
      updateDisplay();
      break;

    case ECO_MODE_LCD_OFF:
      // LCD自体をパワーセーブ(さらに省電力)
      M5.Display.sleep();           // 念のためバックライトも0
      M5.Display.setBrightness(0);           // 見た目は真っ暗
      // このモードでは updateDisplay() は呼ばない
      break;

    case ECO_CPU:
      M5.Display.wakeup();        // LCDは有効
      M5.Display.setBrightness(normalBrightness);
      setCpuFrequencyMhz(80);
      M5.Power.setExtOutput(false);
      updateDisplay();
      break;
  }
}

// ---------- メッセージ送信 ----------
void publishBattery() {
  seq++;

  // バッテリー残量(%)
  int8_t batLevel = M5.Power.getBatteryLevel();

  // ecoModeに応じた文字列
  const char* modeStr = "normal";
  if (ecoMode == ECO_MODE_BACKLIGHT_OFF) {
    modeStr = "backlight_off";
  } else if (ecoMode == ECO_MODE_LCD_OFF) {
    modeStr = "lcd_off";
  } else if (ecoMode == ECO_CPU) {
    modeStr = "80MHz";
  }

  // JSON生成
  // 例: {"seq":1,"battery":78,"millis":12345,"ecoMode":1,"mode_str":"backlight_off"}
  char payload[192];
  snprintf(
      payload,
      sizeof(payload),
      "{\"seq\":%lu,\"battery\":%d,\"millis\":%lu,\"ecoMode\":%d,\"mode_str\":\"%s\"}",
      (unsigned long)seq,
      (int)batLevel,
      (unsigned long)millis(),
      ecoMode,
      modeStr
  );

  bool ok = mqttClient.publish(MQTT_TOPIC, payload);

  if (!ok) {
    M5_LOGW("MQTT publish failed");
  } else {
    M5_LOGI("Published: %s", payload);
  }
}

// ---------- 初期化 ----------
void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);

  M5.Display.setBrightness(normalBrightness);
  ecoMode = ECO_MODE_NORMAL;
  applyMode();

  connectWiFi();
  connectMQTT();

  lastPublishMs = millis();
}

// ---------- メインループ ----------
void loop() {
  M5.update();

  // Aボタンで ecoMode を 0→1→2→0… とローテーション
  if (M5.BtnA.wasPressed()) {
    ecoMode = (ecoMode + 1) % 4;
    applyMode();
  }

  // MQTT接続確認
  if (!mqttClient.connected()) {
    connectMQTT();
  }
  mqttClient.loop();

  // 1秒ごとに送信
  unsigned long now = millis();
  if (now - lastPublishMs >= publishIntervalMs) {
    lastPublishMs = now;
    publishBattery();
  }

  delay(1);
}

参考サイト

Discussion