四传感器融合 + MQTT 远程推送

一、系统功能拆解

功能模块实现方式设计要点
烟雾监测MQ-2(AO → ADC)MOS 气敏,烟雾+可燃气体综合响应
温湿度DHT11(单总线)温升速率参与火灾判定(双因子之一)
火焰检测红外火焰传感器(YG1006 类远红外管模块)响应 760~1100nm 火焰红外,别拿环境光传感器凑数
CO 监测MQ-7(AO → ADC)专为 CO 设计,加热周期交替工作
报警输出有源蜂鸣器 + LED本地声光报警(补上原文缺失的电路)
阈值设定3 按键(模式/+/−)OLED 菜单式设定四路阈值
远程推送ESP8266-01S(AT 固件,MQTT)异常事件推送 + 周期数据上报
显示OLED12864(I2C)四路参数 + 报警状态轮显

1.1 火灾判定:从"单路超标就叫"到"双因子确认"

双因子确认 + 分级报警:

单因子预警(只推送,不本地报警):
  任一参数超阈值 → OLED 显示"WARNING" → MQTT 推送预警消息 → 持续计时

双因子火警(本地报警 + 推送):
  以下任一组合在 10s 窗口内同时成立:
  ① 烟雾超标 AND (温升速率 > 1°C/s 或温度 > 50°C)
  ② 火焰直接检测到(火焰单因子即可触发火警——明火没有商量余地)
  ③ CO 超标 AND 烟雾超标
  → 蜂鸣器连续鸣响 + LED 闪烁 + MQTT 推送 FIRE ALARM

CO 单独超标 → 按"燃气/燃烧不充分"事件处理:本地报警 + 推送(CO 中毒风险高,单因子也必须报)

这套逻辑代码量不到 50 行,但答辩演示时"喷一口烟不报警、烟+打火机立刻报警"的效果,比纯阈值比较有说服力得多。


二、模块选型对比(含成本)

2.1 主控:STM32F103C8T6 vs PIC32MX795

选 C8T6,理由充分:本系统外设需求 = 2 路 ADC + 1 路单总线 + 1 路 I2C + 1 路串口 + 若干 GPIO,C8T6 绰绰有余,且比 PIC32 便宜一半。这条选型没大毛病,只有一处小错:4.3.1 节说 C8T6 有"多个 GPIO 端口(PA、PB、PC、PD)"——C8T6(LQFP48)只有 PA、PB 和残缺的 PC13~PC15,PD 端口在 64/100 脚以上的型号才有。

2.2 气体传感器:把 CO 和 CO₂ 分清楚

先上对照表,这三张脸必须认全:

传感器检测目标原理本系统角色
MQ-2烟雾、液化气、甲烷等可燃气体SnO₂ 半导体气敏(加热)烟雾监测 ✅
MQ-7一氧化碳(CO),10~1000ppmSnO₂ 半导体气敏(高低温交替加热)CO 监测 ✅(修正后)
MQ-135氨气、苯系物、烟雾等空气质量SnO₂ 半导体气敏(加热)
SCD30二氧化碳(CO₂),NDIR 红外非分散红外测 CO₂ 才用它,原文拿来对比测 CO

原论文 4.2.5 节的混乱程度:标题"CO 检测模块选型",方案 1 写 MQ-135"基于电化学传感技术,检测包括二氧化碳在内的多种气体,CO 浓度测量范围 10-1000ppm",方案 2 拿 SCD30(纯 CO₂ 传感器)说"提供高精度的 CO 浓度测量"——一节之内,电化学/MOS 两种原理、CO/CO₂ 两种气体全搅在一起。而且它 4.3.5 节又自己打脸写了"加热元件将 SnO₂ 加热至约 200°C"——SnO₂ 加热是 MOS 气敏,不是电化学。

修正:CO 检测换 MQ-7(约 9 元),它就是为 CO 设计的。另注意 MQ-7 工作条件特殊:高低温交替加热(60s 高温 5V / 90s 低温 1.4V),读数只在低温段有效——这个坑在软件节展开。

2.3 火焰检测:TEMT6000 是环境光传感器(原文翻车点)

