メインコンテンツまでスキップ

XIAO ESP32S3 Sense スリープモード

ここでは、これらの低消費電力スリープモードの使い方を示すために、いくつかの簡単な例を紹介します。すべての ESP32 ボードは汎用性がありますが、ここで使用している開発ボードは XIAO ESP32S3 Sense です。

ハードウェア概要

Seeed Studio XIAO ESP32S3 Sense

Deep-Sleep

概要

Deep-Sleep モードでは、ESP32 は CPU、RAM の大部分、および APB_CLK からクロック供給されるすべてのデジタルペリフェラルの電源をオフにします。電源が維持されるコンポーネントは次のとおりです:

  • RTC コントローラ
  • ULP コプロセッサ
  • RTC FAST メモリ
  • RTC SLOW メモリ
警告

Deep Sleep 中の USB ペリフェラル無効化: 内蔵 USB ペリフェラル(USB-Serial-JTAG)を含むすべてのデジタルペリフェラルは Deep Sleep 中に電源がオフになります。デバイスが Deep Sleep 中は USB 経由のシリアル出力は利用できません。 デバッグが必要な場合は、ハードウェア UART ピンに接続した外部 USB-UART チップを使用してください。

ウェイクアップ方法

  • タイマーウェイクアップ: ESP32 は、タイマーを設定することで、指定した時間が経過すると自動的にウェイクアップできます。

  • タッチパッド割り込みウェイクアップ: デバイスはタッチパッド上の動作によって起動でき、ユーザー操作が必要なアプリケーションに適しています。

  • 外部ウェイクアップ: ESP32 は外部信号(例:ボタン押下)によってウェイクアップでき、低消費電力アプリケーションに最適です。

  • ULP コプロセッサ動作によるウェイクアップ: ULP コプロセッサは独立して動作し、特定の条件を監視してメイン CPU をウェイクアップできるため、電力を節約できます。

  • GPIO ウェイクアップ: デバイスは GPIO ピン状態(High または Low)の変化によってウェイクアップでき、さまざまなセンサやペリフェラルに柔軟に対応できます。

以下に、DeepSleep モードを使用する XIAO ESP32 S3 Sense の簡単な例を 3 つ示します。

コード実装


#define uS_TO_S_FACTOR 1000000ULL
#define TIME_TO_SLEEP 5

RTC_DATA_ATTR int bootCount = 0;

void print_wakeup_reason() {
esp_sleep_wakeup_cause_t wakeup_reason;

wakeup_reason = esp_sleep_get_wakeup_cause();

switch (wakeup_reason) {
case ESP_SLEEP_WAKEUP_EXT0: Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1: Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP: Serial.println("Wakeup caused by ULP program"); break;
default: Serial.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason); break;
}
}

void setup() {
Serial.begin(115200);
delay(1000);


++bootCount;
Serial.println("Boot number: " + String(bootCount));


print_wakeup_reason();


esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds");

Serial.println("Going to sleep now");
Serial.flush();
esp_deep_sleep_start();
Serial.println("This will never be printed");
}

void loop() {

}

詳細な説明

#define uS_TO_S_FACTOR 1000000ULL 
  • マクロを定義してマイクロ秒を秒に変換します。1000000ULL はマイクロ秒を秒に変換するために使用される係数です。
#define TIME_TO_SLEEP  5     
  • ESP32 がスリープする時間を設定するマクロを定義します。この例では 5 秒です。
RTC_DATA_ATTR int bootCount = 0;
  • RTC_DATA_ATTR 属性を持つ整数変数 bootCount を宣言します。これにより、Deep Sleep 中も値が保持されます。
