做电池供电的 IoT 设备,最头疼的不是代码怎么写,而是电池能撑多久。
我用 ESP32 做过一个土壤湿度监测节点,一开始待机 3 天就没电了。后来花了一周时间抠功耗,待机时间拉长到了 6 个月。今天就把这中间踩过的坑和学到的技巧,一次性讲清楚。
为什么待机电流这么重要
先算一笔账。假设你用一块 2000mAh 的锂电池:
- 待机 10mA → 200 小时,约 8 天
- 待机 1mA → 2000 小时,约 83 天
- 待机 100μA → 20000 小时,约 2.3 年
差了 250 倍。
大多数 IoT 设备 99% 的时间都在待机,只有偶尔采集数据、发送报文时才醒过来。所以待机功耗直接决定了电池寿命。
硬件层面的优化
1. 选对芯片
不是所有 MCU 都适合电池供电。看看主流芯片的典型待机电流:
| 芯片 | Deep Sleep 电流 | 唤醒时间 |
|---|---|---|
| ESP32-C3 | ~7μA (Hibernation) | ~20ms |
| STM32L053 | ~0.6μA (Stop 模式) | ~5μs |
| nRF52840 | ~1μA (System OFF) | ~6μs |
| ATmega328P | ~0.1μA (Power Down) | ~65ms |
如果续航是第一优先级,STM32L 系列和 nRF 系列是更好的选择。ESP32 的优势在于集成了 Wi-Fi/蓝牙,但代价是功耗相对较高。
2. 去掉不必要的负载
很多开发板上有你根本不需要的东西在持续耗电:
- 状态 LED:每颗 2-5mA,关掉能省不少
- USB 转串口芯片:CH340 静态电流约 5-10mA
- LDO 稳压器:老旧 LDO 的静态电流可能高达 1-5mA
实战技巧: 用低静态电流的 LDO(如 MCP1702,Iq 仅 2μA)替换板载稳压器,或者直接用锂电池供电(3.0-4.2V 范围,很多 MCU 支持)。
3. 浮空引脚处理
未使用的 GPIO 如果处于浮空状态,可能导致内部电路反复翻转,增加 μA 级别的漏电。
做法: 所有未使用的引脚配置为输出低电平,或者内部下拉。
软件层面的优化
ESP32 的四种低功耗模式
ESP32 提供了丰富的功耗模式,从低到高:
| 模式 | 典型电流 | 保留内容 | 唤醒方式 |
|---|---|---|---|
| Active (Wi-Fi) | 80-240mA | 全部 | – |
| Modem Sleep | ~20mA | Wi-Fi 连接 | 自动 |
| Light Sleep | ~0.8mA | RTC 内存 | GPIO/定时器 |
| Deep Sleep | ~10μA | RTC 内存 | 定时器/GPIO/触摸 |
| Hibernation | ~7μA | 无 | 定时器 |
Light Sleep 实战
Light Sleep 适合需要频繁唤醒的场景,比如每秒采样一次传感器:
#include "esp_sleep.h"
#include "esp_pm.h"
void setup() {
// 配置 Light Sleep 自动触发
// 当 CPU 空闲时自动进入
esp_pm_config_esp32_t pm_config = {
.max_freq_mhz = 80,
.min_freq_mhz = 80,
.light_sleep_enable = true
};
esp_pm_configure(&pm_config);
}
void loop() {
// 读取传感器
float temp = read_sensor();
// 发送数据
send_data(temp);
// 等待 1 秒后再次采集
// CPU 空闲期间会自动进入 Light Sleep
vTaskDelay(pdMS_TO_TICKS(1000));
}
Deep Sleep 实战
Deep Sleep 适合间歇性采集的场景,比如每 30 分钟发送一次数据:
#include "esp_sleep.h"
#define uS_TO_S 1000000ULL
#define SLEEP_TIME 1800 // 30 分钟
void setup() {
// 配置定时器唤醒
esp_sleep_enable_timer_wakeup(SLEEP_TIME * uS_TO_S);
// 可选:GPIO 唤醒(比如按下按钮唤醒)
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 1);
// 可选:保存数据到 RTC Fast Memory
RTC_DATA_ATTR uint32_t boot_count = 0;
boot_count++;
// 进入 Deep Sleep
esp_deep_sleep_start();
}
void loop() {
// 永远不会执行到这里
// Deep Sleep 唤醒后从 setup() 重新开始
}
STM32 的 Stop 模式
STM32L 系列在 Stop 模式下电流可以低至 0.6μA:
#include "stm32l0xx_hal.h"
void enter_stop_mode(void) {
// 关闭不需要的时钟
__HAL_RCC_GPIOA_CLK_DISABLE();
__HAL_RCC_GPIOB_CLK_DISABLE();
// ... 关闭所有不用的 GPIO 时钟
// 进入 Stop 模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,
PWR_STOPENTRY_WFI);
// 唤醒后重新配置时钟
SystemClock_Config();
}
功耗测量方法
没有测量就没有优化。你需要一个准确的方法来测量待机电流。
方法一:万用表(入门)
把万用表串入供电回路,直接读数。简单但精度有限,万用表的采样率太低,看不到电流尖峰。
方法二:示波器 + 采样电阻(推荐)
在供电回路串联一个 10Ω 采样电阻,用示波器测量电阻两端电压:
I = V / R = V(采样电阻) / 10Ω
示波器的采样率高,可以看到唤醒瞬间的电流尖峰——这些尖峰往往占总功耗的很大一部分。
方法三:专用功耗分析仪(专业)
像 Nordic Power Profiler Kit II 或者 Joulescope 这种设备,可以精确到 nA 级别,还能实时绘制功耗曲线。预算充足的话强烈建议入手。
常见坑和解决方案
坑一:Wi-Fi 连接后忘记断开
// 错误写法 - Wi-Fi 一直保持连接
WiFi.begin(ssid, password);
send_data();
delay(30000); // 这 30 秒 Wi-Fi 一直在耗电!
// 正确写法 - 用完就断
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED);
send_data();
WiFi.disconnect(true); // 断开 Wi-Fi
WiFi.mode(WIFI_OFF); // 关闭 Wi-Fi 模块
esp_deep_sleep_start();
坑二:传感器待机功耗被忽略
很多传感器即使不工作也有静态电流。比如 DHT22 待机时约 1.5mA,对于整个系统来说这可能是最大的耗电大户。
解决方案: 用 GPIO 控制传感器的供电,需要时才上电:
#define SENSOR_POWER_PIN 4
void setup() {
pinMode(SENSOR_POWER_PIN, OUTPUT);
digitalWrite(SENSOR_POWER_PIN, HIGH); // 给传感器供电
delay(500); // 等待传感器稳定
float temp = read_dht22();
digitalWrite(SENSOR_POWER_PIN, LOW); // 关闭传感器供电
}
坑三:RTC GPIO 唤醒配置错误
ESP32 的 Deep Sleep 可以用 GPIO 唤醒,但引脚有限制——只能用 RTC GPIO(GPIO 0/2/4/12-15/25-27/32-39),而且需要配置 ULP 协处理器或者 ext0/ext1 唤醒源。
// ext0 唤醒:只能用一个引脚,电平触发
esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 1); // 高电平唤醒
// ext1 唤醒:可以用多个引脚,支持 OR/AND 逻辑
esp_sleep_enable_ext1_wakeup(
GPIO_SEL_35 | GPIO_SEL_34, // 两个引脚
ESP_EXT1_WAKEUP_ALL_LOW // 全部低电平触发
);
坑四:ADC 校准导致电流异常
ESP32 的 ADC 在 Deep Sleep 期间如果启用了 ULP 协处理器读取 ADC,需要确保正确配置,否则可能导致唤醒后电流异常。
// 启用 ADC 校准
esp_adc_cal_characteristics_t adc_chars;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11,
ADC_WIDTH_BIT_12, 1100, &adc_chars);
优化效果对比
这是我实际优化一个土壤监测节点的过程:
| 阶段 | 待机电流 | 电池寿命 |
|---|---|---|
| 初始状态(开发板 + Wi-Fi 常开) | 15mA | 5.5 天 |
| 去掉 LED + 改用独立供电 | 8mA | 10 天 |
| 启用 Light Sleep | 1.2mA | 69 天 |
| 改用 Deep Sleep + 定时采集 | 15μA | 15 个月 |
| 去掉板载稳压器 + 直连锂电池 | 8μA | 28 个月 |
从 5 天到 28 个月,优化了 170 倍。核心思路就是:能关的都关掉,该睡的时候赶紧睡。
总结
低功耗设计不是一蹴而就的,而是一个持续优化的过程。记住几个核心原则:
- 选对芯片:电池供电项目优先考虑 STM32L、nRF 系列
- 硬件做减法:去掉 LED、换低 Iq 稳压器、处理浮空引脚
- 软件该睡就睡:Light Sleep 适合频繁唤醒,Deep Sleep 适合间歇采集
- 传感器按需供电:用 GPIO 控制传感器电源
- 测量验证:用示波器或功耗分析仪确认优化效果
希望这篇博客文章对您有所帮助!