Stamp-S3Bat 内部には M5PM1 が集積されており、ハードウェア回路と組み合わせることで多段階電源スイッチを実現しています。各段階の電源スイッチは、対応する周辺機器およびインターフェースへの電力供給を制御します。ユーザーは動作要件に応じて、異なる段階の電源を有効に切り替え、未使用の周辺機器部分の電源をオフにすることで、装置全体の低消費電力化を実現できます。
M5PM1 ドライバライブラリを使用することで、低消費電力状態からの復帰および周辺機器への電力供給の切り替えに用いる M5PM1 のピン機能を非常に簡単に設定できます。
M5PM1 の起動通電後、L1、L2、L3A は自動的にオンになります(既定でDCDC3V3_EN_PP、CHG_EN_PPがオン)。この時点で装置上の ESP32-S3 は通電し初期化を完了しますが、EXT_5V_OUT の出力は無効であり、RGB LED への電力供給もオフの状態です。
この電源段階では、電池から M5PM1 への電力供給が維持されます。電池残量がなくならない限り、この段階の電源は保持され、M5PM1 は基本的なボタン操作による電源オン/オフをサポートします。
この電源段階では、ESP32-S3 メインコントローラーへの電力供給が有効になります。ESP32-S3 がスリープ状態の場合は電源が L2 段階となり、ESP32-S3 が動作状態の場合は電源が L3A 段階となります。
ESP32-S3 は M5PM1 を制御してスリープ状態に移行させることで、自身への電力供給を遮断(L2→L1/L0)することができます。
この電源段階では、M5PM1 を介して EXT_5V_OUT 出力インターフェースおよび RGB LED への電力供給をさらに有効にすることができます。以下の API によりスイッチ制御を実現できます。
EXT_5V_OUT出力をオンにする:PY_G1(5VOUT_EN)により制御します。pm1.gpioSetFunc(M5PM1_GPIO_NUM_1, M5PM1_GPIO_FUNC_GPIO);
pm1.gpioSetMode(M5PM1_GPIO_NUM_1, M5PM1_GPIO_MODE_OUTPUT);
pm1.gpioSetDrive(M5PM1_GPIO_NUM_1, M5PM1_GPIO_DRIVE_PUSHPULL);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_1, true); RGB LEDへの電力供給をオンにする:PY_LED_EN により制御します。pm1.setLedEnLevel(true) M5PM1 はプログラムにより手動でスリープ状態に移行させ、装置全体の消費電力を低減することができます。既定の状態では、スリープを設定すると電源は L1 段階に戻ります。このとき M5PM1 のみに電力が供給され、その他の復帰要因がない場合は、PWRボタンを 1 回押して復帰させる必要があります。
スリープ前に外部復帰 IO またはその他の復帰要因(タイマーなど)を設定しておくと、装置がスリープ状態に移行した後、復帰要因によるトリガーで M5PM1 を復帰させ、ESP32-S3 への電力供給を再開することができます。
pm1.shutdown(); Stamp‑S3Bat で本サンプルプログラムを実行すると、起動後に M5PM1 の G4(WAKE)が復帰 IO として設定されます。シャットダウン後、WAKE ピンをローレベルにして立ち下がりエッジを発生させることで、装置を復帰させることができます。
#include <Arduino.h>
#include <M5PM1.h>
#include <Wire.h>
M5PM1 pm1;
static const uint8_t PIN_SDA = 48;
static const uint8_t PIN_SCL = 47;
void setup()
{
Serial.begin(115200);
delay(300);
Wire.end();
Wire.begin(PIN_SDA, PIN_SCL, 100000U);
// Initialize PM1
m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PIN_SDA, PIN_SCL, M5PM1_I2C_FREQ_100K);
if (err != M5PM1_OK) {
Serial.printf("[PM1][E] PM1 initialization failed: %d\r\n", err);
while (true) {
delay(1000);
}
}
Serial.printf("[PM1][I] PM1 initialization successful\r\n");
pm1.irqClearGpioAll();
pm1.irqClearSysAll();
pm1.irqClearBtnAll();
pm1.irqSetGpioMaskAll(M5PM1_IRQ_MASK_ENABLE);
pm1.irqSetSysMaskAll(M5PM1_IRQ_MASK_ENABLE);
pm1.irqSetBtnMaskAll(M5PM1_IRQ_MASK_ENABLE);
pm1.irqSetGpioMask(M5PM1_IRQ_GPIO4, M5PM1_IRQ_MASK_DISABLE);
pm1.gpioSetMode(M5PM1_GPIO_NUM_4, M5PM1_GPIO_MODE_INPUT);
pm1.gpioSetPull(M5PM1_GPIO_NUM_4, M5PM1_GPIO_PULL_UP);
// Configure G4 as IO wake source: low-level trigger (falling edge)
m5pm1_err_t err_wake_en = pm1.gpioSetWakeEnable(M5PM1_GPIO_NUM_4, true);
m5pm1_err_t err_wake_ed = pm1.gpioSetWakeEdge(M5PM1_GPIO_NUM_4, M5PM1_GPIO_WAKE_FALLING);
if (err_wake_en != M5PM1_OK || err_wake_ed != M5PM1_OK) {
Serial.printf("[PM1][E] GPIO wake config failed, en:%d edge:%d\r\n", err_wake_en, err_wake_ed);
while (true) {
delay(1000);
}
}
Serial.printf("[PM1][I] Shutdown now, wait GPIO4 low level to wake\r\n");
delay(1000);
m5pm1_err_t err_shutdown = pm1.shutdown();
if (err_shutdown != M5PM1_OK) {
Serial.printf("[PM1][E] shutdown failed: %d\r\n", err_shutdown);
}
}
void loop()
{
} 特殊な使用シナリオにおいて、M5PM1 をスリープ状態に移行させて消費電力を低減しつつ、チップの GPIO ピンの現在の状態を維持することが求められます。本機能を実現するには、M5PM1 がスリープ状態に移行する前に対象ピンの状態設定を完了し、GPIO 状態保持機構を有効にする必要があります。
M5PM1_GPIO_NUM_1(EXT_5V_OUT)の電源出力を維持します。pm1.gpioSetMode(M5PM1_GPIO_NUM_1, M5PM1_GPIO_MODE_OUTPUT);
pm1.gpioSetDrive(M5PM1_GPIO_NUM_1, M5PM1_GPIO_DRIVE_PUSHPULL);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_1, true);
pm1.gpioSetPowerHold(M5PM1_GPIO_NUM_1, true);
Serial.println("[DEMO] GPIO1 HIGH + HOLD");
delay(1000);
Serial.println("[DEMO] Shutdown now");
delay(300);
pm1.shutdown(); M5PM1_GPIO_NUM_4(WAKE)のハイレベル出力を維持します。pm1.gpioSetMode(M5PM1_GPIO_NUM_4, M5PM1_GPIO_MODE_OUTPUT);
pm1.gpioSetDrive(M5PM1_GPIO_NUM_4, M5PM1_GPIO_DRIVE_PUSHPULL);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_4, true);
pm1.gpioSetPowerHold(M5PM1_GPIO_NUM_4, true);
Serial.println("[DEMO] GPIO4 HIGH + HOLD");
delay(1000);
Serial.println("[DEMO] Shutdown now");
delay(300);
pm1.shutdown(); M5PM1 は、I2C 通信がアイドル状態のときに自動的にスリープ状態に移行するよう設定することで、装置全体の消費電力を低減する機能をサポートします。スリープ状態に移行した後、ESP32-S3 と M5PM1 の最初の通信は M5PM1 の復帰に使用されるため通信に失敗し、有効な通信は復帰後の次の通信から行われます。
m5pm1_err_t setI2cSleepTime(uint8_t seconds); M5PM1 はタイマー機能の設定をサポートしており、カウントダウン終了後に電源オン、電源オフ、リセットなどの対応する動作を実行することができます。
m5pm1_err_t timerSet(uint32_t seconds, m5pm1_tim_action_t action); typedef enum {
M5PM1_TIM_ACTION_STOP = 0b000, // 停止、動作なし
// Stop, no action
M5PM1_TIM_ACTION_FLAG = 0b001, // フラグ設定のみ
// Set flag only
M5PM1_TIM_ACTION_REBOOT = 0b010, // システムリセット
// System reboot
M5PM1_TIM_ACTION_POWERON = 0b011, // 電源オン
// Power on
M5PM1_TIM_ACTION_POWEROFF = 0b100 // 電源オフ
// Power off
} m5pm1_tim_action_t; 装置に電源を投入して本プログラムを実行すると、10 秒後に自動的に電源がオフになります。
#include <M5Unified.h>
#include <M5PM1.h>
#include <Wire.h>
M5PM1 pm1;
void setup(void)
{
M5.begin();
Serial.begin(115200);
auto sda = M5.getPin(m5::pin_name_t::in_i2c_sda);
auto scl = M5.getPin(m5::pin_name_t::in_i2c_scl);
Wire.end();
Wire.begin(sda, scl, 100000U);
if (pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, sda, scl, M5PM1_I2C_FREQ_100K) != M5PM1_OK) {
Serial.println("PM1 init failed");
return;
}
Serial.println("Set timer: power off after 10s");
pm1.timerSet(10, M5PM1_TIM_ACTION_POWEROFF);
}
void loop(void) {
}装置で本プログラムを実行すると直ちに電源がオフになり、10 秒後に自動的に再起動します。
#include <M5Unified.h>
#include <M5PM1.h>
#include <Wire.h>
M5PM1 pm1;
void setup(void)
{
M5.begin();
Serial.begin(115200);
auto sda = M5.getPin(m5::pin_name_t::in_i2c_sda);
auto scl = M5.getPin(m5::pin_name_t::in_i2c_scl);
Wire.end();
Wire.begin(sda, scl, 100000U);
if (pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, sda, scl, M5PM1_I2C_FREQ_100K) != M5PM1_OK) {
Serial.println("PM1 init failed");
return;
}
Serial.println("Set timer: power on after 10s");
pm1.timerSet(10, M5PM1_TIM_ACTION_POWERON);
pm1.shutdown(); // Must enter shutdown first, then PM1 can wake it up by timer.
}
void loop(void) {
}#include <Arduino.h>
#include <M5PM1.h>
#include <Wire.h>
M5PM1 pm1;
#define PIN_SCL 47
#define PIN_SDA 48
static const uint8_t LED_COUNT = 1;
static const uint8_t BRIGHTNESS = 64;
static const m5pm1_rgb_t COLOR_GREEN = {0, BRIGHTNESS, 0};
void setup()
{
Serial.begin(115200);
delay(2000);
Wire.end();
Wire.begin(PIN_SDA, PIN_SCL, 100000U);
// Initialize PM1
m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PIN_SDA, PIN_SCL, M5PM1_I2C_FREQ_400K);
if (err != M5PM1_OK) {
Serial.printf("[PM1][E] PM1 initialization failed: %d\r\n", err);
while (true) {
delay(1000);
}
}
Serial.printf("[PM1][I] PM1 initialization successful\r\n");
// Configure PM1 GPIO0 for LED output path.
pm1.gpioSetFunc(M5PM1_GPIO_NUM_0, M5PM1_GPIO_FUNC_OTHER);
pm1.gpioSetDrive(M5PM1_GPIO_NUM_0, M5PM1_GPIO_DRIVE_PUSHPULL);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_0, true);
pm1.pinMode(M5PM1_GPIO_NUM_0, M5PM1_OTHER);
// Enable LED output and set LED count.
m5pm1_err_t err1 = pm1.setLedEnLevel(true);
if (err1 != M5PM1_OK) {
Serial.printf("[PM1][E] Failed to enable LED output: %d\r\n", err1);
}
m5pm1_err_t err2 = pm1.setLedCount(LED_COUNT);
if (err2 != M5PM1_OK) {
Serial.printf("[PM1][E] Failed to set LED count: %d\r\n", err2);
}
// Keep LED solid green.
m5pm1_err_t err3 = pm1.setLedColor(0, COLOR_GREEN);
m5pm1_err_t err4 = pm1.refreshLeds();
if (err3 != M5PM1_OK || err4 != M5PM1_OK) {
Serial.printf("[PM1][E] Failed to set GREEN LED, color:%d refresh:%d\r\n", err3, err4);
}
}
void loop()
{
delay(1000);
}PY_G3_CHG_PROGにより、充電 IC による電池への充電電流を切り替えます(ローレベル:650mA/フローティング:200mA)。
マクロ定義CHARGE_CURRENT_650MA_ENABLEDを 1 に設定すると、650mA の大電流充電モードが有効になります。
#include <Arduino.h>
#include <M5PM1.h>
#include <Wire.h>
M5PM1 pm1;
#define PIN_SCL 47
#define PIN_SDA 48
#define CHARGE_CURRENT_650MA_ENABLED 1
static const uint8_t LED_COUNT = 1;
static const uint8_t LED_BRIGHTNESS = 64;
static const m5pm1_gpio_num_t PY_G3_CHG_PROG = M5PM1_GPIO_NUM_3;
#if CHARGE_CURRENT_650MA_ENABLED
static const m5pm1_rgb_t LED_COLOR = {0, LED_BRIGHTNESS, 0};
#else
static const m5pm1_rgb_t LED_COLOR = {0, 0, LED_BRIGHTNESS};
#endif
static bool applyChargeCurrentConfig()
{
#if CHARGE_CURRENT_650MA_ENABLED
return pm1.gpioSetMode(PY_G3_CHG_PROG, M5PM1_GPIO_MODE_OUTPUT) == M5PM1_OK &&
pm1.gpioSetPull(PY_G3_CHG_PROG, M5PM1_GPIO_PULL_NONE) == M5PM1_OK &&
pm1.gpioSetOutput(PY_G3_CHG_PROG, false) == M5PM1_OK;
#else
return pm1.gpioSetMode(PY_G3_CHG_PROG, M5PM1_GPIO_MODE_INPUT) == M5PM1_OK &&
pm1.gpioSetPull(PY_G3_CHG_PROG, M5PM1_GPIO_PULL_NONE) == M5PM1_OK;
#endif
}
static bool applyRgbLedConfig()
{
bool ok = pm1.gpioSetFunc(M5PM1_GPIO_NUM_0, M5PM1_GPIO_FUNC_OTHER) == M5PM1_OK &&
pm1.gpioSetDrive(M5PM1_GPIO_NUM_0, M5PM1_GPIO_DRIVE_PUSHPULL) == M5PM1_OK &&
pm1.gpioSetOutput(M5PM1_GPIO_NUM_0, true) == M5PM1_OK;
pm1.pinMode(M5PM1_GPIO_NUM_0, M5PM1_OTHER);
return ok && pm1.setLedEnLevel(true) == M5PM1_OK && pm1.setLedCount(LED_COUNT) == M5PM1_OK &&
pm1.setLedColor(0, LED_COLOR) == M5PM1_OK && pm1.refreshLeds() == M5PM1_OK;
}
void setup()
{
delay(200);
Wire.end();
Wire.begin(PIN_SDA, PIN_SCL, 100000U);
m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PIN_SDA, PIN_SCL, M5PM1_I2C_FREQ_400K);
pm1.setChargeEnable(true);
if (err != M5PM1_OK) {
while (true) {
delay(1000);
}
}
if (!applyChargeCurrentConfig()) {
while (true) {
delay(1000);
}
}
if (!applyRgbLedConfig()) {
while (true) {
delay(1000);
}
}
}
void loop()
{
delay(1000);
}Stamp-S3Bat の現在の電源状態を読み取ります。
#include <Arduino.h>
#include <M5PM1.h>
#include <Wire.h>
M5PM1 pm1;
static const uint8_t PIN_SCL = 47;
static const uint8_t PIN_SDA = 48;
static const uint32_t PRINT_INTERVAL_MS = 2000;
static void printVoltages()
{
uint16_t mv = 0;
if (pm1.readVref(&mv) == M5PM1_OK) {
Serial.printf("[PM1][I] Vref: %u mV (%.3f V)\r\n", mv, mv / 1000.0f);
}
if (pm1.readVbat(&mv) == M5PM1_OK) {
Serial.printf("[PM1][I] VBAT: %u mV (%.3f V)\r\n", mv, mv / 1000.0f);
}
if (pm1.readVin(&mv) == M5PM1_OK) {
Serial.printf("[PM1][I] VIN: %u mV (%.3f V)\r\n", mv, mv / 1000.0f);
}
if (pm1.read5VInOut(&mv) == M5PM1_OK) {
Serial.printf("[PM1][I] 5V IN/OUT: %u mV (%.3f V)\r\n", mv, mv / 1000.0f);
}
}
void setup()
{
Serial.begin(115200);
delay(300);
Serial.println("[DEMO] Boot");
Wire.end();
Wire.begin(PIN_SDA, PIN_SCL, 100000U);
if (pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PIN_SDA, PIN_SCL, M5PM1_I2C_FREQ_400K) != M5PM1_OK) {
Serial.println("[DEMO] PM1 init failed");
return;
}
Serial.println("[DEMO] PM1 init ok");
}
void loop()
{
static uint32_t lastMs = 0;
if (millis() - lastMs >= PRINT_INTERVAL_MS) {
lastMs = millis();
printVoltages();
}
}