ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32环境质量监测系统开源实战:DHT11+MQ135+OLED完整方案

STM32环境质量监测系统开源实战:DHT11+MQ135+OLED完整方案 做ST嵌入式这些年我隔三差五就会刷到“环境质量监测系统”这类开源项目。为什么这个方向这么火因为它基本覆盖了嵌入式开发的完整链路传感器驱动、数据处理、显示交互、报警输出、仿真验证五个环节一个不少而且难度曲线平缓不会有那种一半人直接被劝退的硬门槛。这次分享的STM32环境质量监测系统就是一个完整的开源工程包含工程代码、原理图文件和Proteus仿真文件核心器件是STM32F103C8T6、DHT11温湿度传感器、MQ135空气质量传感器和OLED屏幕。先交代清楚这套系统到底能干什么。上电之后DHT11负责读温湿度MQ135负责读空气质量以模拟电压形式给到ADCSTM32把数据处理之后刷新到OLED屏上如果温度、湿度或者空气质量超过预设阈值蜂鸣器会响LED会亮。同时串口每2秒输出一帧当前数据方便在PC端用串口助手观察也为后续接入WiFi模块或上位机留了接口。整体功能不花哨但非常适合学习和二次开发。什么人适合拿这套东西第一类是正在做课程设计或毕业设计的同学拿来改一改就能满足大部分要求第二类是刚学完STM32基础、想找完整项目练手的开发者代码和原理图对照着读理解会深很多第三类是想快速搭环境监测原型的硬件工程师传感器选型、电路、驱动都有现成参考能省掉不少踩坑时间。1. 项目总体设计与方案选型1.1 环境质量监测系统需要哪些核心能力环境质量监测系统的本质是一个“采集-处理-呈现-响应”的闭环。采集端需要考虑传感器种类和数据接口处理端需要处理ADC转换或单总线时序呈现端需要把数据变成人能看懂的形式响应端则要快速判断并触发报警。如果只做采集显示难度确实不高但一套合格的原型系统还需要考虑数据可靠性、报警滞后、低功耗和扩展接口。这套开源项目在设计时把“容易复现、便于扩展、教学价值高”放在第一位所以没有盲目追求传感器数量而是在温湿度和空气质量这两个最核心的指标上做扎实。温湿度决定了环境舒适度空气质量决定了环境安全性这两个维度合在一起基本能覆盖室内环境监测的典型需求。而且从电子设计角度看DHT11用单总线、MQ135用ADC模拟量、OLED用I2C刚好能把嵌入式开发里最常用的三种外设接口都练一遍这也是选这个组合的一个重要原因。1.2 硬件选型为什么是STM32F103 DHT11 MQ135STM32F103C8T6这块芯片在开源项目里快被用成“国民级”了但恰恰因为用的人多它的生态是最好的。CubeMX可以一键生成底层代码HAL库文档齐全Keil和STM32CubeIDE都能直接编译网上遇到问题基本都有现成答案。C8T6虽然是低配型号但主频72MHz、64KB Flash、20KB RAM拿来跑一个环境监测系统绰绰有余还能剩大量IO和定时器资源做扩展。传感器方面DHT11的知名度基本是“新手必备”级别。它采用单总线协议一根线同时完成通信数据格式是40bit8bit湿度整数、8bit湿度小数、8bit温度整数、8bit温度小数、8bit校验和。精度不算高温度正负2℃湿度正负5%RH但胜在成本低、连线和时序简单非常适合教学和原型验证。MQ135则是模拟量输出对CO2、氨气、苯类蒸汽都有响应直接接到STM32的ADC引脚就能读数价格也很便宜适合做空气质量趋势监测而非气体精确定量。OLED屏幕选的是I2C接口的SSD1306只有两根信号线接线简单显示内容丰富度比1602强好几个档次。1.3 开源三件套的分工与用法这套项目能直接跑起来靠的是三个文件块的配合工程代码负责逻辑原理图负责说明器件怎么接仿真负责在没有实体板卡的情况下做逻辑验证。拿到资料之后我建议的顺序是先看原理图理清引脚分配再开仿真把外设逻辑跑通最后再看代码对照原理去理解每个驱动函数为什么要这么写。千万别一上来就打开main.c想着赶紧编译过那样容易只见树木不见森林。这个顺序背后是有逻辑的。原理图是硬件层面的说明书引脚映射、电源网络、上下拉电阻、去耦电容全都体现在这里仿真是逻辑层面的验证工具能在你焊板之前先确认程序方向没搞反代码是最终交付物理解它需要前两步打底。如果是纯学习跑完仿真、看懂代码知识收获就很大了。如果是做实物那再加一步先仿真、再焊板、最后下载调试可以省掉大量反复修改的时间。2. 核心硬件设计与原理图细节2.1 主控最小系统与电源设计要点STM32F103C8T6的最小系统并不复杂8MHz外部晶振、两个20pF负载电容、复位电路、BOOT0下拉到地、VBAT接3.3V、每个电源引脚旁边放一个100nF去耦电容。这些都是常规操作但有两个细节值得重点强调。第一去耦电容不要省。不要只放一个0.1uF就完事最好是靠近MCU每个电源脚各放一个100nF再在电源入口放一个10uF的钽电容或陶瓷电容实测对ADC采样的稳定性有明显帮助。第二BOOT0不能悬空。如果BOOT0浮空引脚电平可能被干扰拉到高导致芯片进入串口下载模式而不是从Flash启动程序就永远跑不起来。所以BOOT0要明确接一个10k电阻到地。电源部分用的是USB 5V输入经过AMS1117-3.3稳压到3.3V。原理图上输入输出电容一定要加AMS1117这类LDO对输入输出电容有要求一般输入放10uF输出放10uF并联100nF否则带载时容易振荡。另外如果系统里接了蜂鸣器这类感性负载建议在电源输出端预留一个较大的电解电容位置仿真时可能看不出来但实物上电源跌落导致复位是很常见的问题。2.2 传感器接口DHT11与MQ135的电路设计DHT11是单总线器件数据线上必须接一个4.7k左右的上拉电阻到3.3V这是它正常工作的前提。原理图里要给DHT11的数据引脚预留一个排座或焊盘最好再在靠近传感器位置加一个100nF滤波电容减小电磁干扰对时序的影响。这里有一个电平匹配的坑DHT11的供电范围虽然能到5V但它的数据输出高电平接近自身供电电压。如果传感器用5V供电而MCU是3.3V数据线直连可能超出STM32的IO承受范围长时间运行有风险。更稳妥的做法是让DHT11和MCU共用3.3V供电整个系统电源轨就统一了原理图也更好画。MQ135模块一般自带比较器和电位器有AO和DO两个输出。模拟量AO接到STM32的ADC引脚比如PA1ADC参考电压直接用3.3V。如果用的是裸传感器而不是集成模块那输出端要接一个4.7k到10k的负载电阻把传感器电流信号转成电压信号。还有一点要注意气体传感器的加热电阻工作电流较大电源设计时得留足余量不然加热瞬间的电压跌落会让ADC读数跟着一起跳。2.3 显示、报警与人机交互电路OLEDSSD1306在I2C模式下只需要SCL、SDA、VCC、GND四根线。SCL建议用PB6SDA用PB7正好对应STM32的I2C1外设。地址方面SA0引脚接GND时器件地址是0x7C7位寻址下的0x3C这是大多数OLED模块的默认配置。如果程序里I2C扫描不到设备先查一下是不是模块地址需要配置成0x3D。报警部分的核心是有源蜂鸣器加一个LED指示灯。有源蜂鸣器自带振荡源给高电平就会响但这东西工作电流有几十毫安直接接STM32的IO口驱动能力不够需要加一个S8050三极管做开关。电路形式是蜂鸣器一端接3.3V另一端接三极管集电极发射极接地基极经过1k电阻接MCU IO口。同时在蜂鸣器两端反向并联一个1N4148二极管吸收断电瞬间的反向电动势。这个二极管非常关键不加的话蜂鸣器断电瞬间产生的感应电压可能把IO口打坏。这类问题仿真里表现不出来但实物上我见过好几块板子因为省了续流二极管MCU的IO口慢慢就坏了。3. 代码实现从驱动到应用逻辑3.1 用STM32CubeMX搭工程骨架代码部分基于STM32CubeMX加HAL库生成开发环境用Keil MDK。CubeMX里需要配置的外设包括RCC外部晶振、SYSSerial Wire调试口、GPIODHT11数据引脚、蜂鸣器、LED、ADC1PA1模拟输入、I2C1OLED、USART1调试打印、TIM可选用于校准微秒延时。系统时钟配置为8MHz外部晶振倍频到72MHz这个倍频关系要搞清楚。如果实际板上用的不是8MHz晶振而是别的大小配置就要跟着改不然串口波特率、定时器定时全是乱的。HAL库的好处是外设初始化代码自动生成应用层自己控制。但坏处也很明显HAL库的时序开销比标准库大尤其是DHT11这种对微秒级时序有要求的单总线协议直接调用HAL_Delay根本不行因为它的单位是毫秒微秒级延时得自己基于SysTick或裸循环实现。工程里提供了一套delay_us函数底层用定时器或多个空循环实现实测时序比较稳定。3.2 DHT11单总线驱动与数据校验DHT11的驱动是整个工程最需要死磕的部分。主机要先拉低总线至少18ms建议直接拉低20ms然后释放总线并等待DHT11响应。传感器拉低80us表示应答再拉高80us准备发送数据之后就是连续40bit的数据。每一位都是以50us低电平开头关键是后面高电平持续的时间如果高电平持续26到28us代表逻辑0如果高电平持续70us左右代表逻辑1。代码里的典型做法是先等引脚变低再等引脚变高然后延时40us再读取引脚状态。如果40us之后引脚还是高电平说明这一位是1否则就是0。代表性的读取函数如下uint8_t DHT11_ReadByte(void) { uint8_t i, data 0; for (i 0; i 8; i) { while (GPIO_ReadLevel(DHT11_PIN) 0); // 等待50us低电平结束 delay_us(40); if (GPIO_ReadLevel(DHT11_PIN) 1) data | (0x80 i); // 高电平持续超过40us则为1 while (GPIO_ReadLevel(DHT11_PIN) 1); // 等待当前位结束 } return data; }这段代码看着简单实际容易在三个地方翻车。第一循环等待没有超时机制如果传感器没接或者损坏程序会卡死在里面。真实工程里建议加一个超时计数器超时就退出并返回错误。第二delay_us的准确性特别关键如果延时不准40us的判断点偏移数据就会频繁读错。第三40bit数据读完之后一定要做校验校验和等于前面四个字节相加的低8位校验不过直接丢弃整帧数据。不要用错误数据去刷新显示宁可不刷新也不能显示错的。3.3 ADC采集、滤波与阈值报警逻辑MQ135的模拟输出接到ADC1的PA1引脚。HAL库的ADC读取方式很固定先启动转换再等待转换完成最后拿结果。核心代码就几行HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); uint16_t raw HAL_ADC_GetValue(hadc1); float voltage raw * 3.3f / 4096.0f;但直接读单次值噪声会比较大因为气体传感器本身输出就在不断波动。我在代码里做了滑动平均值滤波每200ms采样一次累计5次后取平均再更新一次数据。滑动窗口的好处是既能平滑噪声又不会让响应变得太迟钝很适配这种缓慢变化的模拟量。更进阶一点可以用一阶低通滤波公式是y[n] alpha * x[n] (1 - alpha) * y[n-1]alpha取0.1到0.3之间效果也不错。报警逻辑设计上我设置了三组阈值温度超过30℃、湿度超过70%RH、空气质量原始ADC值超过2500大约对应2V电压任何一个条件满足就触发蜂鸣器和LED。阈值定义成宏常量放在配置文件里想改直接改宏不需要动主逻辑。报警还引入了10秒的持续判断数据连续超限10秒才真正报警避免瞬时尖峰误触发。这个“去抖”思路在实际产品里非常常见简单有效。3.4 OLED显示与串口打印OLED驱动基于SSD1306的I2C命令集实现写字符和汉字需要取模。工程里我把中文字库精简成了几个常用字比如温度、湿度、质量、报警这几个词这样占用Flash空间小又能满足显示需求。显示刷新放在主循环里每500ms刷新一次内容包括温度值、湿度值、空气质量等级。空气质量等级怎么划分我根据MQ135的电压做了简单三档低于1.5V算是良好1.5V到2.5V算一般高于2.5V算较差。这个分档逻辑完全可以根据自己的实际环境调整比如家里通风好可能长期都在1V以下那就应该把阈值下限再压低。串口打印用USART1波特率115200输出格式是“Temp: 26.5C, Humi: 48%RH, Air: 1800”。串口的作用不只是调试后续接ESP8266上云或者接USB转TTL做上位机显示这套接口都能直接复用。有一个我经常遇到的坑在Keil里做printf重定向到串口MDK必须勾选Use MicroLIB否则程序编译没问题但串口输出会卡住甚至完全没输出坑过不少新手。4. Proteus仿真搭建与系统联调4.1 仿真能验证什么、不能验证什么很多朋友希望仿真能“完全替代实物”这个期望要放平。Proteus仿真最大的价值是能验证逻辑层面的正确性程序能不能跑起来、有没有死循环、GPIO状态对不对、显示和报警逻辑是否正常。但它模拟不了真实传感器的物理特性DHT11在仿真里的时序和真实器件有很大差异MQ135的输出也不会真的随气体浓度变化。所以在仿真时一般会用虚拟电位器来模拟MQ135的电压变化测试ADC逻辑或者直接给DHT11模型固定一组温度湿度值让程序去读重点验证的是程序读时序和解析逻辑有没有写对。换句话说仿真是“逻辑验证工具”不是“物理仿真工具”。明白这一点仿真阶段的目标就会很清晰不会花大量时间纠结仿真和实物的细微差别。4.2 仿真电路搭建要点在Proteus里搭建这套电路需要准备的元器件包括STM32F103C8T6模型、DHT11模型、LCD或虚拟串口终端、LED、蜂鸣器、电阻和电位器。要注意的是不同版本Proteus的DHT11模型行为不完全一致有的版本仿真时序很严格有的比较宽松。如果仿真里DHT11读取一直失败先怀疑微延时函数在仿真环境里的执行速度——Proteus的CPU仿真速度和真实芯片不一样有可能需要把delay_us的延时次数调大或调小。OLED屏在Proteus里不一定有现成的SSD1306模型。遇到这种情况有两个可行方案一是用虚拟I2C调试设备观察数据是否正常二是在仿真阶段先改成LCD1602验证主逻辑等实物阶段再换回OLED。这不是偷懒而是“仿真验证逻辑、实物验证外设”的开发思路很多商业项目也是这么干的。4.3 联调流程与仿真独有坑我推荐的联调流程是一步一步叠加上去先在Proteus里只放MCU、LED和蜂鸣器编译烧录后确认最基本的程序能跑然后加上串口虚拟终端确认打印输出正常再加DHT11确认能读到数据最后加电位器模拟MQ135验证ADC和报警逻辑。每次都只增加一个变量出了问题范围非常小定位很快。仿真里有一个挺常见的问题程序在Keil能编译烧录到Proteus里却不跑或者乱跑。大部分时候是晶振配置和Proteus的模型不匹配。有些仿真模型默认使用内部RC时钟而你在CubeMX里配置的是外部晶振输入上电后时钟源不对程序自然起不来。解决办法是在CubeMX里先临时改成HSI内部时钟验证逻辑确认没问题之后再到实物上改回外部晶振配置。这个坑几乎每个用Proteus做STM32仿真的人都会遇到提前知道能省很多时间。5. 常见问题排查与避坑实录5.1 DHT11读不到数据的几种典型原因DHT11读不到数据这个问题在交流群里几乎每天都会被问。按照出现频率排序我把踩过的坑列一下第一数据引脚模式配置错误。DHT11数据引脚要配置成开漏输出带上拉不要用推挽输出推挽输出在释放总线后拉高电平的驱动能力过强会影响时序。第二上拉电阻没接或者阻值太大。4.7k是公认比较合适的值10k能用但边沿变缓33k以上在长导线场景下基本就别指望时序还能对了。第三主机起始信号的低电平时间不够长。DHT11规格要求至少18ms我实测拉低20ms最稳定少于15ms基本就是偶发失败。第四微秒延时函数不准。用HAL_Delay做不了微秒级定时必须用独立延时函数这是所有DHT11驱动问题的根源之一。5.2 数值跳变、偏差大的处理思路温湿度读数偶尔跳变很多人第一反应是传感器坏了其实大概率是电源问题。DHT11再便宜它对电源噪声也是敏感的。如果用的是开关电源供电纹波大一点读数就会时不时抽风一下。建议在传感器电源引脚旁边加100nF电容如果还不行就加一个10uF电解电容同时采样频率不要太高DHT11本身就建议读取周期不要小于1秒。MQ135的ADC读数抖动也是类似的排查思路先加滤波再看电源。数据跳变时先检查是不是ADC的参考电压VREF不稳定如果VREF和DHT11共用一路LDO输出传感器加热瞬间拉低电压也会影响ADC读数。所以硬件上最好把模拟部分和数字部分的电源稍微分隔一下软件上做滑动滤波两者搭配基本能解决90%的跳变问题。5.3 问题速查表现象可能原因排查方法程序不跑OLED无显示时钟配置错误、BOOT0悬空检查CubeMX时钟树BOOT0接10k下拉DHT11一直读不到数据单总线时序不对、上拉缺失逻辑分析仪看波形确认起始信号18ms以上串口输出乱码波特率不匹配、时钟倍频错误核对CubeMX时钟树确认波特率配置ADC读数不变化通道配置错误、引脚被复用检查CubeMX的PinoutPA1确认配置为ADC1_IN1蜂鸣器不响三极管驱动接错、缺续流二极管对照原理图查基极电阻和C/E极方向仿真跑飞外部晶振配置和模型不匹配先用HSI内部时钟验证逻辑OLED白屏I2C地址不对、SCL/SDA接反扫描I2C设备地址核对0x3C或0x3D数据偶尔跳变电源纹波大、滤波不足加100nF去耦软件加滑动平均滤波这个项目我前后做过三个版本最大的体会是环境监测类项目的技术难点不在主控而在“数据是怎么从传感器可靠进到MCU里的”。DHT11的单总线时序、MQ135的ADC采样噪声、电源纹波对模拟量的影响这些才是真正值得花时间琢磨的地方。把它完整调通一遍你对GPIO、定时器、ADC、I2C、串口的理解比看十遍教程都深刻。后续想继续扩展的话方向也很多加ESP8266模块联网上云、加SD卡存历史数据、换成精度更高的SHT30或BME280都行。这套代码和原理图已经给这些扩展留好了接口从这个基础上出发会比从零开始顺很多。
返回列表