💡 划重点
- 低功耗门磁的第一原则是“平时什么都不做”:ESP32 大部分时间 deep sleep,只在门被打开时唤醒。
- 磁簧开关比摄像头、雷达更适合门/抽屉开合检测,因为它便宜、稳定、状态明确。
- 真正要调的是唤醒脚、上拉方式、Wi-Fi 超时和电池电压采样,而不是复杂算法。
需求洞察
设备箱、仓库门、抽屉、药品柜和工具柜都需要一个简单的开合记录。持续联网的设备耗电高,普通机械锁又没有记录能力。门磁节点的价值在于把一次“开门事件”变成可记录的数据:什么时候打开、持续多久、电池是否快没电。
这个项目很适合硬件编程入门后进阶,因为它逼你考虑真实设备的生命周期:休眠、唤醒、联网、失败重试、上报、再次休眠。能把这条链路跑稳,比多接几个传感器更有工程价值。
方案设计
感知层使用常闭磁簧开关,门关闭时磁铁靠近,GPIO33 保持稳定电平;门打开时电平变化,把 ESP32 从深度睡眠中唤醒。控制层读取门状态和电池分压,判断这次是开门事件还是误触发。执行层可以选配蜂鸣器,但电池版默认不开启常驻执行器。日志/联网层通过 MQTT 发布事件,也可以先用 HTTP Webhook 或串口替代。
为了省电,Wi-Fi 连接必须有超时。如果 8-10 秒连不上网络,就先把事件写入 RTC 计数或本地缓存,然后重新睡眠,避免电池被失败重试耗光。
物料清单
| 元件 | 数量 | 用途 | 估算价 |
|---|---|---|---|
| ESP32 DevKitC | 1 | 主控与 Wi-Fi 上报 | 25 元 |
| 常闭磁簧开关 | 1 | 检测门/抽屉开合 | 3 元 |
| 小磁铁 | 1 | 与磁簧开关配合 | 1 元 |
| 18650 电池盒 | 1 | 移动供电 | 6 元 |
| 低静态电流稳压模块 | 1 | 降低待机损耗 | 8 元 |
| 100kΩ 电阻 | 2 | 电池分压采样 | 0.2 元 |
接线与结构
- 磁簧开关一端接 GPIO33,另一端接 GND,GPIO33 使用
INPUT_PULLUP。 - 电池电压通过两个 100kΩ 电阻分压后接 GPIO35,GPIO35 只做 ADC 输入。
- 可选蜂鸣器接 GPIO27;正式电池版建议默认不装或只在维护模式启用。
- 磁簧开关固定在门框,磁铁固定在门板,闭合时两者距离最好小于 10mm。
GPIO33 属于 ESP32 可用于外部唤醒的 RTC GPIO,适合 deep sleep 唤醒。注意不同开发板的稳压器和电源指示灯会明显影响待机电流,如果追求长续航,后期应换低静态电流板或裸模块。
核心代码
#include <Arduino.h>
#include <WiFi.h>
#define DOOR_PIN GPIO_NUM_33
#define BAT_ADC 35
#define BUZZER_PIN 27
const char* ssid = "YOUR_WIFI";
const char* password = "YOUR_PASSWORD";
bool connectWiFi(uint32_t timeoutMs) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
uint32_t start = millis();
while (WiFi.status() != WL_CONNECTED && millis() - start < timeoutMs) {
delay(200);
}
return WiFi.status() == WL_CONNECTED;
}
float readBattery() {
int raw = analogRead(BAT_ADC);
float v = raw * 3.3f / 4095.0f;
return v * 2.0f; // 100k + 100k 分压
}
void publishEvent(bool opened, float battery) {
// 这里可以替换成 PubSubClient MQTT publish。
Serial.printf("door=%s battery=%.2fV\n", opened ? "open" : "closed", battery);
}
void goSleep() {
esp_sleep_enable_ext0_wakeup(DOOR_PIN, 0); // GPIO33 被拉低时唤醒
Serial.flush();
esp_deep_sleep_start();
}
void setup() {
Serial.begin(115200);
pinMode((int)DOOR_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
analogReadResolution(12);
delay(100);
bool opened = digitalRead((int)DOOR_PIN) == LOW;
float battery = readBattery();
if (opened) {
digitalWrite(BUZZER_PIN, HIGH);
delay(80);
digitalWrite(BUZZER_PIN, LOW);
}
if (connectWiFi(8000)) {
publishEvent(opened, battery);
WiFi.disconnect(true);
} else {
Serial.println("wifi timeout, skip publish");
}
delay(200);
goSleep();
}
void loop() {}
调试步骤
- USB 供电调试阶段先不要睡眠,把门磁状态每秒打印一次,确认开关逻辑正确。
- 打开 deep sleep 后,用串口观察唤醒原因,确认 GPIO33 能触发启动。
- 给 Wi-Fi 连接加 8 秒超时,模拟路由器断电,确认设备不会一直卡住。
- 用万用表测电池分压点,校准
readBattery()的换算系数。 - 最后再装外壳,反复开合 50 次,检查磁铁位置是否会误触发。
设计图
ESP32 低功耗门磁告警器结构flow
1
门关闭时进入 deep sleep
→
2
磁簧开关电平变化唤醒
→
3
读取门状态和电池电压
→
4
Wi-Fi 连接并发布事件
→
5
断网超时后重新睡眠