ESP32 低功耗优化实战:Power Management + Light Sleep 让实测电流降 67%

关键词:ESP32、低功耗、Light Sleep、Power Management、FreeRTOS、ESP-IDF
Github 源码:https://github.com/WuQinghui-00/ESP32-Light-Sensor-Monitor

一、背景

我在做一款基于 ESP32 的智能光照监测系统:FreeRTOS 多任务架构(Sensor / Control / Display / Monitor),光敏传感器每 3 秒采集一次,Wi-Fi 连接 + MQTT 每 3 秒上报,LCD 实时显示,暗环境自动开灯。

系统跑通后,下一步就是低功耗。作为可能电池供电的物联网节点,两次采样之间 CPU 完全空闲,这部分的电不能白白浪费。本文记录我基于 ESP-IDF Power Management(DFS + Light Sleep)做的真实优化:实测空闲电流从 21mA 降到 7mA,约降 67%

说明:网上很多文章写的"80mA → 2mA、续航提升 40 倍"是估算值,本文所有数据均为万用表实测。

二、方案选型

ESP32 的几种低功耗状态:

模式 典型电流 特点
Modem Sleep ~20mA+ 保留 Wi-Fi 连接
Light Sleep ~1mA 级(芯片) CPU 暂停、外设保留,可定时器/GPIO 唤醒,唤醒后继续执行
Deep Sleep ~10µA 深度睡眠,唤醒后程序从头执行

我的系统是周期性采集(3 秒一次),唤醒后要继续原来的任务流,而不是重新跑一遍启动流程,所以选 Light Sleep

实现上我没有手动调用 esp_light_sleep_start(),而是用 ESP-IDF 的 Power Management:配置好之后,FreeRTOS 的 Tickless Idle 会在系统空闲时自动进入 Light Sleep,任务代码完全无感知,这是更工程化的做法。

三、代码实现

3.1 开启电源管理(sdkconfig.defaults)

sdkconfig.defaults 中启用以下配置:

CONFIG_PM_ENABLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y

注意:修改 defaults 后必须删除旧的 sdkconfig 再重新编译,否则配置不会生效。

3.2 应用层配置(light_sensor_main.c)

esp_pm_config_esp32_t pm_config = {
    .light_sleep_enable = true,
    .max_freq_mhz = 80,
    .min_freq_mhz = 40,
};
esp_err_t pm_err = esp_pm_configure(&pm_config);
if (pm_err == ESP_OK) {
    ESP_LOGI(TAG, "Power management configured");
}

启动日志确认:

I (652) pm: Frequency switching config: CPU_MAX: 80, APB_MAX: 80, APB_MIN: 40, Light sleep: ENABLED
I (652) MAIN: Power management configured

3.3 关键修复:控制任务轮询 20ms → 200ms

配置好之后实测电流纹丝不动,排查发现是控制任务的问题:

// 修改前:每 20ms 轮询一次队列
vTaskDelay(pdMS_TO_TICKS(20));

// 修改后:每 200ms 轮询一次
vTaskDelay(pdMS_TO_TICKS(200));

原因:FreeRTOS Tickless Idle 要求系统空闲窗口大于 CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP(默认 3 ticks = 30ms)才会进入 Light Sleep。控制任务 20ms 就醒一次,系统永远凑不出 30ms 的空闲窗口,Light Sleep 一次都进不去。改成 200ms 后,LED 控制和命令响应依然足够快,但系统有了充足的睡眠窗口。

四、效果验证

4.1 测试方法

  • 外部 5V 供电(VIN 引脚),USB 断开
  • DMM6500 万用表串联在电源与 VIN 之间(红表笔 AMPS 孔、黑表笔 COM 孔),直流电流档
  • 为排除外设影响,数据统一在拔掉 LCD 的条件下采集
  • 四组对照:PM 开/关 × WiFi 开/关

4.2 实测数据

PM WiFi 谷底电流 峰值电流 说明
24mA 101mA WiFi 发射瞬间
21mA ~23mA 无 PM 基线
8mA 101mA WiFi 保活拉高功耗
7mA ~23mA Light Sleep 生效

4.3 结论

  1. Light Sleep + DFS 使谷底电流降低约 67%:WiFi 关 21mA → 7mA,WiFi 开 24mA → 8mA。
  2. 101mA 峰值是 WiFi 发射瞬间的射频代价,PM 无法消除,属于射频本身的功耗。
  3. WiFi 连接时系统被保活机制占用,Light Sleep 收益有限;WiFi 关场景收益最大。

五、踩坑记录

坑 1:ESP_ERR_NOT_SUPPORTED,Light Sleep 配置失败

启动日志一直报 Power management configuration failed: ESP_ERR_NOT_SUPPORTED。查了 IDF 源码 esp_pm/pm_impl.c,发现只要 CONFIG_FREERTOS_USE_TICKLESS_IDLE 没开,请求 Light Sleep 就必然返回该错误。补齐配置并删除旧 sdkconfig 重建后解决。

坑 2:配置成功但电流不降

Power management configured 已经打印,但 PM 开/关实测都是 21mA。通过任务时序分析定位到控制任务 20ms 轮询堵死了 Light Sleep(见 3.3),改 200ms 后立刻见效。

坑 3:"关热点"不等于"WiFi 关"

测 WiFi 关时只把手机热点关了,结果电流反而升到 111mA。原因是固件立即重连(无退避),射频疯狂扫描/重试。正确做法是用测试固件变体注释 wifi_init(),真正关闭射频再测。

坑 4:LCD 背光掩盖芯片功耗

接 LCD 33mA、拔 LCD 21mA——背光约占 12mA。评估芯片功耗必须先排除外设,否则 PM 开关的差异会被外设电流淹没。

坑 5:万用表接法

测电流红表笔必须插 AMPS 孔并与电源、板子串联;插 INPUT HI 是测电压的并联接法,测不出电流。

坑 6:外部电源冷启动 WiFi 连不上

外部 5V 上电瞬间 3.3V 还没稳,WiFi 发射大电流把电压拉低,首次连接失败;按 EN 复位(电源已稳定)后正常。测试统一"上电等 1~2 秒按 EN";产品化方向是加大 VIN 电容 + 重连退避。

六、总结

通过这次优化,我掌握了:

  • ESP-IDF Power Management(DFS + Light Sleep)的正确打开方式:配置开关 + esp_pm_configure + Tickless Idle 自动睡眠
  • 读框架源码定位问题的能力:ESP_ERR_NOT_SUPPORTED 直接翻 pm_impl.c 找到根因
  • 任务时序对低功耗的影响:轮询频率必须给系统留出足够的空闲窗口
  • 低功耗测量的方法学:串联电流表、四组对照、排除外设、区分整板功耗与芯片功耗

最终效果:实测空闲电流 21mA → 7mA,约降 67%。下一步计划:WiFi 重连退避、VIN 电容优化、Deep Sleep 电池供电方案。

七、项目链接

GitHub:https://github.com/WuQinghui-00/ESP32-Light-Sensor-Monitor

低功耗实测报告(含完整数据):https://github.com/WuQinghui-00/ESP32-Light-Sensor-Monitor/blob/fae-improvement/docs/low-power-test-report.md

八、参考资源

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