4、功耗管理:配网阶段的低功耗设计

配网阶段的功耗,说实话,是很多物联网产品翻车的重灾区。

我见过不少方案,平时休眠功耗能到 10µA 以下,一进配网模式直接飙到 200mA,持续几十秒。用户拿着手机等半天,设备烫得不行。更糟的是,有些电池供电的设备,一次配网失败再重试,电量直接掉一截。

所以这一章,我们专门聊聊配网阶段的低功耗设计。核心就三个方向:动态时钟、休眠策略、分段传输。

核心原则:配网不是全速跑,而是“快、准、省”。快是指快速完成关键交互,准是指精准控制唤醒时机,省是指能不跑就不跑,能慢跑就慢跑。

4.1 动态时钟:别让 CPU 一直跑在最高频

很多工程师习惯性把 WiFi 芯片配网阶段的主频拉到最高,觉得这样处理快。其实没必要。

配网阶段,CPU 大部分时间在等待——等 Beacon 帧、等手机发来的 Probe Request、等 UDP 包。这些等待场景,CPU 跑 160MHz 和跑 40MHz,效果一样,但功耗差了好几倍。

4.1.1 时钟降频策略

我个人习惯把配网过程拆成几个阶段,每个阶段用不同的时钟频率:

阶段 典型操作 推荐主频 说明
信道扫描 切换信道、监听 Beacon 40-80 MHz 大部分时间在等待,低频足够
收到 Probe Response 解析帧、校验 SSID 80-120 MHz 需要快速处理,避免丢包
与手机交互 UDP 收发、解析配网数据 80 MHz 数据量不大,中频即可
连接路由器 DHCP、4-way handshake 160 MHz 关键阶段,需要快速完成

小技巧:在 ESP32 上,我习惯用 esp_wifi_set_ps(WIFI_PS_MIN_MODEM) 配合动态调频。配网开始时先降到 40MHz,等收到有效帧再临时提频。处理完再降回去。这样整体功耗能降 30% 左右。

4.1.2 外设时钟管理

除了 CPU 主频,外设时钟也是耗电大户。配网阶段其实用不到 SPI Flash 的高频读写,也基本用不到 I2C、UART 等外设。

我建议在进入配网前,先把不用的外设时钟关掉。比如 ESP32 的 periph_module_disable() 接口,可以逐个关闭外设时钟。只保留 WiFi MAC、定时器、RTC 这几个必要的模块。

// 示例:配网前关闭不必要的外设时钟
void disable_unused_peripherals(void) {
    periph_module_disable(PERIPH_I2C0_MODULE);
    periph_module_disable(PERIPH_SPI2_MODULE);
    periph_module_disable(PERIPH_UART1_MODULE);
    periph_module_disable(PERIPH_LEDC_MODULE);
    // 保留 WiFi、定时器、RTC
}

4.2 休眠策略:别让设备一直醒着

配网阶段最常见的功耗浪费,就是设备一直在“全速监听”。

你想想看,手机可能隔 2 秒才发一个配网包,但设备在这 2 秒里一直开着接收机,白白耗电。正确的做法是:让设备在大部分时间休眠,只在需要的时候醒来。

4.2.1 间断监听模式

我做过一个项目,设备用 Smart Config 配网。手机每 100ms 发一个 UDP 包,持续 30 秒。如果设备一直开着接收机,功耗大概 80mA。后来我改成每 50ms 醒来监听 10ms,没数据就继续睡。功耗直接降到 15mA 左右。

具体实现思路:

  • 监听窗口:每次醒来只开 5-10ms 的接收机
  • 休眠间隔:根据配网协议调整,通常 20-100ms
  • 自适应调整:如果连续几次都没收到数据,适当延长休眠间隔

注意:休眠间隔不能太长。如果手机发包间隔是 100ms,你的休眠间隔超过 100ms,就可能漏包。我曾经踩过这个坑,设备休眠 150ms,结果手机发了 3 个包都没收到,配网超时。后来我把休眠间隔设为 80ms,留了 20ms 余量,问题解决。

4.2.2 深度休眠与浅度休眠切换

