4. ESP32功耗模式详解

ESP32 的省电能力并不是靠“关掉 CPU”这一招实现的,而是由一组层层递进的电源与时钟管理机制组成。理解 Active、Modem-sleep、Light-sleep、Deep-sleep、Hibernation 这五种模式的关键,是搞清楚三件事:哪些电源域还活着、哪些时钟还在跑、唤醒后程序从哪里继续。这三者决定了功耗数量级,也决定了它适合什么场景。

核心结论先给出:Active 是毫安级,Modem-sleep 与 Light-sleep 是亚毫安到百微安级,Deep-sleep 是十微安级,Hibernation 是微安级。每下降一个数量级,代价都是“可用的外设更少、唤醒后恢复的上下文更多”。
ESP32 功耗模式:电源域 / 时钟 / 唤醒路径 功耗高 功耗低 Active CPU 全速运行 RF / Wi-Fi 可收发 所有时钟开启 ≈ 80–240 mA 唤醒:无需唤醒 Modem-sleep CPU 仍运行 Wi-Fi/BT 按 DTIM 休眠 自动周期性唤醒射频 ≈ 3–20 mA 唤醒:射频自动 Light-sleep CPU 暂停,RTC 运行 RAM 保持供电 GPIO / 触摸可唤醒 ≈ 0.8–2 mA 唤醒:毫秒级恢复 Deep-sleep CPU / 主时钟关闭 仅 RTC 域供电 RTC RAM 可保留 ≈ 10–150 µA 唤醒:复位重启 Hibernation RTC 核心也断电 仅 RTC 定时器/IO RAM 内容丢失 ≈ 2–5 µA 唤醒:复位重启 唤醒源与恢复代价对照 Light-sleep 唤醒源 GPIO 电平 / 触摸 UART / I2C 活动 定时器 程序从暂停处继续 Deep-sleep 唤醒源 RTC 定时器 EXT0 / EXT1 外部 IO 触摸 / ULP 协处理器 从 setup() 重新执行 Hibernation 唤醒源 RTC 定时器 指定 RTC IO 无 RAM 保留 完全冷启动 选型口诀 要联网待机 → Modem 要快速响应 → Light 要长周期采集 → Deep 要极限待机 → Hibernation

一、五种模式的工作原理

Active(运行模式)是芯片的默认状态。此时 CPU、Wi-Fi/蓝牙射频、外设时钟全部开启,程序全速执行。功耗取决于 CPU 主频、是否开启射频、外设负载,典型值在 80 mA 到 240 mA 之间。任何“省电”讨论的起点,都是先让系统尽快离开 Active。

Modem-sleep(调制解调器睡眠)针对的是“必须保持 Wi-Fi 连接”的场景。CPU 仍然运行,但 Wi-Fi/蓝牙基带会在每个 DTIM 周期之间自动关闭射频,只在信标到达前醒来接收。它不需要用户显式调用 API,只要连接了 AP 并开启省电模式即可生效。功耗通常降到 3–20 mA,是智能插座、联网传感器这类“常在线但低吞吐”设备的基础档位。

Light-sleep(浅睡眠)会暂停 CPU 与数字外设时钟,但保留 RTC 域和 RAM 供电。它的最大价值是上下文不丢失:唤醒后程序从暂停处继续执行,无需重新初始化。GPIO、触摸、UART 等都可以作为唤醒源,唤醒延迟在毫秒级。代价是功耗仍在 0.8–2 mA 量级,因为 RAM 需要持续刷新。

Deep-sleep(深睡眠)关闭 CPU、主时钟和大部分电源域,只保留 RTC 控制器、RTC 内存和少量唤醒逻辑。唤醒后芯片执行复位流程,程序从 setup() 重新开始。它支持 RTC 定时器、EXT0/EXT1 外部 IO、触摸和 ULP 协处理器唤醒,功耗可降到 10–150 µA。这是电池设备最常用的主力模式。

Hibernation(休眠)是 ESP32 系列中功耗最低的状态,连 RTC 核心与 RTC 内存都断电,仅保留极少数 RTC IO 和定时器逻辑。功耗可低至 2–5 µA,但 RAM 内容全部丢失,唤醒等同于完全冷启动。它适合“几个月才上报一次”的极端低功耗节点。

二、模式切换与配置流程

