Chapter 09

低功耗与电池优化

用深度睡眠将 ESP32 平均电流降至微安级,实现电池供电数月乃至数年

ESP32 五种电源模式

IoT 设备往往依赖电池供电,低功耗设计直接决定设备寿命。ESP32 提供五种由高功耗到超低功耗的工作模式,理解每种模式的特性是优化电池寿命的基础。

电源模式典型电流Wi-FiCPU唤醒延迟
Active(活跃)240mA(峰值)/ 80mA(平均)发送/接收满速运行N/A
Modem-sleep3~20mA定时休眠(DTIM 间隔)正常运行毫秒级
Light-sleep0.8mA暂停,可保持连接暂停约 3ms
Deep-sleep10~150μA关闭关闭(仅 RTC 运行)约 300ms(含启动)
Hibernation2.5μA关闭关闭,RTC 外设也关约 300ms + 完整启动
电流数字背后的含义

一块常见的 18650 锂电池容量约 2500mAh。在纯 Active 模式下(80mA),只能用约 31 小时。而用深度睡眠(每 10 分钟唤醒一次,每次活跃 3 秒),平均电流约 30μA,理论寿命超过 9 年(当然实际还受自放电影响)。

深度睡眠(Deep Sleep)详解

深度睡眠是 IoT 设备最重要的省电手段。进入深度睡眠后,主 CPU、大部分 RAM 和外设全部断电,只有 RTC 时钟域(RTC 定时器、RTC 内存、RTC GPIO)保持供电。

深度睡眠时序: ESP32 活跃 唤醒 ───────┐ ┌───────────────────── │ │ 完整启动流程: │ 深度睡眠 │ bootloader (300ms) │ 仅 RTC 运行 │ → app_main() ───────┘────────────────────────┘ │←────── sleep time ───────────→│←── active time ──→│ 能耗占比(每 10 分钟唤醒一次,活跃 3s): 睡眠 597s × 10μA = 5970 μAh 活跃 3s × 80mA = 66.7 μAh(= 240000 μAh × 3/3600) 总计 ≈ 73 μAh/次 → 平均电流 ≈ 30 μA

唤醒源类型

RTC 定时器
最常用的唤醒方式,设置睡眠时长后到时自动唤醒。精度约 ±1%(RTC 时钟为低精度 RC 振荡器,150kHz)。如需精准时间戳,唤醒后需同步 NTP。
外部 GPIO(ext0/ext1)
ext0:单个 RTC GPIO 电平触发(高或低);ext1:多个 RTC GPIO 的逻辑 AND 或 OR 触发。可用于按键唤醒、PIR 运动传感器触发等。
触摸唤醒
触摸传感器的电容变化作为唤醒源,适合无物理按键的设计。RTC 子系统持续监测触摸值,无需 CPU 参与。
ULP 协处理器
超低功耗协处理器(Ultra Low Power Coprocessor),可在深度睡眠中独立运行简单程序,读取 ADC/I2C,满足条件时唤醒主 CPU,功耗仅 100μA 级别。
RTC SRAM(8KB)
深度睡眠期间保持数据的唯一 RAM(前 4KB 由 ULP 使用,后 4KB 可由主 CPU 存储跨睡眠数据)。通过 RTC_DATA_ATTR 宏声明的变量存放在此。

深度睡眠完整示例

#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-S2/S3 的 ULP-RISC-V 核

ESP32 原版 ULP 只能用汇编编写,学习曲线陡峭。ESP32-S2 和 S3 引入了 ULP-RISC-V 核,支持用 C 语言编写 ULP 程序,大大降低了开发门槛。新项目建议优先考虑 ESP32-S3。

电流实测与分析

电流实测工具推荐: Nordic PPK2(Power Profiler Kit 2) └─ 专业 IoT 电流分析仪,0.2μA 分辨率 └─ 可实时显示电流波形,与代码执行对应 └─ 约 ¥400,强烈推荐 平替方案: INA219 电流采样芯片(精度 0.1mA) └─ 连接到 Arduino/ESP32 采集 └─ 成本约 ¥5,适合粗略测量 典型优化目标(每 10 分钟上报一次): ┌─────────────────────────────────────────┐ │ 未优化(Always Active): ~80mA │ │ Modem Sleep(TCP 保活): ~15mA │ │ Deep Sleep + 快速上报 : ~500μA │ │ Deep Sleep + Wi-Fi 关 : ~30μA │ │ Deep Sleep + BLE Adv : ~20μA │ │ Hibernation(仅 RTC) : ~5μA │ └─────────────────────────────────────────┘
深度睡眠功耗异常的常见原因

1. GPIO 有外接上拉/下拉电阻:睡眠时 GPIO 浮空,电阻持续漏电。解决:睡眠前将相关 GPIO 配置为 RTC GPIO 并设置保持(hold)状态。
2. 外部传感器/模块仍在供电:用 MOSFET 或 Load Switch 在睡眠前切断外设供电。
3. I2C 总线上拉电阻:ESP32 I2C 引脚低电平时,上拉电阻到 3.3V 持续产生漏电流。睡眠前释放 I2C 总线并切断电源。