TEMT6000 是 Vishay 的环境光传感器(可见光光电三极管,光谱响应 540nm 左右,模拟人眼),它测的是"房间有多亮",不是"有没有火"。原文 4.2.4 说它"精确测量火焰发出的红外辐射,可在 125°C 以上高温环境稳定工作"——TEMT6000 的额定工作温度上限是 100°C,且它对火焰的响应本质是"火很亮",对强阳光、灯光一样响应,等于把"开个灯"当火警。

修正:换红外火焰传感器模块(YG1006 类远红外接收管 + 比较器,约 3 元),检测波长 760~1100nm,对火焰红外辐射敏感,对可见光干扰有一定抑制。调试要点:检测角度约 60°,避免正对阳光/白炽灯安装。

2.4 显示 / 无线:OLED12864 + ESP8266-01S

  • OLED12864(I2C):正确,沿用;
  • ESP8266-01S:能用,但供电必须注意——01S 在 Wi-Fi 发射瞬间峰值电流可达 300mA 以上,C8T6 最小系统板上的 1117 LDO 勉强够但发热明显,建议加一颗 470μF 电容在 01S 的 VCC-GND 附近,或直接用 AMS1117 独立供电。原文完全没提供电问题,这是 ESP8266 系毕设的通病。

2.5 BOM 成本清单

器件型号/规格数量参考价
主控板STM32F103C8T6 最小系统板1¥15
温湿度DHT111¥4
烟雾MQ-2 模块1¥7
COMQ-7 模块1¥9
火焰YG1006 红外火焰传感器模块1¥3
无线ESP8266-01S1¥10
显示0.96" OLED12864 I2C1¥10
报警有源蜂鸣器 + 红色 LED各1¥3
按键轻触按键3¥1
电源、电阻、杜邦线、洞洞板杂若干¥20
合计≈¥82

三、硬件接线(含原文翻车点修正)

3.1 接线总表

STM32 引脚外设说明
PA0DHT11 DATA单总线,10kΩ 上拉 3.3V
PA1MQ-2 AOADC1_IN1,分压后接入(见 3.2)
PA2MQ-7 AOADC1_IN2,同上
PB0火焰传感器 DO数字输入,多数模块检测到火焰输出低电平(实测为准)
PA5/PA6/PA7按键(模式/+/−)内部上拉,按下接地
PB1蜂鸣器推挽输出,驱动有源蜂鸣器(小电流模块可直推,≥30mA 需三极管)
PB5报警 LED推挽输出 + 1kΩ 限流
PA9/PA10ESP8266 RX/TXUSART1,115200,交叉连接(MCU TX→ESP RX)
PB6/PB7OLED SCL/SDAI2C1,4.7kΩ 上拉
5VMQ-2、MQ-7、火焰模块气敏传感器必须 5V 供电
3.3VOLED、DHT11、ESP826601S 电源处并 470μF 电容

3.2 接线坑 ①:ADC 参考电压是 3.3V,不是 5V(原文两处写错)

原文 4.4.3 写"电压范围为 0-5V,ADC 分辨率 12 位",4.4.5 又写"使用参考电压(5V)"——STM32 的 ADC 参考是 VDDA=3.3V。更麻烦的是 MQ-2/MQ-7 模块 5V 供电时 AO 输出 0~5V,直接接 PA1/PA2 会超量程。

修正:两路 AO 各加 10kΩ+20kΩ 分压(分压比 2/3),换算公式 V = adc * 3.3 / 4096 * 1.5。这和纯软件错误不同——是硬件接线就必须处理的,忘加分压的话 STM32 引脚长期在超量程电压下工作,ESD 保护二极管会慢慢漏电,板子用一阵子开始随机跳 ADC 值,极难排查。

3.3 接线坑 ②:蜂鸣器电路

如果用裸蜂鸣器:PB1 → 1kΩ → S8050 基极,集电极接蜂鸣器负极,蜂鸣器正极接 5V,发射极接 GND。酒店场景建议用105dB 以上的有源蜂警器(约 5 元),穿过两堵墙还能听见的那种。

3.4 接线坑 ③:火焰传感器

红外火焰管对太阳光中的红外成分也有响应。安装位置避开窗户直射光,检测面朝房间内侧。如果环境实在避不开,软件上把火焰设为"仅双因子参与项"(火焰+烟雾同时成立才报火警),但默认还是单因子触发——酒店宁可误报不能漏报。


