💡 划重点
- 用MQ-2监测烟雾与可燃气体,用火焰传感器识别明火,双传感器交叉判断,降低单一传感器误报。
- ESP32本地驱动蜂鸣器和状态灯,同时通过服务器发送事件(SSE)把报警与遥测推送到局域网网页看板。
- 按给定接线:MQ-2 A0接34、火焰传感器DO接35、蜂鸣器接27、状态灯接2,物料成本控制在55元内。
- 适合厨房灶台上方安装,重点解决“人在客厅或卧室、油锅起烟起火时不能第一时间发现”的问题。
需求洞察
目标用户是小户型家庭、租房用户、独居老人家庭,以及经常做饭但容易忘记关火的人。真实痛点不是“没有报警器”,而是传统单一烟雾报警器反应链条长:油锅先冒烟、后起火,烟雾可能被抽油烟机带走;等烟雾浓度达到阈值时,明火已经出现。另一类痛点是误报,例如蒸汽、油烟、烹饪喷雾触发烟雾报警,用户久而久之会拔电池。
这个项目值得做,原因有三点:第一,烟雾和火焰是两种不同物理信号,双通道判断比单传感器更贴近厨房事故场景;第二,ESP32成本低,能同时完成本地声光报警和联网事件推送;第三,SSE实现简单,浏览器打开网页即可看实时数据,不需要额外搭建复杂平台。方案不追求工业级认证,而是做一个可学习、可迭代、成本可控的家庭安全原型。
方案设计
感知层由MQ-2和火焰传感器组成。MQ-2输出模拟电压,接ESP32的ADC引脚34,用于观察烟雾/可燃气体浓度变化;火焰传感器输出数字电平,接35,检测到火焰时通常输出低电平,模块上的电位器可调灵敏度。控制层在ESP32内完成采样、滑动平均、阈值判断和报警状态机:连续多次超过阈值才触发,避免瞬时尖峰误报;报警后保持声光提示,直到条件恢复。执行层为有源蜂鸣器和状态灯:正常时状态灯常亮,报警时蜂鸣器鸣叫、状态灯闪烁。日志/联网层连接WiFi,提供 /events 的SSE端点,把报警事件和周期遥测推送到浏览器看板,附带时间戳与传感器读数,便于回看和调阈值。
物料清单
| 物料 | 数量 | 用途 | 估算单价 | 小计 |
|---|---|---|---|---|
| ESP32开发板 | 1 | 主控、WiFi、ADC与GPIO | 18元 | 18元 |
| MQ-2烟雾模块 | 1 | 烟雾/可燃气体模拟量检测 | 8元 | 8元 |
| 火焰传感器模块 | 1 | 明火红外检测,数字输出 | 5元 | 5元 |
| 有源蜂鸣器模块 | 1 | 本地声音报警 | 2元 | 2元 |
| LED及限流电阻 | 1套 | 状态指示 | 1元 | 1元 |
| 面包板与杜邦线 | 1套 | 原型搭建 | 8元 | 8元 |
| 5V电源线 | 1 | 供电 | 9元 | 9元 |
| 阻燃塑料外壳 | 1 | 绝缘与安装 | 2元 | 2元 |
| 合计 | 55元 |
接线与结构
按给定建议接线:MQ-2的VCC接5V、GND接GND、A0接GPIO34;火焰传感器VCC接3.3V、GND接GND、DO接GPIO35;有源蜂鸣器信号接GPIO27,VCC和GND按模块要求供电;状态灯正极经限流电阻接GPIO2,负极接GND。火焰传感器使用DO而不是AO,是因为35适合数字输入,且模块自带比较器,现场调节电位器更直接;若必须读模拟量,可改接34以外的ADC引脚并调整代码。结构上,MQ-2和火焰传感器安装在灶台上方15至30厘米处,避开抽油烟机强气流直吹;ESP32放入阻燃外壳,蜂鸣器开孔,USB供电线远离灶火和油污。MQ-2需要预热3至5分钟,首次上电读数漂移属正常。
核心代码
#include <WiFi.h>
#include <WebServer.h>
const char* SSID = "你的WiFi名称";
const char* PASS = "你的WiFi密码";
WebServer server(80);
WiFiClient sseClient;
const int PIN_MQ2 = 34, PIN_FLAME = 35, PIN_BUZZ = 27, PIN_LED = 2;
bool alarm = false;
unsigned long lastRead = 0, lastPush = 0;
void handleEvents() {
server.sendHeader("Content-Type", "text/event-stream");
server.send(200, "text/event-stream", "retry: 2000\n\n");
sseClient = server.client();
}
void pushSSE(const String& data) {
if (sseClient && sseClient.connected()) {
sseClient.print("data: ");
sseClient.print(data);
sseClient.print("\n\n");
}
}
void setup() {
Serial.begin(115200);
pinMode(PIN_BUZZ, OUTPUT);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_BUZZ, LOW);
digitalWrite(PIN_LED, HIGH);
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
server.on("/events", handleEvents);
server.on("/", []() {
server.send(200, "text/html",
"<h3>厨房安全看板</h3><div id='log'></div><script>"
"const e=new EventSource('/events');"
"e.onmessage=m=>document.getElementById('log').innerHTML+=m.data+'<br>';"
"</script>");
});
server.begin();
}
void loop() {
server.handleClient();
if (millis() - lastRead > 500) {
lastRead = millis();
int smoke = analogRead(PIN_MQ2);
bool flame = (digitalRead(PIN_FLAME) == LOW);
bool nowAlarm = (smoke > 2500) || flame;
digitalWrite(PIN_BUZZ, nowAlarm ? HIGH : LOW);
digitalWrite(PIN_LED, nowAlarm ? ((millis() / 200) % 2) : HIGH);
if (nowAlarm != alarm) {
alarm = nowAlarm;
String msg = "{\"alarm\":" + String(alarm ? "true" : "false") +
",\"smoke\":" + String(smoke) +
",\"flame\":" + String(flame ? "true" : "false") +
",\"ms\":" + String(millis()) + "}";
pushSSE(msg);
Serial.println(msg);
}
if (millis() - lastPush > 2000) {
lastPush = millis();
String msg = "{\"smoke\":" + String(smoke) +
",\"flame\":" + String(flame ? "true" : "false") + "}";
pushSSE(msg);
}
}
delay(5);
}
调试步骤
先只接ESP32和状态灯,确认串口与供电正常;再接MQ-2,预热后记录洁净空气下的ADC基线,用打火机不点火的燃气靠近探头,观察34读数上升并调整代码阈值;随后接火焰传感器,用打火机火焰在安全距离外靠近,观察35是否变为低电平,必要时调模块电位器;单独测试蜂鸣器和LED,确认27高电平鸣叫、2正常常亮;连WiFi后访问设备IP,打开看板确认SSE数据持续刷新;最后联调报警与恢复,记录误报情况。调试时不要用真实油锅起火试验,避免烫伤和火灾风险。