ARTICLE DETAIL

资讯详情

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

基于STM32的仓库环境控制系统:温湿度与粉尘监测实战

基于STM32的仓库环境控制系统:温湿度与粉尘监测实战 1. 项目从哪来仓库环境不只是温度湿度那么简单做这个项目之前我一直在想一个问题仓库这东西看着就是个“大铁皮盒子货架”值得专门上单片机去管吗直到我亲眼见过一家电子元器件仓库梅雨季节墙角返潮整箱的贴片电阻引脚氧化到客户手里直接虚焊退回来一大批货才知道环境失控的代价不是几块钱电费而是几万块的损失。还有粉尘尤其是粮库、木材库、塑料原料库粉尘浓度一旦上来静电引爆就是大事故。所以“基于STM32的仓库环境控制系统”这个标题一出来其实背后是一整套很实际的需求温湿度监测、粉尘监测、通风除湿自动执行、远程掌握仓库状态四件事串起来才算一套能用的系统。就“能做什么”而言这套系统解决了三个核心痛点第一传感器装上去之后能实时读数不用每天派个人拎着温湿度计去仓库里转一圈第二超过阈值之后不用人跑过去开风机单片机直接驱动继电器把风机和除湿机拉起来第三数据通过ESP8266往云端推手机或者电脑上就能看人在外面出差也能知道仓库里现在是什么状态。这套系统适合谁适合正在做嵌入式毕业设计的学生、刚入行搞物联网开发的工程师以及想给自家小仓库或者小型车间做环境改造但不想交几万块“智慧仓储”方案费的动手派。我最初定的方案其实挺朴素的STM32F103C8T6做主控DHT22采集温湿度GP2Y1010AU0F采集粉尘浓度继电器控制两台设备——风机和除湿机ESP8266负责把数据推上云。整体思路是“本地闭环控制为主远程监测为辅”也就是说就算网络断开仓库里面的风机和除湿机照样自己干活不会因为云平台掉线就变成一座孤岛。这个设计思路我觉得是整套系统最值得学的地方后面会展开讲。2. 硬件设计与选型每个器件都不是随手抓来的2.1 主控选型逻辑为什么是STM32而不是51或Arduino仓库环境控制这类项目对主控的要求其实不高IO口十几路足够ADC一路采样粉尘USART一路和ESP8266通信再加几个普通IO控制继电器和蜂鸣器8位单片机理论上也能干。但我还是选了STM32F103C8T6原因有三条。第一性能余量。仓库环境控制虽然核心功能简单但后续很可能会加OLED显示、加FreeRTOS实时操作系统、加多少天的历史数据本地存储51这种8位机跑起来就比较吃力了而F103是72MHz的Cortex-M3小马拉大车和牛刀杀鸡之间我宁可留足余量。第二ADC的精度和稳定性。粉尘传感器输出的模拟电压信号幅度很小放大和采样都要求ADC有足够的分辨率和较低的噪声STM32的12位ADC比很多51外置ADC方案省事得多。第三也是最实际的一点生态成熟。无论是HAL库还是标准外设库网上随便一搜就是一堆例程遇到问题容易排查这对一个需要长期维护的项目来说太重要了。选型时记得核对一件事F103C8T6是LQFP48封装Flash是64KBRAM有20KB带3个USART、2个SPI、1个I2C、10个ADC通道。咱们这个项目外设占用情况是USART1给ESP8266USART2留着调试打印PA0接DHT22PA1接粉尘传感器模拟输出PB0和PB1分别接除湿机和风机继电器PB2接蜂鸣器。资源完全够用还剩下大概三分之一引脚可以给以后扩展。2.2 温湿度传感器DHT22才是这个项目的最佳平衡点市面上常见的温湿度传感器有DHT11、DHT22也叫AM2302、SHT30这三种。我直接做了个表对比项目DHT11DHT22/AM2302SHT30温度精度±2℃±0.5℃±0.3℃湿度精度±5%RH±2%RH±2%RH分辨率10.10.01采样周期1s2s0.5s通信方式单总线单总线I2C参考价格3-5元8-15元10-20元驱动难度简单中等简单DHT11的精度放在粮仓或者电子元件仓库里是不够看的尤其湿度误差5%RH卡在60%RH报警阈值附近的货品可能就因此受潮报警边界模糊等于没报警。SHT30精度和稳定性确实好I2C通信写起来也简单但价格稍高而且I2C总线上如果以后要挂其他设备地址冲突问题还得处理。所以DHT22站出来成了最优解精度满足一般仓储要求单总线协议在短距离下问题不大成本也压得住。接线特别简单DHT22的VCC接3.3V或者5V都可以我建议接3.3V因为它的数据脚电平本来就和STM32兼容如果接5V供电数据脚上最好加一个10K上拉电阻到3.3V免得电平不匹配把IO口打坏。DATA脚和GND脚之间记得并一个0.1uF电容这能滤掉电源纹波对时序信号的影响实测下来读数会稳不少。2.3 粉尘传感器GP2Y1010AU0F的接线和采样时序要仔细搞粉尘检测这块我用的是夏普GP2Y1010AU0F就是那个红外LED照射空气、检测反射光强度的模块。它的工作原理可以一句话概括红外光在空气中遇到粉尘颗粒会产生散射接收管接收到的散射光强弱和粉尘浓度成正比传感器把光信号转换为模拟电压输出。所以它本质上是把“粉尘浓度高不高”翻译成了一个电压值你只需要用ADC去读就行不用真的去数颗粒数量。但这里有两个关键细节。第一它内部的红外LED需要脉冲驱动不是一直通电。手册规定需要在VLed引脚给出一个周期10ms、占空比0.32即高电平持续3.2ms左右的脉冲信号而且这个脉冲和输出采样要精确配合——LED亮起后大约280微秒时才是读取模拟输出的最佳窗口。很多新手直接把模块插上就采样读出来全是抖动值就是因为没卡这个时间点。我用STM32的一个定时器TIM3输出频率100Hz、占空比32%的PWM接到VLed引脚然后ADC触发设置为定时器更新事件延迟280us触发的软件延时这样每次采样都能踩在波形最平缓的那个小平台上。第二传感器供电用5V但它的模拟输出电压范围大约是0到3.6V而STM32的ADC输入范围是0到3.3V。如果不做处理理论上超过3.3V的电压会损坏ADC输入引脚。稳妥的做法是在信号输出端串一个10K电阻再对地接一个10K电阻构成一个1/2分压器这样最大电压就被限制在1.8V左右ADC读起来也更安全。然后根据分压比例把ADC值换算回真实电压再用公式计算浓度。实测中我会在算法里做一个1秒窗口的滑动平均因为风扇气流流动会让粉尘读数周期性波动单点采样毫无意义。粉尘浓度换算公式GP2Y1010的典型特性是输出电压V和粉尘浓度近似线性关系可以用mg/m³ ( V - V_offset ) / 某系数来估算具体系数因传感器个体差异不同建议拿到手之后用标准粉尘环境标定一下。如果没条件标定至少也要做一个相对值监测记录通电后前30秒的基线电压之后读数总是超过基线电压1.5倍以上就报警。这个思路虽然没有绝对精度但用来做“环境突然恶化”告警是完全够用的。2.4 继电器控制电路让STM32安全地驱动风机和除湿机STM32的GPIO输出能力非常有限灌电流和拉电流都是毫安级别直接驱动继电器根本不行。我用的方案是STM32 GPIO先接一个NPN三极管S8050三极管的集电极接继电器线圈线圈两端反向并联一个1N4007续流二极管。GPIO输出高电平时三极管导通继电器吸合输出低电平时继电器断开。续流二极管必须加因为继电器线圈是感性负载断电瞬间会产生反电动势没有二极管钳位的话这个尖峰电压可能直接击穿三极管甚至打坏STM32引脚。驱动芯片ULN2003是一个更省心的选择8路达林顿管阵列内部自带续流二极管接继电器只要把线圈一端接5V另一端接到ULN2003的输出脚输入脚由STM32控制就行。我在这个项目里用了两路ULN2003一路控制除湿机一路控制风机还留了两路备用以后想加电动窗户或者加热器也方便。继电器本身的额定电压必须选5V的别买12V的还要额外整一路电源。触点容量方面风机和除湿机的功率一般不超过1000W选用250V/10A的继电器已经足够注意实际负载电流不要超过触点电流的70%留出安全余量。2.5 电源分配ESP8266和继电器是两大坑这个项目最容易翻车的地方不在程序而在电源。我一开始图省事用一个5V/2A的手机充电头给整个系统供电结果反复出现“继电器一吸合ESP8266就掉线”的毛病。原因很简单继电器线圈通电瞬间电流冲击大导致5V电压跌落ESP8266灵敏度高供电低于3.0V就自动重启。后面改成这样供电220V进开关电源输出12V/3A然后分成两路。一路用LM2596降压模块降到5V给继电器和粉尘传感器供电另一路再用AMS1117-3.3从5V降到3.3V给STM32和ESP8266供电。注意AMS1117的最大输入电流有限ESP8266在Wi-Fi发射瞬间电流可达300mA所以我给ESP8266单独又加了一个100uF电解电容和0.1uF瓷片电容并联放在它的VCC和GND引脚附近用来吸收瞬态电流。这样“电源分级就近滤波”的办法实测下来系统就稳定多了。3. 软件实现从底层驱动到控制策略3.1 工程搭建直接用STM32CubeMX生成骨架我用的是STM32CubeMXKeil MDK这套组合原因就是快。CubeMX里选好芯片型号按照这组引脚配置就行PA0GPIO输入接DHT22数据线PA1ADC1_IN1接粉尘传感器分压后的模拟输出PA2/PA3USART2PA2接ESP8266的RXPA3接ESP8266的TXPB0输出接ULN2003输入0控制除湿机PB1输出接ULN2003输入1控制风机PB2输出接蜂鸣器TIM3输出比较通道CH1PA6或PB0重映射一下生成100Hz的PWM给粉尘传感器VLed引脚时钟配置上我开了内部8MHz晶振然后倍频到72MHz外接8MHz晶振更推荐但对仓库这种稳定环境内部晶振也不是不行。ADC我配置成连续转换模式扫描序列长度为1采样周期尽量设长一点比如239.5周期这样可以减少源阻抗对采样精度的影响。DHT22的读取我放在一个100ms周期的定时中断里执行主循环只负责判断状态和发指令。3.2 DHT22时序读取单总线的脾气要摸清楚DHT22的单总线协议看起来不复杂实际写代码时最容易踩坑的是时序窗口很窄。主机先拉低总线至少1ms然后释放并拉高这是起始信号。DHT22响应时先把总线拉低约80us然后再拉高80us这是应答信号。之后就是40位数据每一位的开始都是50us的低电平接着是26到28us的高电平表示“0”或者70us的高电平表示“1”。DHT22读时序的关键就是把这26us和70us的差别检测出来。我用的方法是读到应答信号结束后进入一个循环用定时器计数等待引脚拉低然后测量高电平持续的时间。具体实现是开启TIM2定时器在GPIO检测到上升沿时清零计数值检测到下降沿时读取计数值根据计数值判断是0还是1。判断阈值取45us高于就是1低于就是0。这样比单纯的空循环延时可靠因为STM32主频72MHz空循环对编译优化级别特别敏感换个优化等级同样的代码延时可能就变了安全问题也就跟着来了。还有一个细节读DHT22期间必须关中断。比如DHT22正在通过总线发数据时如果进程被一个高优先级中断打断超过几微秒后边的位时序就全歪了读出来就是一个离谱的湿度值。所以我在读取函数开头用__disable_irq()关掉全局中断读完再开。实测中我在仓库现场测试时每次读DHT22都会偶发一次校验失败加了关中断之后这个概率大幅下降。3.3 粉尘ADC采集与滤波算法粉尘传感器的模拟输出信号很弱而且绝大部分是低频噪声。我采集方式用的是PWM脉冲触发后延时280us读取ADC连续采样16次算平均。然后是二级滤波第一级是中值滤波把采集到的16个值排序取中间4个再平均去掉突发毛刺第二级是30秒滑动平均每1秒推入一个新值滤掉风扇气流引起的周期性波动。这两级加完之后数据曲线肉眼可见地平滑了误报警情况也几乎消失。在写ADC代码时我建议用DMA循环模式把ADC数据自动搬运到内存数组每采集固定次数就在DMA传输完成中断里做一个标志位。这样主循环不用死等ADC30秒滑动平均数组实时更新系统的整体实时性会好很多。不过如果只是基于CubeMX生成的ADC中断模式对仓库控制这种慢速系统来说也没有问题这个项目实时性要求并不高。3.4 控制策略滞回控制比PID更适合这套硬件环境控制策略是整个系统的灵魂也是很多人不知道从那下手的部分。一开始我脑子里闪过PID控制思路但马上否决了。原因很简单PID适合输出连续量的执行器比如调节阀开度、变频器转速。而仓库里接的是继电器只有“开”和“关”两种状态输出是开关量PID参数调得再好系统也会在阈值附近来回抖动继电器每秒吸合一次用不了半天触点就烧了。所以我采用了滞回控制思路很直观湿度报警阈值设为70%RH超过这个值就启动除湿机一直干到湿度降到低于65%RH才停止。这个中间5%RH的差值就是滞回区间。有了滞回区间执行器不会频繁动作。同样的逻辑用在温度上温度高于40℃启动风机降到35℃以下停止粉尘浓度高于300ug/m³启动风机强力排风低于200ug/m³再停。三条控制逻辑互不冲突因为除湿机、风机、排风机本来就是三路独立执行器。控制代码也不复杂主循环里做状态判断状态机分为“正常”“报警”“强制模式”三态。强制模式是为了调试方便手工把某一路继电器吸合一段时间这也是现场维护时特别需要的功能。3.5 ESP8266通信链路初始化流程ESP8266在系统里的角色很明确只是“业务员”负责把数据送出去所有业务判断都在STM32本地完成。我用的模块是ESP-01S出厂自带AT固件不需要刷固件直接把USART2接到模块的串口引脚上波特率设成115200。开机初始化流程如下延时2秒让ESP8266模块完成上电自检发送“AT”测试串口是否通畅如果返回“OK”继续下一步发送“ATE0”关掉回显发送“ATCWMODE1”设置STA模式发送“ATCWJAPWiFi名,密码”连接路由器等待返回“WIFI GOT IP”发送“ATMQTTUSERCFG0,1,设备标识,用户名,密码,0,0,”配置MQTT账号信息发送“ATMQTTCONN0,MQTT服务器地址,端口1”建立MQTT连接这里要提醒一下AT指令的应答串每个指令都要等。我写了一个串口接收缓冲区的解析函数收到完整一行“OK”或者“ERROR”再发送下一条。很多新手喜欢在延时之后无脑循环发指令结果第一条还在握手第二条就到了命令顺序就乱了。实际调试时我发现ESP8266模块重启后最长需要3-5秒才就绪这个等待时间千万不能省。4. 上云协议选型与数据链路设计4.1 为什么选MQTT而不是HTTP仓库环境控制系统的上云需求有两个特征数据上报频率低大约每分钟一条消息就够了二是希望云端保持最新的状态快照随时可以查。用HTTP也能做每30秒POST一条温湿度数据到服务器服务器存一下数据库逻辑很简单。但HTTP是短连接每次请求都要经历建立连接、发送请求、等待响应、断开连接的过程在信号不好的仓库里这种高频连接方式既耗电又容易超时。MQTT就合适得多。它是长连接协议设备连上之后一直保持连接数据帧头部非常小一条温湿度数据包也就几十个字节对ESP8266这种只有几百KB RAM的模块非常友好。而且MQTT的推送机制是“发布/订阅”STM32只需要往一个主题发布数据就行服务端天然就能收到不需要处理一堆80状态码之类的问题。这个项目我用的是“ESP8266 AT指令集自带的MQTT指令自己搭建在云主机上的MQTT Broker”整体流程很顺。4.2 数据帧格式与主题设计数据格式我用的是JSON虽然比二进制协议多几个字节但可读性特别强以后接小程序或者Web前端只需要几十行代码就能解析。上传的数据帧长这样{device_id:WH01,temp:25.6,humi:68.3,dust:156.7,fan:1,dehum:0,alarm:0}字段含义分别是设备编号、温度、湿度、粉尘浓度、风机状态、除湿机状态、告警标志位。控制状态也一起传上去这样在远端看到的不只是环境数据还能确认执行器是不是真的动作了这个细节对远程运维特别重要。主题我按设备ID拆分比如“wh01/env”作为上报主题“wh01/cmd”作为下发控制命令主题。设备端订阅“wh01/cmd”可以接收云端的远程控制指令。有人可能会问数据都自动控制了为什么还要下发命令因为现场电控箱里的手动/自动切换开关不可能全躲在云端一台真正好用的设备必须支持远程强制启动风机这种操作比如工人早上刚进仓库觉得闷手机上点一下就通风。4.3 ESP8266模块收到“发布成功”这个细节如果你也用AT指令集连MQTT broker有一个典型的卡点ATMQTTPUB这个指令如果执行正确模块会返回“OK”值但实际的“发布成功”回包格式可能因为固件版本不同而略有差异有的是“OK\r\n”有的是“”。我建议不要在指令返回“OK”之后就立即认为数据已经上云而是再判断串口是否有“MQTTPUBLISH:OK”之类的成功打印。在测试中我遇到过服务器端收不到任何消息但客户端返回OK的情况抓包后发现是客户端把MsgID置成了0broker直接丢弃了消息。直到我把发布函数的MsgID参数改为非零值才恢复正常。如果可以建议直接用CubeMX的内存池和串口DMA完成接收收到完整的一帧JSON后校验最后一位校验字节是否正确再通过发布函数推送。为了不阻塞主循环发布动作放在事件标志位的触发下执行主循环里只检查标志位和执行发布避免串口阻塞在长字符串发送过程中导致其他任务卡死。5. 现场调试实录与问题速查表做这类系统最大的学习机会其实是调试阶段。我按照自己实际调试时踩过的坑做了一张问题速查表应该对大家有直接帮助。故障现象可能原因排查方法与解决办法继电器一吸合ESP8266就重启电源瞬间跌落用示波器测5V在继电器动作时是否有明显跌落给ESP8266单独加滤波电容电源分级供电温湿度读数为0或校验失败DHT22引脚接触不良或时序中断检查上拉电阻读时序时关闭中断换个新传感器验证粉尘浓度数值一直是0没有给VLed引脚输出PWM用万用表测VLed引脚是否有脉冲电平确认定时器通道正确使能粉尘值波动很大风扇气流扰动软件加滑动平均滤波采样点避开风道直吹位置ESP8266能连路由但MQTT连不上端口或保权密码错误在PC上用MQTT客户端测试相同参数查看broker日志确认是否有CONNECT帧到达远程控制指令下发无响应下发的主题格式不匹配检查STM32订阅的主题是否和下发端一致打印串口接收缓冲区内容5.1 继电器动作导致ESP8266复位的完整检修过程这个问题我印象最深刻。现象手动触发除湿机继电器蜂鸣器和LED都正常但系统日志中断了几秒恢复后发现ESP8266重启了云端少了一条数据。我用万用表量了5V电压继电器动作瞬间从5.02V跌到4.28V这个跌落幅度其实不会让STM32重启但ESP8266对供电质量要求高4.28V时候Wi-Fi射频部分会较大的衰弱。修改方案有三步先在大电容上并联一个470uF电解电容储能补偿瞬态跌落然后把ESP8266的供电从5V电源轨改接到一个独立AMS1117后面经过LDO稳压和RC滤波后电压纹波明显降低最后在继电器线圈两个引脚之间并联了一个TVS二极管SMBJ5.0A把感性负载的反峰吸收掉。三管齐下之后现象完全消失。5.2 MQTT掉线后自动重连逻辑ESP8266的TCP连接在仓库这种复杂电磁环境下偶尔掉线是很正常的事。掉线不可怕可怕的是掉线之后不重连。我在主循环里加了一个“保活探测”逻辑每个周期检查上一次成功收到broker心跳响应的时间超过30秒没有收到就判断为掉线然后执行“ATMQTTDISC0”断开残留连接延时5秒后重新执行上面的初始化流程。注意MQTT断线的标志不只是收到“ERROR”有些时候broker会强行关闭TCP连接此时模块会返回“CLOSED”这种情况也要纳入掉线判断。5.3 空调外机干扰下的ADC采样有次仓库工作人员反映粉尘数值偶尔跳到上千检查发现空调外机启动的瞬间数值就飙升。空调外机起动时会有电机火花产生强烈的电磁干扰AD转换如果正好在这个瞬间采样就会读到干扰毛刺。中值滤波已经能滤掉绝大多数毛刺但每天总有一两次数值异常跳变最终把滑动平均窗口的数据全部污染了。解决方法是在ADC数据链路上增加一个抗混叠RC低通滤波器10K电阻加100nF电容带宽约1kHz效果立竿见影。这种抗干扰设计其实应该在硬件阶段就考虑我一开始图省事没加后来还是补上的大家可以直接抄作业在粉尘传感器输出端加这个RC滤波器一劳永逸。6. 复盘与给后来者的建议整个系统跑下来最深的感受是这类环境控制项目真正决定成败的不是某个硬件有多高级而是整个链路的稳定性和可维护性。你要是只做个DEMO传感器插杜邦线、代码写死阈值、ESP8266能发一条测试数据就算成功但要落地到真实仓库场景需要考虑的东西就多了继电器动作时的电源冲击、电磁干扰下ADC采样的稳定性、MQTT断线重连的策略、现场维护时强制手动操作的可能性。这些东西文档里不会写只能靠一次次现场问题排查积累出来的。从成本上讲F103C8T6核心板价格很低DHT22十几块GP2Y1010模块二十块左右ESP-01S十几块继电器模块和电源加起来小几十块整个系统的物料成本控制在一百元左右性价比远高于市面上一套几千块的商用仓储环境监测设备。当然商用的贵在工业级传感器校准和长期稳定性保证上但在小型仓库和车间场景里这套自制系统已经能解决大部分实际问题。最后分享一个经验而且是我在调试后期才领悟出来的所有上报数据都要带时间戳和源标志位。比如JSON数据里除了温湿度我还加了系统运行时间“uptime_ms”这样在云端排查问题的时候一眼就能看出设备是不是发生过重启——如果时间戳连续但uptime_ms清零了那基本可以判断是软复位如果时间戳直接跳了一段那就是断电或者掉线了。这个小小的字段在远程运维时帮了大忙。做得再好的控制逻辑也不如一个稳定的数据链路重要这是我做这个仓库环境控制系统最大的感悟。
返回列表