四、软件设计(完整源码)

4.1 主程序(双因子判定 + MQTT 上报)

#include "stm32f10x.h"
#include "delay.h"
#include "dht11.h"
#include "oled.h"
#include "key.h"
#include "adc.h"
#include "flame.h"
#include "buzzer.h"
#include "esp8266_mqtt.h"

/* ---- 阈值(按键可调,EEPROM 可选保存)---- */
typedef struct {
    float smoke_v;      /* MQ-2 报警电压阈值 */
    float co_v;        /* MQ-7 报警电压阈值 */
    float temp_high;   /* 高温阈值 */
    float temp_rate;   /* 温升速率阈值 °C/s */
} Thresh_t;

static Thresh_t th = { 2.0f, 1.8f, 50.0f, 1.0f };

/* ---- 双因子状态 ---- */
static uint8_t fct_smoke, fct_cotemp, fct_flame, fct_co;
static float   last_temp = 25.0f;

int main(void)
{
    SysTick_Init(); OLED_Init(); Key_Init(); DHT11_Init();
    ADC1_Init(); Flame_Init(); Buzzer_Init();
    ESP8266_Init();                 /* 连Wi-Fi + 连MQTT broker,失败不阻塞报警 */

    while (1)
    {
        /* ---- 1. 采集 ---- */
        uint8_t hum, tmp;
        DHT11_Read(&hum, &tmp);                     /* 1.5s一次更稳 */
        float v_smoke = MQ2_ReadVoltage();
        float v_co    = MQ7_ReadVoltage();
        uint8_t flame = Flame_Detected();
        float temp_rate = (tmp - last_temp) / 1.5f; /* °C/s */
        last_temp = tmp;

        /* ---- 2. 因子计算 ---- */
        fct_smoke = (v_smoke > th.smoke_v);
        fct_cotemp = ((tmp > th.temp_high) || (temp_rate > th.temp_rate));
        fct_flame = flame;
        fct_co = (v_co > th.co_v);

        /* ---- 3. 分级判定 ---- */
        uint8_t fire = 0, warn = 0, coEvent = 0;
        if (fct_flame)                        fire = 1;   /* 明火零容忍 */
        if (fct_smoke && fct_cotemp)          fire = 1;   /* 烟+温 双因子 */
        if (fct_smoke && fct_co)              fire = 1;   /* 烟+CO 双因子 */
        if (fct_smoke || fct_cotemp)          warn = 1;   /* 单因子预警 */
        if (fct_co)                           coEvent = 1;/* CO 事件独立报 */

        /* ---- 4. 输出 ---- */
        if (fire)      { Buzzer_On();  LED_BlinkFast(); MQTT_Publish("hotel/room101/alarm", "FIRE"); }
        else if (coEvent) { Buzzer_On(); LED_On();     MQTT_Publish("hotel/room101/alarm", "CO"); }
        else if (warn) { Buzzer_Off(); LED_BlinkSlow(); MQTT_Publish("hotel/room101/warn",  "WARN"); }
        else           { Buzzer_Off(); LED_Off(); }

        /* ---- 5. 阈值设定(按键进入菜单)---- */
        Key_ThresholdMenu(&th);

        /* ---- 6. 显示 + 周期上报 ---- */
        OLED_ShowAll(tmp, hum, v_smoke, v_co, flame, fire|coEvent|warn);
        MQTT_PeriodicReport(tmp, hum, v_smoke, v_co);  /* 30s一次 */
        delay_ms(10);
    }
}

4.2 MQ-7 交替加热读取(多数人不知道的坑)

MQ-7 的 datasheet 要求5V 高温加热 60s(清除表面附着物)→ 1.4V 低温加热 90s(检测段),只有低温段的读数才反映 CO 浓度。而常见 MQ-7 模块(蓝色那种)AO 输出是模块内部决定的,两种用法:

  1. 带交替加热控制脚的模块:用一路 PWM(或定时切换 GPIO+稳压)控制加热电压,按时序切换,在低温段读 ADC——严谨做法;
  2. 普通模块(加热恒 5V):牺牲精度,只能做相对监测,阈值靠现场标定。毕设演示用这种,但论文里要写清楚局限,别吹 ppm 精度。
