简介:面向计算机与电子信息类专业学生及毕业设计开发者,提供一份基于单片机粮仓温湿度无线监测系统的完整设计文档。压缩包内仅含一个Word格式的毕业设计论文,文件大小约2.06MB,已有144人浏览学习。文档围绕系统总体设计方案展开,详细介绍数字温湿度传感器DHT11与单片机AT89C51的选型依据,并逐一说明无线通信模块的电路连接;内容涵盖传感器检测原理与工作时序、硬件电路搭建、无线通信协议选择以及单片机接口软件设计等,同时配有摘要、目录和完整章节,结构清晰便于查阅。对需要完成相关课程设计、毕业设计或了解粮仓环境监测系统实现路径的读者,具有较好的参考价值,也适合作为同类无线监测课题的模板。
1. 基于单片机粮仓温湿度无线监测系统:从课题文档到能上线跑的实物
很多工程师或学生第一次接触“基于单片机粮仓温湿度无线监测系统”这个题目,往往是从一份课程设计文档或毕设任务书开始的。但文档里画得再漂亮的系统框图,落地到真正的粮仓现场时,会冒出一堆写不了纸面的问题:传感器埋到粮堆多深数据才可信,无线模块隔着粮食到底能传多远,湿度探头在高湿环境里多久会漂移,电池能撑多久。这篇文章不打算复述文档里的总体设计图,而是按我实际调试过的一套方案来讲——主控和传感器怎么选、无线频段怎么定、固件怎么写、上位机怎么收数据,以及现场部署时真正让我翻过车的几个坑。如果你正准备做同类项目,从选型到落地,照着这套思路走可以少走很多弯路。
2. 系统架构与器件选型:粮仓环境先立规矩,再谈选型
2.1 粮仓给监测设备定的三条硬规矩
粮仓不是普通室内环境,选型之前得先把环境约束列清楚。第一条是温度跨度。北方粮库夏天粮堆表层能到 40°C 甚至更高,冬天仓温逼近 0°C,传感器工作范围至少要覆盖 -10°C 到 +50°C,如果选消费级芯片就得确认手册里的工作温度上限。第二条是湿度。粮食安全储存的水分标准一般在 12%~14%,但粮仓内部相对湿度在通风不良时长期徘徊在 70%RH~90%RH,传感器探头封装不好,腔内一结露,数据就直接失真,而且这种失真往往是缓慢漂移,很难靠程序判断。第三条是粮堆对无线信号的遮挡。散装粮食是颗粒介质,对微波频段的衰减非常明显,把 2.4GHz 的模块埋在 3 米深的粮堆里还期望它稳定上报,基本是玄学。
我的选型顺序是反着推的:先定无线通信频段,再定传感器,最后定主控。直接照搬开发板上的“DHT22 + NRF24L01 + STM32F103”组合,在浅粮堆的小场景能跑通,但粮堆深一点、仓房大一点就断连;换成 433MHz 或 LoRa,穿透能力完全不一样。下面把每个环节的取舍讲清楚。
2.2 温湿度传感器怎么选:DHT22、SHT30 还是 DHT11
粮仓监测最常用的是这三类温湿度传感器,参数差异直接决定数据可信度,先看对比表:
| 传感器 | 温度精度 | 湿度精度 | 接口 | 采样周期 | 典型价格区间 | 坑点 |
|---|---|---|---|---|---|---|
| DHT11 | ±2°C | ±5%RH | 单总线 | 1s | 极低 | 精度差,适合演示不适合测量 |
| DHT22/AM2302 | ±0.5°C | ±2%RH | 单总线 | 2s | 低 | 时序敏感,读取要关中断 |
| SHT30 | ±0.3°C | ±2%RH | I2C | 可到 8ms | 中 | 焊接注意,地址可选 |
DHT11 我不建议用,哪怕课程设计里经常出现。湿度误差 ±5%RH 意味着 75%RH 时实际可能是 70% 或 80%,粮食存储恰恰对水分敏感,这种误差会直接导致误判。DHT22 是性价比平衡点,精度够用、价格便宜、单总线只占一个 IO,缺点是时序窗口窄,用带操作系统的平台读取时容易翻车。SHT30 是更稳的选择,I2C 接口抗干扰强,测量速度也快得多,适合做多节点轮询,价格略高但在可接受范围内。
实际部署还有一种常见组合是“DS18B20 测温度 + 单独湿度传感器”,DS18B20 只有温度,防水封装版本适合插到粮堆内部测粮温,湿度探头放粮堆表面或者仓内空间。这个组合适合要测粮堆内部温度的场合,因为普通温湿度传感器探头没法做得很长。
我一般建议:预算敏感或者课程设计场景选 DHT22,工程现场长期部署选 SHT30,要测粮堆内部温度就加 DS18B20 防水探头。至于温度量程,以上三款都满足粮仓需求。
2.3 无线通信选型:NRF24L01、433MHz 与 LoRa 的取舍
无线方案是这套系统里最值得花时间的选型点。先看主流方案的对比:
| 方案 | 频段 | 典型发射功率 | 空旷传输距离 | 粮堆穿透能力 | 功耗 | 成本 |
|---|---|---|---|---|---|---|
| NRF24L01+PA | 2.4GHz | 20dBm | 500m-1km | 弱 | 中 | 低 |
| CC1101 | 433MHz | 10dBm | 300m | 中 | 中 | 低 |
| SX1278 LoRa | 433MHz | 20dBm | 2-5km | 较强 | 低 | 中 |
NRF24L01 最大的问题是 2.4GHz 频段对粮堆这类颗粒介质的穿透力差。我做过的模拟项目里,节点埋在 1.5 米深的粮堆下,发射功率调到最大,接收端在 30 米外就丢包严重。不是模块不行,是频段物理特性决定的。433MHz 的 CC1101 和 SX1278 在穿透能力上好很多,同样的粮堆深度,信号强度差距非常明显。
具体怎么选?我的经验是分场景:单个粮仓、节点距离接收端 50 米以内、粮堆不深,NRF24L01 完全够用,成本最低,代码也简单;多个粮仓或粮堆深度超过 2 米,直接用 LoRa,SX1278 模块价格贵一点,但不用折腾中继。433MHz 透传模块(比如自带协议的那种串口模块)也可以考虑,部署最快,但数据帧格式受限,扩展性差。
还有一点容易被忽略:接收端天线建议引出仓外。节点在粮仓内,接收端在值班室,中间隔着墙和粮堆,天线位置对接收灵敏度的影响比发射功率还大。
2.4 主控选型:STM32 还是 STC89C52
主控是整个系统里选择最灵活、也最容易走弯路的一环。很多课程设计用 STC89C52,51 内核,5V 供电,编程简单,老资料多;但它的低功耗能力几乎没有,定时唤醒做不好,而且片上资源少,接个 LCD 再跑无线协议就有点吃力。如果系统要求电池供电、定时上报、低功耗休眠,STM32 是更合理的选择。
STM32 系列里最推荐 STM32F103C8T6,便宜、资料多、HAL 库和标准库都有现成例程;对功耗敏感的可以选 STM32L0 系列或者 STM32L151,待机电流能到微安级别。ESP8266 或 ESP32 这类带 WiFi 的方案我不建议用在粮仓场景:一是 2.4GHz WiFi 需要靠近路由器或热点,粮仓里部署不现实;二是功耗比单纯的 2.4G 无线模块高一个量级,电池供电撑不了太久。
另外,如果这个系统的无线方案选的是 LoRa,市面上很多 SX1278 模块都是 SPI 接口,STM32 的硬件 SPI 跑起来很顺。51 内核也能用软件模拟 SPI,但速率和稳定性差一些,数据量稍大就明显吃力。
3. 硬件实现与调试:传感器节点从电路到能通电
3.1 节点硬件接线:STM32F103 + DHT22 + NRF24L01 的引脚对应
选好器件后,先把最小节点电路理清楚。这里给出一套我常用的接线方案,以 STM32F103C8T6 最小系统板、DHT22 和 NRF24L01 模块为例:
| 功能 | STM32 引脚 | 外部器件引脚 | 说明 |
|---|---|---|---|
| 温度湿度采集 | PA1 | DHT22 DATA | 单总线数据线,需 4.7kΩ 上拉到 3.3V |
| 无线 SPI 时钟 | PA5 | NRF24L01 SCK | SPI 时钟 |
| 无线 SPI 数据出 | PA7 | NRF24L01 MOSI | SPI 主机输出 |
| 无线 SPI 数据入 | PA6 | NRF24L01 MISO | SPI 主机输入 |
| 无线片选 | PA4 | NRF24L01 CSN | 片选,低电平有效 |
| 无线使能 | PA3 | NRF24L01 CE | 收发切换控制 |
| 无线中断 | PA2 | NRF24L01 IRQ | 可选,轮询可不接 |
| 电源 | 3.3V / GND | 各模块 VCC / GND | 注意 NRF24L01 必须 3.3V |
接线的时候有两个细节要特别注意。第一个是 DHT22 的 DATA 线上拉电阻,这个电阻必不可少,不加上拉会导致信号边沿不够陡峭,读出来的温度偶尔跳变、湿度偶尔错乱。第二个是 NRF24L01 模块的供电,千万不能接 5V,这模块一接 5V 就发烫,轻则通信失败,重则烧模块。如果用的是带 PA 功放的 NRF24L01 模块,电流会到 100mA 级别,3.3V 必须由独立的 LDO 提供,不能用最小系统板上的 AMS1117 硬扛,否则一发射就把电压拉垮。
3.2 洞洞板布局与焊接顺序:先电源、再传感器、最后无线模块
我焊接节点的顺序是固定的:先焊电源部分,用万用表量一遍各点电压,确认 3.3V 输出正常后再焊传感器和主控;最后才焊无线模块。这个顺序能让你在出问题时快速定位是电源问题还是模块问题。如果一上电 STM32 板上的 LED 亮但 DHT22 读数超时,先别怀疑程序,用示波器或逻辑分析仪看 DATA 引脚有没有波形;没有波形就是接线问题,有波形但电平不对就是上拉电阻的问题。
洞洞板布局上,无线模块的天线要远离主控晶振和 DHT22 探头。我做过一个模拟项目,NRF24L01 模块离 STM32 的 8MHz 晶振只有 1 厘米,通信距离直接缩短了一半。后来把模块移到板边,天线垂直伸出板外,通信距离才恢复正常。另外 DHT22 探头如果用的是成品模块,上面基本有上拉电阻,但如果你买的是裸传感器,一定记得自己加。
3.3 供电方案:电池供电还是 12V 总线供电
粮仓现场供电方式决定了系统的可用性。如果节点数量少、分布集中,拉 12V 直流总线最省事,每个节点用一个 DC-DC 降压到 3.3V 就行,不用考虑电池没电的问题。但粮仓里布线的成本往往比设备本身还高,所以无线节点普遍用电池供电。
电池供电的一个常用方案是两节 18650 串联加 DC-DC,或者单节 18650 加升压,但升压电路本身有静态损耗。我推荐单节 18650 直接给系统供电,前提是主控和无线模块都支持 2.7V~3.6V 工作范围。这个方案没有升压损耗,只有 LDO 的压差损耗,如果直接电池供电不经过 LDO,甚至可以把静态功耗压到最低。
不过电池供电最大的敌人不是测量过程的电流,而是休眠时的静态电流。STM32 停机模式的待机电流可到微安级,但很多最小系统板上的电源指示灯和稳压芯片会把静态电流拉到几毫安,两个月的续航直接变成一周。后面避坑章节会专门讲这个问题。
4. 固件实现:温湿度采集、无线发送与低功耗唤醒
4.1 DHT22 单总线读取:STM32 实现与时序说明
DHT22 单总线的时序要求严格,读取时必须关闭中断,否则一个微秒级的调度切换就会导致数据错位。下面是用标准库实现的读取函数,适合 STM32F103 系列:
// dht22.c - STM32F103 DHT22 单总线读取 #include "dht22.h" // 微秒级延时,依赖 DWT 计数器(需要先配置 DWT) static void delay_us(uint32_t us) { DWT->CYCCNT = 0; while (DWT->CYCCNT < us * 72) ; // 72MHz 主频,1us = 72个时钟周期 } // 读取一个字节,先读低位再读高位 static uint8_t dht22_read_byte(void) { uint8_t data = 0; for (int i = 0; i < 8; i++) { while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0); // 等待低电平结束 delay_us(40); data = (data << 1); if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 1) { data |= 1; // 高电平持续超过40us,判定为1 } while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 1); // 等待高电平结束 } return data; } // 读取完整温湿度数据 uint8_t dht22_read(float *temperature, float *humidity) { uint8_t data[5] = {0}; // 主机拉低至少 1ms 启动采集 GPIO_ResetBits(GPIOA, GPIO_Pin_1); delay_us(1000); GPIO_SetBits(GPIOA, GPIO_Pin_1); delay_us(30); // 等待 DHT22 拉低应答 while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 1); while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0); while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 1); // 读取 40 位数据:湿度高16位 + 温度高16位 + 校验8位 for (int i = 0; i < 5; i++) { data[i] = dht22_read_byte(); } // 校验:前四个字节之和的低8位等于第五个字节 uint8_t sum = (data[0] + data[1] + data[2] + data[3]) & 0xFF; if (sum != data[4]) return 1; // 校验失败返回1 *humidity = ((uint16_t)data[0] << 8 | data[1]) / 10.0f; *temperature = ((uint16_t)data[2] << 8 | data[3]) / 10.0f; return 0; }代码逻辑说明:DHT22 通信分两步走,第一步是主机拉低总线至少 1ms 发起采集,第二步是读取 40 位数据,包括湿度整数和小数、温度整数和小数、以及 8 位校验和。读取每个位时,通过判断高电平持续时间来区分逻辑 0 和逻辑 1。DHT22 的高电平持续 26~28us 表示 0,持续 70us 表示 1,所以延时 40us 后采样,电平为高就是 1,为低就是 0。
注意 delay_us 用的是 DWT 计数器,不是软件循环,这样在开启编译器优化或改变主频后依然精准。使用 SysTick 或软件空循环做微秒延时很容易在这种时序敏感的器件上翻车。另外,每次读取间隔必须大于 1 秒,DHT22 的采样周期是 2 秒,读太频繁会一直拿不到数据。
4.2 无线数据帧协议:帧头、长度、数据和 CRC 校验
无线节点每隔一段时间上报一组数据,协议帧格式必须自己定。我建议最小帧结构如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| 帧头 | 2 字节 | 固定为 0xAA 0x55,用于帧同步 |
| 节点地址 | 1 字节 | 节点编号,0x01~0xFE,0xFF 为广播 |
| 数据长度 | 1 字节 | 后续数据字节数,固定 6 字节 |
| 温度值 | 2 字节 | 有符号整数,实际值 = 原始值 / 10 |
| 湿度值 | 2 字节 | 无符号整数,实际值 = 原始值 / 10 |
| 电池电压 | 1 字节 | 单位 0.01V,ADC 采集换算 |
| CRC 校验 | 1 字节 | 前面所有字节的累加和取低 8 位 |
用累加和做 CRC 在嵌入式场景里够用,关键是要处理字节序和符号问题。温度是带符号的,负温时转成 uint16_t 再发送,接收端同样按有符号解析,保证 -20°C 的数据传输不丢符号位。下面看发送函数:
// dht22_node.c - 组装数据帧并通过 NRF24L01 发送 void send_data_frame(uint8_t node_id, float temp, float humi, uint8_t battery_v) { uint8_t frame[10]; int16_t temp_raw = (int16_t)(temp * 10); uint16_t humi_raw = (uint16_t)(humi * 10); frame[0] = 0xAA; // 帧头1 frame[1] = 0x55; // 帧头2 frame[2] = node_id; // 节点地址 frame[3] = 6; // 数据长度 frame[4] = (temp_raw >> 8) & 0xFF; // 温度高字节 frame[5] = temp_raw & 0xFF; // 温度低字节 frame[6] = (humi_raw >> 8) & 0xFF; // 湿度高字节 frame[7] = humi_raw & 0xFF; // 湿度低字节 frame[8] = battery_v; // 电池电压 uint8_t crc = 0; for (int i = 0; i < 9; i++) crc += frame[i]; frame[9] = crc; // 发送前拉高 CE,写入发送缓冲区后触发发送 NRF24L01_TxMode(); NRF24L01_WritePayload(frame, 10); while (NRF24L01_GetStatus() & 0x10); // 等待发送完成 NRF24L01_ClearInterrupts(); }这段代码注意两点:一是发送前必须切换模块到发送模式,收发切换之间要给模块留出稳定时间;二是发送完成后要清除中断标志,否则下一次发送不会触发。帧头设计为 0xAA 0x55 是方便接收端做字节流同步,帧头中间不会出现完全相同的连续字节,误判概率较低。
4.3 低功耗唤醒策略:定时上报与停机模式
粮仓节点不需要实时上报,一般 5 分钟到 15 分钟采集一次就足够,因为粮温变化很慢。低功耗方案的核心是让系统在两次上报之间进入停机模式。用 STM32 的 RTC 定时唤醒,主控在 STOP 模式下电流约几个微安,几乎可以忽略。我实际配置的流程如下:
// low_power.c - RTC 定时唤醒 + STOP 模式的配置片段 void enter_stop_mode_with_rtc(uint32_t wakeup_seconds) { // 配置 RTC 唤醒定时器,单位是秒 RTC_WakeUpCmd(DISABLE); RTC_SetWakeUpCounter(wakeup_seconds - 1); RTC_WakeUpCmd(ENABLE); // 清除唤醒标志,避免一进 STOP 就立刻被唤醒 RTC_ClearFlag(RTC_FLAG_WUTF); // 进入 STOP 模式,配置为 RTC 唤醒 PWR_WakeUpPinCmd(ENABLE); PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); SystemInit(); // 唤醒后必须重新配置时钟 }参数说明:wakeup_seconds 就是两次采集之间的间隔,我建议粮仓场景设 300 秒,也就是 5 分钟。如果设太短,电池消耗太快;设太长,调温和通风时数据变化不能及时发现。300 秒是平衡值。
这里有一个隐藏的坑:进入 STOP 模式前,NRF24L01 必须进入掉电模式,即 PWR_DOWN 状态,否则模块还会维持 20mA 级的接收电流。唤醒后先恢复正常 SPI 时钟,再发送数据。低功耗设计的完整链路是:RTC 唤醒 → 恢复时钟 → 读取传感器 → 发送数据 → 关闭传感器和无线模块电源 → 再进 STOP。如果你用 GPIO 直接给 DHT22 供电,这一步能在采集间隙把传感器电流完全切断。
4.4 重传机制:丢包是常态,不重传才反常
无线通信丢包是常态,尤其是粮仓这种对 2.4GHz 不友好的环境。我建议每个节点发送数据时连续发两帧,帧内加序列号,接收端只取最新一帧。这个办法简单粗暴但非常有效,数据显示成功率能从 85% 提到 99% 以上。发送两帧间隔 100ms 就够了,不需要等到下个周期。接收端根据节点地址和序列号判断,地址匹配且序列号大于上次记录才更新数据,避免旧帧覆盖新数据。
5. 现场部署避坑指南:5 个让我翻过车的真实问题
5.1 现象:粮堆内部的节点信号频繁丢失
项目部署后,埋在粮堆深处的节点隔三差五掉线,有的节点半小时才收到一次数据。原因不是模块坏了,而是 2.4GHz 信号在粮堆里衰减太厉害。解决思路分三步走:第一步把节点天线移到粮堆表面附近,用延长线引出,而不是埋在粮堆内部;第二步是换 433MHz 或 LoRa 方案,穿透力质的提升;第三步是接受端天线尽量架高,减少仓内障碍物遮挡。如果节点已经做好没法换方案,只能加中继器或者调整布点位置,让粮堆深度控制在 1 米以内。
5.2 现象:湿度读数一天比一天偏大
某个节点湿度数据从 60%RH 慢慢涨到 75%RH,但实际仓内湿度只有 65%。这个现象在 DHT22 上尤其常见,原因是探头长期处于高湿环境,传感器表面凝水或灰尘吸附水分,导致测量值偏高。处理办法是定期校准,更稳妥的是选带防护罩的探头,或者把传感器放在透气但不直接接触粮食的护套里。我后来把裸板 DHT22 换成带金属滤网护套的版本,漂移速度明显减慢。
5.3 现象:电池续航只有预期的一半
一节 18650 电池容量 2500mAh,按计算应该能跑 3 个月,结果 1 个多月就没电了。用万用表量静态电流,发现待机电流高达 8mA。原因是 STM32 最小系统板上的电源 LED 和 AMS1117 稳压芯片本身就在耗电,还有 NRF24L01 一直在接收模式。解决方法是三管齐下:去掉板载 LED,用 GPIO 控制传感器和无线模块的电源,进入停机前把无线模块设为掉电模式。改造后静态电流降到 20uA 以下,续航立刻回到预期。
5.4 现象:上位机收到的数据字段错乱,温度湿度对不上
接收数据时出现温度值变成湿度值,偶尔还有乱码。排查后发现是接收端串口中断处理和主循环没有做缓冲保护,数据帧被拆散,解析时帧头没有正确同步。解决方法是接收端用环形缓冲区接收完整帧,解析时先搜索帧头再解析数据字段,校验和不对就丢弃整帧。还有一个隐藏问题:串口波特率误差累计在长数据帧上会放大,检查一下收发双方的晶振误差,115200 波特率下 10 字节以内一般没事,更长帧建议降到 57600。
5.5 现象:NRF24L01 模块上电就发烫,SPI 通信失败
这个属于最典型的硬件烧毁原因:把 5V 直接接在 NRF24L01 模块的 VCC 上。模块发烫是输入电压超过 3.6V 的表现,几秒钟就会损坏芯片。另一个原因可能是 SPI 引脚电平不匹配,如果主控是 5V 的 51 单片机,需要加电平转换或者串联电阻限流。还要注意带 PA 的模块启动瞬间电流可能到几百毫安,电源电压会被拉低,可以在 VCC 和 GND 之间加一个大电容,比如 100uF,稳压效果明显。
6. 把系统调到能长期跑:传感器校准、数据验证与扩展方向
系统能跑通只是第一步,真正考验人的是数据可信度。我的习惯是每周做一次数据抽查,用便携式温湿度计和系统读数对比,温差超过 1°C 或湿度差超过 3%RH 就标记该节点,安排现场检查。此外还有一个简单有效的校准验证方法:把传感器放进一个密封袋,袋内放一杯饱和盐水,环境湿度大约维持在 75%RH,等 2 小时再读取读数,能快速验证传感器是否漂移。盐的种类不同对应不同湿度点,这是现场没有标准湿度源时的实用替代方案。
掉线重传机制应该做成参数可调的状态机,而不是写死在代码里。节点连续两次上报失败,等下一个周期再尝试;连续五次失败,本地记录缓存,下次成功发送时补传。这个逻辑不复杂,但能显著提升长周期运行的可靠性。
更进一步的扩展方向有几个:一是把数据上报到云平台或本地数据库,上位机从串口改成以太网或 4G 模块,方便手机远程查看;二是在接收端加声光报警器,湿度超过阈值时提醒值班人员;三是做数据曲线和趋势分析,查看仓内温湿度变化趋势,提前发现异常升温和返潮现象。这些扩展不需要改节点硬件,主要在接收端和上位机侧实现。
我个人的习惯是给每个节点贴标签记录部署日期和初始校准偏差,这样后续做数据分析时能定位传感器漂移。这套系统做完后最深刻的教训是:硬件选型阶段多花的 30 分钟,往往能省掉现场调试的 3 天时间,尤其是无线频段的选择,改方案的成本远比你想象的高。希望这篇笔记对你做粮仓温湿度监测系统有用。
本文还有配套的精品资源,点击获取