实际项目中,模式不是孤立使用的,而是按任务节奏动态切换。典型流程是:上电初始化 → 采集/联网 → 进入低功耗模式 → 被定时器或外部事件唤醒 → 再次采集。下面是一段 Deep-sleep 定时唤醒的配置示例:

#include <esp_sleep.h>

#define WAKE_INTERVAL_US  (60ULL * 1000000ULL)  // 60 秒

void setup() {
  Serial.begin(115200);
  // 1. 配置 RTC 定时器作为唤醒源
  esp_sleep_enable_timer_wakeup(WAKE_INTERVAL_US);

  // 2. 可选:配置外部按键唤醒(EXT0)
  // esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, 0);

  // 3. 判断本次是冷启动还是定时唤醒
  esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
  if (cause == ESP_SLEEP_WAKEUP_TIMER) {
    Serial.println("定时唤醒,执行周期采集");
  } else {
    Serial.println("上电冷启动,执行初始化");
  }

  // 4. 采集并上报数据
  read_sensor_and_upload();

  // 5. 进入 Deep-sleep
  Serial.flush();
  esp_deep_sleep_start();
}

void loop() {
  // Deep-sleep 模式下不会执行到这里
}

如果使用 Light-sleep,则改为调用 esp_light_sleep_start(),它会在函数返回后继续执行后续代码,因此需要把它放在循环中,并配合 esp_sleep_enable_timer_wakeup() 或 GPIO 唤醒源使用。

三、五种模式对比清单

模式 CPU / 时钟 RAM 保持 典型功耗 唤醒后行为 适用场景
Active 全开 是 80–240 mA 持续运行 计算、通信、控制
Modem-sleep CPU 运行 是 3–20 mA 射频自动唤醒 保持 Wi-Fi 连接
Light-sleep CPU 暂停 是 0.8–2 mA 从暂停处继续 快速响应外部事件
Deep-sleep 关闭 RTC RAM 可选 10–150 µA 复位重启 周期采集、电池节点
Hibernation 全部关闭 否 2–5 µA 完全冷启动 超长待机、极低频上报

四、案例:温湿度节点的模式选择

假设一个电池供电的温湿度节点,要求每 5 分钟采集一次并通过 Wi-Fi 上报,使用 2000 mAh 电池。若全程 Active,平均电流约 100 mA,续航不到一天;若采用 Deep-sleep 定时唤醒,每次唤醒工作约 2 秒、平均 80 mA,其余时间 20 µA,则平均电流约为:

平均电流 ≈ (80 mA × 2 s + 0.02 mA × 298 s) / 300 s
         ≈ (160 + 5.96) / 300
         ≈ 0.55 mA
理论续航 ≈ 2000 mAh / 0.55 mA ≈ 3600 小时 ≈ 150 天

如果采集间隔拉长到 30 分钟,平均电流可进一步降到 0.1 mA 以下,续航进入一年以上。这说明占空比才是电池寿命的第一决定因素,模式选择只是把“睡眠期间的底电流”压到最低。

实践建议:先用 Deep-sleep 把平均电流压到 100 µA 以下,再考虑是否需要 Light-sleep 来缩短唤醒响应时间。不要一上来就追求 Hibernation,因为它会丢失所有 RAM 上下文,很多联网协议栈需要重新握手,反而增加单次唤醒能耗。

五、常见误区与复盘

  • 误区一:以为 Light-sleep 和 Deep-sleep 只是功耗不同。两者唤醒后的程序状态完全不同,Light-sleep 继续执行,Deep-sleep 从 setup() 重启,代码结构必须相应调整。
  • 误区二:忽略外设漏电。开发板上 USB 转串口芯片、LDO 静态电流、上拉电阻都可能带来几百微安漏电,把 Deep-sleep 的 10 µA 优势完全吃掉。测功耗时要断开调试器,单独给核心板供电。
  • 误区三:忘记关闭未用外设。进入睡眠前应关闭 ADC、LED、传感器电源,把未用 GPIO 设为合适电平,避免悬空输入造成额外功耗。
  • 误区四:唤醒源配置冲突。EXT0 与 EXT1 不能同时使用,RTC IO 与普通 IO 的编号也要区分清楚,否则会出现无法唤醒或误唤醒。

复盘时建议用电流表或功耗分析仪记录完整的一次“唤醒—工作—睡眠”波形,确认工作电流、睡眠底电流和占空比三个数字,再反推电池寿命。只有把模式原理、唤醒路径和实际漏电一起考虑,ESP32 的低功耗设计才真正可控。