基于STM32的智能安防与燃气监测系统设计实战
2026/9/18 11:43:49 网站建设 项目流程

去年冬天的一个晚上,我在厨房热牛奶,转身去客厅拿了个东西,回来时整个厨房已经弥漫着一股焦糊味——锅烧干了。那一刻我就在想,如果有个东西能在我离开时盯着燃气和火情,该多好。后来我花了两周时间,用STM32做了一整套智能安防与燃气监测系统,从原理图设计、代码编写到Proteus仿真验证,全部开源出来。这篇文章把整个项目的设计思路、关键电路、代码逻辑和调试过程中踩过的坑完整记录下来,给同样想做嵌入式项目或者准备参加电赛、毕设的同学一个可以直接复现的参考。

1. 系统需求拆解:安防和燃气监测到底要做什么

动手画原理图之前,我先把需求一条条列清楚。很多人做项目上来就画图写代码,做到一半发现功能之间互相冲突,再回头改就很痛苦。这个系统的核心场景是家庭厨房和入户区域,需要同时解决两个问题:一是燃气泄漏的实时监测与告警,二是非法入侵的探测与报警。

1.1 功能需求清单

  • 燃气浓度实时采集,超过阈值立即声光报警
  • 人体红外感应,检测到非法闯入时触发报警
  • 支持布防/撤防切换,白天在家时关掉入侵报警,晚上睡觉或外出时开启
  • 报警状态通过OLED屏幕和指示灯直观显示
  • 蜂鸣器声音告警,同时预留继电器接口控制电磁阀切断燃气或启动排风扇

这里有个容易忽略的点:燃气报警和入侵报警是两套完全不同的逻辑。燃气监测必须全天候工作,不管你是否布防;而入侵报警只有在布防状态下才需要响应。所以代码里不能简单用一个全局标志位控制所有报警输出,必须把两条逻辑链分开设计。

1.2 系统框图设计

整个系统的数据流向是这样的:燃气传感器和人体红外传感器的模拟量/数字量信号进入STM32的GPIO和ADC引脚,经过内部逻辑判断后,输出到蜂鸣器、LED指示灯、继电器和OLED屏幕。同时通过按键输入实现人机交互,设定布防状态。

从开发顺序上,我建议先做燃气监测这条链路,再做入侵报警,最后整合到一起。因为燃气监测涉及ADC采集、阈值判断、模拟量校准,链路更长,先跑通它能把底层的驱动代码框架定下来。

1.3 供电方案的取舍

系统供电我设计了两种方式:USB 5V直接供电,以及通过AMS1117-3.3稳压芯片降压给主控和传感器供电。燃气传感器MQ-2的加热丝需要5V供电,这一点特别容易踩坑——如果你只给传感器供3.3V,加热温度不够,输出数据会严重漂移,甚至完全检测不到气体。所以原理图上传感器的VCC必须接5V,而它输出的模拟量信号经过分压后进入STM32的ADC引脚(3.3V容忍度以内),这个匹配关系要提前算清楚。

2. 硬件选型与原理图设计的关键节点

核心主控我选了STM32F103C8T6,也就是俗称的“蓝丸”核心板。原因很简单:性价比极高、资料最多、引脚够用、ADC资源丰富。这个项目用到1路ADC、若干GPIO、I2C和定时器中断,C8T6的资源配置完全够用,没必要上F407这种更大的芯片。

2.1 传感器选型:MQ-2还是MQ-5

燃气传感器我对比了MQ-2和MQ-5。MQ-2对丙烷、丁烷、液化石油气灵敏度高,MQ-5对天然气(甲烷)更敏感。考虑到国内家庭燃气管道以天然气为主,同时部分场景还在用液化气罐,我最终选了MQ-2做通用方案,响应范围更宽。如果项目定位是天然气专用检测,换成MQ-5只需要改一下引脚配置,代码里的校准逻辑完全通用。

人体红外传感器选了HC-SR501,这个模块内置了BISS0001芯片,能把热释电红外探头输出的微弱信号放大并处理成数字电平,输出直接接STM32的GPIO即可。模块上有两个可调电位器:一个调感应距离,一个调延时时间。实测下来感应距离调到中间位置(约5米),延时调到最左端(约3秒)适合安防场景。

2.2 原理图设计的几个关键节点

电源电路

VIN 5V ──┬── AMS1117-3.3 ── 3.3V ── STM32 VDD │ ├── MQ-2 VCC(5V直接供电) │ └── 去耦电容 100uF + 100nF

这里需要注意去耦电容的放置:100uF电解电容放在电源入口,100nF陶瓷电容尽量靠近STM32的VDD引脚。MQ-2的加热丝启动瞬间电流接近150mA,如果电源设计余量不足,会导致3.3V电压跌落,单片机直接复位。实测下来USB供电时这个问题不明显,但如果用劣质充电宝供电,启动瞬间确实出现过复位现象,后来加了100uF电容解决。