void print_wakeup_reason() {
  • ESP32 がウェイクアップした理由を出力する print_wakeup_reason() という名前の関数を定義します。
esp_sleep_wakeup_cause_t wakeup_reason;
  • ウェイクアップイベントの原因を保存するために、esp_sleep_wakeup_cause_t 型の変数 wakeup_reason を宣言します。
wakeup_reason = esp_sleep_get_wakeup_cause();
  • 関数 esp_sleep_get_wakeup_cause() を呼び出してウェイクアップ理由を取得し、その結果を wakeup_reason 変数に代入します。
  switch (wakeup_reason) {
case ESP_SLEEP_WAKEUP_EXT0: Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1: Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP: Serial.println("Wakeup caused by ULP program"); break;
default: Serial.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason); break;
}
  • ESP_SLEEP_WAKEUP_EXT0 : このウェイクアップ理由は、RTC(リアルタイムクロック)I/O 用に設定された GPIO ピンで検出された外部信号によって ESP32 がウェイクアップしたことを示します。これは通常、ボタンやセンサがトリガされたときにスリープから復帰する用途に使用されます。
  • ESP_SLEEP_WAKEUP_EXT1 : これは、RTC コントローラによって管理される GPIO ピン上の外部信号によってウェイクアップが発生したことを示します。EXT0 と異なり、EXT1 は複数のピンを扱うことができ、指定されたピンのいずれかの状態が変化したとき(例:Low または High になる)にウェイクアップできます。
  • ESP_SLEEP_WAKEUP_TIMER : このウェイクアップ理由は、あらかじめ設定されたタイマー時間が経過した後に ESP32 がウェイクアップしたことを示します。ユーザー操作を必要とせずに定期的なタスクを実行する必要があるアプリケーションに有用です。
  • ESP_SLEEP_WAKEUP_TOUCHPAD : これは、タッチパッドイベントによって ESP32 がウェイクアップしたことを示します。ウェイクアップ用に設定されたタッチパッドがタッチを検出すると、デバイスをスリープモードから復帰させることができます。
  • ESP_SLEEP_WAKEUP_ULP : このウェイクアップ理由は、ULP(Ultra-Low Power)プログラムによってウェイクアップがトリガされたことを意味します。ULP はメイン CPU が Deep Sleep 中でも動作でき、特定の条件が満たされたときに ESP32 をウェイクアップできるため、バッテリー消費を最小限に抑えた低消費電力動作が可能になります。
++bootCount;
  • ブート回数をインクリメントし、デバイスが再起動するたびにその値を出力します。
print_wakeup_reason();
  • ESP32 のウェイクアップ理由を出力します。
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds");

Serial.println("Going to sleep now");
Serial.flush();
esp_deep_sleep_start();
Serial.println("This will never be printed");
  • esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);ESP32 が指定時間後にタイマーでウェイクアップするように有効化します。
  • Serial.flush();スリープに入る前に、すべてのシリアルデータが送信されるようにします。
  • esp_deep_sleep_start();ESP32 を Deep Sleep モードに移行させます。
ヒント

ディープスリープモードに入った後にプログラムを書き込み直すには、boot ボタンを押し続けたまま reset ボタンを押して ESP32 を再起動してください。

結果表示

ライトスリープ

概要

ライトスリープモードは、ESP32 におけるもう一つの低消費電力モードであり、素早い応答時間を維持しながらデバイスの電力を節約することができます。このモードでは CPU コアは停止しますが、RAM と一部のペリフェラルは電源が入ったままのため、特定のイベントに応じてデバイスをすばやくウェイクアップさせることができます。

ライトスリープは、低消費電力が求められる一方で WiFi や Bluetooth への接続を維持する必要があるアプリケーションに最適であり、無線通信モジュールをアクティブな状態に保つことができます。

警告

ライトスリープ中の USB ペリフェラル無効化: 省電力のため、内部 USB ペリフェラル(USB-Serial-JTAG)はライトスリープ中に無効化されます。これは、デバイスがライトスリープ中は USB 経由のシリアル出力が利用できない ことを意味します。USB ポートを使用してシリアルログを表示している場合、スリープ期間中は何も出力されません。デバッグするには、ハードウェア UART ピンに接続した外部 USB-UART チップを使用するか、GPIO ウェイクアップを利用して、デバイスのウェイクアップ後に出力を監視することを検討してください。

ウェイクアップ方法

  • タイマーウェイクアップ: デバイスは指定した時間経過後にウェイクアップでき、定期的なタスクを実行できます。
  • 外部割り込みウェイクアップ: ESP32 は、ボタン押下やその他のハードウェア割り込みなどの外部信号によってウェイクアップさせることができます。
  • ネットワークアクティビティによるウェイクアップ: デバイスは、受信したネットワークパケットに応答してウェイクアップでき、常にアクティブ状態にしておくことなく効率的な通信を実現します。
  • GPIO ウェイクアップ: 特定の GPIO ピンを設定して、状態変化や信号などのイベントが発生したときにライトスリープからデバイスをウェイクアップさせることができます。

コード実装

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

const int sleepTime = 10000;
const int ledPin = LED_BUILTIN;

void ledTask(void *pvParameters) {
digitalWrite(ledPin, HIGH);
Serial.println("LED is ON");
vTaskDelay(pdMS_TO_TICKS(1000));
digitalWrite(ledPin, LOW);
Serial.println("LED is OFF");

vTaskDelete(NULL);
}

