简介:本资源是一套面向嵌入式初学者与物联网项目开发者的完整智慧农业灌溉系统实战方案,基于STM32F103主控与华为云IoT平台构建,解决农业环境参数采集、智能决策灌溉、多端远程监控等典型应用场景问题。压缩包含412个文件,总计162.65MB,涵盖Keil工程源码(c/h/o/d/uvprojx等)、硬件原理图(schdoc)、设计文档(pdf/docx)、实物接线图(jpg)、Android手机APP安装包(apk)及Qt开发源码、ESP8266联网配置脚本、流媒体服务部署说明等核心内容。已有2546人学习下载,资料结构清晰,模块划分明确——如传感器驱动(ds18b20、bh1750、usart)、云平台对接(json/esp8266.__i)、GUI显示(fontdata.__i)与移动端通信逻辑均独立成块,配合详细使用说明与接线指引,可零基础复刻整套系统。
1. 项目缘起:从“看天吃饭”到“数据决策”的灌溉革命
在传统农业里,浇水这事儿,很大程度上是门“玄学”。靠经验、看天色、凭感觉,要么是水漫金山浪费资源,要么是旱得冒烟影响收成。我老家就有几亩地,以前每次回去,都能看到邻居大爷扛着铁锹,在田埂上开渠放水,一忙就是大半天,累得够呛不说,效果还时好时坏。后来接触到物联网和嵌入式开发,我就琢磨,能不能用技术把这事儿给“管”起来?让浇水变得精准、省力、还省钱。
这就是“基于STM32+华为云的智慧农业灌溉系统”项目的初衷。它不是什么高深莫测的科研,而是一个实实在在能落地的工程实践。核心思路很简单:用STM32单片机作为田间地头的“智能管家”,负责采集土壤湿度、环境温湿度等关键数据;然后通过无线网络(比如4G Cat.1或NB-IoT模块)将这些数据上传到华为云IoT平台;云平台对数据进行汇聚、分析和决策,判断是否需要浇水、浇多少水;最后,决策指令下发给STM32,由它来控制水泵、电磁阀等执行机构,完成精准灌溉。整个过程,人只需要在手机或电脑上看看数据、设置一下策略,甚至完全不用管,系统自己就能运行。
这个项目麻雀虽小,五脏俱全。它涵盖了嵌入式硬件开发(STM32)、传感器技术、无线通信、云平台接入(华为云IoT)、以及简单的上层应用逻辑,是一个非常好的物联网全栈入门和实践案例。无论你是嵌入式新手想找个综合项目练手,还是有一定经验的开发者想了解物联网云边协同的完整链路,这个项目都能给你带来清晰的认知和实用的代码。接下来,我就结合这个项目的源码包,把里面的门道和踩过的坑,掰开揉碎了讲给你听。
2. 系统架构全景:云、管、端三层拆解
一个完整的物联网系统,通常可以清晰地划分为“云”、“管”、“端”三个层次。我们这个智慧灌溉系统也不例外。理解这个架构,是读懂后续所有细节的基础。
2.1 终端层:STM32的“五官”与“手脚”
终端层,也就是部署在农田现场的硬件部分,是整个系统的感知和执行末梢。它的核心是STM32微控制器,我项目中选用的是STM32F103C8T6这款经典的“蓝桥杯”芯片,性价比高,资源足够。
感知部分(五官):
- 土壤湿度传感器:这是灌溉系统的“眼睛”。我选用的是常见的电容式土壤湿度传感器,它通过检测土壤的介电常数来反映湿度,比电阻式更耐腐蚀。STM32的ADC模块读取其模拟电压值,并换算成百分比湿度。这里有个关键点:传感器需要做现场校准。在完全干燥(取出晾干)和充分湿润(浸泡后静置)两种状态下,分别读取ADC值,建立电压与湿度的线性对应关系。源码中的
sensor.c文件里的SoilMoisture_GetPercentage函数就实现了这个逻辑。 - 温湿度传感器:我选用DHT11,虽然精度一般,但胜在简单便宜。它通过单总线协议与STM32通信,提供环境温度和湿度数据,用于辅助决策(例如,高温蒸发快,可适当提高灌溉阈值)。
- 光照强度传感器:可选配,使用BH1750等I2C接口的传感器。光照数据可以用于判断白天黑夜,实现“仅在白天灌溉”等策略。
执行部分(手脚):
- 继电器模块:STM32的GPIO口驱动能力有限,无法直接控制水泵(交流220V)或大型电磁阀(直流24V)。继电器模块起到了“开关”的作用。STM32输出一个3.3V的低电平信号控制继电器吸合,继电器的常开触点就接通了水泵的电源回路。源码中
relay.c文件的Pump_Ctrl函数就是控制逻辑,务必注意:控制逻辑是低电平有效还是高电平有效,取决于你使用的继电器模块的电路设计,接错了可能一上电就误触发。
核心控制器(大脑):STM32负责调度一切:定时唤醒传感器采集数据,处理数据,通过通信模块上传,解析云端指令,控制继电器。程序框架通常采用前后台(超级循环)或实时操作系统(如FreeRTOS)。对于这个项目,前后台足够用了。主循环里不断检查各种标志位,比如“数据上传时间到”、“收到云平台消息”、“本地手动按钮按下”等,然后执行相应任务。
2.2 管道层:数据上云的“高速公路”
数据从田间到云端,需要一条可靠的“路”。对于户外农业场景,Wi-Fi通常不现实,移动网络是首选。
通信模块选型:
- ESP8266/ESP32:如果现场有Wi-Fi,这是最经济的选择。但农田通常没有。
- 4G Cat.1模块:如移远EC200S/EC600N系列。这是当前的主流选择,速率比NB-IoT快(下行10Mbps,上行5Mbps),支持TCP/IP协议栈,可以直接用Socket或HTTP连接华为云,延迟低,适合需要较快响应的场景(如实时控制),但功耗和成本略高。
- NB-IoT模块:如移远BC35-G。其特点是低功耗、广覆盖、低成本,但速率慢(约100kbps),适合数据量小、上报不频繁的场景。如果几个小时上报一次数据,NB-IoT是更省电的选择。
本项目源码包中,我默认提供了基于ESP8266(AT指令连接路由器)和4G Cat.1模块的两种连接示例。华为云IoT平台支持MQTT和LwM2M/CoAP协议,对于Cat.1模块,我强烈推荐使用MQTT over TCP的方式,因为华为云提供了完善的设备侧SDK(C语言),封装了连接、鉴权、订阅、发布等复杂操作。
关键协议:MQTT:这是一个基于发布/订阅模式的轻量级消息协议,非常适合物联网。设备(STM32)作为一个客户端,连接到华为云IoT平台的MQTT Broker。设备发布(Publish)消息到某个主题(Topic),比如
/soil_moisture来上报土壤湿度;云平台或其他设备订阅(Subscribe)这个主题,就能收到数据。同样,云平台可以向设备订阅的主题(如/cmd/watering)发布命令,设备订阅该主题后就能接收并执行。这种异步、解耦的通信方式非常灵活。
2.3 云端层:华为云IoT的“智慧大脑”
云端是系统的指挥中心。华为云IoT平台提供了设备管理、数据采集、规则引擎、应用集成等一系列服务,让我们无需从零搭建服务器。
- 产品与设备:在华为云IoT控制台,你需要先创建一个“产品”,比如“智慧灌溉控制器”。在产品中定义好设备的“服务属性”(即数据模型),例如“SoilMoisture”(土壤湿度,int类型)、“Temperature”(温度,float类型)、“PumpSwitch”(水泵开关,bool类型)。然后,在这个产品下“注册设备”,每个实际的STM32硬件对应一个设备,平台会为每个设备生成唯一的
device_id和密钥,这就是设备的“身份证”。 - 设备鉴权:STM32连接华为云时,需要使用“三元组”进行鉴权。三元组包括:
ProductID(产品ID)、DeviceID(设备ID)、DeviceSecret(设备密钥)。源码中的iot_config.h文件就是让你填写这些信息的地方。安全警告:切勿将包含真实三元组的代码提交到公开仓库! - 规则引擎:这是实现“智慧”的关键。你可以在控制台配置规则,例如:“当
SoilMoisture属性值低于30%时,平台自动向该设备下发一条命令,将PumpSwitch属性设置为true”。这样,就实现了基于云规则的自动灌溉。你也可以将设备数据转发到华为云的其他服务,比如数据可视化(DLV)制作实时仪表盘,或者对象存储服务(OBS)进行长期存储分析。 - 设备影子:这是一个非常实用的功能。它本质上是云端为设备维护的一个JSON文档,存储了设备的“预期状态”和“上报状态”。比如,你在手机App上点击“开水泵”,这个指令会先修改设备影子中
PumpSwitch的“预期状态”为true。即使此时设备离线,这个指令也会被保存在影子中。当设备下次上线时,会主动同步影子,发现预期状态变化,然后就自己执行开水泵的操作,并将执行后的“上报状态”同步回影子。这保证了控制指令的可靠送达。
3. 核心代码深度剖析与移植指南
拿到源码包,直接编译烧录大概率是跑不起来的,因为硬件连接、云平台配置各不相同。下面我带你把核心代码模块过一遍,告诉你如何适配你自己的项目。
3.1 硬件驱动层:传感器与执行器的“翻译官”
这一层代码直接与硬件打交道,高度依赖你的具体电路连接。
土壤湿度传感器(ADC读取):
// sensor.c 节选 #define SOIL_ADC_DRY_VALUE 3800 // 传感器在空气中(干燥)的ADC原始值,需实测 #define SOIL_ADC_WET_VALUE 1800 // 传感器在水中(湿润)的ADC原始值,需实测 uint16_t SoilMoisture_GetPercentage(void) { uint16_t adc_value = ADC_ReadChannel(ADC_CHANNEL_0); // 读取指定ADC通道 // 线性映射计算。注意:ADC值越小,湿度越大(电容式传感器特性) if (adc_value >= SOIL_ADC_DRY_VALUE) { return 0; // 低于干燥值,视为0% } else if (adc_value <= SOIL_ADC_WET_VALUE) { return 100; // 高于湿润值,视为100% } else { // 线性插值计算百分比 return (uint16_t)((SOIL_ADC_DRY_VALUE - adc_value) * 100 / (SOIL_ADC_DRY_VALUE - SOIL_ADC_WET_VALUE)); } }移植要点:
- 确定你的STM32 ADC采集通道,修改
ADC_ReadChannel函数的参数。 - 必须实测
SOIL_ADC_DRY_VALUE和SOIL_ADC_WET_VALUE。将传感器完全干燥和插入水中,分别读取稳定的ADC值填入。 - 映射公式的方向要根据传感器特性调整。有些传感器是ADC值越大,湿度越大。
- 确定你的STM32 ADC采集通道,修改
继电器控制(GPIO输出):
// relay.c #define PUMP_GPIO_PORT GPIOA #define PUMP_GPIO_PIN GPIO_Pin_1 void Pump_Ctrl(FunctionalState state) { if (state == ENABLE) { GPIO_ResetBits(PUMP_GPIO_PORT, PUMP_GPIO_PIN); // 拉低,继电器吸合 } else { GPIO_SetBits(PUMP_GPIO_PORT, PUMP_GPIO_PIN); // 拉高,继电器断开 } }移植要点:
- 修改
PUMP_GPIO_PORT和PUMP_GPIO_PIN为你实际连接的GPIO口。 - 使用
GPIO_ResetBits(拉低)还是GPIO_SetBits(拉高)来开启继电器,取决于你的继电器模块电路是低电平触发还是高电平触发。务必用万用表测试或查阅模块手册确认。
- 修改
3.2 网络通信层:连接华为云的“信使”
这是项目中最复杂但也最核心的部分。我以使用华为云IoT设备侧C SDK配合4G Cat.1模块为例。
SDK集成与配置:
- 从华为云IoT官网下载C版本的设备侧SDK。源码包中
HuaweiCloudIoT文件夹可能已经包含,但建议使用最新版。 - 将SDK源码(主要是
include和libs目录)添加到你的MDK或IAR工程中。 - 修改
iot_config.h文件,填入你的设备三元组、服务器地址和端口。// iot_config.h #define CONFIG_DEVICE_ID "your_device_id" #define CONFIG_DEVICE_SECRET "your_device_secret" #define CONFIG_SERVER_IP "your_iotda_endpoint" // 例如: 188.188.188.188 #define CONFIG_SERVER_PORT "8883" // MQTT over TLS端口 - 实现SDK要求的底层接口(
HAL_开头),主要是网络(socket连接、发送、接收)、定时器、内存操作等。这些函数需要你根据你使用的TCP/IP协议栈(比如Cat.1模块的AT指令套接字实现)来编写。这是移植工作量最大的地方。
- 从华为云IoT官网下载C版本的设备侧SDK。源码包中
数据上报与命令接收:
// iot_agent.c 示例逻辑 void ReportSoilMoisture(int moisture) { char payload[128]; // 按照华为云IoT物模型格式组织JSON数据 snprintf(payload, sizeof(payload), "{\"services\":[{\"service_id\":\"Agriculture\",\"properties\":{\"SoilMoisture\":%d}}]}", moisture); // 调用SDK接口发布属性上报消息 IoTDevicePropertyReport(g_device, payload); } // 命令回调函数 static void OnCommandReceived(void* context, const char* cmdId, const char* serviceId, const char* cmdName, const char* paras) { // 解析JSON格式的命令参数 paras // 例如,解析到 "PumpSwitch": true if (strstr(paras, "\"PumpSwitch\":true") != NULL) { Pump_Ctrl(ENABLE); // ... 执行成功后,上报属性更新 ReportPumpStatus(true); } // ... 回复命令执行结果给平台 }关键点:上报的数据和接收的命令格式,必须与你在华为云IoT控制台定义的产品模型完全一致,否则平台无法正确解析。
3.3 主业务逻辑:系统运行的“调度中心”
主程序main.c负责把各个模块串起来,形成一个有机整体。
// main.c 主循环节选 int main(void) { // 1. 硬件初始化 System_Init(); // 时钟、GPIO、ADC、UART等 Sensor_Init(); Relay_Init(); printf("System Boot OK.\r\n"); // 2. 网络与云平台初始化 while(Network_Init() != SUCCESS) { // 初始化4G模块,附着网络 Delay_ms(5000); printf("Retrying network init...\r\n"); } while(IoT_Platform_Init() != SUCCESS) { // 连接华为云 Delay_ms(5000); printf("Retrying IoT connect...\r\n"); } // 3. 主循环 while (1) { // 3.1 定时上报任务(例如每5分钟) if (IsTimerExpired(&report_timer, 5*60*1000)) { int moisture = SoilMoisture_GetPercentage(); float temp = DHT11_GetTemperature(); ReportSoilMoisture(moisture); ReportTemperature(temp); ResetTimer(&report_timer); } // 3.2 本地手动控制检测(如按键) if (Key_Scan() == KEY_PRESS) { Pump_Ctrl(TOGGLE); // 切换水泵状态 // 状态变化后,立即上报给云平台,保持影子同步 ReportPumpStatus(GetPumpCurrentStatus()); } // 3.3 处理来自云平台的命令(在MQTT或SDK的回调函数中处理,此处是轮询网络接收) IoT_Platform_Data_Process(); // 接收并处理网络数据包,触发OnCommandReceived回调 // 3.4 低功耗处理(如果使用NB-IoT) // Enter_LowPower_Mode(); // WaitFor_Interrupt(); Delay_ms(100); // 短暂延时,防止CPU空转 } }逻辑设计心得:
- 状态机思维:网络初始化、云连接可能失败,要用
while循环重试,并加入最大重试次数和休眠间隔,避免死循环耗电。 - 定时器管理:不要用
Delay来做定时上报,会阻塞整个程序。使用硬件定时器中断设置标志位,或者用HAL_GetTick()这类毫秒计时函数来检查是否到时。 - 异常处理:在网络中断、传感器失效等情况下,要有降级策略。比如网络断开时,可以将数据暂存到Flash,等网络恢复后重传;传感器读值异常时,使用上一次的有效值或默认值。
- 功耗考量:如果使用电池供电,低功耗设计至关重要。在不需要采集和上报的间隙,让STM32进入
Stop或Sleep模式,并通过RTC或外部中断(如定时器、传感器中断)唤醒。
4. 实战部署与调试避坑指南
代码写完了,烧录到板子上,只是万里长征第一步。部署到真实环境,才是问题开始的时候。
4.1 硬件组装与电源管理
- 电源是老大难:农田现场可能只有交流电,也可能用太阳能电池板。给STM32、传感器、继电器、4G模块供电需要仔细计算。
- STM32和数字传感器通常是3.3V。
- 4G模块(如EC200S)的峰值电流可能达到2A,电压范围3.3V-4.2V,必须使用独立的LDO或DC-DC电源芯片供电,且输入电容要足够大,否则模块在发射信号时电压会被拉低,导致STM32复位。
- 继电器线圈电压可能是5V或12V,需要另外的电源。控制水泵的交流220V部分,必须严格与低压控制电路隔离,使用合格的交流接触器,并由专业电工操作,安全第一!
- 天线与信号:4G/NB-IoT模块的天线一定要放在信号好的位置,最好使用外置天线并引到机箱外部。金属机箱会严重屏蔽信号。
- 防水防潮:整个控制箱必须达到IP65或更高的防护等级。传感器接线处要用防水胶密封,电路板可以喷涂三防漆。
4.2 华为云平台配置详解
- 创建产品与物模型:在华为云IoTDA控制台,仔细定义属性(
properties)、命令(commands)和服务(services)。命名要有意义,数据类型要选对。例如,水泵开关用bool型,土壤湿度用int型(0-100)。 - 注册设备与获取三元组:创建设备后,妥善保存
设备ID和密钥。设备ID通常可以自己命名,如field_001。 - 规则引擎配置:这是实现自动化的核心。进入“规则”->“数据转发”,创建一条规则。
- 触发条件:选择“设备属性”,筛选你的产品,条件设为
SoilMoisture < 30。 - 执行动作:选择“下发命令”,命令名选择你在物模型中定义的“水泵开关”命令,参数设为
{"PumpSwitch": true}。 - 你还可以增加一个“延迟下发”动作,比如5分钟后下发
{"PumpSwitch": false},实现定时关水。
- 触发条件:选择“设备属性”,筛选你的产品,条件设为
- 设备调试:在设备详情页,有“在线调试”功能。你可以手动模拟设备上报属性,或者给设备下发命令,非常方便用于联调,无需每次都烧录代码。
4.3 联调与问题排查
当设备不上线、数据收不到、命令不执行时,按以下步骤排查:
- 设备不上线:
- 查网络:通过串口打印,确认4G模块是否成功附着网络(
AT+CGATT?返回1)、是否获取到IP地址(AT+CGPADDR)。 - 查连接:确认MQTT服务器地址和端口是否正确。华为云不同区域的接入地址不同。确认设备三元组(特别是
DeviceSecret)是否填写无误,一个字符都不能错。 - 查日志:华为云IoT控制台有设备日志,可以看到设备连接、鉴权失败的具体原因。
- 查网络:通过串口打印,确认4G模块是否成功附着网络(
- 数据上报成功但平台看不到:
- 查物模型:上报的JSON数据格式必须完全匹配物模型定义。用在线JSON格式化工具检查你的payload,确保字段名、嵌套结构正确。
- 查服务ID:上报数据中的
service_id字段必须与物模型中的服务ID一致,默认是Agriculture。
- 平台命令下发但设备不执行:
- 查设备订阅:设备初始化后,必须订阅命令下发的Topic。华为云SDK通常会自动完成。
- 查回调函数:确认命令接收的回调函数
OnCommandReceived已正确注册,并且内部的JSON解析逻辑能正确提取出参数。 - 查设备影子:如果设备离线时下发命令,命令会保存在影子中。检查设备上线后是否同步了影子,以及影子中的“预期状态”是否已更新。
4.4 从原型到产品:可靠性增强建议
- 看门狗:一定要启用STM32的独立看门狗(IWDG)和窗口看门狗(WWDG),防止程序跑飞。喂狗操作要放在主循环合适位置,避免在长时间阻塞的循环中饿死看门狗。
- 断线重连:在网络处理函数中,要检测TCP连接或MQTT连接是否断开,并实现自动重连机制。
- 数据本地缓存:在Flash中开辟一小块区域,存储关键状态和未成功上报的数据。设备重启或网络恢复后,能恢复之前的状态并补报数据。
- 远程升级(OTA):华为云IoT平台支持设备固件升级。可以实现这个功能,以后修复bug或增加新特性,无需跑到田间地头去烧录。
- 多传感器与冗余:重要的监测点可以部署两个土壤湿度传感器,取平均值或中值,提高数据可靠性。
这个项目就像一把钥匙,帮你打开了物联网应用开发的大门。从STM32的GPIO点灯,到传感器数据采集,再到通过网络与云端对话,最后形成闭环控制,每一个环节都充满了工程实践的细节。过程中你会遇到电源噪声、信号干扰、协议解析、网络波动等各种问题,但每解决一个,你的经验值就涨一大截。希望这份超详细的拆解,能让你少走些弯路,更快地把这个“智慧农业”的想法,变成田地里实实在在的、自动运转的系统。
本文还有配套的精品资源,点击获取