ARTICLE DETAIL

资讯详情

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

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

基于STM32的仓库环境控制系统:温湿度粉尘监测与ESP8266上云 1. 从仓库管理的真实痛点说起做仓储管理的人都有一个共识货物损坏的原因里温湿度失控和粉尘超标占了相当大的比例。特别是存放电子元器件、精密仪器、食品原料、药品这类对存储环境敏感的货物一旦温湿度超出安全区间轻则性能下降重则整批报废。粉尘的问题同样棘手高浓度粉尘不仅影响货物表面洁净度还存在安全隐患。传统仓库的做法是什么靠人工巡检拿个手持温湿度计每隔几小时去转一圈粉尘浓度基本靠肉眼判断。这种方式的问题很明显数据不连续、响应滞后、无法追溯。等巡检人员发现问题的时候可能货物已经受了几个小时的影响。而且人工记录的数据很难做长期趋势分析你根本不知道仓库环境在夜间的波动规律。这套基于STM32的仓库环境控制系统核心目标就是解决三个问题实时监测温湿度粉尘浓度、自动响应通风除湿、远程可视通过ESP8266上云。整套系统围绕STM32微控制器搭建前端接传感器采集数据中间做逻辑判断和阈值控制后端通过ESP8266模块把数据推到云平台实现手机端或网页端的远程查看。适合哪些人参考如果你正在做嵌入式相关的课程设计、毕业设计或者手头有类似的环境监测项目需求这套方案可以直接复用。即使你是刚接触STM32的新手只要跟着思路走也能把整个系统跑起来。下面我会从硬件选型、传感器驱动、控制逻辑、上云通信、调试踩坑几个维度把整个项目的关键细节拆开讲清楚。2. 硬件选型为什么选这些器件而不是别的2.1 主控芯片的取舍STM32系列型号非常多从F0到H7跨度很大。这个项目选的是STM32F103C8T6也就是常说的蓝板或最小系统板。为什么选它几个原因第一资源够用。F103C8T6有64KB Flash、20KB SRAM、2个SPI、3个USART、2个I2C、多个定时器和ADC通道。对于温湿度采集、粉尘传感器读取、ESP8266串口通信、OLED显示这几个任务来说资源绰绰有余。你不需要上F4或F7那些多出来的DSP指令和浮点单元在这个场景里用不上。第二生态成熟。F103的标准库和HAL库资料最丰富网上随便搜一个外设的配置代码都能找到参考。对于做项目来说生态成熟意味着你踩坑的概率大幅降低。第三成本低。一块F103C8T6最小系统板十几块钱批量做的话更便宜。仓库环境控制系统可能需要多点部署成本控制是实际考量。如果你手头只有STM32F401或者其他型号也完全可以只需要注意引脚分配和外设时钟配置的差异。核心逻辑不变。2.2 温湿度传感器的选择温湿度传感器常见的有DHT11、DHT22AM2302、SHT30、SHT31等。这个项目我建议用SHT30理由如下DHT11精度太差温度±2°C、湿度±5%RH而且响应速度慢采样间隔不能低于1秒。DHT22好一些但仍然是单总线协议时序要求严格中断干扰下容易读失败。SHT30是I2C接口精度±0.3°C温度和±3%RH湿度响应快而且I2C通信在STM32上非常稳定。SHT30的接线也简单VCC接3.3VGND接地SDA和SCL分别接STM32的I2C引脚比如PB6/PB7再加上两根4.7K的上拉电阻就行。I2C地址默认是0x44ADDR引脚接地或0x45ADDR接VCC。如果你预算紧张用DHT22也能做但要做好数据滤波比如连续读三次取中间值避免单次读取异常导致误判。2.3 粉尘传感器的选型分析粉尘监测是这个项目里比较特殊的一环。常见方案有GP2Y1010AU0F夏普的光学粉尘传感器模拟输出需要驱动LED脉冲价格便宜但精度一般适合定性检测。PM2.5激光传感器如攀藤PMS5003、SDS011串口输出精度高能给出PM1.0/PM2.5/PM10的具体数值但价格贵一些。ZH03A激光粉尘传感器串口输出体积小精度不错适合嵌入式项目。综合考虑精度和开发难度我推荐用攀藤PMS5003或ZH03A。它们都是UART接口STM32只需要一个串口就能读取数据协议也简单。以PMS5003为例它每帧32字节帧头是0x42 0x4D后面跟着数据长度、PM1.0/PM2.5/PM10的浓度值各占2字节高字节在前最后是校验和。如果你只是做课程设计用GP2Y1010AU0F也够但要注意它的模拟输出需要ADC采集而且需要定时驱动内部红外LED周期约10ms代码上稍微麻烦一点。2.4 执行机构通风和除湿怎么驱动通风用继电器排风扇的方案最直接。STM32的GPIO输出3.3V驱动能力有限不能直接带继电器线圈中间需要加一级三极管或光耦隔离。典型电路是GPIO→1K电阻→S8050三极管基极发射极接地集电极接继电器线圈一端线圈另一端接5V继电器线圈两端反并联一个1N4007续流二极管。除湿的话小功率场景可以用半导体制冷片帕尔贴做冷凝除湿大功率场景直接控制除湿机。不管哪种控制逻辑是一样的STM32输出高低电平控制继电器通断继电器控制设备电源。注意继电器模块一定要选带光耦隔离的否则继电器吸合瞬间的反向电动势可能干扰STM32甚至烧毁GPIO。如果自己搭电路续流二极管绝对不能省。2.5 ESP8266的角色定位ESP8266在这个项目里只做一件事把STM32采集的数据传到云平台。它不负责传感器采集也不负责控制逻辑那些都是STM32的活。ESP8266通过UART和STM32通信STM32发AT指令ESP8266执行。为什么不用ESP8266直接做所有事因为ESP8266的GPIO和ADC资源有限而且它的实时性不如STM32。分工明确STM32管采集和控制ESP8266管通信各司其职。ESP8266模块选ESP-01S就行便宜、体积小。但要注意ESP-01S的供电必须是3.3V而且电流要够峰值可能到300mA不能用STM32板子上的3.3V稳压器直接供最好单独用一个AMS1117-3.3或者专门的3.3V电源模块。3. 传感器驱动与数据采集的实操细节3.1 SHT30的I2C驱动要点SHT30的I2C通信看起来简单但有几个细节容易翻车。首先是时钟拉伸。SHT30在测量期间会拉低SCL线告诉主机我还没准备好。如果你的I2C驱动没有处理时钟拉伸读出来的数据可能是错的。STM32的硬件I2C是支持时钟拉伸的但软件模拟I2C需要自己判断SCL电平。其次是测量命令的选择。SHT30支持多种测量模式常用的有命令模式特点0x2C06高重复性时钟拉伸精度最高但阻塞时间长0x2C10中重复性时钟拉伸平衡选择0x2400高重复性无时钟拉伸适合中断环境我一般用0x2C06测量时间约15ms。发送命令后延时20ms再读数据读回来6个字节温度高8位、温度低8位、温度CRC、湿度高8位、湿度低8位、湿度CRC。温度换算公式T -45 175 * (raw_temp / 65535.0)湿度换算公式RH 100 * (raw_hum / 65535.0)CRC校验建议做虽然大部分时候数据没问题但I2C总线受干扰时CRC能帮你过滤掉错误数据。SHT30的CRC多项式是0x31初始值0xFF。3.2 粉尘传感器的串口读取以PMS5003为例它的串口参数是9600波特率、8数据位、无校验、1停止位。数据帧格式如下帧头: 0x42 0x4D 帧长度: 2字节高字节在前 数据区: 28字节包含PM1.0/PM2.5/PM10等 校验和: 2字节STM32这边用串口接收中断或者DMA来收数据。我建议用DMA空闲中断的方式DMA负责把串口数据搬到缓冲区空闲中断负责判断一帧结束。这样CPU不用一直守着串口效率高。收到一帧后先检查帧头是不是0x42 0x4D然后算校验和把帧头到数据区最后一个字节全部加起来取低16位和帧尾的校验和比较。校验通过再提取PM2.5的值。实操心得PMS5003上电后风扇需要预热前30秒的数据不稳定建议上电后延时30秒再开始采信数据。另外传感器要定期清洁进风口否则积灰会导致读数偏高。3.3 数据滤波别让一个异常值毁掉整个逻辑传感器数据偶尔跳变是正常的。如果你直接用单次采样值做阈值判断可能会因为一个尖峰误触发通风或除湿。我的做法是滑动平均中值滤波组合。具体来说维护一个长度为5的队列每次新数据进来先做中值滤波排序取中间值然后再做滑动平均。这样既能去掉脉冲干扰又能平滑数据。代码上可以这样实现#define FILTER_LEN 5 float temp_buf[FILTER_LEN]; int buf_index 0; float filter_temperature(float new_val) { temp_buf[buf_index] new_val; buf_index (buf_index 1) % FILTER_LEN; // 复制一份排序取中值 float sorted[FILTER_LEN]; memcpy(sorted, temp_buf, sizeof(sorted)); // 简单冒泡排序 for (int i 0; i FILTER_LEN - 1; i) { for (int j 0; j FILTER_LEN - 1 - i; j) { if (sorted[j] sorted[j1]) { float tmp sorted[j]; sorted[j] sorted[j1]; sorted[j1] tmp; } } } return sorted[FILTER_LEN / 2]; }这个滤波逻辑对温湿度和粉尘浓度都适用。实测下来数据曲线会平滑很多不会因为开门、人员走动等瞬时干扰误触发控制逻辑。4. 自动控制逻辑阈值怎么定迟滞怎么加4.1 温湿度阈值的确定依据阈值不是拍脑袋定的要根据存储货物的要求来。一般来说温度大多数电子元器件和药品的存储温度是15~25°C食品原料可能要求更低。我一般设温度上限28°C、下限10°C。湿度电子元器件怕潮湿一般要求40%~60%RH。设湿度上限65%RH、下限35%RH。粉尘PM2.5超过75μg/m³就属于轻度污染仓库环境建议控制在50μg/m³以下。这些阈值应该做成可配置的存在STM32的Flash里方便后期调整。可以用一个结构体保存typedef struct { float temp_high; float temp_low; float humi_high; float humi_low; uint16_t dust_high; } ThresholdConfig; ThresholdConfig config {28.0, 10.0, 65.0, 35.0, 50};4.2 迟滞比较防止继电器频繁跳动如果你直接用超过阈值开、低于阈值关的逻辑会出现一个问题当温湿度刚好在阈值附近波动时继电器会频繁吸合断开不仅噪音大还会缩短继电器寿命。解决办法是加迟滞。比如温度上限设28°C但关闭通风的温度不是28°C而是26°C。也就是说温度升到28°C时开启通风温度降到26°C时才关闭。中间这2°C就是迟滞带。#define TEMP_HYSTERESIS 2.0 #define HUMI_HYSTERESIS 5.0 void control_logic(float temp, float humi, uint16_t dust) { static uint8_t fan_on 0; static uint8_t dehumidify_on 0; // 通风控制温度或粉尘超标都开 if (!fan_on (temp config.temp_high || dust config.dust_high)) { fan_on 1; GPIO_SetBits(FAN_PORT, FAN_PIN); } else if (fan_on (temp config.temp_high - TEMP_HYSTERESIS) (dust config.dust_high - 10)) { fan_on 0; GPIO_ResetBits(FAN_PORT, FAN_PIN); } // 除湿控制 if (!dehumidify_on humi config.humi_high) { dehumidify_on 1; GPIO_SetBits(DEHUM_PORT, DEHUM_PIN); } else if (dehumidify_on humi config.humi_high - HUMI_HYSTERESIS) { dehumidify_on 0; GPIO_ResetBits(DEHUM_PORT, DEHUM_PIN); } }迟滞带的大小要根据实际环境调整。如果仓库环境变化剧烈迟滞带可以设大一点如果要求精确控制迟滞带就设小一点但继电器的动作频率会相应增加。4.3 定时轮询与中断的配合整个系统的任务调度我建议用定时器中断主循环的方式。TIM2配置成1ms中断在中断里做软件计数每500ms触发一次传感器采集每2秒触发一次控制逻辑判断每5秒触发一次ESP8266上传。为什么不在中断里直接做所有事因为传感器读取和ESP8266通信都是耗时操作放在中断里会阻塞其他中断。正确的做法是中断只负责设置标志位主循环检测到标志位后再执行具体任务。volatile uint8_t flag_sample 0; volatile uint8_t flag_control 0; volatile uint8_t flag_upload 0; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); static uint16_t cnt 0; cnt; if (cnt % 500 0) flag_sample 1; if (cnt % 2000 0) flag_control 1; if (cnt % 5000 0) flag_upload 1; } } int main(void) { // 初始化... while (1) { if (flag_sample) { flag_sample 0; sample_sensors(); } if (flag_control) { flag_control 0; control_logic(current_temp, current_humi, current_dust); } if (flag_upload) { flag_upload 0; upload_to_cloud(); } // 其他任务... } }这种架构清晰、可扩展后期要加新任务只需要在中断里加一个标志位主循环里加一个处理分支就行。5. ESP8266上云AT指令的坑与解决方案5.1 ESP8266的初始化流程ESP8266通过AT指令和STM32通信初始化流程一般是发送AT确认模块响应返回OK发送ATCWMODE1设置为Station模式发送ATCWJAPSSID,PASSWORD连接WiFi发送ATCIPSTARTTCP,服务器地址,端口建立TCP连接发送ATCIPSEND长度然后发送数据等待服务器响应每一步都要等ESP8266返回OK或ERROR不能连续发。我见过很多人初始化失败就是因为AT指令发太快模块还没处理完上一条就收到了下一条。正确的做法是每条AT指令后等待响应超时重试。超时时间设2~3秒重试3次。如果3次都失败重启ESP8266拉低RST引脚再拉高重新初始化。uint8_t esp8266_send_cmd(char *cmd, char *expected, uint16_t timeout) { USART_SendString(USART2, cmd); USART_SendString(USART2, \r\n); uint32_t start get_tick(); while (get_tick() - start timeout) { if (strstr((char*)rx_buffer, expected) ! NULL) { memset(rx_buffer, 0, RX_BUF_SIZE); return 1; // 成功 } } memset(rx_buffer, 0, RX_BUF_SIZE); return 0; // 超时 }5.2 数据上传协议的选择ESP8266上传数据有几种方式TCP直连直接和服务器建立TCP连接发送自定义格式的数据。简单直接但需要自己有服务器。MQTT通过MQTT协议发布到Broker适合多设备管理。ESP8266有AT指令支持MQTTATMQTTUSERCFG等但配置稍复杂。HTTP POST通过HTTP协议提交数据到云平台API。兼容性好但每次都要建立连接开销大。对于这个项目我推荐用MQTT。原因数据量小、实时性要求高、需要远程查看。MQTT的发布/订阅模型很适合这种场景。云平台可以选择巴法云、OneNet等它们都提供了MQTT接入方式。以巴法云为例STM32通过ESP8266发送的AT指令序列大致是ATCWMODE1 ATCWJAPwifi_name,wifi_password ATMQTTUSERCFG0,1,client_id,username,password,0,0, ATMQTTCONN0,bemfa.com,9501,1 ATMQTTPUB0,topic_name,{\temp\:25.3,\humi\:58.2,\dust\:32},0,0注意ATMQTTPUB的数据长度要精确计算包括JSON字符串的每一个字符。长度算错了会返回ERROR。建议在代码里用strlen()动态计算不要手写固定值。5.3 断线重连与心跳机制WiFi网络不稳定是常态ESP8266掉线后如果不处理数据就断了。必须加心跳和重连逻辑。心跳的做法每隔30秒发送一次ATPINGbemfa.com如果返回OK说明连接正常如果超时或返回ERROR就触发重连流程。重连流程就是重新执行初始化步骤。另外MQTT本身有Keep Alive机制ATMQTTCONN的最后一个参数就是Keep Alive时间秒。设成60秒Broker在60秒内没收到数据会认为客户端离线。所以你的上传间隔不能超过这个时间否则会被Broker踢下线。void check_connection(void) { static uint32_t last_heartbeat 0; if (get_tick() - last_heartbeat 30000) { last_heartbeat get_tick(); if (!esp8266_send_cmd(ATPING\bemfa.com\, OK, 3000)) { esp8266_reconnect(); } } }6. 调试过程中踩过的坑与排查思路6.1 串口收不到ESP8266的响应这是最常见的问题。排查顺序检查波特率。ESP8266默认波特率是115200但有些模块出厂固件是9600。先发AT试试如果没反应换波特率再试。检查供电。ESP8266峰值电流300mA如果供电不足模块会不断重启。用万用表量一下模块VCC和GND之间的电压正常应该是3.3V±0.1V。检查TX/RX交叉。STM32的TX接ESP8266的RXSTM32的RX接ESP8266的TX。这个搞反了很常见。检查AT指令结尾。ESP8266要求AT指令以\r\n结尾只发\r或只发\n都可能不响应。我遇到过一次排查了半天发现是串口助手的波特率设成了9600而模块是115200。这种低级错误在调试初期特别容易犯建议每次调试前先确认一遍基础参数。6.2 粉尘传感器数据一直是0PMS5003数据为0的原因可能有串口没接对。PMS5003的TX要接STM32的RXRX接STM32的TX。如果只接了TX没接RX传感器不会主动发数据有些型号需要先发命令才发数据。波特率不对。PMS5003默认9600但有些批次是115200。查一下手册确认。传感器没预热。上电后风扇要转起来激光管要稳定前30秒数据不可信。进风口堵塞。如果传感器用久了进风口积灰会导致读数偏低甚至为0。用气吹清理一下。6.3 继电器吸合导致STM32复位这个问题很典型继电器吸合瞬间线圈产生反向电动势通过电源线或地线耦合到STM32导致复位。解决方案继电器线圈两端加续流二极管1N4007方向是阴极接VCC阳极接线圈驱动端。继电器模块和STM32分开供电或者至少在电源入口加一个大电容100μF以上做储能。继电器模块选带光耦隔离的切断电气连接。STM32的复位引脚加一个0.1μF电容到地提高抗干扰能力。我一开始没加续流二极管继电器一吸合STM32就重启数据全丢。加上二极管之后问题解决。这个坑很经典做继电器控制一定要提前考虑。6.4 ESP8266发送MQTT消息返回ERRORATMQTTPUB返回ERROR的原因通常有JSON格式错误。比如引号没转义、括号不匹配。建议先在PC上用MQTT客户端测试消息格式确认无误后再移植到STM32。数据长度不对。ATMQTTPUB的第二个参数是消息长度必须和实际发送的字节数完全一致。中文字符占3个字节计算时要注意。MQTT连接已断开。先确认ATMQTTCONN返回OK再发MQTTPUB。Topic名称错误。有些平台要求Topic必须提前创建没创建就发布会失败。我的做法是在代码里加一个调试串口把每次发送的AT指令和收到的响应都打印出来方便对比。调试阶段不要怕串口输出多信息越全越好排查。7. 系统联调与长期运行的经验7.1 上电自检流程系统上电后不要直接进入主循环先做一轮自检检查SHT30是否响应发测量命令看是否有ACK检查PMS5003是否有数据帧等5秒看串口是否有数据检查ESP8266是否在线发AT看是否返回OK检查OLED是否正常显示任何一项失败在OLED上显示对应的错误码方便快速定位问题。自检通过后再进入正常采集和控制流程。7.2 数据记录与追溯除了上云建议在STM32的Flash里留一块区域做本地数据记录。每小时存一条记录温度、湿度、粉尘、通风状态、除湿状态存满后覆盖最旧的数据。这样即使网络断了数据也不会丢后期可以导出分析。Flash写入要注意STM32F103的Flash页大小是1KB或2KB取决于型号写入前要先擦除整页。擦除次数有限约1万次所以不要频繁写。每小时写一次的话一年也就8760次完全在寿命范围内。7.3 外壳与安装位置传感器不要装在密闭的盒子里否则测的是盒子内部的温湿度不是仓库的真实环境。外壳要开通风孔SHT30和PMS5003的进气口要暴露在空气中。安装位置建议在仓库中部、离地1.5米左右避开空调出风口、门窗直射和发热设备。如果仓库面积大可以考虑多点部署每个点一套采集节点通过无线或有线方式汇总到一个主控。7.4 长期运行的稳定性观察系统跑起来之后我建议连续观察一周的数据曲线。重点看温湿度是否有周期性波动比如白天高、夜间低这是正常的通风和除湿的触发频率是否合理如果一天触发几十次说明阈值设得太紧粉尘浓度是否有异常峰值如果有排查是否有外部污染源ESP8266是否频繁掉线如果掉线频繁考虑换一个WiFi信道或加一个外置天线这些观察结果会帮你优化阈值参数和控制策略让系统真正贴合仓库的实际环境。8. 关于扩展方向的个人建议这套系统的框架是开放的后期想加功能并不难。比如加一个SD卡模块做本地数据存储用SPI接口FatFS文件系统每小时存一个CSV文件。或者加一个蜂鸣器做声光报警当温湿度或粉尘严重超标时提醒现场人员。再或者加一个RTC模块DS3231给每条数据打上精确的时间戳方便后期做趋势分析。如果你要做多点组网可以把ESP8266换成ESP32用ESP-NOW协议做节点间通信或者用LoRa模块做远距离传输。但那是另一个项目的事了先把单节点的采集、控制、上云跑通再考虑扩展。我在实际做这类项目的时候最大的体会是硬件选型决定下限软件架构决定上限。传感器选对了数据质量有保障控制逻辑写好了系统稳定可靠通信协议选对了远程查看才顺畅。每一个环节都别凑合凑合的地方迟早会出问题。
返回列表