起因是去年帮朋友做的一个整改。他老家房子布线较早,厨房和客厅没有预留独立的报警线路,平时只有一只电池式的独立烟雾报警器,摆放位置又不合理,油烟和灰尘经常让它半夜误响。索性帮他从头做了一套基于STM32的烟雾和火焰联动报警系统,既能本地声光报警,又能输出继电器干接点信号,方便外接通风设备和远程推送模块。做完之后把设计思路、选型过程、电路和代码里的取舍都整理一下,也给打算自己动手做类似安全类设备的同学一个参考。
这个项目的核心硬件选型如下:
- 主控:STM32F103C8T6(最小系统板)
- 烟雾检测:SS-205离子式烟雾传感器模块(模拟量输出)
- 火焰检测:四路红外火焰传感器模块(数字量输出,带灵敏度电位器)
- 声光报警:有源蜂鸣器 + 高亮红色LED
- 继电器输出:5V单路低电平触发继电器模块
- 交互:一个独立按键(消音/自检),一个绿色电源指示灯
开发环境和工具链:
- 开发环境:Keil MDK5(老旧的工具但生态成熟,用顺手了)
- 标准外设库:STM32F10x标准外设库(ST出的官方固件库,层级比HAL库直观一些)
- 下载调试:ST-Link V2
- 示波器调波形,万用表查虚焊,两个都是必备
1. 系统整体架构与STM32单片机选型
1.1 为什么用STM32,而不是51或者ESP8266?
做报警器,第一优先级是“可靠”,不是“能用WIFI”。ESP8266确实能联网,但一个油烟或灰尘干扰导致的误报警,再加上WIFI模块本身电源纹波大、灵异复位多,这种场景下其实并不省心。51单片机便宜是便宜,但抗干扰能力和外设资源(尤其是ADC和定时器)都紧巴巴,火焰检测要做频率分析,烟雾采集要多次采样做滤波,51的算力虽然勉强够,但留给后续扩展的余量几乎为零。
STM32F103C8T6这颗芯片,单颗价格和成本在可接受范围内,性能却完全够用:
- 72MHz主频,Cortex-M3核心,做滤波算法、多路逻辑判定时没什么压力
- 双ADC,12位精度,烟雾传感器的模拟输出电压范围是0~3.3V,用12位采样能识别微小的浓度变化
- 丰富的定时器、GPIO、引脚数量刚好合适,整个系统只用到了PA0、PC13等少数几个引脚,大量的引脚留给了后续功能扩展
- 成熟的生态,标准外设库、各种例程、参考资料极其丰富,遇到问题基本都能搜到解决方案
1.2 系统的信息流分几个层次
这套报警系统的工作逻辑并不复杂,但分层思考能让你设计时不容易漏东西:
感知层:烟雾和火焰传感器负责把环境中的烟雾微粒、火焰红外辐射转变成电信号(模拟电压或者电平)。
处理层:STM32对传感器信号进行采集、滤波、阈值判断、逻辑判定。这层是系统的大脑,也是防误报的关键所在。
执行层:当处理层判定火情发生后,STM32输出控制信号,点亮高亮LED、驱动蜂鸣器响起、继电器动作接通电磁阀或排烟设备。
交互层:按键负责消音和自检,状态指示灯让用户能一眼看出系统是正常工作还是发生报警。
整个系统的架构就是“传感器—处理—执行—交互”,这四层各司其职,调试的时候也能针对具体模块去定位问题。
2. 传感器选型与对比:烟雾和火焰两套思路
2.1 烟雾检测传感器的逻辑差异很大
市面上烟雾传感器主要分离子式和光电式,核心原理完全不同。早期烟雾报警器大量使用离子式传感器,内部有一种微量的放射性物质来电离空气,当烟雾颗粒进入电离腔后改变电离电流,从而检测烟雾。这个方案对阴燃火的响应速度非常快,但要注意的是,含放射源,在一些区域处理不好会有环保问题。
我这次选用的是光电式烟雾传感器模块(SS-205),它的检测原理是散射光法:传感器内部有一对红外发射管和接收管,正常状态下接收管收不到发射管的光线;当烟雾颗粒飘入迷宫腔体后,光线被烟雾颗粒散射,部分光线被接收管收到,输出信号随之变化。
光电式相比离子式更安全、更环保、功耗更低,适合家用。但它的缺点是:对阴燃火(比如电线绝缘皮慢慢熔化冒烟)的响应速度不如离子式,对颗粒极小的烟雾不敏感。实际应用时需要靠阈值设定来平衡灵敏度。
市场上的模块类型很多,我把常见类型整理成一份对比表:
| 传感器类型 | 检测原理 | 响应速度 | 功耗 | 成本 | 适用场景 |
|---|---|---|---|---|---|
| 离子式 | 电离电流变化 | 快 | 低 | 中 | 早期阴燃预警 |
| 光电式 | 红外散射 | 中等 | 低 | 低 | 家庭、办公室常用 |
| 半导体气体式(MQ-2) | 金属氧化物半导体电导率变化 | 快 | 高(需要加热) | 极低 | 广泛,但选择性差 |
| 激光式 | 激光散射计数 | 极快 | 中 | 高 | 高端工业、高精度场景 |
MQ-2是很多新手入门爱选的模块,价格便宜、超灵敏,但它的功耗大,内部有一个镍铬加热丝,开机就会发热。而且它对酒精、煤气、氢气都比较敏感,很容易导致误报,放在智能家居环境里不太合适。我朋友家里如果用了MQ-2,估计三天两头在叫。
2.2 火焰传感器为什么不能只看数字量?
火焰检测比烟雾检测更讲究原理。绝大多数低价火焰传感器模块都在探测火焰中的红外辐射,也就是火焰发出的特定波长红外线。火焰燃烧时,在某个波长区域有强烈的红外峰值,传感器把这个红外信号转成电压或者电平跳变。
我选用的四路火焰传感器模块,每一路都有独立的红外接收管、比较器和灵敏度调节电位器。模块输出的是数字量,检测到火焰时给出低电平信号,四路之间的“或”逻辑在主控里完成。
重点来了:火焰传感器本质上就是个红外接收器,它对阳光、白炽灯、甚至暖气片发出的红外辐射都有反应。放在有阳光直射的窗口,或者靠近暖气的位置,它随时可能被误触发。这导致很多人的火焰报警器根本没法正常工作。
为了应对红外干扰,程序不能只看“有没有低电平”,还要看“低电平持续了多长时间”。突发性的光线闪烁(比如阳光移动、手电筒扫过)持续时间短,而真实的火焰抖动频率有一定范围。我在软件里做了“持续低电平确认”,只有在火焰信号保持低电平超过设定时间后,才判定为火情发生。
四路火焰传感器的好处还在于,它在物理位置上能以不同角度布置,可以覆盖更广的探测范围。适合安装在炉灶正上方或室内角落。四路中只要有两路同时触发,可靠性就比单路判断高得多。
2.3 传感器布局的物理位置如何安排
传感器的位置安排直接影响检测效果。烟雾传感器装在厨房门口的死角区域不太合适,因为抽烟机的排烟口就在附近,炒菜高峰期烟雾浓度高,极容易误报。我的做法是装在天花板靠近客厅与走廊的交界处,稍微偏离油烟直接冲击的位置。
火焰传感器则装在对准燃气灶方向的位置,距离炉灶约1~2米,避开阳光直射和暖气片正对面。
3. 硬件电路设计:从模块选型到电源分配
3.1 电路框图与信号流
系统采用单5V供电,通过USB供电口或DC插座供电,然后:
- 5V电源直接给蜂鸣器、继电器、烟雾模块、火焰模块供电
- STM32最小系统板上的AMS1117稳压器把5V降成3.3V给MCU供电
- 烟雾传感器模块的模拟输出电压(0~3.3V)连接到STM32的PA0(ADC采样引脚)
- 火焰传感器模块的数字输出连接到STM32的PA1、PA2、PA3、PA4(GPIO输入,内部上拉)
- 继电器模块IN引脚连接到PB0(推挽输出)
- 蜂鸣器连接到PB1(推挽输出)
- LED报警灯连接到PB10,电源指示灯连接到PB11
- 按键连接到PC13(原本接在板载LED上,改作按键输入)
选型时特别注意电源的分配。烟雾传感器的模拟量输出精度对电源稳定性的要求其实不高,但继电器的线圈动作瞬间会有几十毫秒的电流冲击,如果MCU和继电器共用一条电源线,继电器吸合时的电压跌落会给MCU带来干扰。
解决办法是:继电器模块单独从电源输入处取电,不经过内部稳压器到MCU的走线。同时在继电器模块的VCC和GND之间并联一个100uF的电解电容和0.1uF的瓷片电容,用来吸收线圈动作时的瞬态电流。整个系统的地在电源输入处单点汇合,避免形成地环路。
3.2 烟雾传感器模块的模拟量输入处理
烟雾传感器模块通常有模拟量输出(AOUT)和数字量输出(DOUT)两个引脚。DOUT输出经过模块上的比较器处理后的开关量,阈值由板载电位器调节;AOUT则输出一个随烟雾浓度连续变化的模拟电压。
我这里用的是AOUT接ADC,而不是DOUT直连。原因有三:
- 阈值可编程。模块的物理电位器调起来麻烦,而且会产生机械漂移,软件阈值则随时可以调整。
- 能区分“有烟雾”和“劣势状态”。模拟量可以测出当前烟雾的基线浓度,如果浓度缓慢爬升但没到报警阈值,说明环境有变化,可能是有慢性泄漏,软件可以记录并提示,这种特性是数字量给不了的。
- 便于做多级报警。浓度到达50%阈值时,可以输出一个“预警”状态(比如LED闪烁),到达80%时再启动完整报警逻辑,这样更人性化。
STM32F103的ADC是12位,参考电压是3.3V,采样值0~4095,对应的电压范围是0~3.3V。烟雾模块输出0~3V左右,通过分压电阻或模块内的分压电路,可以直接进入ADC。
采样时注意两点:
- ADC采样需要稳定时间,每次转换前加一小段延迟
- 我去掉了引脚上的外接滤波电容,因为烟雾模块的输出阻抗不算低,一个大电容会把信号变化变得非常迟钝
3.3 火焰传感器的数字输入与硬件滤波
火焰传感器模块输出数字量,当检测到红外辐射时输出低电平。这种数字量输入不需要ADC,但需要做去抖处理。
硬件上加了两道保险:
- 每一路的MCU引脚配置成输入上拉模式,模块输出低电平时能正确识别
- 模块和MCU之间的连接线使用双绞线,降低空间电磁干扰的耦合
软件去抖就是用定时器做周期扫描,在中断中进行计数。如果检测到低电平连续超过200ms,才认为是真实火焰信号。这个时间参数在调试时通过示波器实际观察火焰燃烧的波形来标定,火焰在自然燃烧时红外信号的波动频率大约在几赫兹到十几赫兹之间,信号低电平持续的时间通常在几十到几百毫秒,200ms的窗口能过滤掉大部分瞬态干扰。
3.4 执行机构驱动:继电器也要注意控制逻辑
继电器模块选用的是5V低电平触发,模块上带光耦隔离和驱动三极管,低电平触发是工业控制中比较常见的约定。
注意一点:继电器是低电平触发,意味着MCU上电瞬间如果不小心把PB0拉低,继电器会误动作。很多人在调试时遇到“开机就报警”的情况,原因就在这儿。
解决办法:在main函数初始化GPIO时,先把PB0置高(即关闭继电器),再进行其他外设配置。而不是初始化完再置高。如果先配置GPIO,默认状态下PB0可能是低电平,瞬间会触发继电器。
为了在断电时也能让继电器处于安全位置,继电器的常闭触点接线接到了排烟电机的控制回路上,即默认情况下排烟电机回路是通路状态,报警时继电器线圈上电,常闭触点断开,排烟电机断电。这个设计考虑了失效安全:即使报警器本身出故障断电,排烟设备仍然可以正常工作,不会影响厨房日常使用。
3.5 按键、指示灯和蜂鸣器
按键接在PC13上,这个引脚支持唤醒功能,但本项目没有用到休眠,就是普通的输入检测。按键采用软件消抖方式,检测到下降沿后延迟20ms再次确认,防止按键簧片抖动导致误判。
蜂鸣器使用有源蜂鸣器,只需给它一个高低电平就能发声,声音频率固定。有源蜂鸣器比无源蜂鸣器简单,不需要PWM驱动,但声音单调。我在报警时让蜂鸣器以1Hz频率间断鸣叫:响0.5秒,停0.5秒,这种节奏比连续鸣叫更容易引起注意,也更“像”警报而不是单纯的吵闹。
高亮LED在报警状态下常亮,同时软件让LED以2Hz频率闪烁,强化视觉提醒。
电源指示灯(绿色LED)正常工作时保持常亮,让用户能快速确认系统在正常运行。如果电源指示灯熄灭,即使蜂鸣器没响,也说明系统断电或程序跑飞——这是我特意留下的状态信号。
4. 软件核心逻辑:采样滤波、判定与联动控制
4.1 主循环与外设初始化的顺序问题
程序结构其实很常规:初始化外设、进入while死循环、轮询传感器阈值、处理报警状态机。关键在初始化的顺序,顺序错了会有坑。
我在初始化时的顺序:
- 配置时钟(HSE,72MHz主频)
- 配置GPIO(先把继电器控制引脚PB0置高,确保继电器不误动)
- 配置ADC(DMA方式采集烟雾值)
- 配置定时器(定时器2用于按键消抖和火焰信号去抖,定时器3用于状态轮询)
- 配置UART(预留调试接口,通过串口打印状态信息)
- 最后进入while主循环
为什么不先配ADC再配GPIO?因为MCU上电复位那一刻,所有引脚都处于浮空状态。如果PB0在浮空时被外部噪声拉低,继电器的控制信号会不稳。先把GPIO配置为推挽输出并输出高电平,MCU就能稳定控制住继电器。先把继电器的状态定死了,再去做测量性的工作(ADC、定时器),不容易出bug。
4.2 烟雾浓度的ADC采样与软件滤波
烟雾传感器输出的是模拟电压,电压越高意味着浓度越高。我采集PA0引脚的电压值,经过多级滤波后映射成“浓度百分比”(0~100%)。
滤波方案分了层,每一层解决一个层面的问题:
第一层:滑动中值滤波(窗口5)
uint16_t adc_value[5] = {0}; uint8_t adc_index = 0; uint16_t get_adc_median(uint8_t index) { uint16_t temp[5]; uint8_t i, j; uint16_t t; for (i = 0; i < 5; i++) { temp[i] = adc_value[i]; } // 直接插入排序 for (i = 1; i < 5; i++) { t = temp[i]; j = i; while (j > 0 && temp[j - 1] > t) { temp[j] = temp[j - 1]; j--; } temp[j] = t; } return temp[2]; // 取中值 }中值滤波能有效抑制单次采样时出现的异常尖刺。比如户外闪电、手机充电器的突发干扰,都可能导致某次ADC转换结果异常,取中值可以把这个尖刺过滤掉。它不适合滤除连续的高频噪声,但对于单点尖峰干扰非常有效。
第二层:滑动平均滤波(窗口16)
中值滤波输出后,再送入一个16点的环形缓冲区做滑动平均,得到一个平滑的浓度值。这种平均滤波能滤掉高频随机噪声,但代价是会让信号变化变迟钝。所以在设定报警阈值时,我会特意留出余量——报警阈值设在平滑值达到60%浓度,而不是更低的阈值,就是为了抵消滤波带来的滞后。
第三层:迟滞阈值判断
报警之后,浓度降到一定程度才能解除报警。这里引入一个迟滞区间:报警阈值设定为60%,解除阈值设定为50%。也就是说,浓度从正常升到60%以上才报警,报警后必须降到50%以下才恢复。这样就避免了浓度在阈值附近抖动时,蜂鸣器和继电器反复吸合释放。
这三层滤波加起来,程序里的“浓度”概念已经很稳定了,我用串口打印观察过连续几分钟的信号,数值波动控制在±3%以内。
4.3 火焰信号的多级确认
火焰传感器的输入是数字量。检测逻辑的核心是持续低电平确认,配合周期扫描:
#define FLAME_CONFIRM_MS 200 #define FLAME_RELEASE_MS 500 static uint32_t last_flame_falling_edge = 0; static uint8_t flame_active = 0; void flame_scan(void) { uint8_t i; uint8_t active_count = 0; uint32_t now = timer_get_ms(); for (i = 0; i < 4; i++) { if (GPIO_ReadInputDataBit(FLAME_PORT, flame_pins[i]) == 0) { active_count++; } } // 至少2路同时触发才认为有效 if (active_count >= 2) { if (flame_active == 0) { last_flame_falling_edge = now; flame_active = 1; } else if (now - last_flame_falling_edge >= FLAME_CONFIRM_MS) { fire_detected = 1; } } else { if (flame_active == 1 && (now - last_flame_falling_edge >= FLAME_RELEASE_MS)) { flame_active = 0; fire_detected = 0; } } }这段逻辑里有两个要点:
- 两路同时触发才有效。单路触发可能是传感器被局部光线干扰,但两路同时触发,基本可以确认真实火焰存在。
- 确认时间200ms。在这个时间内,如果信号消失,说明是瞬态干扰,不是火焰。这个时间用示波器实测过,火焰抖动时红外信号的多次“断开-重建”间隔通常在几十毫秒到一百多毫秒之间,200ms刚好可以让瞬态干扰被过滤掉,真实火焰又能被捕获。
4.4 报警状态机设计
整个系统的状态机是我觉得最值得分享的一块。很多新手直接写“if (烟浓度>阈值) { 报警; }”这种逻辑,但一旦涉及多个优先级、消音按键、自检模式,代码马上乱成一锅粥。
我用了一个有限状态机,把系统拆成四个状态:
typedef enum { STATE_MONITOR, // 正常监测 STATE_PREALARM, // 预警(烟雾浓度30%~60%) STATE_FIRE_ALARM, // 火警 STATE_SILENCE // 消音(用户按了消音键) } sys_state_t;STATE_MONITOR(正常监测)
这个状态下,不触发任何报警动作。每隔500ms轮询一次传感器,计算烟雾浓度、扫描火焰信号,更新状态显示。
STATE_PREALARM(预警)
烟雾浓度达到30%以上但不足60%时进入。绿色指示灯从常亮变为1Hz闪烁,提醒用户“注意环境可能有异常”,但不触发蜂鸣器和继电器。这个状态在厨房很实用——炒菜时偶有油烟飘出,浓度到了30%~50%时,闪烁提醒用户注意通风,但不至于吓人一跳。用户如果在这个状态按消音键,系统会回到监测状态并重置计数。
STATE_FIRE_ALARM(火警)
火警状态由两个来源触发:
- 烟雾浓度超过60%阈值,持续2次确认
- 火焰检测确认200ms且两路以上信号有效
进入火警状态后:蜂鸣器以1Hz节奏鸣叫,红色LED以2Hz闪烁,继电器吸合或断开(取决于接线逻辑)。系统自动开启一个“冷却时间”定时器,即使传感器信号恢复正常,火警状态至少保持5秒,防止信号瞬间下降到阈值以下又立刻恢复报警,造成反复跳变。
STATE_SILENCE(消音)
用户按消音键后,进入此状态。蜂鸣器停止鸣叫,但红色LED继续闪烁。如果5分钟内检测到新的危险信号,系统会重新激发蜂鸣器;如果5分钟内无新的报警信号,系统自动恢复到监测状态。
这种消音逻辑比一按消音就永久闭嘴要安全得多:它不会让用户永久关闭警报,只是在短时间(比如炒菜飘烟时)临时静音,之后自动恢复监测,给用户的也是一种安全感,而不是“我按了消音就永远不出声”的错误预期。
4.5 按键自检功能
自检按键是个容易被忽略但很有价值的功能。用户按下按键并保持2秒,系统强制触发一次完整的报警测试:
- 红色LED点亮,绿色LED熄灭
- 蜂鸣器鸣叫2秒
- 继电器动作1次
- 系统通过串口打印自检结果
这个功能在安装调试时非常有用。安装人员装完设备后按一下自检键,立刻能确认所有执行机构是否正常工作,不用再去用打火机烧传感器触发报警来测试。每个月低运营者也可以按自检键检查设备是否健康。
5. 调试过程与关键问题排查
5.1 焊接和接线时的常见地坑
第一次搭这套系统的时候,我在焊接上走了不少弯路。主要问题还是地线处理。
继电器模块在动作瞬间会有几十毫秒的电流冲击,如果MCU的GND和继电器的GND接入同一个回路,这个冲击会拉低MCU的参考地,导致ADC采样出现波动。排查时用示波器看PA0引脚的波形,发现继电器动作时PA0电压明显出现小幅毛刺,频率大概几百千赫兹。这其实是电源瞬间跌落导致的采样参考电压波动。
解决方式是把继电器和MCU的供电回路分开走线。继电器模块直接从电源输入点取电,MCU从稳压器输出点取电,两个地平面在电源输入处单点汇合。
如果你的板子本身是覆铜板,可以直接分割地平面,将继电器区的地和模拟量采集区的地分隔开,中间用0欧电阻跨接。0欧电阻的作用是让两个地平面保持等电位,同时隔绝高频干扰的能量耦合路径。
5.2 烟雾模块输出的“假高值”现象
烟雾传感器模块有一个非常经典的坑:开机瞬间,模块输出的电压会短暂冲到高位然后回落到接近0V的基线。这是因为内部的自检和预热过程。如果程序在开机后立刻采样,会把这个短时电压当作烟雾浓度,触发虚假报警。
解决方式是在上电初始化时,加上2~5秒的预热等待时间。这段时间内程序只做加热,不对传感器采样。预热完成后,读取传感器当前电压值并记录为“基线校准值”。
基线校准逻辑:
uint16_t baseline = 0; uint8_t i; // 预热后再采基线 for (i = 0; i < 16; i++) { baseline += get_adc_median(); delay_ms(50); } baseline /= 16;后续判断浓度时,使用的是当前采样值相对于基线的增量百分比,而不是绝对电压值。这样做可以适应不同环境下传感器静态工作点的漂移,避免长期运行后因传感器老化、灰尘积累导致基线抬升而出现常报警。
5.3 火焰传感器的阳光干扰
安装后调试时,发现一种现象:白天把设备放在窗边测试,时不时会误报警。反复排查后确认,是阳光直射墙面后反射过来的红外辐射干扰了火焰传感器。红外接收管接收到阳光中的红外分量,模块输出一个低电平脉冲,而在200ms确认窗口内,如果阳光恰好以节奏性抖动(比如窗帘摆动)照射到传感器,就可能触发报警。
解决方式有三种,最终方案三者叠加使用:
- 物理遮挡,给火焰传感器模块套一个半圆柱形的遮光罩,只留正前方一个开口朝向炉灶方向
- 软件逻辑增强,保持两路同时触发才确认,靠单路误触发无法激活报警
- 调节模块灵敏度电位器,把灵敏度适当调低(顺时针拧到底比较灵敏,逆时针拧则迟钝),让它的触发阈值高于背景红外的强度
实际调出来的效果是:正对着打火机火焰在1米内能可靠触发,而室内的自然光不会误报。
5.4 继电器衔铁打火和持续抖动
还有一个实际问题:继电器触点断开时会出现打火,产生高频辐射干扰MCU的ADC采样。这个在高压感性负载(比如排烟电机)场景下很明显。
缓解措施:在继电器的负载触点两端并联一个RC阻容吸收电路(R取47~100欧姆,C取0.1uF),这个电路能吸收触点断开瞬间的反向电动势尖峰,降低打火强度。RC电路直接并联在电机接线端,效果更好。
另外,继电器的线圈两端并联一个续流二极管。继电器线圈断电时会产生反向感应电压,如果不加续流,这个瞬间的高压会击穿驱动三极管甚至反灌到MCU引脚。续流二极管让线圈电流在断开瞬间走二极管回路,电压被钳位在0.7V左右,安全得多。
大多数模块带续流二极管,但我实际核对过几款便宜的模块,有些确实省了这颗二极管,外接时最好自己补焊一个。
5.5 按键消抖和状态机之间的交互坑
最后遇到的调试问题比较隐蔽,跟按键消抖有关。我将按键扫描放在定时器中断里做,中断周期是10ms,读取到按键按下后延迟一个周期再读,确认低电平持续了两个周期(20ms),才判定为有效按键。
问题出在按键和系统状态机的交互上:用户在火警状态下按消音键,系统进入“消音状态”,但如果这个按键动作触发了状态机的“自检”功能(长按2秒),就会导致火警和自检同时出现,系统逻辑混乱。
解决办法是把按键处理逻辑从状态机逻辑中完全剥离。按键扫描只负责识别“短按”和“长按”两个事件,然后把事件作为消息发送到状态机的输入队列。状态机在同一时刻只处理一个输入事件,这样就不会出现“一边报警一边自检”的怪异状态。
6. 进一步扩展的思路与工程化改进方向
6.1 远程告警:加个WIFI模块是否可行?
很多朋友看完这套系统后第一反应是“怎么没有联网报警”。确实,这套系统目前只做了本地声光和继电器联动,远程告警需要外接模块才能实现。
直接加一个ESP8266模块到串口上是常规做法,也能快速打通远程推送。但要注意几点:
- 电源隔离要做,ESP8266的峰值电流能达到300mA以上,直接和STM32共用3.3V电源会拉低电压
- 网络重连接逻辑要做,WIFI断开后需要定时重连,避免长时间断网不自知
- 需要区分“设备故障”和“无网络”两种状态,避免远程报警失效时用户完全不知情
实际方案可以在STM32和ESP8266之间加一个串口隔离,采用三极管电平转换电路,或者直接用支持隔离的串口模块。
6.2 多机联动与总线的选择
单套报警系统只覆盖一个房间,如果房子大,需要多套设备联动的话,最简单的做法是每个报警器独立运行,不做互联。但这样有个缺点:如果火源在卧室没有装报警器的地方,客厅装了报警器可能感应不到。
一套低成本的方法是使用RS-485总线把多台报警器串联起来。每台设备分配一个地址,主设备轮询从设备的状态,任意一台检测到火情,主设备通过继电器输出控制全屋联动设备(电磁阀、排烟风机等)。
RS-485的好处是传输距离远(可达1200米),抗干扰能力强,而且STM32上只需要外接一个收发器芯片(比如MAX485)就能实现。不过这个方案需要单独布一条两芯线,费用和施工成本会有所增加。
6.3 软硬件协同的升级:增加浓度变化率判断
目前的烟雾报警逻辑只看浓度阈值,但一些场景下合理设定“变化率”能更好地识别火情。比如,烟雾浓度在10秒内从20%快速上升到80%,即使绝对值没到60%(因为基线可能动态变化),也应该触发报警。相反,如果浓度缓慢爬升(比如室内的空气湿气凝结),即使过了60%阈值也不一定需要立刻报警。
这个方案需要增加一个速率判断逻辑:
// 每100ms采样一次,计算最近5秒的浓度变化斜率 float slew_rate = (current_concentration - previous_concentration) / delta_time_seconds; if (slew_rate > 0.08f) { fire_detected = 1; }实际测试时发现,烟气扩散的速率和物体材质、空间大小、通风条件关系很大,一个固定的变化率阈值并不通用。想做好,得在不同场景下实测标定。不过这个方向至少说明软件逻辑可以在不换硬件的前提下优化报警体验。
6.4 低功耗与电池供电的考虑
当前系统是5V USB供电,功耗不是主要关注点(STM32工作电流大约30mA,烟雾模块电流也不大,蜂鸣器报警时电流比较大,但报警是瞬时动作,整体平均功耗并不高)。如果希望装在无插座的位置,走到电池供电路线,可以使用STM32L系列(低功耗系列M0/M4核),配合RTC定时唤醒,把系统改成事件驱动模式:平时睡眠,传感器有事件唤醒MCU,平均功耗能降到微安级。
但低功耗模式下要特别注意:烟雾传感器的稳压输出会不稳定,传感器本身就不是低功耗器件,需要给传感器单独供电或采用间歇供电方式,即每隔几秒给传感器通一次电,读数后断电。这样会把功耗压下来,但代价是响应速度变慢了。
7. 更进一步的工程化:从原型到可安装的设备
把面包板上的原型装进外壳,这个过程比想象中麻烦。我踩了几个坑值得提一下。
外壳开孔和走线布局
刚开始直接用定制图纸外壳,钻孔后安装,结果发现传感器模块的摆放角度和进烟孔的位置不匹配,开孔没对准传感器的探测区域,导致灵敏度大幅下降。后来我调整了进烟孔布局,在传感器正前方开若干小孔,并且让孔的位置和传感器探头保持同一水平线,让烟雾颗粒更容易直接飘入探测器。
防虫防凝露
报警器长期挂在墙顶,灰尘、小飞虫都有可能进入传感器迷宫腔,积累久了会影响灵敏度。我在外壳进风口处加了一层防虫不锈钢网(目数不用太高,40目即可),既能防虫防尘又能保证气流进入。
程序升级的可靠性
如果后期需要远程OTA升级固件,STM32F103本身不带以太网,必须通过WIFI模块转发。为了安全考虑,可以在程序中实现ST官方的IAP(应用内编程),预留一个“升级模式”引脚,长按自检键2次进入升级模式,此时只允许WIFI模块对MCU进行固件写入,避免意外进入。IAP本身不复杂,但要在工程里拆分BootLoader和APP两个工程,布局好Flash分区,普通个人DIY不着急做,等确实需要了再上。
8. 最后总结一下设计心得
做完这个烟雾报警器系统,我最大的感受是:安全类设备的本质是可靠性,不是功能炫技。
报警器最大的价值在于:在正确的时候叫,不在错误的时候叫。误报比漏报更令人崩溃,因为用户会养成忽略报警的习惯,真出事时反而无动于衷。所以整个系统花时间最多的地方都在处理假信号:中值滤波、滑动平均、迟滞阈值、两路确认、持续低电平判断,每一个细节都是为了让系统更“沉稳”。
第二个感受是:选择单片机不一定要追求最新最强。STM32F103C8T6虽然发布了很多年,在这个场景下依然是够用且稳定可靠的选择。工程上真正难的不是写代码,而是把每个环节的干扰、误差源都梳理清楚,尤其是传感器这种受环境影响大的模拟器件,软硬件协同处理好了,才是一套能实际使用的系统。
第三个感受是:安装位置和用户习惯对报警器的影响比技术参数更大。再好的算法,如果传感器装在不合理的角度,或者用户嫌吵直接拔了电源,系统就废了。做这类设备时,一定要在设计阶段就把安装方式和交互逻辑考虑进去,让用户“不能乱动”而且“不想乱动”。
这套系统目前稳定运行了两个多月,无论是炒菜油烟的短暂干扰,还是室外施工带来的粉尘冲击,都没有再误报过。打火机测试能准确报警。后续我会数据积累一段时间后,把esp8266远程推送模块完善起来,再整理一版带实时曲线和事件日志的版本,让报警信息能发送到手机。
如果你也在做类似的环境监测或报警系统,希望这篇分享能帮你少踩几个坑。有问题也可以在评论区交流一起学习。