STM32端侧判断的火灾感知节点:多传感器采集与MQTT上报

STM32端侧判断的火灾感知节点:多传感器采集与MQTT上报 简介面向嵌入式入门到进阶学习者、课程设计与毕业设计开发者的一份火灾感知系统实战方案围绕 STM32F103RCT6 主控搭建温湿度、火焰、烟雾多传感器融合的本地预警与云端联动链路。内容覆盖硬件选型与原理图、SHT30 与 MQ2 等模块技术细节、ESP8266 联网、华为云 IoT 平台产品创建与设备注册、MQTT 主题订阅发布及三元组配置还包含 Qt 上位机界面开发与 Android 端交互协议、Keil 工程移植与主逻辑代码分析形成从设备端到云端的完整闭环。整包为 1 个 PDF 文件共 30.34MB按章节顺序编排图文与代码片段集中呈现便于通读或检索查阅。已有 240 人学习适合借助真实项目理解物联网火灾预警的数据采集、报警控制与远程监控实现思路也可作为答辩材料与二次开发的参考底稿。1. 为什么火灾感知节点更适合用 STM32 做端侧判断在实验室、小仓库或出租屋做火灾感知最常见的问题是烟雾传感器输出模拟量直接接 Wi-Fi 模块要么没有 ADC要么被网络任务打断采样。STM32 的价值在于它把「采、滤、判、报」拆开ADC 和定时器负责稳定采样GPIO 中断接火焰开关量看门狗兜底Wi-Fi 只做透明传输。这样网络断了本地报警仍然有效也不会因为一次丢包把误报传到手机。适合做物联网毕业设计、课程设计也适合小规模现场部署。后面按硬件、采集、上报、排错四段推进代码基于常见 STM32F103 HAL 库思路换到其他 STM32 型号时改引脚和时钟即可。2. STM32 火灾感知节点的硬件选型与最小系统搭建2.1 火灾感知三类传感器的接口与量程对照火灾早期特征通常不是明火而是烟雾颗粒和温升。选型时不要只盯一个传感器MQ-2 对烟雾和可燃气体敏感DS18B20 负责温度趋势火焰传感器只在明火阶段触发。三者接口不同正好对应 STM32 的 ADC、单总线和 GPIO 中断。传感器接口典型输出适用阶段注意点MQ-2 烟雾ADC 模拟0.3~2.8V阴燃、烟雾需预热湿度影响大DS18B20单总线数字温度温升判断走线长要加 4.7k 上拉火焰传感器GPIO/ADC数字低电平明火易受日光干扰要遮光DHT22单总线温湿度环境补偿响应慢不适合秒级报警供电上MQ-2 加热丝电流能到 150mA 左右Wi-Fi 发射瞬间也有 200mA 以上脉冲。AMS1117 如果只挂一个 100uF 电容ADC 基准会跟着 Wi-Fi 一起跳烟雾电压读数就会漂。常见做法是 3.3V 和 5V 分开走MQ-2 用 5V 加热AO 输出经分压后再进 STM32 的 ADC 引脚。2.2 STM32 最小系统与 GPIO/ADC 接线以 STM32F103C8T6 为例最小系统只需要 8MHz 晶振、复位电路、BOOT0 下拉和 SWD 调试口。在 Keil5 里装好 STM32 芯片包之后新建工程时把 PA0 配成 ADC1_IN0PA1 配成带中断的 GPIO 输入PA2 配成开漏输出接 DS18B20PA9/PA10 接 ESP8266 串口PB0 推挽输出接蜂鸣器。下面这段初始化代码把 ADC 和 GPIO 一次配好。// PA0: ADC1_IN0 接 MQ-2 AOPA1: 火焰 DO 下降沿中断PB0: 蜂鸣器 static void Board_GPIO_ADC_Init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_ADC1_CLK_ENABLE(); GPIO_InitTypeDef gpio {0}; gpio.Pin GPIO_PIN_0; gpio.Mode GPIO_MODE_ANALOG; // 模拟输入不配上拉 HAL_GPIO_Init(GPIOA, gpio); gpio.Pin GPIO_PIN_1; gpio.Mode GPIO_MODE_IT_FALLING; // 火焰低电平有效 gpio.Pull GPIO_PULLUP; HAL_GPIO_Init(GPIOA, gpio); gpio.Pin GPIO_PIN_0; gpio.Mode GPIO_MODE_OUTPUT_PP; // 蜂鸣器推挽输出 gpio.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, gpio); }这段代码里PA0 设成模拟输入是为了让 ADC 直接采集不会引入内部上拉误差PA1 设成下降沿中断火焰传感器检测到明火时输出低电平直接触发中断比轮询快。PB0 控制蜂鸣器推挽输出能直接驱动小功率蜂鸣器如果蜂鸣器电流超过 20mA中间加三极管或 ULN2003A 这类驱动芯片。2.3 复位、看门狗与调试口保留火灾感知节点最怕程序卡死本地报警也不响。独立看门狗 IWDG 用内部 40kHz 时钟分频后喂狗周期可以设到几百毫秒到几秒。主循环里定时喂狗如果某个传感器读取卡住看门狗复位后重新初始化。调试口 SWD 要保留不要为了省两个引脚把 PA13/PA14 复用掉否则后面排错只能靠串口打印。注意IWDG 一旦启动就不能靠软件关闭调试时先在初始化里加条件编译避免单步调试时被反复复位。3. 用 STM32 采集烟雾、温度、火焰三类信号的代码实现3.1 定时器触发 ADC 采样烟雾模拟量烟雾信号不是越快越好。MQ-2 的输出本身有波动用 STM32 定时器触发 ADC再让 DMA 搬 64 个点既减轻 CPU 负担也方便做滑动平均。下面用 TIM3 的更新事件触发 ADC1采样率约 1kHz64 点窗口约 64ms。// TIM3 触发 ADC1DMA 循环搬运 64 个采样点到 adc_buf static uint16_t adc_buf[64]; static void ADC_TIM_DMA_Init(void) { // TIM3: 72MHz / (711) / (9991) 1kHz 更新事件 htim3.Instance TIM3; htim3.Init.Prescaler 71; htim3.Init.Period 999; htim3.Init.CounterMode TIM_COUNTERMODE_UP; HAL_TIM_Base_Init(htim3); hadc1.Instance ADC1; hadc1.Init.ScanConvMode ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode DISABLE; hadc1.Init.ExternalTrigConv ADC_EXTERNALTRIGCONV_T3_TRGO; hadc1.Init.DataAlign ADC_DATAALIGN_RIGHT; HAL_ADC_Init(hadc1); HAL_ADC_Start_DMA(hadc1, (uint32_t *)adc_buf, 64); HAL_TIM_Base_Start(htim3); }逻辑说明TIM3 每 1ms 产生一次更新事件ADC 启动一次转换DMA 把结果搬到数组。参数上预分频 71 和重装载 999 对应 1kHz如果你把系统时钟改成 48MHz这两个值要跟着改。ADC 是 12 位参考电压 3.3V所以电压换算为adc_buf[i] * 3.3f / 4095.0f。MQ-2 干净空气下大约 0.5V 到 1.0V烟雾升起后能到 1.5V 以上具体阈值要在现场标定。3.2 DS18B20 读取温度与 CRC 校验温度用 DS18B20 单总线读关键点不是读得快而是读到坏数据要能识别。DS18B20 的暂存器第 9 字节是 CRC校验不过直接丢弃不要拿错误温度去判断火灾。// 读 DS18B20返回摄氏度失败返回 -1000 float DS18B20_ReadTemp(void) { uint8_t buf[9]; if (!DS18B20_Reset()) return -1000; DS18B20_WriteByte(0xCC); // 跳过 ROM单节点场景够用 DS18B20_WriteByte(0x44); // 启动温度转换 HAL_Delay(750); // 12 位精度最大转换时间 if (!DS18B20_Reset()) return -1000; DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读暂存器 for (int i 0; i 9; i) buf[i] DS18B20_ReadByte(); if (buf[8] ! DS18B20_CRC8(buf, 8)) return -1000; int16_t raw (buf[1] 8) | buf[0]; return raw * 0.0625f; // 12 位分辨率每 LSB 0.0625 度 }参数说明HAL_Delay(750)对应 12 位精度转换时间如果改成 9 位精度可以缩短到 94ms但火灾判断里 750ms 完全可以接受。CRC 校验失败返回-1000上层看到这个值就知道本次温度无效不会拿 85.0 这种上电默认值去报警。3.3 火焰 GPIO 中断与去抖火焰传感器在明火出现时会拉低 DO但日光灯、打火机近距离测试也会造成抖动。中断里只置标志不做复杂判断主循环里再结合烟雾和温度综合判断。// PA1 火焰 DO 下降沿中断10ms 去抖 volatile uint8_t flame_flag 0; static uint32_t last_flame_tick 0; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin GPIO_PIN_1) { uint32_t now HAL_GetTick(); if (now - last_flame_tick 10) { flame_flag 1; last_flame_tick now; } } }这里用HAL_GetTick()做 10ms 去抖适合火焰这种开关量。中断函数里不调用HAL_Delay也不做串口打印避免在中断上下文里长时间阻塞。主循环每 100ms 检查一次flame_flag如果连续三次为 1再触发报警。4. 从 STM32 到 MQTTESP8266 上报、报警联动与看门狗4.1 ESP8266 与 STM32 串口 AT 指令对接STM32 通过 UART 发 AT 指令控制 ESP8266常见流程是配模式、连热点、连 TCP、发 MQTT 报文。串口波特率先用 115200如果丢包严重降到 9600。下面这段是发送函数的核心逻辑。// 向 ESP8266 发送 AT 指令等待 OK uint8_t ESP_SendAT(const char *cmd, uint32_t timeout_ms) { HAL_UART_Transmit(huart1, (uint8_t *)cmd, strlen(cmd), 100); HAL_UART_Transmit(huart1, (uint8_t *)\r\n, 2, 100); return ESP_WaitResponse(OK, timeout_ms); } // 上报 JSON主题由上层拼接 void Report_Fire_Data(float smoke_v, float temp, uint8_t flame) { char json[128]; snprintf(json, sizeof(json), {\smoke\:%.2f,\temp\:%.1f,\flame\:%d}, smoke_v, temp, flame); ESP_SendAT(ATCIPSEND48, 2000); // 先声明长度 HAL_Delay(50); HAL_UART_Transmit(huart1, (uint8_t *)json, strlen(json), 1000); }逻辑说明AT 指令的响应解析很脆弱工程上不要用HAL_Delay死等而是用ESP_WaitResponse在超时内轮询串口空闲中断收到的缓冲区。参数上ATCIPSEND后面的长度要和实际 JSON 长度一致否则 ESP8266 会一直等剩余字节。如果现场丢包多把timeout_ms从 2000 放宽到 5000并增加重发次数。4.2 MQTT 主题与 JSON 报文格式主题设计要能区分楼栋、房间和节点。建议用fire/{site}/{node}/data发数据fire/{site}/{node}/alarm发报警fire/{site}/{node}/cmd接收下行。QoS 用 1保证报警至少到达一次。主题方向QoS载荷示例fire/site01/node01/data上行1{smoke:1.82,temp:34.5,flame:0}fire/site01/node01/alarm上行1{type:smoke,level:2}fire/site01/node01/cmd下行1{buzzer:1}本地可以用 Python 脚本订阅验证确认 STM32 上报的 JSON 能被正确解析。import paho.mqtt.client as mqtt def on_message(client, userdata, msg): print(topic:, msg.topic) print(payload:, msg.payload.decode()) # 这里可以接入短信、邮件或本地声光报警 client mqtt.Client(fire_test) client.on_message on_message client.connect(broker.local, 1883, 60) client.subscribe(fire/site01/node01/#, qos1) client.loop_forever()4.3 本地报警联动与看门狗喂狗策略报警判断不能只看单一传感器。烟雾电压高、温度超过 50 度、火焰标志为 1三个条件满足任意两个才升级为报警否则只记 warning。主循环里做状态机同时喂 IWDG。// 主循环采集、判断、上报、喂狗 while (1) { float smoke_v ADC_GetVoltage(); float temp DS18B20_ReadTemp(); uint8_t flame flame_flag; if (smoke_v 1.5f temp 50.0f) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // 蜂鸣器 Report_Fire_Data(smoke_v, temp, flame); } else if (flame) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); Report_Fire_Data(smoke_v, temp, flame); } else { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); } HAL_IWDG_Refresh(hiwdg); // 喂狗周期 1s HAL_Delay(100); }看门狗预分频和重装载决定复位时间IWDG 时钟 40kHz分频 64、重装载 625 大约 1s。主循环里每 100ms 喂一次留出足够余量。如果某个传感器读取阻塞超过 1s看门狗直接复位节点重新初始化并尝试重连。5. 阈值整定、误报排查与低功耗部署的进阶技巧5.1 用温升速率和滑动窗口压误报固定阈值最容易在做饭、抽烟、加湿器喷雾时误报。更稳的做法是看温升速率10 分钟内温度上升超过 8 度同时烟雾电压持续高于基线 0.4V才进入预警。烟雾用 64 点滑动窗口取中位数再取平均能滤掉尖峰。代码上可以在主循环外开一个 10 分钟窗口记录初始温度和当前温度。判据阈值持续时间动作烟雾电压高于基线 0.4V连续 5swarning温升速率10min 上升 8 度连续 2 次warning温度绝对值大于 60 度连续 3salarm火焰下降沿中断3 次去抖后alarm现场标定的时候先把节点放在干净空气里跑 10 分钟记录 MQ-2 电压作为基线再点一根线香放在半米外观察电压上升曲线取上升沿中段作为预警阈值。不要用出厂电位器随便调那个值换一个环境就偏。5.2 现场排错ADC 跳变、串口丢包、看门狗复位ADC 跳变先看电源MQ-2 加热丝和 Wi-Fi 发射共用 3.3V 时ADC 读数会跟着 Wi-Fi 周期波动。把 MQ-2 移到 5V 供电模拟输出分压后再进 ADC跳变通常会小一个数量级。串口丢包先降波特率再检查 ESP8266 的供电电容100uF 不够就并 470uF。看门狗复位频繁多半是某个HAL_Delay等传感器超时把等待改成带超时的循环超时后标记传感器故障而不是一直卡住。5.3 低功耗部署与无源物联网的折中如果节点装在取电困难的点位可以让 STM32 在空闲时进 STOP 模式RTC 定时唤醒每 30 秒采一次Wi-Fi 只在报警时上电。MQ-2 加热丝持续耗电不适合电池长期供电可以换成低功耗烟雾传感器或者用间隙加热。无源物联网的思路是用能量采集代替电池但当前阶段更现实的方案是低功耗休眠加本地声光报警云端只做远程通知。最后一招把报警判断放在 STM32 端Wi-Fi 断连时蜂鸣器仍然按本地逻辑响远程恢复后补发最近 10 条报警记录。本文还有配套的精品资源点击获取