/* 用法2(恒压模块):滑动平均 + 预热判断 */
float MQ7_ReadVoltage(void)
{
    static float filt = 0.0f;
    float v = ADC1_ReadCH(2) * 3.3f / 4096.0f * 1.5f;   /* 分压补偿 */
    filt = filt * 0.9f + v * 0.1f;                       /* 一阶低通 */
    return filt;
}

/* 用法1(可控加热):状态机 */
typedef enum { MQ7_HEAT_HIGH, MQ7_HEAT_LOW } MQ7State;
static MQ7State mq7 = MQ7_HEAT_HIGH;
static uint32_t t0 = 0;
static float mq7_val = 0;

void MQ7_Task(void)   /* 主循环每100ms调用一次 */
{
    uint32_t now = GetTick();
    switch (mq7) {
    case MQ7_HEAT_HIGH:                       /* 5V 加热 60s */
        Heater_5V();
        if (now - t0 >= 60000) { t0 = now; mq7 = MQ7_HEAT_LOW; }
        break;
    case MQ7_HEAT_LOW:                        /* 1.4V 加热 90s,后30s读数 */
        Heater_1V4();
        if (now - t0 >= 60000) mq7_val = MQ7_ReadVoltage();
        if (now - t0 >= 90000) { t0 = now; mq7 = MQ7_HEAT_HIGH; }
        break;
    }
}

注意 MQ-2 没有 MQ-7 这么讲究,恒 5V 加热即可,但两者上电都要预热 2 分钟以上,预热期的读数毫无意义——调试时看到上电 ADC 狂跳不是坏了,是传感器还没热透。

4.3 DHT11 读取(时序沿用标准实现)

单总线时序:主机拉低 20ms → 释放 30μs → 等 DHT11 的 80μs 低 + 80μs 高响应 → 收 40bit(每 bit 前导 50μs 低,高电平 26~28μs=0 / 70μs=1)→ 校验前 4 字节和的低 8 位。完整代码与常见坑(关中断、≤1Hz 读取、上拉电阻)在《火灾报警系统》一篇里已详述,此处不重复贴,工程直接移植 dht11.c。

用温升速率做火灾因子时注意:DHT11 采样最快 1Hz,用 1.5s 间隔算速率,rate = (t_now - t_last) / 1.5。明火蔓延时室温每秒可升好几度,阈值 1°C/s 是安全下限。

4.4 ESP8266 AT 指令连接(修正原文顺序错误)

原文 4.4.7 的流程是:连 Wi-Fi → AT+CIPMUX=0 → AT+CIPSEND 进透传 → AT+CIPSTART 建 TCP 连接。顺序反了:AT+CIPSEND 必须在 AT+CIPSTART 建立连接之后才能执行,先 SEND 后 START 只会收到 link is not valid 错误。而且第二章大篇幅介绍 MQTT 协议,软件流程却是裸 TCP 透传——讲的是 MQTT,用的是 TCP,云平台那头按 MQTT 报文解析的话,透传的裸数据根本不会被识别。

修正流程(AT 固件跑真 MQTT,顺序不能乱):

/* 正确顺序:复位 → 模式 → 连网 → 单连接 → 建TCP → (透传)→ 发数据 */
1. AT+RST                    → 等 "ready"
2. AT+CWMODE=1               → STA模式
3. AT+CWJAP="SSID","PASS"    → 等 "WIFI GOT IP"
4. AT+CIPMUX=0               → 单连接
5. AT+CIPSTART="TCP","broker_ip",1883   → 等 "CONNECT OK"    ← 先建连接
6. AT+CIPMODE=1              → 透传模式
7. AT+CIPSEND                → 进透传,之后串口数据直发服务器  ← 后发数据
   此后发送的是标准 MQTT 报文(CONNECT/PUBLISH/SUBSCRIBE)
8. 退出透传:单独发送 "+++"(前后不回车,1秒内不发其它数据)
/* MQTT PUBLISH 报文手工组包(QoS0,主题 "hotel/room101/alarm")*/
void MQTT_Publish(const char *topic, const char *msg)
{
    uint8_t pkt[128];
    uint8_t tl = strlen(topic), ml = strlen(msg);
    uint8_t i = 0;
    /* 剩余长度 = 2 + tl + ml */
    pkt[i++] = 0x30;                    /* PUBLISH, QoS0 */
    pkt[i++] = 2 + tl + ml;             /* 剩余长度(假设<128) */
    pkt[i++] = 0x00; pkt[i++] = tl;     /* 主题长度 */
    memcpy(&pkt[i], topic, tl); i += tl;
    memcpy(&pkt[i], msg, ml);   i += ml;
    UART1_SendBuf(pkt, i);              /* 透传模式下直接发 */
}