燃气传感器输出电路

MQ-2的模拟输出AO引脚内部是一个分压电路,输出阻抗较高,直接进STM32的ADC会影响采样精度。我加了一级RC低通滤波:1kΩ电阻串联 + 0.1uF电容到地,截止频率约1.6kHz,有效滤除工频干扰和传感器本身的高频抖动。ADC采样率不需要很快,20Hz就够了。

继电器驱动电路

继电器控制用的是S8050三极管驱动,继电器的线圈端并联了一个1N4007续流二极管。这个二极管是必须的——继电器断电瞬间线圈会产生反向电动势,如果不接续流二极管,高压脉冲会沿着GPIO引脚打进去,轻则单片机死机,重则烧毁引脚。我见过不止一个新手在这个地方翻车,原理图里已经加上了。

OLED屏幕用的I2C接口,SDA和SCL分别接PB7和PB6,加了4.7kΩ上拉电阻。虽然STM32内部有上拉,但OLED模块在长线连接时还是需要外部上拉保证信号质量。

2.3 原理图复查清单(个人经验)

  • 所有IC的电源引脚都有0.1uF去耦电容
  • 复位引脚接了10kΩ上拉电阻和0.1uF电容到地
  • BOOT0用10kΩ下拉到GND,确保从Flash启动
  • 按键电路接了下拉电阻(不是直接悬空),配合软件内部上拉,双保险
  • 蜂鸣器用的有源蜂鸣器,加了三极管驱动,没有直接用GPIO推

3. 核心代码架构:状态机驱动的安防逻辑

代码是整个系统的大脑。我没有用简单的顺序轮询方式,而是设计了一个轻量级状态机,这样后期增加传感器或者修改报警逻辑时不用大改代码结构。

3.1 工程文件组织

SmartSecurity/ ├── Core/ │ ├── Inc/ │ └── Src/ ├── Drivers/ │ ├── BSP/ │ │ ├── bsp_adc.c // ADC采集与滤波 │ │ ├── bsp_oled.c // OLED显示驱动 │ │ ├── bsp_key.c // 按键扫描 │ │ ├── bsp_buzzer.c // 蜂鸣器控制 │ │ └── bsp_relay.c // 继电器控制 │ └── CMSIS/ ├── App/ │ ├── app_main.c // 主循环状态机 │ ├── app_gas_alarm.c // 燃气报警逻辑 │ └── app_intrusion_alarm.c // 入侵报警逻辑 └── MDK-ARM/

分层的基本思路是:BSP层只做寄存器操作,不包含业务逻辑;App层负责决策,不直接操作硬件。这样以后想把系统迁移到其他STM32型号,只需要改BSP层,App层几乎不用动。

3.2 燃气报警的核心逻辑

燃气报警这部分的代码逻辑是这样的:

