简介:基于STM32与机智云平台的智慧农业监控方案,面向物联网初学者、嵌入式开发人员以及农业环境监测相关实践者。整套工程以正点原子精英板为硬件核心,使用DHT11数字温湿度传感器采集环境温湿度,利用板载光敏传感器检测光照强度,再经ESP8266 WiFi模块将实时数据上传至机智云云端,用户通过手机APP即可随时查看参数,同时具备阈值报警功能。压缩包共200个文件,核心代码包含46个.h头文件与44个.c源文件,覆盖DHT11驱动、ADC模拟量采集、ESP8266通信、LCD显示等模块;另有Keil工程配置、编译输出、程序烧录等辅助文件,整体仅5.58MB,目录结构清晰,便于按模块阅读。该项目至今已有3292人学习浏览,特别适合希望完整掌握单片机外设驱动、云平台接入以及物联网数据链路的读者。通过源码,可以透彻理解从传感器采集、WiFi传输到手机APP远程监控的完整流程,并能直接基于工程进行移植或二次开发。
1. 项目整体架构与方案选型
这套“智慧农业环境监控”项目,说白了就是把种大棚、养花卉、搞菌菇房时最关心的三个环境参数——光照强度、空气温度、空气湿度——通过单片机采集上来,再用无线网络扔到云端,最后掏出手机就能随时看数据曲线。你人不在现场,但棚里的环境变化全在掌握中,这才是物联网落地农业最有价值的地方。
我一开始就没打算用别人现成的农业物联网盒子,那些商业产品贵不说,数据还在别人的服务器上绕一圈,想二次开发、想接自己的执行机构都费劲。自己从模组层面搭一套,既能吃透整个数据链路,后续扩展喷灌、通风、补光灯控制也顺手得多。整套系统的核心逻辑可以拆成四层来看。
1.1 数据链路四层拆解:从传感器到手机屏幕
第一层是感知层,负责把物理量变成电信号。这里用的是三样东西:BH1750光照传感器负责测光照强度,量程0到65535勒克斯,室内大棚、温室补光场景完全够用;DHT11温湿度传感器负责测空气温湿度,虽然精度一般,但胜在便宜稳定,做环境趋势监测足够了。
第二层是控制层,也就是主控MCU。我用的是STM32F103C8T6,这颗Cortex-M3内核的单片机在物联网入门项目里简直是“工地标配”,价格便宜、资料海量、引脚够用。它干的事就是定时读取传感器数据,打包成JSON格式,然后通过串口扔给WiFi模块。
第三层是传输层,核心是一个ESP8266-12F模块。这芯片本身自带处理器,但在这里我让它干老本行——跑AT指令固件,做透传通道。STM32通过串口(USART2)把数据发给它,它负责通过WiFi把数据推送到的机智云MQTT服务器上。为什么用MQTT协议而不是HTTP?后文细说。
第四层是平台层和应用层,也就是机智云平台和手机APP。机智云做的事情其实很关键:它帮你把设备接入互联网的脏活累活全包了——设备认证、MQTT长连接保活、数据存储、API接口、APP端SDK,全都有现成的。你要做的只是定义好数据点(Data Point),平台就会自动生成一套可以跟APP联动的协议。
1.2 为什么是STM32+ESP8266,而不是ESP32或Arduino
很多新手问我:现在ESP32一片芯片就自带WiFi蓝牙,成本十几块钱,比STM32加ESP8266两块芯片的方案简单多了,为什么还要绕远路?这个我确实认真想过,也实际对比测试过,我的结论是:看你的目标是什么。
如果纯粹是为了“快速做出一个小demo、今天画板明天出数据”,那ESP32确实最优解,Arduino IDE里面几十行代码就能跑通。但这个项目的主要目的是吃透“采集—传输—上云—展示”的完整链路,同时给后续接更多传感器和执行器留足余量。STM32F103C8T6有丰富的GPIO、多路ADC、多个串口、I2C、SPI,后续想加土壤湿度传感器、PH值传感器、继电器组、水泵控制,几乎不需要换主控。
再换个角度,STM32串口和ESP8266之间的数据交互是纯AT指令,这反而让你对“数据是怎么从MCU一步步爬到云端”这件事理解得更深刻。你用ESP32的时候,WiFi栈和传感器读取全在一个芯片里,出了问题很难定位是传感器的问题、协议的问题还是网络的问题。分开来做,故障边界非常清晰:串口有数据就是主控的事,云端收不到就是WiFi模块的事。
当然,我也认同ESP32单芯片方案在集成度、功耗、成本上确实有优势。如果你已经有ESP32的开发基础,完全可以把本文的主控逻辑移植过去,数据点的定义和云端配置思路是通用的。
下表是当时我做的方案对比,供参考:
| 方案 | 成本(约) | 上手难度 | 可扩展性 | 适合场景 |
|---|---|---|---|---|
| STM32 + ESP8266 | 25~35元 | 中等,需分别掌握MCU和WiFi模块 | 高,GPIO/串口/I2C资源丰富 | 想系统学习物联网全链路,后续接多设备 |
| ESP32单芯片 | 15~25元 | 低,Arduino/ESP-IDF均可 | 中高,引脚够但共享资源多 | 快速原型验证,追求简洁 |
| Arduino Uno + ESP8266 | 30~40元 | 最低 | 低,Uno性能弱、引脚少 | 纯入门体验,不推荐实际部署 |
2. 硬件搭建与传感器读取细节
硬件是整个项目的基础,我在这部分踩过的坑最多。很多人的项目死在“软件调通了但数据跳来跳去”或“传感器读数明显不对”上,根源基本都是硬件设计太随意。尤其是电源部分,别小看一颗AMS1117稳压芯片的滤波电容,它直接决定了传感器数据的稳定程度。
2.1 主控最小系统与供电设计要点
STM32F103C8T6这种“蓝 pills”小板子,板上自带一个USB转串口芯片(通常是CH340或CP2102),能供电也能下载程序,但我不建议直接把整块板子接到充电宝或USB口上供电给传感器模块用。因为板载的AMS1117稳压芯片输入输出压差大,带载能力一般,接上WiFi模块和几个传感器后,3.3V电压容易被拉低,造成WiFi模块频繁重启。
我的供电方案是这样的:外部用一个5V/2A的电源适配器(淘汰的手机充电器就行),先进入一个单独的AMS1117-3.3模块降压到3.3V。关键是在这个3.3V输出端并两个电容——一个100uF电解电容吸收低频波动,一个0.1uF陶瓷电容滤高频噪声。这两个电容并排焊在电源轨上,基本能解决大半“传感器读数跳变”“WiFi掉线”的毛病。
STM32的PA0-PA7、PB0-PB1都可以做普通GPIO,PA9/PA10是USART1(板载调试串口),PA2/PA3是USART2(留给ESP8266通信)。这里有个新手容易犯的错:ESP8266模块的串口电平是3.3V,而很多USB转串口模块是5V电平,如果直接交叉接线,长时间运行会损坏WiFi模块。正确做法是确保两边都工作在3.3V逻辑电平下。
2.2 BH1750与DHT11接线及读取逻辑
BH1750光强传感器用的是I2C协议,模块总共四个引脚:VCC接3.3V、GND接地、SCL接STM32的PB6(I2C1_SCL)、SDA接PB7(I2C1_SDA)。模块上一般还有一个ADDR引脚,悬空时器件地址是0x23,接高电平则是0x5C。默认悬空即可,代码里用0x23。
DHT11是单总线协议,只有一根数据线,我这里接到PB0上。DHT11的读取时序看着简单,实际是个容易翻车的地方:主机先拉低数据线至少18ms,然后拉高,再释放总线,DHT11响应后会先拉低80us再拉高80us,随后开始输出40bit数据。问题在于拉低和延时的毫秒级时序,如果用HAL库的HAL_Delay(),经常因为延迟不稳定导致读取失败。我后来干脆改用DWT(数据观察点与跟踪单元)做微秒级延时,稳定多了。
读取到原始数据后,DHT11返回5个字节:湿度整数、湿度小数、温度整数、温度小数、校验和。校验和等于前四个字节之和的低8位,这一项必须要校验,否则偶尔读到一组乱码数据你都察觉不到。温度超过50摄氏度或湿度超过90%时DHT11基本就失真了,这个场景下建议直接换DHT22或者SHT30。
// DHT11 读取核心片段(伪代码) uint8_t dht11_read(uint8_t *humidity, uint8_t *temperature) { uint8_t data[5] = {0}; // 1. 主机拉低至少18ms gpio_set_mode(PIN_PB0, GPIO_MODE_OUTPUT); gpio_write(PIN_PB0, 0); delay_ms(20); // 2. 拉高并释放总线 gpio_write(PIN_PB0, 1); delay_us(30); gpio_set_mode(PIN_PB0, GPIO_MODE_INPUT); // 3. 等待从机响应信号(低->高) wait_dht11_response(); // 4. 循环读取40bit数据 for (int i = 0; i < 40; i++) { while (gpio_read(PIN_PB0) == 0); // 等待低电平结束 delay_us(40); if (gpio_read(PIN_PB0)) { data[i / 8] |= (1 << (7 - (i % 8))); // 高电平持续>40us为1 } while (gpio_read(PIN_PB0) == 1); } // 5. 校验和验证 if ((data[0] + data[1] + data[2] + data[3]) == data[4]) { *humidity = data[0]; *temperature = data[2]; return 1; } return 0; }提示:DHT11的采样周期建议大于2秒,频繁读取容易造成数据错乱。我的代码里设置了2.5秒的循环采集间隔,既满足实时监控需求,又给传感器留足了恢复时间。
3. 机智云平台配置与设备接入
硬件采集这边通了,接下来就是让数据“上云”。机智云平台是我个人认为对单片机开发者最友好的一类物联网平台,它不需要你自己搭MQTT服务器,不需要处理证书、加密那些头疼的东西,只要在网页上点点鼠标定义一个数据点,平台就自动帮你把云端通信链路都搞定了。
3.1 创建产品与数据点定义
登录机智云开发者中心后,第一步是“创建产品”。产品名称一般写“智慧农业监控站”或“大棚环境监测仪”,技术方案选“WiFi”,数据加密方式建议先选“明文传输”方便调试,等联调通过再考虑加密。通信方式那里选MQTT,这是机智云默认也是推荐的方式,比TCP长连接更省流量、抗弱网能力强一些。
创建完产品后,最关键的就是“数据点”定义。数据点本质上是告诉云端和APP,“我的设备会上报哪些数据、这些数据是什么类型、单位是多少”。我的项目里定义了三个:
- 光照强度(LightIntensity),标识符是
light_intensity,数据类型选“数值型”,取值范围0~65535,单位lx,读写属性填“上报”即可。 - 温度(Temperature),标识符
temperature,数值型,范围-40~80,单位℃。 - 湿度(Humidity),标识符
humidity,数值型,范围0~100,单位%RH。
这里有一个容易忽略的细节:机智云的数值型数据在传输时是以整数形式走的。如果你的温度实际是25.5摄氏度,你得在MCU端放大10倍传255,再在APP端约定好“显示时除以10”。这个“数据放大约定”在定义报警阈值、图表显示范围时非常关键,我当时没注意,导致APP上温度曲线一直显示255度,排查了整整一个下午。
定义好数据点后,平台会自动生成一份通信协议文档,包含了设备的product_key、数据上报的topic格式、JSON报文的字段名。仔细读一遍这份文档,能少踩很多坑。
3.2 ESP8266刷GAgent固件与设备配网
ESP8266刚买回来时里面一般是AT指令固件,但机智云的设备接入需要专用的GAgent固件,它实现了机智云云协议与WiFi驱动的适配。你不需要自己写MQTT协议栈,只要让STM32通过串口向ESP8266发送机智云定义的JSON格式数据,GAgent固件就会自动打包成MQTT报文发到云端。
刷固件的方法很简单:用USB转TTL模块连接ESP8266,模块的GPIO0引脚拉低进入烧录模式,打开机智云官网下载的ESP8266固件烧写工具,选好串口和固件.bin文件,一键烧录即可。固件版本一定要跟模块的Flash大小匹配,ESP8266-12F一般是4MB,选对应类型的固件,刷错固件会导致模块一直重启。
烧完GAgent固件后再遇到的第一个坎就是“配网”。机智云有两种配网模式:AirLink(一键配置)和SoftAP(热点配置)。AirLink模式比较省事——手机连上家里的2.4G WiFi,APP里点击“一键配置”,手机开始广播SSID和密码,设备端在信道上监听并自动连接。这里要特别注意:路由器必须开启2.4G频段,拿5G WiFi配网大概率失败,因为ESP8266根本不支持5G频段。
配网成功后在机智云APP上就能看到在线设备了。但这里有个小小的顺序问题:你得先把STM32和ESP8266按串口接好,让主控程序跑起来,再在APP上配网,否则设备在云端处于“未激活”状态,APP列表里找不到它。激活的逻辑是:ESP8266连上WiFi后,第一次成功上报数据才会在云端完成设备激活。所以建议先用串口调试助手单独给ESP8266发一条合法JSON数据,确认云端收到后再接主控。
4. 手机APP实时监控与联动控制实现
数据到了云端,接下来就是最激动人心的部分:掏出手机,实时看到大棚里的光照和温湿度。这里有两步走:先用机智云公版APP验证整个链路,再考虑做自己品牌化的应用。
4.1 用公版APP快速验证链路
机智云官方有成熟的APP(机智云APP或“智云”APP),支持iOS和Android。先在应用商店下载安装,注册账号登录。然后按以下步骤绑定设备:
- 确保设备通电,ESP8266处于配网状态(如果之前已经连上WiFi就不需要重复配网)。
- APP首页点击右上角“添加设备”,选择“一键配置”或“热点配置”。
- 输入当前WiFi密码,等待设备连接。正常情况下10秒内设备会变为“在线”状态。
- 进入设备详情页,就能看到我定义的那三个数据点,如果硬件正常、接线正确,光照、温度、湿度三个数值会以卡片形式展示出来,而且会实时刷新。
“实时”这个体验,MQTT长连接和轮询的差异就体现出来了。HTTP轮询一般是5秒或10秒请求一次,你有延迟感;MQTT是发布/订阅模型,设备端每次上报,服务器立刻推送给APP,延迟通常在200毫秒以内。这也是为什么机智云把MQTT作为默认通信方式的原因。
公版APP的数据展示以数字卡片为主,也有简单的曲线图。如果你只是自己用,公版APP完全够了。但是如果你想做一个能发给客户用的产品,或者想自定义界面风格、增加远程控制按钮,那就需要走一遍“App中的SDK”路线了。
4.2 从数据监控升级到远程主动控制
光看数据还不够“智慧”,真正的智慧农业还包含“根据环境变化主动调节设备”。我在这个项目里加了两个执行机构:一路继电器控制补光灯,一路继电器控制排风扇。
实现方式其实并不复杂,在于数据点的定义需要从“只上报”改为“可下发”。我在机智云产品里新增两个布尔型数据点:light_switch(补光灯开关)、fan_switch(排风扇开关)。布尔型数据点在APP上会自动渲染成开关控件,用户点击开关,云端下发指令到设备,STM32通过串口收到指令后解析,再控制继电器模块。
关键部分是STM32的串口中断处理。ESP8266会把云端下发的数据通过串口发送给STM32,STM32需要在中断服务函数里接收完整一帧数据(注意要设置合适的串口空闲中断或定时器超时判断帧结束),提取出light_switch或fan_switch的值,然后操作GPIO口电平。我在代码里加了一个状态机,根据数据点标识符做匹配,避免把下发指令误当成传感器上报数据处理。
// 串口接收云端下发命令的简化处理逻辑 void USART2_IRQHandler(void) { uint8_t byte = USART2->DR & 0xFF; // 将字节写入环形缓冲区,主循环中解析 ring_buffer_write(&rx_buf, byte); } // 主循环中对机智云下行报文进行关键字匹配 void process_cloud_command(char *payload) { if (strstr(payload, "\"light_switch\":1")) { relay_light_on(); // 打开补光灯 } else if (strstr(payload, "\"light_switch\":0")) { relay_light_off(); } if (strstr(payload, "\"fan_switch\":1")) { fan_on(); } }注意:机智云旧版协议的下发指令是
{"cmd":"write","data":{...}}结构,新版协议版本可能略有差别,一定要以你当前固件版本对应的协议文档为准。判断字段时用严格的JSON key匹配,不要用“包含某个字符串”这种方式直接操作,否则极易误触发。
远程控制的另一个实用场景是阈值告警联动:比如当光照低于2000勒克斯时,补光灯自动打开;温度高于35摄氏度时,排风扇自动启动。这个逻辑最好放在MCU端做,不要依赖云端和APP,因为一旦断网,云端控制就失效了,而棚内设备必须在任何情况下都能可靠工作。MCU端只需在每次采集到传感器数据之后,跟设定阈值比一下,然后直接驱动继电器就行了。
5. 常见问题与排查技巧实录
项目做到最后,你会发现硬件连上了、代码也跑通了,但问题总是出在一些看似不起眼的细节上。这里把我实际调试中遇到的典型问题整理成一张速查表,照着排查能省很多时间。
5.1 典型故障速查表
| 故障现象 | 可能原因 | 排查与解决方法 |
|---|---|---|
| ESP8266不联网,手机搜不到设备 | GPIO0被拉低;模块电源不足;WiFi密码错误 | 检查GPIO0是否悬空(运行时必须悬空或接高);用独立3.3V电源供电;重新配网 |
| 手机APP一直显示“设备离线” | GAgent固件与Flash不匹配;设备没上报过数据 | 重新确认固件版本与模组匹配;手动发送一条JSON数据触发激活 |
| 温湿度数值明显偏差或剧烈跳动 | 电源纹波过大;DHT11线序接触不良;采样间隔过短 | 加100uF+0.1uF滤波电容;检查杜邦线;采样间隔调到2秒以上 |
| 光照强度固定显示最大值65535 | BH1750地址不对;SDA/SCL接反;I2C总线上拉电阻缺失 | 检查ADDR引脚;确认SCL/SDA对应关系;一般模块自带上拉,独立裸芯片需加4.7k上拉 |
| 温度数据为255或湿度为0 | DHT11校验失败;未等待响应信号 | 检查单总线时序是否满足;用逻辑分析仪抓波形对比 |
| APP刷新慢,像有5秒以上延迟 | 上报间隔设置过长;WiFi信号弱;配网连了5G频段 | 缩短上报循环到2~3秒;增加中继路由;重新配网到2.4G频段 |
| 继电器频繁误动作 | 串口解析逻辑过宽;电磁干扰导致串口数据错位 | 严格按JSON字段匹配;继电器驱动加光耦隔离;远离大电流走线 |
5.2 数据校准与长期运行稳定性的实战经验
先聊校准。BH1750出厂精度还可以,但安装在透明塑料箱体内会造成光衰减,测出来的数值会比棚内实际照度偏低。校准方法很简单:找一个晴天中午,用手机上的光强测试APP(或者专业的照度计)和你的设备并排放一起,读两个数,算一个修正系数,代码里乘上去就行。DHT11就比较头疼了,它的湿度传感器在通风不良的环境下会偏高,尤其是把模块放在封闭的电子防水盒里时,盒内湿度跟棚内真实湿度差得远了。建议DHT11探头部裸露在盒子外面,或者用带透气孔的防水盒。
再说长期稳定性。设备是7×24小时运行在农业大棚里的,夏天棚内温度动辄四五十度,冬天又可能零下。STM32的芯片工作温度范围是-40到85度,勉强够用,但电源部分的电容要注意选耐温105度的工业级电容,普通85度电容在高温棚里用几个月就容易鼓包失效。
程序层面我做了两件保命的事:一是开启独立看门狗(IWDG),主循环每1秒喂一次狗,万一程序跑飞,芯片能自动复位重启;二是在代码里加了“连续读数异常自动重启”的逻辑——如果连续5次DHT11读取校验失败,就执行NVIC_SystemReset()系统复位。这个操作看起来简单粗暴,但在无人值守的场景下,能自动恢复的系统才是能真正可靠运行的系统。
云端这块,机智云的免费额度对个人项目来说完全够用,设备在线数、消息条数的限制都不太会碰到。但如果哪天发现数据上报频率明显变慢,先检查是不是上报的数据JSON格式不符合协议,导致云端拒绝接收、设备反复重发。用串口监视器抓一下ESP8266发出去的原始数据,一眼就能看出问题。
根据我个人的实际操作体会,这类智慧农业监控项目做到“手机上看数据”只是起点,真正让农户觉得有用的,是“手机控制设备”和“异常自动报警”这两个功能。出门在外,棚里温度飙升,手机弹个通知说“温度超标,排风扇已自动打开”,这种体验才是物联网带来的实际价值。你把这套链路跑通之后,后面换传感器、换平台甚至对接小程序,底层逻辑都是一通百通的。
本文还有配套的精品资源,点击获取