
做嵌入式这几年见过不少课设级别的消防预警最多就是烟雾传感器加个蜂鸣器——响是响了然后呢实验室里发生不明火情时最希望它做的事不是喊叫而是断电、断气、开排风。这套基于STM32F103的实验室消防预警控制系统从一开始就奔着感知-判断-联动的闭环去设计三路检测源同时盯烟雾、温度和火焰检测结果交给状态机判断一旦确认火情联动声光报警、排风扇、继电器断电和灭火装置。代码、原理图和Proteus仿真工程全部开源适合做嵌入式课设、毕业设计也适合实验室安全改造参考。这套系统不是什么高不可攀的黑科技但它把检测—确认—动作的完整链路做了出来比单纯堆传感器有工程意义得多。1. 实验室消防预警从能响到能联动的思维转变1.1 实验室火灾为什么不能照搬住宅烟雾报警器住宅烟雾报警器是一个典型的只管叫、不管救的设备。它在楼道里冒烟时刺耳报警然后就没有然后了。实验室完全不是这个逻辑化学试剂、精密仪器、通风橱、加热设备挤在一个空间一旦发生火情留给人反应的时间可能只有几十秒而且这个场景下误报率也远比住宅高——电烙铁焊锡的松香烟、酒精灯余味、加热搅拌的轻微焦糊都足以让一只灵敏度正常的烟感神经质。所以实验室消防预警的第一需求不是响而是识别后在最短时间内切断风险源。这套系统把三路探测信号和输出联动绑定检测到异常后先通过状态机确认连续性连续5次超过阈值才进入报警避免瞬时干扰然后按预设策略依次执行排风、断电、断气、报警。这些动作如果只靠传统烟雾报警器是做不出来的。我见过不少实验室装了烟感但没接任何联动每次报警都是人跑过去看一眼这种方式在火情初期也许来得及但在化学品环境下电气线路起火往往几分钟内就能蔓延自动联动比人反应快得多。1.2 为什么选STM32F103C8T6而不是51或Arduino可能有人会问一个消防预警51单片机不够吗够用是够用但有几个硬伤。51的ADC通常只有8位或没有内置MQ-2模拟输出的精度被砍掉一截51没有硬件I2C驱动OLED得软件模拟时序代码一多就乱51的GPIO驱动能力、外部中断资源也紧张。Arduino倒是开发快但最终落地到产品级的时序稳定性、看门狗、低功耗管理都不好做而且开着IDE写跳过库函数的代码学生项目里经常出现莫名其妙的延时问题。STM32F103C8T6就是大家熟悉的蓝丸C8T6在这类项目里属于甜点位72MHz主频、2路12位ADC共10个采样通道、多路I2C/SPI/UART、2个看门狗、几十个GPIO价格还压在10元以内。对于实验室消防这种需要同时采集三路传感器、驱动多个外设、还要保证长时间可靠运行的系统C8T6的资源利用率大概在50%上下既不会捉襟见肘也没有浪费。更关键的是STM32生态太成熟了CubeMX配置引脚、Keil编译调试、ST-Link下载遇到问题一搜一大片新手学习成本被生态摊得很低。2. 系统链路与硬件选型三个传感源加一路联动输出2.1 传感器选型烟雾、温度、火焰怎么搭配烟雾通道用的是MQ-2气体传感器它在丙烷、液化气、烟雾这类可燃性气体上有比较均衡的响应。虽然它没法精确测量ppm浓度但做有烟与无烟的定性判断绰绰有余。温度通道是DS18B20一路数字信号量到0.5℃精度不需要校准而且用OneWire协议单总线就能挂多片以后想增加测温点也方便。火焰通道用近红外火焰传感器模块它对760nm-1100nm的火焰光谱敏感响应速度在毫秒级正好弥补MQ-2对明火反应偏慢的缺点。三者搭配的逻辑是互补验证烟雾传感器管阴燃温度传感器管环境异常升温火焰传感器管明火。当三类信号里有两类同时异常基本可以认为不是误报。比如电烙铁焊接产生的松香烟雾会把烟雾值抬得很高但温度不会跟着爆表火焰传感器也不会有反应此时系统只判异常/污染不触发灭火联动只做轻度提醒。这个多传感融合判断的思想是整套系统区别于简单烟感的灵魂。检测源传感器输出方式主要作用典型特征烟雾MQ-2ADC模拟量阴燃/气体泄漏响应偏慢、需预热温度DS18B20单总线数字量环境异常升温精度0.5℃、防误报火焰近红外火焰模块ADC/DIO明火瞬时发现毫秒级响应、易受干扰2.2 执行机构声光报警、排风、断电、灭火执行端我保留了4路继电器输出分别接排风扇、总电源继电器、燃气电磁阀、灭火装置比如干粉灭火器电磁阀或备用。默认联动策略是一级预警单一指标异常只亮黄灯、OLED显示异常项二级报警多指标确认启动蜂鸣器、吸合排风扇和断电继电器如果火焰传感器直接命中明火信号则直接进入最高级把所有继电器按安全序列动作——先断电、再断气、再启动灭火装置。这个动作顺序很重要先切电再喷灭火剂可以避免灭火过程中电火花二次引燃。选择继电器而不是直接用MOSFET直驱是因为实验室里的执行设备五花八门排风扇可能是交流220V的灭火装置可能是24V电磁阀继电器天然带电气隔离适配性最好。4路输出独立可控以后你想扩展成烟雾超标只开排风或者温度异常自动切断加热台改一下状态机的输出表就行硬件不用动。2.3 人机交互OLED显示与按键阈值调节交互部分我用了一块0.96寸I2C接口的OLED屏实时显示三路检测值、当前状态和本轮阈值。比1602液晶省GPIO也不需要为背光单独供电。按键保留4个设置键、加减键、确认键。长按设置键进入阈值设置模式可以分别调整烟雾、温度、火焰的报警阈值再长按退出并保存。这样做的好处是实验室里环境差异很大焊锡间和化学实验室的基线烟雾浓度差好几倍阈值如果不能现场调那系统就只能在特定场景下好用。OLED的显示刷新我用100ms一次数值跳变看起来平滑又不会因为单路I2C总线上挂太多设备导致卡顿。状态图标用三个简单的方块表示黄色方块亮了说明一级预警红色方块亮了说明二级报警绿色方块表示系统正常。这样值班的人隔着三五米就能看到系统状态不用凑到屏幕前读数字。3. 原理图关键节点照着画也不翻车的细节3.1 MQ-2的分压和预热问题MQ-2本身是5V供电它的模拟输出在洁净空气中大概0.1V-0.3V接触到烟雾后能升到4V以上。这里有一个很多人踩的坑STM32F103C8T6的ADC参考电压是3.3V你直接把MQ-2输出接PA0浓度一高电压超过3.3VADC直接顶格后面怎么判断都失真。我的做法是先用一个2:1电阻分压把模拟输出映射到0-2.5V区间同时在分压点并一个0.1μF电容做低通滤波给ADC一个干净的采样源。另外MQ-2内部有个加热丝上电后需要预热。前3分钟读数会飘得很厉害从零点几伏慢慢爬到稳定值。代码里我专门做了预热窗口进入正常工作模式前只用它做相对变化判断不做绝对阈值判断。你如果直接拿刚上电的传感器读数去跟阈值比大概率会误报一次火灾这个坑我建议所有复现者都提前记在心里。3.2 DS18B20的上拉电阻与寄生供电DS18B20的数据线必须接一个4.7kΩ上拉电阻到3.3V不接的话时序会乱读出来的温度隔几分钟乱跳一次。寄生供电方式可以省掉VDD接线但我在真机测试中发现当OLED、继电器同时动作导致3.3V有轻微波动时寄生供电的DS18B20偶尔会丢一次应答。所以原理图里我直接给了它独立的VDD简单可靠多一根线换稳定性非常值。还有个小细节DS18B20的12位转换时间最长750ms如果你在主循环里每100ms读一次并且用阻塞等待整个系统都会被拖住。我把它放到一个200ms的定时任务里每次启动转换下一次循环时读结果这样温度采样既稳定又不阻塞核心逻辑。3.3 继电器电路光耦、续流二极管和三极管驱动继电器是整个原理图里最容易画错的地方。我用的是低电平触发的5V继电器模块模块内部已经带了光耦隔离STM32的GPIO直接拉低即吸合。如果你是自己画分立继电器电路必须记住三件事GPIO必须先经过一个NPN三极管比如S8050再驱动线圈不能直接拿GPIO灌电流线圈两端必须反并一个1N4007续流二极管否则断开瞬间的反电动势轻则重启单片机重则烧GPIO基极电阻选1kΩ到2.2kΩ太小毁引脚、太大驱动不饱和。使用模块的时候还要注意有源低和有源高的区别。市面上很多5V继电器模块上的跳线默认是低电平触发你程序里得是GPIO输出低——吸合输出高——断开跟直觉正好相反。我第一次用的时候把逻辑写反了上电瞬间继电器全部吸合差点把灭火装置给触发了好在当时测试接的是LED不是真设备。3.4 电源和布线3.3V与5V的共地问题系统里有5V的MQ-2、继电器模块也有3.3V的STM32、OLED和DS18B20。共地是必须的但很多新手会犯只在原理图上画了两条电源轨没在PCB上处理地线的错。继电器吸合瞬间电流接近70mA这个电流脉冲会在地线上形成压降直接影响3.3V域。我在PCB布线时把继电器电源独立走了一路5V并在STM32的VDD脚附近加了10μF钽电容和0.1μF陶瓷电容的组合实测吸合瞬间电压跌落控制在0.15V以内系统稳定不重启。电源输入我用了一个DC005插座接12V或者9V适配器板载三路稳压一路降压到5V给传感器和继电器模块一路LDO到3.3V给MCU。如果你用USB口的5V直接给整板供电继电器动作时5V会被拉低LDO输出也会跟着抖所以要复现的话老老实实配一个12V1A以上的适配器别图省事用USB。4. 代码架构阈值判断背后的状态机与滤波4.1 主循环状态机代码没有上RTOS整个过程用一个2ms滴答驱动的主循环状态机跑。状态分为IDLE监视、FIRST_ALARM一级预警、SECOND_ALARM二级报警、RESET复位与自检。每次循环先读传感器然后喂狗接着做状态迁移判断。这样做的理由很直接消防系统最重要的是行为可预测状态机比自由裸奔的if堆叠更能保证在任何时刻都有明确的行为路径。typedef enum { ST_IDLE 0, ST_FIRST_ALARM, ST_SECOND_ALARM, ST_RESET } SysState;状态迁移表我按输入信号组合来定义只有烟雾超过阈值迁移到FIRST_ALARM温度加烟雾同时超阈值直接跳到SECOND_ALARM火焰信号单独触发也直接进SECOND_ALARM并执行最高级联动。这样设计的好处是以后你改了联动策略只动状态迁移表这一段其他代码不用碰。4.2 滑动平均滤波与连续性判据MQ-2模拟量在AD转换后有明显的抖动直接和阈值比较会频繁误报。我实现了一个深度为8的环形缓冲区滑动平均每次采样后更新平均值。同时报警不能靠单次数据触发而是要求连续5次每次间隔200ms都超过阈值才认为是真实事件。这个平均值连续计数的组合能把电火花、偶尔路过的人体红外这类瞬间干扰过滤掉同时又不牺牲真实报警的响应速度——1秒内一定给出结果。static float adc_sliding_avg(ADC_HandleTypeDef *hadc, uint32_t channel) { static float buf[8]; static uint8_t idx 0; float sum 0; buf[idx] read_adc_channel(hadc, channel); idx % 8; for (uint8_t i 0; i 8; i) sum buf[i]; return sum / 8.0f; }滤波和连续性判据合起来带来的实际效果是拿打火机在不点火的情况下对着火焰传感器晃基本不会误报用真实明火靠近200ms采样5次1秒内必报警。这个响应速度对实验室场景是够用的毕竟火焰传感器自身毫秒级响应已经补足了MQ-2的慢。4.3 阈值学习模式与按键操作阈值如果写死在代码里这套系统换个实验室就得重新烧录固件太蠢。我加了一个学习模式长按设置键进入后系统采集当前环境30秒取这段时间烟雾值平均数的1.2倍作为报警基线温度阈值则默认设为55℃火焰阈值按传感器模块输出反转后的一个固定比例。实测在焊锡间这种烟雾基线偏高的环境里学习模式出来的阈值比手工拍脑袋调的值靠谱得多误报率下降了大概70%。按键逻辑我也做了一定的消抖处理毛刺在100ms以内的按键跳变全部忽略长按需要持续800ms才进入设置模式。这样焊接时手指不小心碰到按键不会把阈值改乱。4.4 故障自检与蜂鸣器诊断系统上电后会做一次自检每个继电器吸合断开一次、蜂鸣器响两声、OLED显示自检通过。另外MQ-2如果断线或者损坏它的模拟输出通常会掉到接近0V或者顶满3.3V代码里检测到ADC值长期低于0.05V或者高于2.5V分压后就判定传感器故障OLED直接显示SMOKE SENSOR FAULT蜂鸣器每10秒响一次短声提醒维护而不是误报火警。这种故障判据在课设里属于加分项在真实系统里属于保命项——你总不希望因为传感器坏了一路整个消防系统就彻底瞎了。DS18B20的故障判断更简单如果连续多次读到的CRC校验失败或者返回85℃这个特征值就判定测温通道异常。火焰传感器的故障检测相对困难但至少能做到无输入变化报警。这些自检信息全部通过OLED轮播显示值班的人一眼就能看出是传感器故障还是真火情。5. Proteus仿真从原理图到hex再到OLED5.1 仿真环境的搭建与芯片配置仿真部分用Proteus 8.11及以上版本器件库里可以直接找到STM32F103C8T6。把Keil编译生成的hex文件加载到单片机里注意晶振频率要和CubeMX配置保持一致我这边是8MHz外部晶振PLL倍频到72MHz。实时动态会慢一些属正常现象。仿真里可以直接看到OLED显示、三个传感器输入和继电器状态变化用来验证状态机逻辑非常方便——不炸板、不冒烟、随便折腾。搭建的时候先放STM32F103C8T6再放OLED、继电器、按键、LED和电位器。注意把电源网络标注清楚Proteus里很多莫名其妙的问题都出在网络标号没对上。把hex烧进去之后如果OLED没显示八成是I2C引脚配置不对回到CubeMX里核对PB6/PB7是不是SCL/SDA。5.2 传感器模型的替代方案Proteus没有直接对应MQ-2的模型我用了两个办法替代一种是用电位器分压模拟MQ-2的模拟输出——拧大电位器就相当于加大烟雾浓度观察系统切换到报警状态另一种是用Proteus里的直流电压源加上模拟输出引脚和代码里的ADC配置对接。温度方面仿真直接调用DS18B20模型它的寄存器按真实时序工作代码不用改。火焰传感器同样用电压源模拟把ADC值推到阈值以上就能触发最高级报警。这套替代方案的逻辑是仿真阶段验证的是逻辑与控制链路传感器本身的非线性响应特性留给真机标定。5.3 仿真和真机的差距给复现者提个醒仿真跑得再顺也不能替代真机。最大的差别是电源质量仿真里电压源是理想源继电器吸合不会有压降而真机上继电器吸合瞬间可能就是一场单片机重启事故其次仿真里看不到传感器预热漂移和个体离散性一个MQ-2模块和另一个MQ-2模块相同浓度下输出可能差0.3V以上。我的建议是先用仿真把状态机、按键逻辑、显示交互全部调通再上真机把真机时间留给传感器标定和电源问题——这两块恰恰是仿真永远教不了你的。仿真里OLED显示正常不代表真机上OLED正常真机的I2C总线上拉、走线长度和电平匹配都会影响显示效果。6. 真机调试踩坑记录三个让我多花一下午的问题6.1 BOOT0跳线和ST-Link下载失败第一次上电ST-Link烧录显示成功但板子就是没反应程序完全不跑。折腾半天发现是BOOT0被跳线帽接成了1——芯片从系统存储器启动根本没执行用户Flash里的程序。解决办法BOOT0接GND0BOOT1随便然后再烧一次程序就正常跑了。这类问题非常基础但真的能卡住人半天因为报错信息永远是程序烧完了但啥都没发生而不会提示你BOOT0 wrong。还有一次是ST-Link死活连不上后来发现是我把SWDIO和SWCLK两根线插反了。蓝丸板上丝印比较小光线不好容易看错建议拿万用表先确认引脚再接线。ST-Link V2用杜邦线连接时线序别按照网上随手找的图死记每块板的丝印位置有差异。6.2 继电器吸合瞬间单片机重启这是我在第3章埋的伏笔真机第一次联调就翻车了。当我让蜂鸣器和继电器同时动作时STM32直接重启OLED闪一下白光状态机回到初始。查了半天先怀疑是开关电源功率不够换了个2A的还是一样。最后用示波器看3.3V电源轨吸合瞬间跌落到了2.7V。解决方案三个动作同时做继电器线圈单独接5V、地线加粗改成星型接地、STM32主电源附近补了10μF钽电容。改完后电压跌落控制在0.15V以内问题消失。后来我又遇到一次类似问题是因为蜂鸣器的电感反电动势没处理。有源蜂鸣器本质是一个电感负载断开瞬间也有反电动势我在蜂鸣器两端也并了一个1N4148二极管作为续流路径问题才彻底消停。这些小细节原理图阶段不画出来调试阶段就会一个一个找你算账。6.3 MQ-2零点漂移导致的误报刚组装完那天晚上误报了一晚上早上起来又好了——典型的新传感器预热不足加基线漂移。MQ-2拿到手至少通电预热24小时再校准前3分钟读数都不稳定。另外实验室空调风口正对着传感器的时候冷热气流导致浓度波动也容易误报。解决办法是两招代码里把连续超阈值的计数门槛从5次调整为8次结构上把传感器装到离空调风口远一点的位置或加一个防直吹的半封闭外壳。这里再补充一个细节MQ-2对酒精蒸气也很敏感。化学实验室里用酒精喷洒消毒后整个房间的烟雾值会明显上升如果阈值按洁净环境设得太低消毒完就会触发一级预警。我的做法是把阈值学习模式的采样窗口从30秒延长到60秒并且加入温度判据——如果烟雾超标但温度没上升只做提醒不联动断电。这样既避免了酒精蒸气误动作又保留了真实火情的响应能力。7. 开源仓库结构与复现指南7.1 目录说明项目以MIT协议开源仓库里四个目录Code放Keil5工程CubeMX生成工程后添加了应用层代码Schematic放立创EDA源文件、PDF导出和PNG图片Simulation放Proteus工程文件Doc放BOM表、接线说明和可选的上位机协议如果接UART的话。其中立创EDA源文件很重要——这意味着你可以直接在线打开工程修改不用重新画图。代码里我保留了详细注释中文注释放得比较多课设答辩的时候对着注释讲逻辑也方便。7.2 从0到1复现需要的准备清单想完整复现这套系统你需要一块STM32F103C8T6最小系统板或自己画的核心板、MQ-2模块、DS18B20、近红外火焰传感器模块、0.96寸OLED、4路5V继电器模块、5V开关电源、有源蜂鸣器、若干按键和排针。烧录工具用ST-Link V2十几块钱那种就够了。软件环境就是Keil5 STM32CubeMX ST-Link驱动Proteus 8.11以上用于仿真。物料成本我实测下来全部加起来不超过120块不含PCB打样如果用洞洞板搭甚至能压到90以内。硬件搭好后按Doc里的接线表逐一核对然后先把示例hex烧进去试运行确认OLED显示正常后再打开源码按自己的传感器型号微调配分压电阻和阈值。整个流程如果基础扎实从零到跑通大概需要两个晚上如果你只是想借代码学习状态机和滤波思路把Code目录里的app_sensor.c和app_scheduler.c单拎出来看就够。我个人在复现过程中的体会是这套项目真正的价值不在消防本身而在于它把嵌入式系统最常见的几个环节——多路传感器采集、软件滤波、状态机控制、外设联动、仿真验证——全部串在了一条完整的工程链上。把这套逻辑吃透以后遇到任何检测控制类的项目你都可以拿它当骨架去套。