void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
Serial.println("Setup complete. Going to sleep...");
}

void loop() {
esp_sleep_enable_timer_wakeup(sleepTime * 1000);
Serial.println("Going to sleep now...");
esp_light_sleep_start();

xTaskCreate(ledTask, "LED Task", 2048, NULL, 1, NULL);

delay(1000);
}

詳細な説明

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
  • FreeRTOS ライブラリをインクルードします
const int sleepTime = 10000; 
const int ledPin = LED_BUILTIN;
  • スリープ時間を 10 秒に設定します
  • 内蔵 LED ピンを使用します
void ledTask(void *pvParameters): 
  • LED の状態を制御する FreeRTOS タスクを定義します。
digitalWrite(ledPin, HIGH); 
Serial.println("LED is ON");
vTaskDelay(pdMS_TO_TICKS(1000));
digitalWrite(ledPin, LOW);
Serial.println("LED is OFF");
vTaskDelete(NULL);
  • vTaskDelay(pdMS_TO_TICKS(1000));LED を 1 秒間点灯させます
  • vTaskDelete(NULL);現在のタスクを削除します
esp_sleep_enable_timer_wakeup(sleepTime * 1000);
Serial.println("Going to sleep now...");
esp_light_sleep_start();
xTaskCreate(ledTask, "LED Task", 2048, NULL, 1, NULL);
delay(1000);
  • esp_sleep_enable_timer_wakeup(sleepTime * 1000);ウェイクアップ用のタイマーを設定します
  • esp_light_sleep_start(); ライトスリープモードに入ります
  • xTaskCreate(ledTask, "LED Task", 2048, NULL, 1, NULL);LED 制御タスクを作成します

実行結果

Modem-Sleep

概要

Modem Sleep モードは ESP32 におけるもう 1 つの重要な低消費電力モードであり、Deep Sleep モードとは異なります。Modem Sleep モードは主に ESP32 の無線通信モジュール向けに最適化されています。

このモードでは、ESP32 の WiFi/Bluetooth モジュールがスリープ状態に入り、CPU コアは動作したままになります。これにより、消費電力を大幅に削減しつつ、ESP32 は一定レベルの無線接続性を維持できます。

ウェイクアップ方法

  • タイマーによるウェイクアップ

  • 外部割り込みによるウェイクアップ

  • タスクによるウェイクアップ

  • ネットワークアクティビティによるウェイクアップ

コード実装

#include "WiFi.h"

void setup() {
Serial.begin(115200);
Serial.println("Connecting to WiFi...");

WiFi.begin("****", "****");

while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}
Serial.println("Connected to WiFi!");

WiFi.setSleep(true);
Serial.println("Modem-Sleep enabled.");
}

void loop() {

Serial.println("Running...");

delay(5000);

WiFi.setSleep(false);
Serial.println("Modem-Sleep disabled. WiFi is active.");

if (WiFi.status() == WL_CONNECTED) {
Serial.println("Still connected to WiFi.");
} else {
Serial.println("WiFi disconnected.");
}

delay(5000);
WiFi.setSleep(true);
Serial.println("Modem-Sleep enabled.");
}

詳細な説明

#include "WiFi.h"
  • WiFi 機能を有効にするために WiFi ライブラリをインクルードします。
Serial.println("Connecting to WiFi...");
  • WiFi への接続を開始することを示すメッセージを出力します。
WiFi.begin("****", "****");
  • 指定した WiFi ネットワークへの接続を開始します。
    while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}
Serial.println("Connected to WiFi!");
  • WiFi に正常に接続されるまでループします。
WiFi.setSleep(true);
  • 省電力のために Modem Sleep モードを有効にします。
WiFi.setSleep(false);
  • WiFi を有効化するために Modem Sleep モードを無効にします。
