ESP32 五种电源模式
IoT 设备往往依赖电池供电,低功耗设计直接决定设备寿命。ESP32 提供五种由高功耗到超低功耗的工作模式,理解每种模式的特性是优化电池寿命的基础。
| 电源模式 | 典型电流 | Wi-Fi | CPU | 唤醒延迟 |
|---|---|---|---|---|
| Active(活跃) | 240mA(峰值)/ 80mA(平均) | 发送/接收 | 满速运行 | N/A |
| Modem-sleep | 3~20mA | 定时休眠(DTIM 间隔) | 正常运行 | 毫秒级 |
| Light-sleep | 0.8mA | 暂停,可保持连接 | 暂停 | 约 3ms |
| Deep-sleep | 10~150μA | 关闭 | 关闭(仅 RTC 运行) | 约 300ms(含启动) |
| Hibernation | 2.5μA | 关闭 | 关闭,RTC 外设也关 | 约 300ms + 完整启动 |
一块常见的 18650 锂电池容量约 2500mAh。在纯 Active 模式下(80mA),只能用约 31 小时。而用深度睡眠(每 10 分钟唤醒一次,每次活跃 3 秒),平均电流约 30μA,理论寿命超过 9 年(当然实际还受自放电影响)。
深度睡眠(Deep Sleep)详解
深度睡眠是 IoT 设备最重要的省电手段。进入深度睡眠后,主 CPU、大部分 RAM 和外设全部断电,只有 RTC 时钟域(RTC 定时器、RTC 内存、RTC GPIO)保持供电。
唤醒源类型
深度睡眠完整示例
#include "esp_sleep.h"
#include "esp_log.h"
#include "driver/rtc_io.h"
static const char *TAG = "SLEEP";
/* RTC_DATA_ATTR:变量存储在 RTC SRAM,深度睡眠后保留 */
static RTC_DATA_ATTR int boot_count = 0;
static RTC_DATA_ATTR float last_temperature = 0.0f;
#define SLEEP_DURATION_S 600 // 10 分钟
#define WAKEUP_PIN GPIO_NUM_33
void app_main(void)
{
boot_count++;
ESP_LOGI(TAG, "第 %d 次启动", boot_count);
/* 判断唤醒原因 */
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
switch (cause) {
case ESP_SLEEP_WAKEUP_TIMER:
ESP_LOGI(TAG, "定时器唤醒,上次温度: %.1f°C", last_temperature);
break;
case ESP_SLEEP_WAKEUP_EXT0:
ESP_LOGI(TAG, "GPIO 唤醒(按键)");
break;
default:
ESP_LOGI(TAG, "上电启动");
break;
}
/* ── 执行业务逻辑(读传感器、上报数据)── */
last_temperature = 25.6f; // 模拟读取传感器
ESP_LOGI(TAG, "数据已处理,准备进入深度睡眠");
/* ── 配置唤醒源 ── */
/* 1. 定时器唤醒 */
esp_sleep_enable_timer_wakeup((uint64_t)SLEEP_DURATION_S * 1000000ULL);
/* 2. GPIO 唤醒(GPIO33 低电平触发,需使用 RTC GPIO)*/
rtc_gpio_pullup_en(WAKEUP_PIN); // 上拉,按键接地
esp_sleep_enable_ext0_wakeup(WAKEUP_PIN, 0); // 0 = 低电平唤醒
/* 进入深度睡眠 */
ESP_LOGI(TAG, "进入深度睡眠 %d 秒...", SLEEP_DURATION_S);
esp_deep_sleep_start(); // 不返回!
}
Light Sleep(浅睡眠)
Light Sleep 比 Deep Sleep 功耗稍高(约 0.8mA),但唤醒后无需完整启动流程(仅约 3ms),程序从调用 esp_light_sleep_start() 的下一行继续执行,RAM 和状态完全保留。适合需要频繁唤醒的场景(如每秒唤醒一次)。
void light_sleep_demo(void)
{
while (1) {
ESP_LOGI(TAG, "开始 5 秒浅睡眠");
esp_sleep_enable_timer_wakeup(5000000); // 5秒
esp_light_sleep_start(); // ← 此处暂停 5 秒
/* 唤醒后从这里继续,变量状态保留 */
ESP_LOGI(TAG, "唤醒!继续执行...");
/* 处理数据... */
}
}
ULP(超低功耗)协处理器
ULP(Ultra Low Power Coprocessor)是 ESP32 内置的独立处理器,在深度睡眠中以极低功耗(100μA 以下)独立运行,可以读取 ADC、I2C 传感器,在满足条件时唤醒主 CPU。
#include "esp32/ulp.h"
#include "driver/rtc_io.h"
#include "ulp_main.h" // 由 ULP 汇编程序生成
/* ULP 读取 ADC 并在超阈值时唤醒主 CPU */
/* ULP 程序用汇编或 C(ESP32-S2/S3 支持 C)编写 */
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
void start_ulp_program(void)
{
/* 加载 ULP 程序到 RTC 内存 */
ulp_load_binary(0, ulp_main_bin_start,
(ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
/* 设置 ULP 唤醒间隔(每 100ms 运行一次) */
ulp_set_wakeup_period(0, 100000); // 100000 μs = 100ms
/* 启动 ULP */
ulp_run(&ulp_main - RTC_SLOW_MEM);
/* 允许 ULP 唤醒主 CPU */
esp_sleep_enable_ulp_wakeup();
esp_deep_sleep_start();
}
ESP32 原版 ULP 只能用汇编编写,学习曲线陡峭。ESP32-S2 和 S3 引入了 ULP-RISC-V 核,支持用 C 语言编写 ULP 程序,大大降低了开发门槛。新项目建议优先考虑 ESP32-S3。
电流实测与分析
1. GPIO 有外接上拉/下拉电阻:睡眠时 GPIO 浮空,电阻持续漏电。解决:睡眠前将相关 GPIO 配置为 RTC GPIO 并设置保持(hold)状态。
2. 外部传感器/模块仍在供电:用 MOSFET 或 Load Switch 在睡眠前切断外设供电。
3. I2C 总线上拉电阻:ESP32 I2C 引脚低电平时,上拉电阻到 3.3V 持续产生漏电流。睡眠前释放 I2C 总线并切断电源。
GPIO Hold 与外设断电
深度睡眠期间,普通 GPIO 状态不确定,需要在睡眠前"锁定"(Hold)引脚状态:
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "esp_sleep.h"
#define SENSOR_PWR_PIN GPIO_NUM_26 /* 传感器供电 MOSFET 控制引脚 */
#define I2C_SDA_PIN GPIO_NUM_21
#define I2C_SCL_PIN GPIO_NUM_22
void prepare_for_deep_sleep(void)
{
/* 1. 关闭外设供电(MOSFET 控制:低电平关断) */
gpio_set_level(SENSOR_PWR_PIN, 0);
/* 2. 释放 I2C 总线(先设为输入,避免驱动总线产生漏电) */
gpio_set_direction(I2C_SDA_PIN, GPIO_MODE_INPUT);
gpio_set_direction(I2C_SCL_PIN, GPIO_MODE_INPUT);
gpio_set_pull_mode(I2C_SDA_PIN, GPIO_FLOATING); /* 关闭内部上拉 */
gpio_set_pull_mode(I2C_SCL_PIN, GPIO_FLOATING);
/* 3. 对 RTC GPIO 设置 Hold:睡眠期间保持当前电平 */
/* 注意:只有 RTC GPIO(GPIO0,2,4,12-15,25-27,32-39)支持 hold */
rtc_gpio_hold_en(SENSOR_PWR_PIN); /* 锁定低电平,传感器持续断电 */
/* 4. 整个芯片级别的 Hold(所有 RTC GPIO 都保持)*/
/* esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); */
esp_deep_sleep_start();
}
void release_hold_after_wakeup(void)
{
/* 唤醒后必须释放 hold,否则引脚无法再驱动 */
rtc_gpio_hold_dis(SENSOR_PWR_PIN);
gpio_set_level(SENSOR_PWR_PIN, 1); /* 重新开启传感器电源 */
vTaskDelay(pdMS_TO_TICKS(50)); /* 等待传感器上电稳定 */
}
Modem Sleep 与 Light Sleep 的应用场景
不是所有场景都适合深度睡眠。当设备需要保持 Wi-Fi 连接(如做 WebSocket 服务器、接收实时推送),可以使用功耗更低的 Modem Sleep:
#include "esp_wifi.h"
#include "esp_pm.h"
void enable_modem_sleep(void)
{
/* Modem Sleep:Wi-Fi 在 DTIM beacon 间隔内休眠,其余时间活跃
* 路由器通常 DTIM = 1(100ms),设为 3 则每 300ms 唤醒一次
* 电流从 80mA 降至 15-20mA,响应延迟增加约 DTIM × 100ms */
esp_wifi_set_ps(WIFI_PS_MIN_MODEM); /* 最小省电 */
/* WIFI_PS_MAX_MODEM:最大省电,延迟更高但更省电 */
}
/* 自动 Light Sleep(ESP-IDF v5.0+):CPU 空闲时自动进入浅睡眠 */
void enable_auto_light_sleep(void)
{
/* 配置动态频率调节(DFS):CPU 空闲时降频 */
esp_pm_config_t pm_config = {
.max_freq_mhz = 240, /* 活跃时最高 240MHz */
.min_freq_mhz = 40, /* 空闲时降至 40MHz */
.light_sleep_enable = true, /* CPU 真正空闲时进入 Light Sleep */
};
esp_pm_configure(&pm_config);
/* 同时配置 Wi-Fi Modem Sleep,使 RF 也可以休眠 */
esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
ESP_LOGI("PM", "自动 Light Sleep 已启用");
}
/* 效果:Wi-Fi 保持连接,整机平均电流约 1~5mA(取决于流量) */
太阳能与超级电容供电设计
在户外 IoT 场景(农业传感器、气象站),太阳能 + 超级电容是无线供电的理想方案:
ESP32 提供五种电源模式,从 Active(240mA)到 Hibernation(2.5μA)。深度睡眠是电池设备的核心省电手段,唤醒源有定时器、外部 GPIO(ext0/ext1)、触摸和 ULP 协处理器。RTC_DATA_ATTR 声明的变量在深度睡眠间保持数据。睡眠前必须用 rtc_gpio_hold_en 锁定 GPIO 状态、断开外设供电,否则功耗异常。Modem Sleep 适合需保持 Wi-Fi 连接的场景;自动 Light Sleep(esp_pm_configure)适合低流量但需快速响应的设备。一块 2500mAh 电池在深度睡眠模式下理论续航可超过 9 年。