1. 项目缘起与整体设计思路
实验室这个地方,说安全也安全,说危险也是真危险。酒精灯、电烙铁、各种化学试剂、大功率仪器设备,再加上有时候学生做实验一走神忘了关设备,隐患其实不少。我读研那会儿,隔壁实验室就因为一个加热台长时间没关,冒了浓烟,幸好发现得早。从那之后我就一直琢磨着,能不能用自己手头最熟的东西——STM32,搭一套成本低、能落地、还方便二次开发的消防预警系统。这个项目就是这么来的。
整套系统核心解决三件事:第一,实时监测环境中的烟雾浓度和温度;第二,异常时本地声光报警并自动切断相关设备电源;第三,把数据通过串口上报给上位机,方便记录和远程查看。它适合电子类专业的学生做课程设计或毕业设计,也适合实验室管理员拿来做一个低成本的补充安全手段。整套方案我做了代码、原理图和仿真三件套,下面会把设计思路、关键细节、实操过程和踩过的坑全部摊开讲。
先说整体架构。系统以STM32F103C8T6为主控,这是最经典的一款Cortex-M3芯片,72MHz主频、64KB Flash、20KB SRAM,资源对于这个项目绰绰有余,关键是便宜、资料多、社区活跃。传感器方面,烟雾检测用MQ-2半导体气敏传感器,温度检测用DHT11数字温湿度传感器。执行机构包括蜂鸣器(声音报警)、LED指示灯(光报警)、继电器模块(控制外部设备电源通断)。人机交互用0.96寸OLED显示实时数据,通信方面用USART串口上报数据到上位机。
为什么这么选?我一个个说。主控选F103C8T6而不是F407或者G系列,是因为这个项目对算力要求极低,采样、比较、显示、串口发送,这些任务用M3内核跑起来毫无压力,没必要为过剩的性能买单。MQ-2选它是因为它对液化气、丙烷、氢气、烟雾都有不错的灵敏度,价格只要几块钱,模拟输出直接接STM32的ADC引脚就能读。DHT11虽然精度一般(温度±2℃,湿度±5%RH),但单总线协议简单,一根数据线搞定,对于实验室环境监测这个精度够用了。继电器我选的是光耦隔离的5V继电器模块,避免继电器线圈反向电动势干扰MCU。
注意:MQ-2的输出是模拟量,需要接STM32的ADC通道。DHT11是数字量,接普通GPIO即可。两者不要搞混。
整个系统的数据流是这样的:STM32定时采集MQ-2的ADC值和DHT11的温湿度值,将采集到的数据与预设阈值比较。如果烟雾浓度或温度超过阈值,立即触发蜂鸣器和LED报警,同时通过继电器切断被控设备的电源,并通过串口向上位机发送报警信息。正常情况下,OLED实时显示当前数值,串口每隔一段时间上报一次数据。
这个设计思路的核心逻辑是分级响应。我没有一上来就切断电源,而是设置了预警和报警两个级别。预警级别只亮黄灯、蜂鸣器间歇响,提醒在场人员注意;报警级别才亮红灯、蜂鸣器长鸣、继电器断电。这样做的好处是避免因为传感器偶尔的波动导致误断电,影响正在进行的实验。这个分级阈值的思路,是我在实际调试中反复调整后才定下来的,后面会详细讲参数怎么定。
2. 硬件核心细节与原理图设计要点
2.1 主控最小系统与电源设计
STM32F103C8T6的最小系统包括晶振电路、复位电路、启动模式配置和电源滤波。晶振我用的是8MHz外部高速晶振配合两个22pF的负载电容,经过内部PLL倍频到72MHz作为系统主频。这里有个细节:负载电容的值不是随便选的,要根据晶振规格书上的负载电容参数来算。一般晶振的负载电容是20pF,PCB上的杂散电容大概3~5pF,所以实际焊接的电容值应该是2*(20-4)≈32pF,但实际用22pF也很稳定,因为杂散电容不好精确估算,22pF是经过大量实践验证的通用值。
复位电路用经典的10K上拉电阻加100nF电容,低电平复位。启动模式BOOT0通过10K电阻下拉到地,BOOT1也接地,这样默认从主Flash启动。电源部分,整个系统用5V供电,STM32需要3.3V,所以我用了一个AMS1117-3.3稳压芯片。输入5V经过AMS1117降到3.3V给MCU和OLED供电,MQ-2和继电器直接用5V。这里要注意AMS1117的输入输出压差不能太小,5V降到3.3V有1.7V压差,完全没问题,但如果输入是4V就不行了,压差不够会导致输出不稳。
每个电源引脚旁边都要放一个100nF的去耦电容,这个不是可选项,是必须的。我见过太多人画板子的时候忽略去耦电容,结果MCU跑着跑着就死机或者ADC读数乱跳。去耦电容的作用是滤除电源上的高频噪声,给芯片提供一个干净的局部电源。VBAT引脚如果不接电池,直接接3.3V即可。VDDA和VSSA是模拟电源引脚,即使不用ADC也要接,VDDA接3.3V并加一个1uF加100nF的滤波组合。
2.2 MQ-2烟雾传感器电路
MQ-2的接线比较简单,它有四个引脚:VCC、GND、DO(数字输出)、AO(模拟输出)。VCC接5V,GND接地,AO接STM32的PA0引脚(ADC通道0)。DO引脚我悬空了,因为数字输出的阈值是通过电位器手动调的,精度和灵活性都不如直接用ADC读模拟量然后软件比较。
MQ-2内部是一个二氧化锡半导体气敏元件,当接触到可燃气体或烟雾时,电导率升高,输出电阻降低,AO引脚上的电压升高。它需要预热一段时间才能稳定输出,通常预热时间在24小时以上效果最好,但实际使用中预热5~10分钟就能基本稳定。我在代码里加了一个上电延时,前30秒不进行报警判断,只显示"预热中",避免刚上电时传感器输出不稳定导致误报。
MQ-2的输出电压与烟雾浓度之间不是线性关系,而是一条对数曲线。如果要做精确的浓度标定,需要知道传感器的负载电阻RL和清洁空气中的电阻R0,然后通过公式计算。但对于实验室预警这个场景,我不需要知道具体的ppm值,只需要知道"正常"和"异常"的分界点在哪里。所以我的做法是:在清洁空气中读取MQ-2的ADC值作为基准,然后设定一个倍数关系作为阈值。比如基准ADC值是200,我设定阈值是600,也就是基准的3倍。这个倍数需要根据实际环境调整,后面实操部分会详细讲。
2.3 DHT11温湿度传感器电路
DHT11也是四引脚:VCC、GND、DATA、NC。VCC接3.3V或5V都可以,DATA接STM32的PA1引脚,同时需要接一个4.7K到10K的上拉电阻到VCC。这个上拉电阻是必须的,因为DHT11的数据线是开漏输出,没有上拉电阻的话总线无法拉高,通信会失败。我用的4.7K,实测很稳。
DHT11的通信协议是单总线协议,一次完整的数据传输包括:主机发送起始信号(拉低至少18ms),然后释放总线,DHT11响应(拉低80us,再拉高80us),然后传输40位数据(湿度整数8位+湿度小数8位+温度整数8位+温度小数8位+校验和8位)。每一位数据以50us低电平开始,高电平的持续时间决定数据是0还是1:26~28us表示0,70us表示1。
这个时序对时间精度要求比较高,如果MCU主频太低或者中断干扰太频繁,容易读错。我在代码里用微秒级延时来实现时序控制,并且在读取DHT11数据的时候关闭了全局中断,避免中断打断时序导致读取失败。DHT11的采样周期不能太快, datasheet要求两次读取间隔至少1秒,我设的是2秒读一次,完全够用。
2.4 继电器驱动与输出控制
继电器模块我用的是现成的5V光耦隔离模块,输入端接STM32的PB0引脚。为什么用光耦隔离?因为继电器线圈在断电瞬间会产生反向电动势,这个电压尖峰可能高达几十伏,如果不隔离,很容易通过电源或者地线耦合到MCU,导致MCU复位甚至损坏。光耦隔离模块内部已经做好了隔离和续流保护,直接用就行。
STM32的GPIO输出电流最大25mA,而继电器线圈的驱动电流通常在60~80mA,所以不能直接用GPIO驱动继电器线圈,必须通过三极管或者专用驱动芯片。我用的模块内部已经集成了驱动电路,STM32只需要输出高低电平控制即可。逻辑是这样的:PB0输出高电平,光耦导通,继电器吸合,常闭触点断开,被控设备断电;PB0输出低电平,继电器释放,常闭触点闭合,设备通电。这里要注意继电器的常开常闭触点选择,我选的是常闭触点串联在设备电源线上,这样系统正常工作时继电器不耗电,只有报警时才吸合断电,更节能也更安全。
2.5 OLED显示与串口通信
OLED我用的是0.96寸I2C接口的SSD1306,接STM32的PB6(SCL)和PB7(SDA)。I2C通信需要上拉电阻,一般模块上已经自带了4.7K上拉,不需要额外加。OLED显示内容包括:当前温度、湿度、烟雾ADC值、系统状态(正常/预警/报警)。
串口通信我用的是USART1,PA9(TX)和PA10(RX),波特率115200。串口的作用是把数据上报给上位机,格式是JSON字符串,方便上位机解析。比如:{"temp":25,"humi":60,"smoke":320,"status":"normal"}。为什么用JSON而不是自定义二进制协议?因为JSON可读性好,调试的时候直接看串口助手就能明白数据含义,而且上位机用Python或者C#解析都很方便。虽然JSON比二进制占更多字节,但在115200波特率下,这点数据量完全不是问题。
3. 软件架构与核心代码实现
3.1 软件整体框架
软件部分我采用的是前后台架构,没有上RTOS。为什么不上RTOS?因为这个项目的任务数量少、实时性要求不高,前后台架构完全够用,而且代码更简单、更容易理解和维护。主循环里轮询各个任务,用SysTick定时器提供毫秒级延时和任务调度基准。
具体来说,系统上电后先初始化各外设(GPIO、ADC、USART、I2C、定时器),然后进入主循环。主循环里做这几件事:每2秒读取一次DHT11温湿度,每500毫秒读取一次MQ-2的ADC值,每100毫秒刷新一次OLED显示,每1秒通过串口上报一次数据,实时判断是否需要报警。这些时间间隔用SysTick的计数来实现,不用阻塞式延时,保证系统响应性。
代码结构上我分了几个模块:main.c(主逻辑)、dht11.c/h(温湿度驱动)、mq2.c/h(烟雾传感器驱动)、oled.c/h(显示驱动)、relay.c/h(继电器控制)、usart.c/h(串口通信)、delay.c/h(延时函数)。每个模块职责清晰,方便移植和复用。
3.2 DHT11驱动代码详解
DHT11的驱动核心是微秒级延时和时序控制。先看延时函数,我用SysTick来实现微秒延时:
void delay_us(uint32_t us) { uint32_t start = SysTick->VAL; uint32_t ticks = us * (SystemCoreClock / 1000000); uint32_t cnt = 0; while (1) { uint32_t now = SysTick->VAL; if (now <= start) cnt += start - now; else cnt += start + (SysTick->LOAD - now); start = now; if (cnt >= ticks) break; } }这段代码的原理是读取SysTick当前计数值,计算经过的时钟周期数,换算成时间。SysTick是递减计数器,从LOAD值减到0再重载,所以要处理回绕的情况。这个延时函数的精度取决于SystemCoreClock的值是否正确,如果系统主频是72MHz,SystemCoreClock就应该是72000000。
DHT11的读取函数流程是这样的:主机拉低数据线至少18ms,然后拉高20~40us,切换到输入模式等待DHT11响应。DHT11会先拉低80us,再拉高80us,然后开始传输40位数据。每一位数据以50us低电平开始,然后高电平持续26~28us表示0,持续70us表示1。代码里通过判断高电平持续时间来区分0和1。
uint8_t dht11_read_byte(void) { uint8_t byte = 0; for (int i = 0; i < 8; i++) { while (!DHT11_DATA_IN); // 等待50us低电平结束 delay_us(40); byte <<= 1; if (DHT11_DATA_IN) { // 40us后还是高电平,说明是1 byte |= 1; while (DHT11_DATA_IN); // 等待剩余高电平结束 } } return byte; }这里有个关键点:delay_us(40)这个延时值的选择。因为0的高电平是26~28us,1的高电平是70us,取中间值40us作为判断点,40us后如果还是高电平就是1,否则就是0。这个40us不是随便定的,是DHT11时序规范里推荐的采样点。
注意:读取DHT11的时候一定要关中断,因为任何中断都可能导致时序错乱。我是在读取函数开头
__disable_irq(),结尾__enable_irq()。但关中断时间不能太长,DHT11一次读取大概4ms左右,这个时间关中断是可以接受的。
3.3 MQ-2 ADC采集与滤波
MQ-2的输出接在PA0,对应ADC1的通道0。STM32的ADC是12位精度,参考电压3.3V,所以ADC值范围是0~4095,对应的电压是0~3.3V。ADC采集的配置包括:使能ADC1时钟和GPIOA时钟,配置PA0为模拟输入模式,配置ADC为独立模式、单次转换、软件触发,设置采样时间为55.5个周期(这个采样时间比较长,适合高阻抗信号源)。
采集到原始ADC值后,不能直接用来判断,因为ADC值会有波动。我用了滑动平均滤波,连续采集10次,去掉最大值和最小值,剩下的8个取平均。这样处理后的值更稳定,避免因为单次采样波动导致误报警。
uint16_t mq2_read_filtered(void) { uint16_t samples[10]; uint16_t sum = 0, max = 0, min = 4095; for (int i = 0; i < 10; i++) { samples[i] = mq2_read_raw(); if (samples[i] > max) max = samples[i]; if (samples[i] < min) min = samples[i]; sum += samples[i]; delay_ms(10); } sum = sum - max - min; return sum / 8; }这个滤波算法简单但有效,去掉了可能的脉冲干扰。10次采样间隔10ms,总共100ms完成一次滤波采集,对于烟雾这种变化相对缓慢的信号来说,响应速度完全够用。
3.4 报警逻辑与阈值判断
报警逻辑是整个系统的核心。我设置了两个阈值:预警阈值和报警阈值。预警阈值是基准值的2倍,报警阈值是基准值的3倍。基准值怎么来?系统上电预热30秒后,自动采集当前MQ-2的ADC值作为基准值,存储在变量里。温度方面,预警阈值设45℃,报警阈值设60℃。
#define SMOKE_WARN_RATIO 2.0f #define SMOKE_ALARM_RATIO 3.0f #define TEMP_WARN 45.0f #define TEMP_ALARM 60.0f void check_alarm(uint16_t smoke, float temp) { if (smoke > smoke_base * SMOKE_ALARM_RATIO || temp > TEMP_ALARM) { set_alarm_level(ALARM_LEVEL_HIGH); } else if (smoke > smoke_base * SMOKE_WARN_RATIO || temp > TEMP_WARN) { set_alarm_level(ALARM_LEVEL_WARN); } else { set_alarm_level(ALARM_LEVEL_NORMAL); } }为什么用倍数而不是绝对值?因为MQ-2的输出受环境影响很大,不同实验室的空气质量、温湿度、通风条件都不一样,用绝对值的话换个环境就得重新标定。用倍数关系的话,系统上电自动标定基准值,适应性更强。这个思路是我在实际部署中总结出来的,一开始用绝对值,结果换了个实验室就频繁误报,后来改成自动标定加倍数判断,稳定多了。
3.5 串口数据上报与协议设计
串口上报我用的是JSON格式,每1秒发送一次。数据内容包括温度、湿度、烟雾ADC值、系统状态。发送函数用printf重定向到USART1,方便格式化输出。
int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); USART_SendData(USART1, (uint8_t)ch); return ch; } void report_data(float temp, float humi, uint16_t smoke, uint8_t status) { printf("{\"temp\":%.1f,\"humi\":%.1f,\"smoke\":%d,\"status\":\"%s\"}\r\n", temp, humi, smoke, status == 0 ? "normal" : (status == 1 ? "warn" : "alarm")); }上位机端我用Python写了一个简单的接收程序,用pyserial库读取串口数据,解析JSON后显示在界面上,同时记录到CSV文件。这个上位机代码很简单,几十行就搞定了,后面会给出完整代码。
4. 仿真验证与实操过程记录
4.1 Proteus仿真电路搭建
在打板之前,我先用Proteus做了仿真验证。Proteus里没有现成的STM32F103C8T6模型,需要自己添加元件库。我用的是Proteus 8.9版本,里面自带了STM32F103系列的部分模型。如果找不到,可以去官网下载元件库或者用替代型号。
仿真电路里,我用电位器模拟MQ-2的模拟输出,用DS18B20模拟DHT11的温度输出(Proteus里DHT11模型不太好用,DS18B20更稳定)。OLED用Proteus自带的12864液晶替代,串口用虚拟终端显示。继电器用LED加三极管模拟。
仿真电路搭建的要点:STM32的电源引脚都要接上,晶振电路要画对,复位电路不能少。ADC输入引脚接电位器中间抽头,电位器两端接3.3V和GND。串口TX接虚拟终端的RX,波特率设置成115200。
仿真跑起来后,我转动电位器模拟烟雾浓度升高,可以看到OLED显示数值变化,超过预警阈值后黄灯亮、蜂鸣器间歇响,超过报警阈值后红灯亮、蜂鸣器长鸣、继电器动作。串口终端也能看到JSON数据正常输出。仿真验证通过后,我才开始画PCB打板。
提示:Proteus仿真STM32的ADC功能有时候不太准确,仿真结果只能作为逻辑验证,不能完全替代实际硬件测试。实际ADC的噪声、温漂等问题在仿真里是看不到的。
4.2 PCB设计与打板注意事项
PCB我用的是嘉立创的EDA工具,两层板,尺寸10cm x 8cm。布局上,STM32放在板子中央,传感器接口放在板子边缘方便接线,继电器放在板子一角远离模拟部分。电源部分单独放在一侧,AMS1117加散热铜皮。
布线要点:电源线走宽一点,我用的20mil;信号线10mil;晶振走线尽量短,包地处理;ADC输入线远离数字信号线,避免干扰;继电器控制线远离模拟部分。地平面我做了完整的地铺铜,模拟地和数字地单点连接。
打板回来后的焊接顺序:先焊电源部分,测电压正常后再焊MCU,然后焊其他外设。这个顺序很重要,如果电源有问题,先焊MCU可能导致芯片烧毁。我第一次打板的时候就是先焊了MCU,结果AMS1117焊接不良输出5V,直接把MCU烧了,损失了几十块钱和一周时间。
4.3 系统联调与参数整定
硬件焊接完成后,开始联调。第一步是测试电源,用万用表测AMS1117输出是不是稳定的3.3V。第二步是测试晶振,用示波器看8MHz晶振有没有起振。第三步是下载程序,用ST-Link连接SWD接口,Keil里能识别到芯片就说明最小系统正常。
然后逐个测试外设:先测OLED,能显示字符就说明I2C通信正常;再测DHT11,能读出温湿度就说明单总线时序正确;再测MQ-2,用手靠近传感器或者用打火机的气体(不点火)靠近,看ADC值有没有明显变化;最后测继电器,听有没有吸合的声音。
参数整定是最耗时间的环节。MQ-2的基准值需要在清洁空气中采集,我是在实验室通风良好的情况下,上电预热30分钟后读取的,连续读10次取平均,得到基准值。然后点燃一根香烟(或者用蚊香)放在传感器附近,观察ADC值上升到多少,根据这个来确定预警和报警的倍数。我实测下来,清洁空气ADC大概在180~220之间,香烟烟雾能让ADC升到800以上,所以2倍预警(约400)、3倍报警(约600)是比较合理的。
温度阈值方面,实验室正常温度在20~30℃之间,我设45℃预警、60℃报警。这个阈值可以根据实际实验室的情况调整,比如有些实验室有加热设备,正常温度就比较高,那阈值就要相应提高。
4.4 实测数据与效果评估
系统连续运行了72小时,记录了每天的误报次数和响应时间。实测数据如下:
| 测试项目 | 测试条件 | 结果 |
|---|---|---|
| 烟雾响应时间 | 蚊香烟雾距离30cm | 约3秒触发预警,5秒触发报警 |
| 温度响应时间 | 电烙铁靠近传感器 | 约8秒触发预警,15秒触发报警 |
| 误报次数 | 正常实验室环境72小时 | 0次 |
| 串口丢包率 | 115200波特率,1秒间隔 | 0% |
| 继电器动作可靠性 | 连续动作100次 | 100次正常 |
响应时间方面,烟雾3秒触发预警我觉得可以接受,因为烟雾扩散需要时间,传感器本身也有响应时间。温度响应慢一些是因为DHT11本身响应就慢,而且电烙铁的热辐射到传感器需要时间。如果要更快的温度响应,可以换DS18B20或者热电偶,但成本会高一些。
误报次数为0是我最满意的结果。之前用绝对值阈值的时候,每天都会有一两次误报,改成自动标定加倍数判断后,72小时零误报。这说明自动标定基准值的思路是对的,能有效适应环境变化。
5. 常见问题排查与避坑经验
5.1 硬件类问题速查
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 上电后MCU不工作 | 电源电压不对 | 万用表测3.3V和5V | 检查AMS1117焊接和输入电压 |
| 晶振不起振 | 负载电容不对或晶振损坏 | 示波器测晶振引脚 | 更换22pF电容或换晶振 |
| DHT11读数为0 | 上拉电阻缺失或时序错误 | 示波器看数据线波形 | 加4.7K上拉,检查延时函数 |
| MQ-2读数不变 | 传感器未预热或接线错误 | 测AO引脚电压 | 预热5分钟以上,检查接线 |
| OLED不显示 | I2C地址错误或接线反了 | 用I2C扫描程序找地址 | 确认地址是0x78还是0x7A |
| 继电器不动作 | 驱动电流不足或GPIO配置错误 | 测GPIO输出电平 | 检查GPIO模式,确认模块供电 |
| 串口无输出 | 波特率不匹配或TX/RX接反 | 示波器测TX引脚 | 确认波特率115200,TX接RX |
5.2 软件类问题排查
DHT11读取失败率高的排查思路:先确认延时函数的精度,用示波器测delay_us(40)实际延时是多少。如果延时不准,检查SystemCoreClock的值是否和实际主频一致。再确认读取时是否关了中断,如果有其他中断在跑,比如SysTick中断,可能会打断时序。我一开始没关中断,DHT11读取成功率只有60%左右,关了中断后成功率99%以上。
ADC读数跳动大的排查思路:先看硬件,ADC输入引脚有没有加滤波电容,我加了一个100nF到地。再看软件,滑动平均滤波的窗口大小是否合适,10次可能不够,可以加到20次。还要检查VDDA和VSSA是否接了滤波电容,模拟电源不干净会直接影响ADC精度。
串口发送乱码的排查思路:首先确认波特率是否一致,STM32和上位机都要设成115200。然后检查系统时钟配置,如果主频不是72MHz,波特率计算就会出错。还要确认串口助手的停止位、数据位、校验位设置是否正确,我用的8N1。
5.3 实操避坑经验汇总
坑一:MQ-2预热时间不够。我第一次调试的时候,上电就开始测,结果ADC值一直在漂,从200慢慢涨到500,我还以为传感器坏了。后来查资料才知道MQ-2需要预热,而且预热时间越长越稳定。现在我都是上电后等30分钟再标定基准值,代码里也加了30秒的预热等待期。
坑二:继电器干扰导致MCU复位。最早我用的是没有光耦隔离的继电器模块,继电器一动作MCU就复位。后来换成光耦隔离模块,并且在继电器线圈两端加了续流二极管,问题解决。如果你也遇到继电器动作时MCU复位,优先检查隔离和续流。
坑三:DHT11和MQ-2互相干扰。DHT11读取的时候要关中断,而MQ-2的ADC采集如果也在中断里做,就会冲突。我的做法是把ADC采集放在主循环里,不用中断,DHT11读取的时候关中断,两者就不会冲突了。
坑四:OLED显示刷新太频繁导致闪烁。我一开始每10ms刷新一次OLED,结果屏幕闪烁严重。后来改成每100ms刷新一次,而且只刷新变化的区域,闪烁问题解决。OLED的刷新不需要太快,人眼能看清就行。
坑五:串口printf重定向后程序卡死。如果你用了printf但没有重定向fputc,程序会卡在默认的fputc里。重定向的时候要注意,fputc里等待发送完成的循环不能死等,要加超时机制。我是在fputc里加了超时计数,超过一定次数就退出,避免死锁。
5.4 系统扩展与升级方向
这个项目的基础版本已经能用了,但如果想做得更完善,有几个方向可以扩展。第一,加WiFi模块,用ESP8266或者ESP32把数据上传到云平台,实现远程监控。第二,加SD卡存储,把历史数据记录到SD卡里,方便事后分析。第三,加多路传感器,比如在实验室不同位置放多个MQ-2,通过485总线或者无线模块汇总到主控。第四,加UPS电源,停电的时候系统还能继续工作一段时间。
我个人最推荐的是加WiFi模块,因为现在云平台很成熟,接入成本低,而且远程监控的实用价值最高。实验室管理员不可能一直盯着OLED看,但手机能收到报警推送就实用多了。ESP8266用AT指令就能控制,和STM32通过串口通信,改动量不大。
代码和原理图我已经整理好了,整个项目从设计到调试完成大概花了三周时间,其中PCB打板等了一周,实际调试两周。成本方面,STM32核心板十几块,MQ-2几块钱,DHT11几块钱,OLED十几块,继电器模块几块钱,加上PCB打板和元器件,整套下来不到一百块。这个成本对于实验室来说完全可以接受,而且所有代码和图纸都是开源的,可以根据自己的需求修改。
最后分享一个小技巧:如果你觉得MQ-2的模拟输出不够稳定,可以在AO引脚和GND之间并联一个10uF的电解电容加一个100nF的陶瓷电容,能明显改善ADC读数的稳定性。这个是我在调试过程中试出来的, datasheet上没写,但实测有效。