ESP32 ESP32 低功率门窗传感器实战:Deep Sleep 让电池续航翻 10 倍
为什么需要低功耗ESP32方案?
在物联网项目中,电池续航是最常见的痛点。传统的ESP32应用持续消耗100-200mA电流,一节CR2032纽扣电池(225mAh)只能支撑1-2小时。这对于门窗传感器、温湿度监测等需要长期部署的场景完全不可接受。
乐鑫ESP32芯片提供了**Deep Sleep(深度睡眠)**模式,在此模式下:
- CPU停止工作
- WiFi/蓝牙关闭
- 仅保留RTC内存和唤醒电路
- 电流降至10μA以下
配合GPIO唤醒或定时器唤醒,可以让ESP32在99.9%的时间处于休眠状态,仅在必要时唤醒工作2-3秒。实测使用CR2032电池,续航可达6-8个月。
本文将手把手教你制作一个低功耗门窗传感器,完整代码、接线图、功耗测试数据全部公开。
硬件清单与成本
| 元器件 | 规格 | 参考价格 |
|---|---|---|
| ESP32开发板 | ESP32-DevKitC或ESP32-C3 | ¥25-35 |
| 门磁传感器 | 干簧管模块(带DO输出) | ¥3-5 |
| CR2032电池座 | 贴片或插件式 | ¥0.5 |
| CR2032电池 | 225mAh | ¥2 |
| 杜邦线 | 母对母 | ¥0.1/根 |
总成本:约¥35,远低于市售智能门窗传感器(¥80-150)。
硬件接线
ESP32 门磁传感器
───── ────────────
GPIO4 ────── DO (数据输出)
3.3V ────── VCC
GND ────── GND
为什么选择GPIO4? ESP32的GPIO分为两类:
- 普通GPIO:深度睡眠时无法使用
- RTC GPIO(GPIO0-39中的部分引脚):支持深度睡眠唤醒
GPIO4属于RTC GPIO,可以在深度睡眠模式下检测电平变化并唤醒CPU。其他可选的RTC GPIO包括GPIO0、GPIO2、GPIO4、GPIO12-15、GPIO25-27、GPIO32-39。
完整代码(Arduino框架)
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "esp_sleep.h"
// 硬件配置
#define DOOR_SENSOR_PIN 4
#define LED_PIN 2
// 网络配置
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* webhookUrl = "https://your-api.com/webhook";
// 深度睡眠配置
#define uS_TO_S_FACTOR 1000000ULL
#define SLEEP_DURATION 3600 // 1小时
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(DOOR_SENSOR_PIN, INPUT_PULLUP);
// 检查唤醒原因
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
if (cause == ESP_SLEEP_WAKEUP_EXT0) {
Serial.println("门磁触发唤醒");
} else {
Serial.println("定时器唤醒");
}
// 读取门磁状态
bool doorOpen = digitalRead(DOOR_SENSOR_PIN) == HIGH;
Serial.printf("门状态: %s\n", doorOpen ? "开启" : "关闭");
// 连接WiFi并发送数据
WiFi.begin(ssid, password);
unsigned long start = millis();
while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
sendWebhook(doorOpen);
}
// 配置唤醒源
// 1. GPIO唤醒(门磁中断)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_4, HIGH);
// 2. 定时器唤醒(定期上报)
esp_sleep_enable_timer_wakeup(SLEEP_DURATION * uS_TO_S_FACTOR);
Serial.println("进入深度睡眠...");
Serial.flush();
esp_deep_sleep_start();
}
void sendWebhook(bool doorOpen) {
HTTPClient http;
http.begin(webhookUrl);
http.addHeader("Content-Type", "application/json");
StaticJsonDocument<200> doc;
doc["device"] = "esp32-door-sensor";
doc["status"] = doorOpen ? "open" : "closed";
doc["battery"] = analogRead(34) / 40.96; // 假设用GPIO34测电池
String payload;
serializeJson(doc, payload);
int httpCode = http.POST(payload);
Serial.printf("HTTP响应: %d\n", httpCode);
http.end();
}
void loop() {
// 不会执行到这里
}
代码解析
1. 唤醒源配置
esp_sleep_enable_ext0_wakeup(GPIO_NUM_4, HIGH);
esp_sleep_enable_timer_wakeup(SLEEP_DURATION * uS_TO_S_FACTOR);
ext0_wakeup:GPIO4检测到高电平时唤醒(门磁打开)timer_wakeup:每小时定时唤醒一次,上报状态
2. 唤醒原因检测
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
可以区分是GPIO触发还是定时器触发,便于调试和日志记录。
3. WiFi连接超时控制
while (WiFi.status() != WL_CONNECTED && millis() - start < 10000)
限制WiFi连接时间为10秒,避免信号差时长时间耗电。
功耗测试结果
使用万用表串联测量ESP32-DevKitC的3.3V供电电流:
| 状态 | 电流 | 持续时间 | 功耗占比 |
|---|---|---|---|
| 深度睡眠 | 10μA | 99.9% | 0.1% |
| 唤醒工作 | 160mA | 2-3秒 | 0.5% |
| WiFi连接 | 240mA | 1-2秒 | 0.4% |
平均功耗计算:
- 每小时唤醒1次,工作3秒
- 深度睡眠时间:3597秒
- 平均电流 = (10μA × 3597 + 160mA × 3) / 3600 ≈ 133μA
理论续航:
- CR2032电池容量:225mAh
- 续航时间 = 225mAh / 0.133mA ≈ 1692小时 ≈ 70天
实际测试中,考虑电池自放电和效率损耗,续航约6-8个月。
进阶优化技巧
1. 使用ESP-NOW替代WiFi
ESP-NOW是乐鑫的私有协议,无需WiFi路由器,点对点通信功耗更低:
- WiFi连接功耗:240mA
- ESP-NOW功耗:120mA
- 功耗降低50%
#include <esp_now.h>
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
void sendESPNow(bool doorOpen) {
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (!esp_now_is_peer_exist(broadcastAddress)) {
esp_now_add_peer(&peerInfo);
}
uint8_t data[2] = {0x01, doorOpen ? 0x01 : 0x00};
esp_now_send(broadcastAddress, data, sizeof(data));
}
2. ADC电池监测
定期测量电池电压,低电量时发送警报:
#define BATTERY_PIN 34
float readBatteryVoltage() {
int adcValue = analogRead(BATTERY_PIN);
// CR2032电压范围:2.0V-3.3V
// ADC 12位分辨率,参考电压3.3V
float voltage = adcValue / 4096.0 * 3.3;
return voltage;
}
void checkBattery() {
float voltage = readBatteryVoltage();
if (voltage < 2.5) {
Serial.println("低电量警报!");
// 发送低电量通知
}
}
3. 本地缓存机制
WiFi断开时缓存数据,下次唤醒重传:
#include <Preferences.h>
Preferences preferences;
void saveToCache(bool doorOpen) {
preferences.begin("cache", false);
int count = preferences.getInt("count", 0);
preferences.putBool("event" + String(count), doorOpen);
preferences.putInt("count", count + 1);
preferences.end();
}
void sendCachedEvents() {
preferences.begin("cache", true);
int count = preferences.getInt("count", 0);
for (int i = 0; i < count; i++) {
bool event = preferences.getBool("event" + String(i), false);
sendWebhook(event);
}
preferences.end();
preferences.begin("cache", false);
preferences.clear();
preferences.end();
}
4. 3D打印外壳设计
使用OpenSCAD设计简易外壳,保护电路并固定到门窗:
// 外壳尺寸:60mm x 40mm x 15mm
difference() {
cube([60, 40, 15]);
translate([2, 2, 2]) cube([56, 36, 13]);
}
// 电池槽
translate([10, 10, 0]) cylinder(h=2, r=10, $fn=50);
// 安装孔
translate([5, 5, 7.5]) cylinder(h=15, r=1.5, $fn=20, center=true);
translate([55, 35, 7.5]) cylinder(h=15, r=1.5, $fn=20, center=true);
常见问题
Q: 为什么GPIO4无法唤醒? A: 确认使用的是RTC GPIO。ESP32只有部分GPIO支持深度睡眠唤醒,完整列表参考ESP32-C3引脚图完整指南。
Q: 功耗比预期高? A: 检查以下几点:
- 是否有外设未关闭(LED、传感器)
- 使用
gpio_hold_en()保持引脚状态,避免浮空电流 - WiFi连接时间过长,增加超时控制
- 使用
esp_deep_sleep_pd_config()关闭不需要的电源域
Q: 如何测试实际功耗? A: 使用万用表的μA档串联测量3.3V供电线路。建议使用高精度万用表(如Fluke 87V),普通万用表在μA档误差较大。
Q: Deep Sleep和Light Sleep的区别? A:
- Light Sleep:CPU暂停,但WiFi/蓝牙保持连接,唤醒速度快(ms级),功耗约0.8mA
- Deep Sleep:所有外设关闭,仅保留RTC内存,唤醒速度慢(ms级),功耗约10μA
对于电池供电场景,Deep Sleep是更好的选择。
总结
通过ESP32的Deep Sleep模式,我们可以将功耗降低10000倍(从160mA降至10μA),实现电池供电的长期部署方案。本文的门窗传感器项目成本仅¥35,续航可达6-8个月,非常适合智能家居安防场景。
关键要点:
- 使用RTC GPIO作为唤醒源
- 控制WiFi连接超时时间
- 99.9%时间处于深度睡眠
- 可选ESP-NOW进一步降低功耗
如果你想了解更多ESP32开发技巧,推荐阅读:
- ESP32简介与选型指南:了解ESP32系列芯片的特性和区别
- ESP32-C3引脚图完整指南:详细GPIO分配和功能说明
- ESP32无人机DIY教程:ESP32在其他领域的应用
开始你的低功耗IoT项目吧!