Skip to main content

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 内存
warning

**深度休眠期间 USB 外设被禁用:**包括内部 USB 外设(USB-Serial-JTAG)在内的所有数字外设在深度休眠期间都会断电。通过 USB 的串口输出在设备处于深度休眠时将不可用。如果你需要调试,请使用外部 USB-UART 芯片连接到硬件 UART 引脚。

唤醒方式

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

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

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

  • **ULP 协处理器活动唤醒:**ULP 协处理器可以独立运行,监测特定条件并唤醒主 CPU,从而节省电能。

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

下面给出了 XIAO ESP32 S3 Sense 使用 DeepSleep 模式的三个简单示例。

代码实现


#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 因在配置为 RTC(实时时钟)I/O 的某个 GPIO 引脚上检测到外部信号而被唤醒。这通常用于在按钮或传感器被触发时从休眠中唤醒。
  • 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

在进入深度睡眠模式后要重新烧录程序,请按住 boot 按钮,然后按下 reset 按钮以重启 ESP32。

结果展示

轻睡眠(Light-Sleep)

介绍

轻睡眠模式是 ESP32 中的另一种低功耗模式,它允许设备在保持快速响应时间的同时节省能耗。在该模式下,CPU 内核会停止运行,但 RAM 和部分外设仍保持上电状态,从而使设备能够在响应某些事件时快速唤醒。

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

warning

轻睡眠期间 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): 
  • 定义一个 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));Keep the LED on for 1 second
  • vTaskDelete(NULL);Delete the current task
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);Set timer for wakeup
  • esp_light_sleep_start(); Enter light sleep mode
  • xTaskCreate(ledTask, "LED Task", 2048, NULL, 1, NULL);Create LED control task

结果展示

Modem-Sleep

介绍

Modem Sleep 模式是 ESP32 中另一种重要的低功耗模式,它不同于 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);
  • 禁用 modem sleep 模式以激活 WiFi。
if (WiFi.status() == WL_CONNECTED) {
  • 检查 WiFi 状态。
WiFi.setSleep(true);
  • 再次启用 modem sleep 模式。

结果展示

睡眠功能应用

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

软件准备

在开始本节内容之前,如果你尚未在 XIAO ESP32S3 Sense 上使用全部硬件功能,请确保已经完成一些软件安装准备工作。

下面是三个功能的介绍,你可以通过以下链接获取更多信息:

  • 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 秒的深度睡眠状态以节省能耗。

tip

这些代码不能直接使用,你需要添加关于摄像头的头文件,请参考上面关于 XIAO ESP32 S3 的示例。

总结

为什么使用 Deep Sleep 模式

在不影响功能的前提下最大化节能,从而延长设备的电池寿命。 适用场景:对电池寿命要求较高的应用,例如远程传感器节点、可穿戴设备以及其他低功耗物联网设备。尽管唤醒时间相对较慢,但这种权衡是值得的。

为什么使用 Modem Sleep 模式

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

小结

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

技术支持与产品讨论

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

Loading Comments...