低功耗模式¶
ESP32 提供多种睡眠模式,从轻度省电到深度休眠,可大幅降低功耗。掌握低功耗技术是电池供电 IoT 项目的核心。
一、功耗模式总览¶
| 模式 | 功耗 | 保留内容 | 唤醒速度 | 典型应用 |
|---|---|---|---|---|
| Active | 80~260 mA | 全部 | — | 正常工作 |
| Modem Sleep | 20~30 mA | CPU + RAM | 即时 | WiFi 间歇通信 |
| Light Sleep | 0.8~2 mA | CPU + RAM(暂停) | ~1 ms | 快速响应唤醒 |
| Deep Sleep | 10~150 μA | RTC 控制器 + RTC 内存 | ~ms 级 | 定时上报数据 |
| Hibernation | ~5 μA | RTC 定时器 | 较慢 | 极低功耗待机 |
graph LR
A[Active<br>80~260mA] -->|关闭射频| B[Modem Sleep<br>20~30mA]
B -->|CPU 暂停| C[Light Sleep<br>0.8mA]
C -->|关闭主CPU/RAM| D[Deep Sleep<br>10~150μA]
D -->|仅 RTC 定时器| E[Hibernation<br>5μA]
style A fill:#ff6b6b
style B fill:#ffa94d
style C fill:#ffd43b
style D fill:#69db7c
style E fill:#4dabf7
Deep Sleep 的本质
Deep Sleep 唤醒后,ESP32 会==重新从 setup() 执行==,相当于重启。普通变量会丢失。
如果需要跨睡眠保留数据,必须存储在 RTC 内存 中。
二、Deep Sleep(深度睡眠)¶
Deep Sleep 是最常用的低功耗模式,功耗可降至 10 μA 级别。
定时器唤醒¶
#define uS_TO_S_FACTOR 1000000ULL // 微秒转秒
#define TIME_TO_SLEEP 30 // 睡眠 30 秒
// RTC 内存中的变量(Deep Sleep 后保留)
RTC_DATA_ATTR int bootCount = 0;
void setup() {
Serial.begin(115200);
delay(100); // 等待串口稳定
++bootCount;
Serial.printf("第 %d 次启动\n", bootCount);
// 做一些工作:读取传感器、上传数据...
doWork();
// 设置唤醒源:定时器
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.printf("设置 %d 秒后唤醒\n", TIME_TO_SLEEP);
Serial.println("进入 Deep Sleep...");
Serial.flush(); // 等待串口发送完毕
esp_deep_sleep_start(); // 进入深度睡眠
// ⚠ 此行以下代码永远不会执行
}
void loop() {
// Deep Sleep 模式下 loop 不会执行
}
void doWork() {
// 模拟传感器读取和数据上传
float temp = 25.0 + random(-50, 50) / 10.0;
Serial.printf("温度: %.1f°C\n", temp);
delay(2000); // 模拟上传
}
GPIO 唤醒(ext0 — 单引脚)¶
#define WAKEUP_PIN GPIO_NUM_33 // 使用 RTC GPIO
RTC_DATA_ATTR int bootCount = 0;
void setup() {
Serial.begin(115200);
++bootCount;
// 检查唤醒原因
esp_sleep_wakeup_cause_t reason = esp_sleep_get_wakeup_cause();
switch (reason) {
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("定时器唤醒");
break;
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("GPIO 唤醒 (ext0)");
break;
case ESP_SLEEP_WAKEUP_EXT1:
Serial.println("GPIO 唤醒 (ext1)");
break;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
Serial.println("触摸唤醒");
break;
default:
Serial.printf("正常启动 / 其他原因: %d\n", reason);
break;
}
// ext0: 单个 RTC GPIO 唤醒,指定高/低电平触发
esp_sleep_enable_ext0_wakeup(WAKEUP_PIN, 0); // 0 = 低电平唤醒
Serial.println("进入 Deep Sleep,等待按键唤醒...");
Serial.flush();
esp_deep_sleep_start();
}
void loop() {}
多 GPIO 唤醒(ext1 — 多引脚)¶
// ext1: 多个 RTC GPIO 组合唤醒
#define BUTTON_1 GPIO_NUM_32
#define BUTTON_2 GPIO_NUM_33
#define BUTTON_3 GPIO_NUM_27
void setup() {
Serial.begin(115200);
// 使用位掩码指定多个引脚
uint64_t bitmask = (1ULL << BUTTON_1) | (1ULL << BUTTON_2) | (1ULL << BUTTON_3);
// ESP_EXT1_WAKEUP_ANY_HIGH: 任一引脚为高电平唤醒
// ESP_EXT1_WAKEUP_ALL_LOW: 所有引脚都为低电平才唤醒
esp_sleep_enable_ext1_wakeup(bitmask, ESP_EXT1_WAKEUP_ANY_HIGH);
// 检查哪个引脚触发唤醒
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1) {
uint64_t status = esp_sleep_get_ext1_wakeup_status();
int pin = __builtin_ffsll(status) - 1;
Serial.printf("触发引脚: GPIO %d\n", pin);
}
Serial.println("进入 Deep Sleep...");
Serial.flush();
esp_deep_sleep_start();
}
void loop() {}
触摸唤醒¶
RTC_DATA_ATTR int bootCount = 0;
void callback() {
// 触摸唤醒回调(可以为空)
}
void setup() {
Serial.begin(115200);
++bootCount;
Serial.printf("第 %d 次启动\n", bootCount);
// 查看唤醒原因
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TOUCHPAD) {
Serial.printf("触摸引脚 T%d 唤醒\n", esp_sleep_get_touchpad_wakeup_status());
}
// 设置触摸唤醒
touchAttachInterrupt(T3, callback, 40); // GPIO 15, 阈值 40
esp_sleep_enable_touchpad_wakeup();
Serial.println("进入 Deep Sleep,触摸唤醒...");
Serial.flush();
esp_deep_sleep_start();
}
void loop() {}
RTC GPIO 对照表
ext0/ext1 唤醒只能使用 RTC GPIO:
| RTC GPIO | 实际引脚 | RTC GPIO | 实际引脚 |
|---|---|---|---|
| RTC_GPIO0 | GPIO 36 | RTC_GPIO9 | GPIO 32 |
| RTC_GPIO3 | GPIO 39 | RTC_GPIO10 | GPIO 33 |
| RTC_GPIO4 | GPIO 34 | RTC_GPIO11 | GPIO 25 |
| RTC_GPIO5 | GPIO 35 | RTC_GPIO12 | GPIO 26 |
| RTC_GPIO6 | GPIO 25 | RTC_GPIO13 | GPIO 27 |
| RTC_GPIO7 | GPIO 26 | RTC_GPIO14 | GPIO 14 |
| RTC_GPIO8 | GPIO 33 | RTC_GPIO15 | GPIO 12 |
三、Light Sleep(浅度睡眠)¶
Light Sleep 保留 CPU 和 RAM 状态,唤醒后从==睡眠处继续执行==(不重启):
void setup() {
Serial.begin(115200);
// 配置唤醒源
esp_sleep_enable_timer_wakeup(5000000); // 5 秒后唤醒
Serial.println("进入 Light Sleep...");
Serial.flush();
// 进入 Light Sleep
esp_err_t ret = esp_light_sleep_start();
// ✅ 唤醒后从这里继续执行
if (ret == ESP_OK) {
Serial.println("Light Sleep 唤醒!");
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
Serial.printf("唤醒原因: %d\n", cause);
}
}
void loop() {
Serial.println("正常运行...");
delay(1000);
// 可以在 loop 中反复进入 Light Sleep
esp_sleep_enable_timer_wakeup(3000000);
Serial.println("再次进入 Light Sleep...");
Serial.flush();
esp_light_sleep_start();
Serial.println("再次唤醒!");
}
Light Sleep vs Deep Sleep
| 特性 | Light Sleep | Deep Sleep |
|---|---|---|
| 功耗 | ~0.8 mA | ~10 μA |
| RAM 保留 | ✅ 全部保留 | ❌ 仅 RTC 内存 |
| 唤醒行为 | 从睡眠处继续 | 重新进入 setup() |
| WiFi 连接 | 可保持(Modem Sleep) | 断开 |
| 适用场景 | 需要快速响应 | 长时间待机 |
四、Modem Sleep(调制解调器睡眠)¶
WiFi 连接状态下自动省电,无需手动配置:
#include <WiFi.h>
void setup() {
Serial.begin(115200);
WiFi.begin("SSID", "password");
while (WiFi.status() != WL_CONNECTED) delay(500);
// 设置 WiFi 省电模式
// WIFI_PS_NONE — 不省电(最低延迟)
// WIFI_PS_MIN_MODEM — 最小省电(DTIM 1)
// WIFI_PS_MAX_MODEM — 最大省电(DTIM 由 AP 决定)
WiFi.setSleep(WIFI_PS_MAX_MODEM);
Serial.println("WiFi 已连接,Modem Sleep 已启用");
Serial.printf("IP: %s\n", WiFi.localIP().toString().c_str());
}
void loop() {
// 正常工作,WiFi 空闲时自动进入 Modem Sleep
delay(1000);
}
五、实际应用:电池供电传感器¶
低功耗环境监测站¶
#include <WiFi.h>
#include <HTTPClient.h>
#define uS_TO_S_FACTOR 1000000ULL
#define SLEEP_MINUTES 5
#define BATTERY_PIN 35 // ADC 检测电池电压
// RTC 内存保留数据
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR float lastTemp = 0;
const char* ssid = "SSID";
const char* password = "password";
void setup() {
Serial.begin(115200);
++bootCount;
unsigned long startTime = millis();
Serial.printf("=== 第 %d 次唤醒 ===\n", bootCount);
// 1. 读取传感器(WiFi 未开启,ADC2 可用)
float temp = readTemperature();
float battery = readBattery();
Serial.printf("温度: %.1f°C, 电池: %.2fV\n", temp, battery);
// 2. 仅在数据变化较大时上传(节省功耗)
bool shouldUpload = abs(temp - lastTemp) > 0.5 || bootCount % 12 == 1;
if (shouldUpload) {
// 3. 连接 WiFi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
int timeout = 20; // 最多等 10 秒
while (WiFi.status() != WL_CONNECTED && timeout > 0) {
delay(500);
timeout--;
}
if (WiFi.status() == WL_CONNECTED) {
// 4. 上传数据
uploadData(temp, battery, bootCount);
lastTemp = temp;
}
// 5. 立即关闭 WiFi
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
unsigned long elapsed = millis() - startTime;
Serial.printf("工作耗时: %lu ms\n", elapsed);
// 6. 电池低电量加长睡眠
int sleepMin = SLEEP_MINUTES;
if (battery < 3.3) {
sleepMin = SLEEP_MINUTES * 4;
Serial.println("低电量,延长睡眠");
}
// 7. 进入 Deep Sleep
esp_sleep_enable_timer_wakeup(sleepMin * 60 * uS_TO_S_FACTOR);
Serial.printf("睡眠 %d 分钟...\n", sleepMin);
Serial.flush();
esp_deep_sleep_start();
}
void loop() {}
float readTemperature() {
// 示例:模拟传感器读取
return 25.0 + random(-30, 30) / 10.0;
}
float readBattery() {
// 电池通过分压电阻接 ADC
// 例:100K + 100K 分压,满量程对应 4.2V * 2 = 8.4V 范围
int raw = analogRead(BATTERY_PIN);
return (raw / 4095.0) * 3.3 * 2; // 分压系数 × 参考电压
}
void uploadData(float temp, float bat, int boot) {
HTTPClient http;
http.begin("http://api.example.com/sensor");
http.addHeader("Content-Type", "application/json");
char json[128];
snprintf(json, sizeof(json),
"{\"temp\":%.1f,\"bat\":%.2f,\"boot\":%d}", temp, bat, boot);
http.POST(json);
http.end();
}
graph TB
A[唤醒] --> B[读取传感器<br>~10ms]
B --> C{数据变化?}
C -->|是| D[连接 WiFi<br>2~5s]
D --> E[上传数据<br>0.5~2s]
E --> F[关闭 WiFi]
C -->|否| G[跳过上传]
F --> H[Deep Sleep<br>5~20 min]
G --> H
H -->|定时器唤醒| A
style H fill:#69db7c
style D fill:#ff6b6b
低功耗设计要点
- 尽快完成工作:WiFi 连接是最耗电的,尽量缩短连接时间
- 按需上传:数据无变化时跳过 WiFi,可节省 90% 以上功耗
- 先读传感器再开 WiFi:ADC2 和 WiFi 冲突
- 用 RTC_DATA_ATTR 保留状态:避免每次唤醒都初始化
- 主动关闭 WiFi:
WiFi.disconnect(true)+WiFi.mode(WIFI_OFF) - 去掉不必要的外设:LED、电压调节器的静态电流可能远大于 ESP32 睡眠电流
六、功耗优化技巧¶
关闭未使用的外设¶
#include "driver/adc.h"
void setup() {
// 关闭蓝牙(如果不用)
btStop();
// 或编译时禁用:menuconfig -> Component config -> Bluetooth
// 关闭 WiFi
WiFi.mode(WIFI_OFF);
// 关闭 ADC
adc_power_off();
// 降低 CPU 频率(默认 240MHz)
setCpuFrequencyMhz(80); // 80 / 160 / 240
Serial.printf("CPU 频率: %d MHz\n", getCpuFrequencyMhz());
}
降低 CPU 频率¶
| CPU 频率 | Active 电流 | 适用场景 |
|---|---|---|
| 240 MHz | ~68 mA | WiFi、复杂计算 |
| 160 MHz | ~45 mA | 一般处理 |
| 80 MHz | ~27 mA | 轻量任务 |
七、常见问题¶
Deep Sleep 功耗实测比官方数据大很多?
常见原因:
- USB-UART 芯片(CP2102/CH340)在供电:开发板自带的串口芯片会消耗 5~20 mA
- LDO 线性稳压器的静态电流:AMS1117 静态电流 ~5 mA
- LED 指示灯:板载 LED 可能消耗 1~5 mA
解决方案:生产用 PCB 去掉 USB 芯片和 LED,使用低静态电流 LDO(如 HT7333、ME6211)。
Deep Sleep 唤醒后 WiFi 连接很慢?
Deep Sleep 唤醒等于重启,WiFi 需要重新关联。加速方法:
- 使用
WiFi.persistent(true)保存连接参数到 Flash - 用 RTC 内存保存信道和 BSSID,通过
WiFi.begin(ssid, pass, channel, bssid)快速重连 - 典型效果:连接时间从 3~5 秒缩短到 0.5~1 秒
RTC 内存有多大?
ESP32 有 8 KB RTC 快速内存(RTC_FAST)和 8 KB RTC 慢速内存(RTC_SLOW)。
RTC_DATA_ATTR存在 RTC_FAST,Deep Sleep 保留- ULP 协处理器程序存在 RTC_SLOW
- 不要存太多数据,8 KB 很容易用满
能在 Deep Sleep 中保持蓝牙/WiFi 连接吗?
不能。Deep Sleep 会关闭射频和主 CPU。如果需要保持连接:
- 使用 Light Sleep + WiFi Modem Sleep(~1 mA)
- 或者接受断线重连的方案(Deep Sleep 唤醒后快速重连)