if (WiFi.status() == WL_CONNECTED) {
  • WiFi の状態を確認します。
WiFi.setSleep(true);
  • 再度 Modem Sleep モードを有効にします。

実行結果

スリープ機能の応用

上記の簡単な例を踏まえて、次は一歩進めて、これらのスリープ機能を ESP32 S3 Sense センサー上で活用してみましょう。

ソフトウェアの準備

この記事を始める前に、まだ XIAO ESP32S3 Sense 上のすべてのハードウェア機能を使用したことがない場合は、いくつかのソフトウェアインストールの準備が完了していることを確認してください。

ここでは 3 つの機能について紹介します。詳細は次のリンクから確認できます。

  • Micrphone Use: XIAO ESP32S3 Sense 上のマイクを使用して周囲の音量を取得し、音声を録音する方法を学びます。

  • MicroSD: プロジェクトでファイルを保存および取得できるように、データ保存用に MicroSD カードを使用する方法を理解します。

  • Camera Use: XIAO ESP32S3 Sense 上のカメラモジュールを使用して写真を撮影し、動画を録画する方法を習得します。

コード実装

#include "esp_camera.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"

#define CAMERA_MODEL_XIAO_ESP32S3

#include "camera_pins.h"

unsigned long lastCaptureTime = 0;
int imageCount = 1;
bool camera_sign = false;
bool sd_sign = false;


void photo_save(const char * fileName) {
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Failed to get camera frame buffer");
return;
}
writeFile(SD, fileName, fb->buf, fb->len);

esp_camera_fb_return(fb);

Serial.println("Photo saved to file");
}

void writeFile(fs::FS &fs, const char * path, uint8_t * data, size_t len){
Serial.printf("Writing file: %s\r\n", path);

File file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.write(data, len) == len) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();
}

void setup() {
Serial.begin(115200);
while (!Serial);

camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.frame_size = FRAMESIZE_UXGA;
config.pixel_format = PIXFORMAT_JPEG;
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.jpeg_quality = 12;
config.fb_count = 1;

esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}

camera_sign = true;

if (!SD.begin(21)) {
Serial.println("Card Mount Failed");
return;
}

uint8_t cardType = SD.cardType();

if (cardType == CARD_NONE) {
Serial.println("No SD card attached");
return;
}

Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}

sd_sign = true;

Serial.println("Photos will begin shortly, please be ready.");
}

void loop() {
if (camera_sign && sd_sign) {
unsigned long now = millis();

if ((now - lastCaptureTime) >= 60000) {
char filename[32];
sprintf(filename, "/image%d.jpg", imageCount);
photo_save(filename);
Serial.printf("Saved picture: %s\r\n", filename);
Serial.println("Entering deep sleep for 10 seconds...");

esp_sleep_enable_timer_wakeup(10000000);
esp_deep_sleep_start();

}
}
}

詳細な説明

このコードは ESP32 カメラモジュールをベースにした画像キャプチャシステムを実装しており、60 秒ごとに自動で写真を撮影し、SD カードに保存します。void setup() 関数ではカメラと SD カードを初期化し、デバイスの状態を確認します。void loop() 関数ではカメラが撮影可能かどうかを確認し、条件を満たす場合は photo_save() 関数を呼び出して画像を保存し、保存後に 10 秒間のディープスリープ状態に入って省電力化を行います。

ヒント

これらのコードはそのままでは使用できません。カメラに関するヘッダーファイルを追加する必要があります。XIAO ESP32 S3 に関する上記のサンプルを確認してください。

まとめ

Deep Sleep モードを使用する理由

デバイスのバッテリー寿命を延ばすために、機能性を損なうことなく電力節約を最大化します。 適したシナリオ:リモートセンサーノード、ウェアラブルデバイス、その他の低消費電力 IoT デバイスなど、バッテリー寿命が重要となるアプリケーション。ウェイクアップ時間は比較的遅いものの、このトレードオフには十分な価値があります。

Modem Sleep モードを使用する理由

ネットワーク接続を維持しながら、無線通信モジュールの消費電力を最適化します。 適したシナリオ:ネットワーク接続を維持する必要がありつつ、低消費電力も求められるアプリケーション。例えば、間欠的に動作する IoT デバイスなどです。Modem Sleep は、無線モジュールの消費電力を大幅に削減しつつ、高速なウェイクアップ応答を提供できます。

総括

これら 3 つのスリープモードは、開発者に対して電力と性能の異なるトレードオフの選択肢を提供し、アプリケーションの具体的な要件に応じて柔軟に選択できます。バッテリー寿命が重視されるデバイスには Deep Sleep モードが適しており、ネットワーク接続を維持する必要がある IoT デバイスには Modem Sleep モードが最適な選択肢となります。

技術サポート & 製品ディスカッション

弊社製品をお選びいただきありがとうございます。製品をできるだけスムーズにご利用いただけるよう、さまざまなサポートをご用意しています。お好みやニーズに応じて選べる複数のコミュニケーションチャネルを提供しています。

Loading Comments...