4、浅度睡眠(Light-sleep)与Modem-sleep:WiFi/蓝牙连接保持下的低功耗策略,UART唤醒,GPIO唤醒
好,咱们接着聊。前面几章我们把睡眠模式的基本概念讲清楚了,这一章要动真格的了——如何在保持WiFi或蓝牙连接的前提下,把功耗压到最低。
你想想看,很多物联网设备不能断网。智能插座要随时响应云端指令,门锁要能远程开锁,传感器要定时上报数据。如果每次休眠都断开WiFi,那体验就太差了。所以,Modem-sleep和Light-sleep就是专门解决这个问题的。
4.1 Modem-sleep:WiFi连接下的省电模式
Modem-sleep,说白了就是让WiFi的Modem(调制解调器)间歇性休息。ESP32的WiFi模块在工作时功耗很高,但并不是每时每刻都需要全速运转。
工作原理是这样的:
- ESP32与路由器协商好Beacon间隔(通常是100ms或102.4ms)
- 在Beacon间隔之间,Modem可以关闭射频电路
- 只在Beacon到来前醒来,接收信标帧,保持同步
- 如果有数据要收发,再临时唤醒
关键数据:
| 模式 | 典型电流 | 说明 |
|---|---|---|
| Active(全速) | ~80-120mA | WiFi持续收发 |
| Modem-sleep(DTIM1) | ~5-15mA | 每100ms唤醒一次 |
| Modem-sleep(DTIM3) | ~2-5mA | 每300ms唤醒一次 |
我在项目中遇到过一个问题:有些廉价的WiFi路由器Beacon间隔不稳定,导致ESP32频繁误唤醒。后来我加了个超时判断,连续收到3个Beacon才确认同步,这才稳定下来。
4.1.1 DTIM参数的影响
DTIM(Delivery Traffic Indication Message)是路由器广播的周期参数。DTIM=1表示每个Beacon都包含广播数据,DTIM=3表示每3个Beacon才包含一次。
我的建议:
- 对延迟敏感的应用(如智能灯控):用DTIM1,响应快但功耗高
- 对功耗敏感的应用(如传感器):用DTIM3,省电但延迟大
- 极端省电场景:可以协商到DTIM10,但要注意丢包风险
小技巧:在ESP-IDF中,可以通过 esp_wifi_set_ps(WIFI_PS_MIN_MODEM) 启用Modem-sleep。默认是WIFI_PS_NONE,记得改过来。
4.2 Light-sleep:更激进的省电方案
Modem-sleep虽然省电,但CPU还在跑,外设还在供电。Light-sleep则更进一步——把CPU也停了,只保留RTC域和WiFi/蓝牙的唤醒逻辑。
嗯,这里要注意:Light-sleep下,WiFi连接是保持的,但CPU不执行代码。这听起来有点矛盾,对吧?
实际上,ESP32的WiFi MAC层有硬件自动管理功能。在Light-sleep期间,WiFi模块自己会按Beacon间隔醒来,接收数据,然后继续睡。CPU只在有数据需要处理时才被唤醒。
功耗对比(实测数据):
| 模式 | WiFi保持 | CPU状态 | 典型电流 |
|---|---|---|---|
| Modem-sleep | 是 | 运行 | 5-15mA |
| Light-sleep(WiFi保持) | 是 | 暂停 | 0.8-2mA |
| Deep-sleep | 否 | 断电 | ~10μA |
你看,Light-sleep比Modem-sleep省了一个数量级。代价是什么?唤醒延迟。从Light-sleep唤醒需要约1-3ms,而Modem-sleep几乎是即时响应。
4.2.1 如何配置Light-sleep保持WiFi
在ESP-IDF中,配置Light-sleep保持WiFi连接其实很简单:
// 启用WiFi的Light-sleep模式
esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
// 进入Light-sleep
esp_light_sleep_start();
// 或者用定时自动进入
esp_sleep_enable_timer_wakeup(10 * 1000000); // 10秒后唤醒
esp_light_sleep_start();
我曾经踩过一个坑:在Light-sleep期间,如果路由器突然重启,ESP32不会自动重连。因为CPU是停的,WiFi模块只负责接收Beacon,不处理连接状态。解决方案是在唤醒后检查WiFi连接状态,必要时手动重连。
注意:Light-sleep保持WiFi时,不能使用 esp_deep_sleep_start(),那会断开所有连接。一定要用 esp_light_sleep_start()。
4.3 UART唤醒:串口数据唤醒系统
很多设备需要通过串口接收指令。比如工业控制器、智能网关。如果系统在Light-sleep中,串口来了数据怎么办?
ESP32支持UART唤醒功能。说白了,就是串口的RX引脚可以配置为唤醒源。当有数据到达时,系统从睡眠中醒来,处理数据。
4.3.1 配置UART唤醒
// 配置UART唤醒
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_param_config(UART_NUM_1, &uart_config);
uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
// 启用UART唤醒
esp_sleep_enable_uart_wakeup(UART_NUM_1);
// 进入Light-sleep
esp_light_sleep_start();
嗯,这里有个细节:UART唤醒是基于边沿检测的。也就是说,只要RX引脚有电平变化,系统就会醒来。但如果你只收到一个字节,系统醒来后可能发现缓冲区里只有1个字节,然后又睡过去了。这会导致频繁唤醒。
我的经验:在UART唤醒后,不要立即再次睡眠。先等一段时间(比如50ms),收集完整的数据帧,再决定是否继续睡。这样可以避免「假唤醒」问题。
小技巧:如果UART数据是固定长度的,可以在唤醒后检查接收到的字节数。不够就继续等,够了再处理。这样能减少不必要的唤醒次数。
4.4 GPIO唤醒:按键与传感器触发
GPIO唤醒是最常用的唤醒方式之一。按键、门磁、PIR传感器、干簧管……这些都可以通过GPIO唤醒系统。
ESP32支持两种GPIO唤醒模式:
- 电平触发:引脚保持高电平或低电平唤醒
- 边沿触发:引脚从高到低或从低到高变化时唤醒
4.4.1 配置GPIO唤醒
// 配置GPIO唤醒(边沿触发)
esp_sleep_enable_gpio_wakeup();
// 设置唤醒引脚
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << WAKEUP_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_NEGEDGE // 下降沿唤醒
};
gpio_config(&io_conf);
// 进入Light-sleep
esp_light_sleep_start();
我曾经犯过一个低级错误:把GPIO唤醒引脚配置为内部上拉,但外部电路也是上拉的。结果引脚一直处于高电平,系统永远无法唤醒。后来我改成下拉,或者用外部电路明确控制电平状态,问题才解决。
避坑指南:GPIO唤醒时,RTC域的GPIO(如GPIO0-17)功耗更低。非RTC域的GPIO虽然也能唤醒,但需要额外的电源管理,功耗会高一些。我建议优先使用RTC GPIO。
4.4.2 多GPIO唤醒
有时候需要多个唤醒源。比如一个按键唤醒,一个传感器唤醒。ESP32支持同时配置多个GPIO作为唤醒源:
// 配置多个GPIO唤醒
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << GPIO_NUM_4) | (1ULL << GPIO_NUM_5),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE // 任意边沿
};
gpio_config(&io_conf);
esp_sleep_enable_gpio_wakeup();
// 唤醒后判断是哪个引脚触发的
uint64_t wakeup_pin = esp_sleep_get_gpio_wakeup_status();
if (wakeup_pin & (1ULL << GPIO_NUM_4)) {
// 按键触发
} else if (wakeup_pin & (1ULL << GPIO_NUM_5)) {
// 传感器触发
}
4.5 综合策略:如何选择
讲了这么多,你可能会问:到底该用哪种模式?
我的建议是这样的:
- 需要实时响应云端指令:用Modem-sleep,DTIM1。功耗高但延迟低
- 定时上报数据,偶尔接收指令:用Light-sleep保持WiFi,配合定时唤醒
- 按键或传感器触发:用Light-sleep + GPIO唤醒,WiFi保持连接
- 串口设备控制:用Light-sleep + UART唤醒,WiFi保持连接
- 极端省电,可以断网:用Deep-sleep,但那是下一章的内容了
核心原则:能睡就睡,能浅睡就不深睡。保持连接的情况下,Light-sleep是最佳平衡点。我做过一个项目,用Light-sleep保持WiFi,配合GPIO唤醒,电池续航从3天延长到了30天。效果非常明显。
好了,这一章的内容就到这里。下一章我们会讲Deep-sleep和RTC内存保持,那才是真正的「极限省电」模式。到时候见。