ESP8266-01S 的 AT 固件(新版)也支持 AT+MQTTUSERCONFIG/AT+MQTTCONN/AT+MQTTPUB 原生 MQTT 指令集,如果你的 01S 刷的是 1.7+ 固件,直接用这套更省事——但毕设里"手组 MQTT 报文"更能证明你看懂了协议。

4.5 OLED 轮显 + 阈值菜单

void OLED_ShowAll(int8_t t, uint8_t h, float vs, float vc, uint8_t fl, uint8_t al)
{
    OLED_ShowString(0, 0, "Hotel Fire Alarm");
    OLED_ShowString(0,12, "T:xx C  H:xx %");  OLED_ShowNum(20,12,t,2);  OLED_ShowNum(68,12,h,2);
    OLED_ShowString(0,24, "Smoke:x.xx V");    OLED_ShowFloat(48,24,vs,2);
    OLED_ShowString(0,36, "CO   :x.xx V");    OLED_ShowFloat(48,36,vc,2);
    OLED_ShowString(0,48, fl ? "Flame: YES!" : "Flame: no  ");
    OLED_ShowString(0,56, al ? "!! ALARM !!" : "Status: OK  ");
}

五、系统调试与验证

调试项方法通过标准常见不通过原因
MQ-2 预热与标定上电 2min 后记录清洁空气 AO 电压 V0V0 稳定(漂移 <0.1V/min)没等预热就标定,阈值虚低导致常报警
烟雾响应香烟烟雾/灭蚊片靠近电压明显上升,超阈值 → 单因子预警(不本地报警)阈值贴着 V0 设,导致常态误报
火焰检测打火机小火苗 30cm 外DO 跳变 → 立即火警传感器对着窗户(阳光误触发);电平极性搞反
双因子验证只喷烟雾不加温 → 不报火警;烟雾+打火机 → 立即报分级正确温升速率算错符号/间隔
CO 响应MQ-7 靠近打火机未点燃的放气口CO 电压上升超阈值 → CO 事件报警MQ-7 没预热 48h 老化,读数漂
蜂鸣器触发火警声压足够(隔墙可闻)用无源蜂鸣器忘了驱动方波
MQTT 上报手机 MQTT 调试助手订阅 hotel/room101/#收到周期数据与报警消息CIPSEND/CIPSTART 顺序反了;broker 地址写成网址未解析
断网兜底拔掉 ESP8266 再触发火警本地报警不受影响通信失败用了死等(加了 while 等待重连)——报警链路绝不能被网络阻塞

MQTT 调试一招:电脑上用 MQTTX 或 mosquitto_sub 订阅主题,先验证 ESP8266 数据链路,再去接 APP。分层排查,别一上来就端到端联调。


六、可扩展方向

  1. 烟感换光电式专用探测器:MQ-2 是"泛可燃气体"传感器,香烟、香水、酒精喷雾全都会抬它的读数。换成光学式烟雾探测(红外散射迷宫结构,如谜你光电烟感拆机模块),抗干扰上一个台阶——这才对得起"减少误报"的设计目标。
  2. 多房间组网:一个 ESP8266 一个房间太贵,改 RS485 总线把多个探测节点串起来,一台主机带 ESP8266 统一上报——真实酒店报警系统的架构雏形。
  3. MQ-7 严格时序版:用 PWM(TIM 输出 1.4V 等效均值)精确控制交替加热,把 CO 读数从"相对值"升级为"半定量值"。
  4. 本地存储报警历史:SPI Flash 或 EEPROM 记录报警事件(时间戳+触发因子),配合 RTC 模块,OLED 加查询菜单——"历史数据查看"是原文摘要承诺了但同样没实现的功能。
  5. 声光报警升级:加 12V 声光警号(需继电器/场效应管驱动),演示效果直接拉满。

七、资料内容完整工程资料(原理图、PCB、代码、元件清单)


八、资料获取方式

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