配网阶段其实可以分得更细:

  • 浅度休眠(Modem-sleep):WiFi 模块部分关闭,CPU 暂停,RAM 保持。醒来快(<1ms),适合监听场景
  • 深度休眠(Deep-sleep):几乎全部关闭,只留 RTC。醒来慢(>10ms),适合长时间等待

我建议这样切换:

  1. 配网刚开始时,用浅度休眠 + 间断监听
  2. 如果连续 5 秒没收到任何配网包,切到深度休眠,每 1 秒醒来一次检查
  3. 收到有效包后,立即切回浅度休眠,加快响应
// 伪代码:休眠策略切换
void provisioning_sleep_loop(void) {
    int idle_count = 0;
    while (!provisioning_done) {
        if (data_received()) {
            idle_count = 0;
            set_sleep_mode(SLEEP_LIGHT);  // 浅度休眠
            process_data();
        } else {
            idle_count++;
            if (idle_count > 50) {  // 连续 5 秒无数据
                set_sleep_mode(SLEEP_DEEP);  // 深度休眠
                deep_sleep(1000);  // 睡 1 秒
            } else {
                light_sleep(80);  // 睡 80ms
            }
        }
    }
}

4.3 分段传输:别一次发太多数据

配网阶段传输的数据,通常包括 SSID、密码、可能还有 token 或设备信息。很多人习惯把这些数据打包成一个大的 UDP 包发过去。但这样做有几个问题:

  • 大包传输时间长,接收机需要一直开着
  • 丢包重传成本高,一次丢包整个包都得重来
  • 功耗峰值高,瞬间电流大

更好的做法是分段传输。

4.3.1 数据分片策略

我习惯把配网数据切成 16-32 字节的小片,每片独立发送和确认。这样:

  • 每片传输时间短,接收机可以快速开关
  • 丢包只重传那一小片,效率高
  • 功耗峰值降低,整体更平滑
方案 单次传输时间 丢包重传代价 平均功耗
整包传输(128 字节) ~10ms 重传整个 128 字节 ~60mA
分段传输(16 字节/片) ~2ms 只重传 16 字节 ~25mA

4.3.2 分段确认机制

分段传输需要配合确认机制。设备收到一片数据后,回复一个 ACK。手机收到 ACK 后再发下一片。如果超时没收到 ACK,手机重发当前片。

这样做的好处是:设备可以在每片之间休眠。比如每片传输 2ms,然后休眠 10ms 等待下一片。整体功耗大幅降低。

我的经验:分段大小要根据信道质量调整。信号好的时候用 32 字节,信号差的时候降到 16 字节。我在一个项目中加了自适应分片逻辑,配网成功率从 85% 提升到 97%,功耗还降了 20%。

4.4 整体功耗管理流程

把上面三个策略整合起来,配网阶段的功耗管理流程大概是这样的:

配网阶段功耗管理流程 进入配网模式 关闭不必要外设时钟 降频至 40-80 MHz 浅度休眠 + 间断监听(80ms 周期) 收到数据? 连续无数据?→深度休眠 提频至 80-120 MHz 分段接收 + 逐片确认 配网完成,恢复全速 深度休眠每 1 秒醒来 检查一次信道状态

这个流程我在几个量产项目里都用过。效果很稳定:配网阶段平均功耗从原来的 60-80mA 降到了 15-25mA,配网成功率反而提升了,因为分段传输减少了丢包重传的代价。

避坑提醒:我曾经在一个项目里把休眠间隔设得太激进,结果设备在配网阶段频繁休眠,导致手机发的配网包大量丢失,配网时间从 5 秒拖到 30 秒。后来我加了一个“连续丢包检测”逻辑:如果连续 3 次没收到数据,自动缩短休眠间隔。这才把配网时间拉回正常范围。

功耗管理说白了就是“在正确的时间做正确的事”。配网阶段不需要全速跑,也不需要一直醒着。动态时钟、休眠策略、分段传输这三个手段配合好,既能省电,又不影响配网体验。嗯,这就是我这些年总结下来的经验。