ESP32 低功耗模式下 Wi-Fi 连接保活策略:Light Sleep 与 Modem Sleep 的边界调优

在物联网应用中,ESP32 常需保持 Wi-Fi 连接以接收指令或上报数据,但持续射频活动会迅速耗尽电池。ESP32 提供了多种低功耗模式,其中 Light Sleep 和 Modem Sleep 可在保留 Wi-Fi 连接的同时降低功耗,但两者在唤醒延迟、连接保持能力和功耗上存在显著差异。本文将从原理到实践,探讨如何根据应用场景调优这两种模式的边界。

1. 低功耗模式原理与差异

1.1 Modem Sleep 模式

Modem Sleep 是 ESP32 在 Wi-Fi 连接状态下默认启用的省电机制(需配置 wifi_set_sleep_type)。其核心原理是:当设备处于连接状态且无数据传输时,关闭 Wi-Fi 射频模块(Modem),但 CPU 保持运行(可运行在 80MHz 或更低频率)。系统通过 DTIM(Delivery Traffic Indication Message)间隔周期性地唤醒射频,以监听 AP 的广播和组播数据。

  • 功耗:约 15-20 mA(取决于 DTIM 周期和 CPU 频率)
  • 唤醒延迟:微秒级,因为 CPU 始终运行
  • 连接保持:依赖 DTIM 周期,通常不会断开,但若 AP 的 DTIM 间隔过长,可能错过关键信标

1.2 Light Sleep 模式

Light Sleep 进一步关闭 CPU 时钟和大部分外设,仅保留 RTC 和必要的中断源。Wi-Fi 连接保持依赖于 RTC 定时器唤醒,在唤醒窗口内处理 Wi-Fi 事件(如接收数据、发送保活包)。

  • 功耗:约 0.8-1.5 mA(不含 Wi-Fi 唤醒窗口)
  • 唤醒延迟:毫秒级(从睡眠到恢复执行)
  • 连接保持:需要主动配置唤醒周期,否则可能因长时间未响应 AP 的信标而断开

1.3 边界调优的核心矛盾

Modem Sleep 功耗较高但连接稳定;Light Sleep 功耗极低但需精细管理唤醒节奏。调优的目标是找到功耗与连接可靠性的平衡点,即“边界”。

2. 配置步骤与代码示例

2.1 配置 Modem Sleep

在 ESP-IDF 中,可通过 esp_wifi_set_ps() 设置 Wi-Fi 省电模式。

#include "esp_wifi.h"

void wifi_modem_sleep_init(void) {
    // 设置 Wi-Fi 省电类型为 Modem Sleep
    esp_wifi_set_ps(WIFI_PS_MIN_MODEM); // 或 WIFI_PS_MAX_MODEM
    
    // 可选:设置 DTIM 间隔(需在 AP 端配置,ESP32 无法直接修改)
    // 但可以设置监听间隔(listen interval)
    esp_wifi_set_listen_interval(10); // 单位:信标周期(通常 100ms)
}
  • WIFI_PS_MIN_MODEM:仅在无数据时关闭射频,唤醒频繁,功耗较高。
  • WIFI_PS_MAX_MODEM:最大化省电,射频关闭时间更长,但可能增加延迟。

2.2 配置 Light Sleep 并保持 Wi-Fi

Light Sleep 需要结合 esp_pm 电源管理框架,并设置唤醒源。

#include "esp_pm.h"
#include "esp_wifi.h"
#include "esp_timer.h"

void wifi_light_sleep_init(void) {
    // 配置电源管理锁,允许进入 Light Sleep
    esp_pm_config_t pm_config = {
        .max_freq_mhz = 80,      // CPU 最大频率
        .min_freq_mhz = 40,      // 最小频率(Light Sleep 时实际关闭)
        .light_sleep_enable = true
    };
    esp_pm_configure(&pm_config);

    // 设置 Wi-Fi 为 Modem Sleep,但允许进入 Light Sleep
    esp_wifi_set_ps(WIFI_PS_MIN_MODEM);

    // 配置定时器唤醒(例如每 500ms 唤醒一次处理 Wi-Fi 事件)
    esp_timer_create_args_t timer_args = {
        .callback = &wifi_keepalive_timer_cb,
        .name = "keepalive"
    };
    esp_timer_handle_t timer;
    esp_timer_create(&timer_args, &timer);
    esp_timer_start_periodic(timer, 500 * 1000); // 500ms
}

void wifi_keepalive_timer_cb(void *arg) {
    // 唤醒后检查 Wi-Fi 连接状态,必要时发送保活包
    if (esp_wifi_is_connected()) {
        // 发送空数据帧或 TCP keepalive
        // 例如:发送 UDP 心跳到服务器
    } else {
        esp_wifi_connect(); // 重连
    }
}

注意:Light Sleep 期间,Wi-Fi 射频默认关闭,因此需要定时唤醒以处理 AP 的信标。若唤醒间隔过长,AP 可能认为设备离线。

2.3 动态调优策略

根据应用场景动态切换模式:

void set_low_power_mode(bool enable_light_sleep) {
    if (enable_light_sleep) {
        esp_pm_config_t pm_config = { .light_sleep_enable = true };
        esp_pm_configure(&pm_config);
        esp_wifi_set_ps(WIFI_PS_MIN_MODEM); // 允许进入 Light Sleep
    } else {
        esp_pm_config_t pm_config = { .light_sleep_enable = false };
        esp_pm_configure(&pm_config);
        esp_wifi_set_ps(WIFI_PS_NONE); // 禁用省电,保持全速连接
    }
}

3. 边界调优的关键参数

  • DTIM 间隔:AP 广播 DTIM 的周期,影响 Modem Sleep 的唤醒频率。若 AP 的 DTIM 为 3,则每 3 个信标唤醒一次。
  • Listen Interval:ESP32 向 AP 声明自己监听信标的间隔,值越大,AP 缓存数据的时间越长,但功耗越低。
  • Light Sleep 唤醒周期:需小于 AP 的关联超时时间(通常 5-10 秒),否则连接会断开。建议设置为 1-2 秒。
  • 保活包类型:TCP keepalive 比 UDP 心跳更可靠,但开销更大。

4. 注意事项

  • AP 兼容性:某些 AP 在设备长时间不响应信标时会主动断开,因此 Light Sleep 的唤醒周期不能过长。
  • 数据延迟:Light Sleep 下,网络事件(如服务器下发指令)可能延迟一个唤醒周期,需在应用层设计超时重传。
  • 测量功耗:使用电流表或功耗分析仪实测,因为实际功耗受射频环境和 CPU 负载影响。
  • 调试技巧:通过 esp_wifi_get_ps()esp_pm_dump_locks() 检查当前状态,确保没有意外锁阻止睡眠。

5. 总结

Modem Sleep 适合对延迟敏感、数据频繁的场景;Light Sleep 适合低频心跳、可容忍延迟的应用。边界调优的关键是测量实际功耗和连接稳定性,动态调整唤醒周期和监听间隔。建议在原型阶段使用 Modem Sleep 保证稳定性,优化后再切换至 Light Sleep 并逐步延长唤醒周期,直到找到功耗与可靠性的最佳平衡点。