💡 划重点

  • 用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数据持续刷新;最后联调报警与恢复,记录误报情况。调试时不要用真实油锅起火试验,避免烫伤和火灾风险。

设计图

厨房烟雾+火焰双重报警结构flow
1
感知输入
→
2
控制处理
→
3
执行反馈
→
4
数据上报