Skip to main content

XIAO ESP32S3 Sense 睡眠模式

note

本文档由 AI 翻译。如您发现内容有误或有改进建议,欢迎通过页面下方的评论区,或在以下 Issue 页面中告诉我们:https://github.com/Seeed-Studio/wiki-documents/issues

在这里,我将展示一些简单的示例来说明如何使用这些低功耗睡眠模式。所有 ESP32 开发板都非常灵活,而我在此上下文中使用的开发板是 XIAO ESP32S3 Sense。

硬件概览

Seeed Studio XIAO ESP32S3 Sense

深度睡眠模式

简介

在深度睡眠模式下,ESP32 会关闭 CPU、大部分 RAM 和所有由 APB_CLK 时钟驱动的数字外设。唯一保持供电的组件是:

  • RTC 控制器
  • ULP 协处理器
  • RTC FAST 内存
  • RTC SLOW 内存

唤醒方式

  • 定时器唤醒:通过设置定时器,ESP32 可以在指定时间后自动唤醒。

  • 触摸板中断唤醒:设备可以通过触摸板上的活动唤醒,适用于需要用户交互的应用。

  • 外部唤醒:ESP32 可以通过外部信号(例如按钮按下)唤醒,非常适合低功耗应用。

  • ULP 协处理器活动唤醒:ULP 协处理器可以独立运行,监控特定条件并唤醒主 CPU,以节省功耗。

  • GPIO 唤醒:设备可以通过 GPIO 引脚状态(高或低)的变化唤醒,为各种传感器和外设提供灵活性。

以下是 XIAO ESP32 S3 Sense 使用深度睡眠模式的三个简单示例。

代码实现


#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;
  • 声明一个整数变量 bootCount,并使用属性 RTC_DATA_ATTR,使其在深度睡眠期间保留其值。
void print_wakeup_reason() {
  • 定义一个名为 print_wakeup_reason() 的函数,用于打印 ESP32 唤醒的原因。
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:此唤醒原因表明 ESP32 因检测到 GPIO 引脚上的外部信号而唤醒,该引脚配置为 RTC(实时时钟)I/O。通常用于按钮或传感器触发的唤醒。
  • ESP_SLEEP_WAKEUP_EXT1:此唤醒原因表明唤醒是由 RTC 控制器管理的 GPIO 引脚上的外部信号引起的。与 EXT0 不同,EXT1 可以处理多个引脚,并在任何指定引脚状态变化(例如变低或变高)时唤醒。
  • ESP_SLEEP_WAKEUP_TIMER:此唤醒原因表明 ESP32 在预定义的定时器持续时间后唤醒。这对于需要定期执行任务且无需用户交互的应用非常有用。
  • ESP_SLEEP_WAKEUP_TOUCHPAD:此唤醒原因表明 ESP32 因触摸板事件而唤醒。如果配置为唤醒的触摸板检测到触摸,则可以将设备从睡眠模式中唤醒。
  • ESP_SLEEP_WAKEUP_ULP:此唤醒原因表明唤醒是由 ULP(超低功耗)程序触发的。ULP 可以在主 CPU 处于深度睡眠时运行,并在满足特定条件时唤醒 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 置于深度睡眠模式。
tip

在进入深度睡眠模式后重新烧录程序时,请按住启动按钮,然后按复位按钮以重启 ESP32。

结果展示

Light-Sleep(轻睡眠)

简介

轻睡眠模式是 ESP32 的另一种低功耗模式,它允许设备在节约能源的同时仍然保持快速响应。在这种模式下,CPU 核心暂停运行,但 RAM 和部分外设仍保持通电状态,使设备能够快速响应某些事件。

轻睡眠模式非常适合需要低功耗但仍需保持 WiFi 或 Bluetooth 连接的应用,因为它允许无线通信模块保持活动状态。

唤醒方式

  • 定时器唤醒: 设备可以在指定的时间段后唤醒,从而执行周期性任务。
  • 外部中断唤醒: 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): 
  • 定义一个 FreeRTOS 任务来控制 LED 状态。
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(调制解调器睡眠)

简介

调制解调器睡眠模式是 ESP32 的另一种重要低功耗模式,与深度睡眠模式不同。调制解调器睡眠模式主要针对 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);
  • 启用调制解调器睡眠模式以节省功耗。
WiFi.setSleep(false);
  • 禁用调制解调器睡眠模式以激活 WiFi。
if (WiFi.status() == WL_CONNECTED) {
  • 检查 WiFi 状态。
WiFi.setSleep(true);
  • 再次启用调制解调器睡眠模式。

结果展示

睡眠功能应用

通过以上简单示例,现在我们进一步使用这些睡眠功能在 ESP32 S3 Sense 传感器上进行应用。

软件准备

在开始本文之前,如果您尚未使用 XIAO ESP32S3 Sense 的所有硬件功能,请确保您已完成一些软件安装准备工作。

以下是三个功能的介绍,您可以通过以下链接找到更多信息:

翻译后的文档

  • 麦克风使用: 学习如何在 XIAO ESP32S3 Sense 上使用麦克风捕获环境声音水平并录制音频。

  • MicroSD: 了解如何使用 MicroSD 卡进行数据存储,确保您能够在项目中保存和检索文件。

  • 摄像头使用: 掌握如何在 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 秒以节省能源。

tip

这些代码不能直接使用,您需要添加关于摄像头的头文件,请查看上述关于 XIAO ESP32 S3 的示例。

总结

为什么使用深度睡眠模式

最大化节能而不影响功能,以延长设备的电池寿命。
适用场景:电池寿命至关重要的应用,例如远程传感器节点、可穿戴设备以及其他低功耗 IoT 设备。虽然唤醒时间相对较慢,但这种权衡是值得的。

为什么使用 Modem Sleep 模式

优化无线通信模块的功耗,同时仍保持网络连接。
适用场景:需要保持网络连接但也要求低功耗的应用,例如间歇性工作的 IoT 设备。Modem Sleep 可以显著降低无线模块的功耗,同时仍提供快速唤醒响应。

总结

这三种睡眠模式为开发者提供了不同的功耗/性能权衡选项,可以根据具体应用需求灵活选择。对于有电池寿命要求的设备,深度睡眠模式是一个不错的选择;而对于需要保持网络连接的 IoT 设备,Modem Sleep 模式是最佳选择。

技术支持与产品讨论

感谢您选择我们的产品!我们致力于为您提供不同的支持,以确保您使用我们的产品时体验顺畅。我们提供多种沟通渠道,以满足不同的偏好和需求。

Loading Comments...