void Gas_Alarm_Process(void) { uint16_t adc_value = ADC_GetValue(); float voltage = adc_value * 3.3f / 4096.0f; float gas_concentration = MQ2_Calibrate(voltage); if (gas_concentration > GAS_ALARM_THRESHOLD) { if (system_state.buzzer_enable == STATE_ON) { Buzzer_On(); Relay_Close(); // 关闭电磁阀 OLED_ShowGasAlarm(); } } else { Buzzer_Off(); Relay_Open(); } }

关键点在于MQ2_Calibrate这个函数。MQ-2的浓度-电压曲线在双对数坐标上是近似线性的,但精确标定需要多点校准。作为低成本方案,我用了简化的经验公式:

float MQ2_Calibrate(float voltage) { // RL=10kΩ,VCC=5V,根据厂商手册拟合曲线 float rs_ro = (5.0f - voltage) / voltage; return pow(10.0f, (log10(rs_ro) - 1.02f) / -0.23f); }

这个公式的系数来自MQ-2手册的典型曲线,实测下来能区分“正常/疑似泄漏/严重泄漏”三个等级,但精确到百分比浓度是不现实的。如果项目需要定量检测,建议用MOX传感器专用的标定气体做多点标定。

3.3 按键扫描与状态切换

按键处理的坑在于误触发和卡键。我没有用最简单的延时消抖,而是用定时器中断做10ms周期扫描,连续3次读到相同电平才确认按键有效:

void Key_Scan(void) { static uint8_t stable_count = 0; static GPIO_PinState last_state = GPIO_PIN_SET; GPIO_PinState current_state = HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin); if (current_state == last_state) { stable_count++; if (stable_count >= 3) { stable_count = 0; if (current_state == GPIO_PIN_RESET) { // 按键短按,切换布防状态 System_ToggleArmState(); } } } else { last_state = current_state; stable_count = 0; } }

布防状态切换时有一个细节:如果当前正在燃气报警中,不允许撤防。这个逻辑是必须的,我在第一版代码里没加这个限制,测试时发现燃气报警期间按键撤防,蜂鸣器停了,但燃气还在漏。后来改成布防状态与燃气报警状态互斥的判断,才把这个问题堵住。

3.4 定时器调度框架

整个系统用了三个定时器:TIM1做1ms时基,是系统心跳;TIM2做10ms周期扫描按键;TIM3做OLED刷新节拍(100ms)。系统主循环里不做任何延时,所有延时都用状态机加计数器实现。这样做的最大好处是:燃气采样、按键响应、报警输出互不阻塞,一个传感器的异常不会拖垮整个系统。

4. Proteus仿真验证:在上实物之前先把逻辑跑通

仿真的价值不在于替代实物测试,而在于把逻辑层的错误在几分钟内暴露出来。我现在每个项目都会先搭一个Proteus仿真环境,把主流程跑通,再画PCB去打样,这样至少能把接线错误和逻辑错误这两类最耗时间的问题提前消灭掉。

4.1 仿真环境搭建

Proteus里需要准备的元件清单:

  • STM32F103C8(Proteus 8.9以上版本自带)
  • MQ-2模型(可以用电位器模拟气体浓度变化)
  • HC-SR501模型(用按键模拟人体触发)
  • OLED 128x64 I2C
  • 蜂鸣器模型、LED、按键、电阻

有个比较取巧的操作:Proteus里没有现成的MQ-2模型,我用一个电位器的输出模拟燃气传感器的模拟量信号。电位器输出电压从0V往上调,ADC读数跟着变,相当于燃气浓度在升高。这样可以验证整个ADC采集和阈值判断链路,唯一的差别是没有真实的传感器非线性特性。

4.2 仿真中发现的两个问题

第一个问题是继电器状态反转。第一版代码里继电器高电平触发,但在Proteus中继电器模型默认高电平断开,导致报警时电磁阀反而打开了。这个问题在电路图上不容易发现,仿真运行时一眼就能看出来逻辑反了,改一条代码就能解决。

第二个问题是I2C OLED在仿真环境下的时序问题。Proteus对I2C时序的模拟和真实硬件有差异,OLED刷新时偶尔会卡死。后来把I2C速率从400kHz降到100kHz解决。实际硬件上100kHz也完全够用,OLED显示文本信息不需要高速刷新。

4.3 仿真与实物之间的预期管理

要说清一件事:Proteus仿真通过的代码,不代表实物一定没问题。仿真让你验证的是“逻辑正确性”,但ADC参考电压偏差、传感器的上电漂移、供电噪声、按键抖动这些都是仿真里不存在的。所以我的建议是:仿真阶段把目标定在“主流程能跑通、状态切换符合预期”这个层面,不要过度依赖仿真器去调ADC精度或者传感器阈值,这些必须在实物上做。

5. 实物调试踩坑实录

实物调试阶段才是真正让人成长的地方。我把我实际踩过、并且确认能复现的几个坑列出来,每个都附上排查过程和修复方案,希望你能绕过去。

5.1 MQ-2上电漂移:一开机就报警

刚焊好板子接通电源,蜂鸣器立刻开始叫,燃气浓度显示值飙得离谱。第一反应是传感器接线有问题,拿万用表量了供电5V正常,AO输出电压2.8V——按换算公式确实超标。后来查资料才明白,MQ-2这类半导体传感器在刚上电的几分钟内,内部加热丝还没到稳定工作温度,气体敏感层的阻值会剧烈变化,输出信号漂移非常大。

解决方案分两步:硬件上,在传感器模块上电后延时30秒再启动ADC采样;软件上,上电后取前100次采样值的平均值作为基线校准值,之后的所有采样都减去这个基线偏移量再和阈值比较。

void MQ2_WarmUp(void) { uint32_t total = 0; for (uint8_t i = 0; i < 100; i++) { total += ADC_GetValue(); HAL_Delay(300); // 100次 * 300ms = 30秒 } mq2_baseline = total / 100; }

这个改动之后,冷启动误报问题彻底解决。后续如果环境温湿度变化大,还可以定期在确认无燃气泄漏时重新校准基线。

5.2 继电器吸合瞬间单片机复位

功能测试时发现一个诡异现象:继电器每吸合一次,OLED屏幕闪一下,严重时整个系统重启。测量3.3V电压轨,发现继电器吸合的瞬间电压跌落超过300mV,持续时间约10ms。原因很简单:继电器线圈是感性负载,虽然已经加了续流二极管,但5V电源轨上的瞬态电流冲击依然造成了压降。

修复方案是继电器电路与逻辑电路分开供电:用USB 5V直接给继电器供电,经过AMS1117稳压后的3.3V只供给MCU和传感器逻辑部分。同时把继电器的GND和逻辑GND单点接地,避免地环流串扰。改动之后复位问题完全消失。

5.3 HC-SR501人感误触发

入侵报警在晚上频繁误报,日志显示传感器输出高电平持续几秒。排查后确定是传感器安装位置正对着空调出风口,热风导致热释电探头误判。重新调整安装角度并降低灵敏度电位器后问题解决。

这个坑提醒了我:系统设计时不能只考虑电路和代码,安装环境对传感器的影响同样重要。建议在项目文档里增加安装注意事项:避免正对窗户/空调/暖气片,传感器距地面2-2.5米,避免阳光直射。

5.4 OLED显示花屏

OLED屏幕上电后显示一团乱码,有时候重启一次又正常。排查发现是I2C总线上没有接上拉电阻(第一版原理图遗漏),内部上拉又不稳定,导致通信时数据出错。补上4.7kΩ外部上拉后问题消失。如果你也遇到类似的花屏问题,先检查I2C上拉和接线长度,别急着怀疑代码。

6. 开源资料的组织与使用方式

开源项目最重要的是让别人能快速跑起来,我花了不少心思整理目录和文档。

6.1 仓库目录结构

SmartSecurity_STM32/ ├── README.md // 项目介绍、功能说明、硬件清单 ├── Hardware/ │ ├── Schematic_PDF/ // 原理图PDF版本,直接用阅读器打开 │ ├── Schematic_Source/ // 原理图源文件(立创EDA/AD) │ └── PCB/ // PCB文件 ├── Firmware/ │ ├── KEIL_Project/ // KEIL工程,双击打开即可 │ ├── Src/ // 用户代码 │ └── Inc/ ├── Simulation/ │ └── Proteus/ // Proteus仿真文件 └── Docs/ ├── UserManual.md // 使用说明 └── TestReport.md // 测试记录

每个源码文件顶部都加了注释说明函数功能、输入输出和修改注意事项。代码中的关键宏定义集中在app_conf.h中,比如报警阈值、ADC采样周期、布防超时时间等,改参数不需要翻源码。

6.2 复现的最低配置

如果你想复现这个项目,最低需要以下核心器件:

器件型号/规格参考数量
主控板STM32F103C8T6核心板1
燃气传感器MQ-2模块1
人体红外HC-SR501模块1
显示0.96寸OLED I2C1
继电器5V单路继电器模块1
蜂鸣器5V有源蜂鸣器1
按键轻触开关2
稳压芯片AMS1117-3.31
三极管S80502

总成本控制在60元以内。所有模块都可以在淘宝买到现成的,电子新手也能根据原理图自己焊接。

6.3 常见问题索引

README里我加了一个常见问题表:

现象可能原因解决方案
上电OLED无显示I2C接线错/上拉电阻缺失检查SDA/SCL接线,确认上拉
报警一直触发传感器未完成预热等待30秒基线校准完成
按键无反应按键引脚无上拉确认代码使能内部上拉
手机充电宝供电自动重启继电器瞬间电流过大继电器独立供电或换大电流电源

7. 如果你要在这个项目上继续扩展

做完这个系统后,我列了几个扩展方向,都是接口设计阶段就预留好的:

对接WiFi模块:在USART1上预留了ESP8266/ESP-01S的接口,可以通过AT指令把燃气浓度和报警状态推送到MQTT服务器,实现远程报警。参考代码我已经写好了,只需要在App层加一个app_mqtt_report.c模块。

接入蜂鸣器变音提示:通过PWM控制有源蜂鸣器,可以模拟消防车的变音告警,比单音蜂鸣器更容易引起注意。TIM3的PWM输出通道已经预留。

气体浓度历史曲线:STM32内部Flash足够存储24小时的采样点数据(按1Hz采样率,每点4字节),断点续传后可以在OLED上回放趋势图。

多传感器融合:增加DHT11温湿度传感器后,可以对MQ-2的基线进行温湿度补偿,减少环境变化带来的误报率。补偿公式是典型的多项式拟合,我已经验证过效果。

如果你有自己的扩展想法,欢迎在开源仓库提PR。项目从设计到落地查了不少资料,也推翻过一版方案(第一版用了纯裸机轮询,重构翻倍工作量),现在分享出来的这版是整个流程走通后的精华版本。复制源码、打样、烧录、搭好面包板,这套安防系统真的可以跑起来,关键是你还能完全理解每一条代码和每一根线的来龙去脉。做嵌入式项目就是这样,能画出原理图、写得动代码的人很多,但能把从需求到仿真的整条链路吃透的人不多。希望这篇记录能帮你少走几个弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询