本文分享自华为云社区《基于STM32的智慧农业管理系统设计与实现》,作者: DS 小龙哥。
一、前言
1.1 项目介绍
【1】项目功能
随着全球农业现代化进程的加快,以及物联网、人工智能等先进技术的发展与应用,智慧农业已经成为现代农业发展的新趋势。基于精准感知、智能控制和远程管理的智慧农业系统能够显著提升农作物生产效率,降低资源消耗,实现环境友好型可持续农业生产。
在当前背景下,我国正大力推进数字乡村建设,智慧农业管理系统作为其中的重要组成部分,对于提高农业生产精细化管理水平,解决传统农业中信息获取不及时、人工管理成本高、决策缺乏科学依据等问题具有重要作用。
本设计开发一套基于 STM32F103RCT6 主控芯片的智慧农业管理系统,通过集成 DHT11 温湿度传感器、BH1750 光照强度传感器以及土壤湿度检测传感器,实时监测农田环境和作物生长状态,并在超出阈值时通过蜂鸣器报警,提醒管理人员进行灌溉、施肥等操作。同时,采用 NBIoT 通信技术(BC26 模块)将采集到的数据上传至云端,利用 EMQX 开源 MQTT 服务器框架部署于华为云 ECS 服务器上的 MQTT 服务器,实现数据的远程展示与处理。
系统支持微信小程序远程控制功能,使得农户或管理者可以随时随地查看农田环境参数、接收预警信息,并能远程手动控制灌溉设备、补光灯等,大大提高了农业生产的智能化和便捷性。此项目的实施不仅有助于推动我国农业信息化水平的提升,也有利于农业资源的高效利用,对保障国家粮食安全、促进农业增效、农民增收具有重要意义。
【2】设计实现的功能
(1)实时环境监测:系统通过集成的 DHT11 温湿度传感器、BH1750 光照强度传感器以及土壤湿度检测传感器,实时监测农田环境中的温度、湿度、光照强度和土壤含水量等关键参数。当这些参数超过或低于预设阈值时,系统将自动触发蜂鸣器报警,提醒管理人员关注并采取相应措施。
(2)自动化管理与预警:根据土壤湿度传感器检测的数据,如果土壤湿度低于设定的适宜作物生长的含水量阀值,则系统会自动提醒管理者进行灌溉操作。同时,可以按照预设周期发送施肥提醒,以确保农作物在最佳时期得到充足的水分和养分供应。
(3)远程控制功能:利用 NBIoT 通信技术(BC26 模块)将现场采集到的各项数据上传至云端 MQTT 服务器,并通过微信小程序实现远程访问和展示。用户可以通过微信小程序查看实时监测数据,以及对农田设备进行远程手动控制,如启动或关闭 5V 抽水泵进行灌溉,开启或关闭白色 LED 补光灯调节光照条件。
(4)数据上云与分析:基于 EMQX 开源 MQTT 服务器框架搭建的 MQTT 服务器,能够接收并处理 STM32 主控板传输的农业环境数据,并对接微信小程序平台,为用户提供直观易懂的数据图表和分析结果,便于农户或农业技术人员进行科学决策和精准管理。
【3】项目硬件模块组成
(1)主控模块: 采用 STM32F103RCT6 微控制器作为核心控制单元,负责整个系统的运行和管理。STM32F103RCT6 具有丰富的外设接口、强大的处理能力和低功耗特性,能够实时处理传感器数据、执行逻辑判断,并通过无线通信模块发送和接收指令。
(2)环境监测模块:
温湿度监测:使用 DHT11 温湿度传感器采集农田环境的温度和湿度信息。
光照强度监测:采用 BH1750 光照强度传感器测量农田的光照强度。
土壤湿度检测:使用土壤湿度检测传感器获取作物生长区域的土壤含水量数据。
(3)控制输出模块:
(4)无线通信模块: 集成 NBIoT-BC26 模块,实现与云端服务器的数据交互。该模块具备广覆盖、低功耗、大连接的特点,可确保在各种复杂农业环境中稳定地传输数据至 MQTT 服务器。
(5)报警模块: 系统配备蜂鸣器用于异常情况报警,当环境参数超出设定范围时,主控板会驱动蜂鸣器发出声音警报。
1.2 设计思路
(1)系统需求分析:根据智慧农业管理的实际需求,确定需要监测的关键环境参数(温度、湿度、光照强度和土壤湿度),以及必要的控制功能(灌溉、补光灯控制等)。同时考虑远程监控与预警的需求,规划通过 NBIoT 通信技术实现数据上传及远程操控。
(2)硬件选型与设计:
主控芯片选择 STM32F103RCT6,因其具有丰富的外设接口、强大的处理能力和低功耗特性,能够满足系统实时数据采集与控制的要求。
选用 DHT11 作为温湿度传感器,BH1750 作为光照强度传感器,以及土壤湿度检测传感器,分别获取农田环境的基本信息。
设计灌溉系统,使用 5V 抽水泵配合继电器控制灌溉,以响应土壤湿度的监测结果。
采用白色 LED 灯作为补光光源,并接入主控板进行智能调节或远程控制。
配备蜂鸣器用于异常情况报警。
选用 NBIoT-BC26 模块确保无线通信稳定可靠,实现数据上云。
(3)软件架构设计:
开发 STM32 的嵌入式软件程序,负责读取各传感器数据,执行逻辑判断,如环境参数超限时触发报警、根据土壤湿度自动或手动控制灌溉、周期性提醒施肥等操作。
实现 NBIoT 通信协议栈,将现场采集的数据通过 BC26 模块发送至云端 MQTT 服务器。
在云端部署 EMQX 开源 MQTT 服务器框架,接收并存储前端设备发送的数据。
开发微信小程序客户端,对接 MQTT 服务器,展示农田环境的各项实时监测数据,提供远程手动控制界面。
1.3 传感器功能介绍
(1)DHT11 温湿度传感器:
(2)BH1750 光照强度传感器:
(3)土壤湿度检测传感器:
(4)蜂鸣器报警模块:
(5)5V 抽水泵与继电器组合:
(6)NBIoT-BC26 模块:
1.4 开发工具的选择
STM32 的编程语言选择 C 语言,C 语言执行效率高,大学里主学的 C 语言,C 语言编译出来的可执行文件最接近于机器码,汇编语言执行效率最高,但是汇编的移植性比较差,目前在一些操作系统内核里还有一些低配的单片机使用的较多,平常的单片机编程还是以 C 语言为主。C 语言的执行效率仅次于汇编,语法理解简单、代码通用性强,也支持跨平台,在嵌入式底层、单片机编程里用的非常多,当前的设计就是采用 C 语言开发。
开发工具选择 Keil,keil 是一家世界领先的嵌入式微控制器软件开发商,在 2015 年,keil 被 ARM 公司收购。因为当前芯片选择的是 STM32F103 系列,STMF103 是属于 ARM 公司的芯片构架、Cortex-M3 内核系列的芯片,所以使用 Kile 来开发 STM32 是有先天优势的,而 keil 在各大高校使用的也非常多,很多教科书里都是以 keil 来教学,开发 51 单片机、STM32 单片机等等。目前作为 MCU 芯片开发的软件也不只是 keil 一家独大,IAR 在 MCU 微处理器开发领域里也使用的非常多,IAR 扩展性更强,也支持 STM32 开发,也支持其他芯片,比如:CC2530,51 单片机的开发。从软件的使用上来讲,IAR 比 keil 更加简洁,功能相对少一些。如果之前使用过 keil,而且使用频率较多,已经习惯再使用 IAR 是有点不适应界面的。
二、EMQX 开源 MQTT 服务器框架
EMQX 是一款开源的、云原生的分布式物联网 MQTT 消息服务器,设计目标是实现高可靠性,并支持承载海量物联网终端的 MQTT 连接,以及在海量物联网设备间实现低延时消息路由。基于 Erlang/OTP 平台开发,充分利用了 Erlang/OTP 的软实时、低延时和分布式特性。
以下是 EMQX 服务器框架的详细介绍:
(1)可扩展性:EMQX 支持亿级的 MQTT 服务订阅,单节点能够支持 500 万 MQTT 设备连接,集群可扩展至 1 亿并发 MQTT 连接。这种强大的扩展能力使其能够适应不同规模的物联网应用。
(2)安全性:EMQX 提供了多种安全机制,包括 SSL/TLS、密码认证、增强认证和 ACL(访问控制列表)等,以保障数据传输和访问的安全性。
(3)规则引擎:EMQX 内置了基于 SQL 的规则引擎,能够实时过滤、转换和处理消息,提供灵活的消息处理机制。这使得应用程序能够根据业务需求对消息进行灵活处理。
(4)数据存储:EMQX 企业版还提供了数据存储功能,将客户端上下线状态、订阅关系、离线消息、消息内容以及消息回执等操作记录到各种数据库中。这一功能在服务崩溃或客户端异常离线后,能够保留数据,确保数据的完整性和可靠性。
(5)集群设计:EMQX 采用 Masterless 的大规模分布式集群架构,实现了系统的高可用性和水平扩展。集群设计包括维护订阅表、路由表和主题树等数据结构,以实现消息转发和投递给各节点上的订阅者。
(6)协议支持:EMQX 完全支持 MQTT 5.0 和 3.x 协议标准,提供了更好的伸缩性、安全性和可靠性。同时,它还提供了对多种其他协议的支持,如 WebSocket、TCP、SSL/TLS 等。
(7)易用性:EMQX 提供了丰富的 API 和插件管理功能,使得用户可以方便地查看在线客户端信息、踢出客户端、管理插件状态等。它还提供了可视化的管理界面和调试工具,方便用户进行监控和管理。
三、购买 ECS 云服务器
3.1 登录官网
https://www.huaweicloud.com/
3.2 购买 ECS 服务器
【1】选择 ECS 弹性服务器
【2】选择 ECS 服务器的区域、配置信息、操作系统(我选择的 Ubuntu18.04 64 位)。
【3】购买弹性公网 IP,配置带宽。
【4】配置密码
【5】选择购买时长,我这里选择了 1 个月时长
【6】确认付费付款
收到邮件提醒,服务器创建成功。 (为了写教程,花费 320 元,买了一个月服务器)
【7】返回弹性服务器的控制台
【8】点击服务器名字,可以进入到详情页面。
3.3 配置安全组
要确保 MQTT 服务器常用的几个端口已经开放出出来。
3.4 安装 FinalShell
Windows 下安装 FinalShell 终端,方便使用 SSH 协议远程登录到云服务器。 (当然,使用其他方式登录也是一样的)
3.5 远程登录到云服务器终端
【1】新建连接,选择 SSH 连接。
【2】填入 IP 地址、用户名、密码
这里的主机就是填服务器的公网 IP 地址,密码就是创建服务器输入的密码,用户名直接用 root。
【3】点击连接服务器
【4】第一次登录会弹出提示框,选择接受并保存
【5】接下来可以看到服务器已经登录成功了。
四、Linux 下安装 EMQX
本章节将介绍如何在 Ubuntu 系统中下载安装并启动 EMQX。
支持的 Ubuntu 版本:
Ubuntu 22.04
Ubuntu 20.04
Ubuntu 18.04
4.1 官网地址
链接:https://www.emqx.io/docs/zh/v5.2/deploy/install-ubuntu.html
4.2 通过 Apt 源安装
EMQX 支持通过 Apt 源安装,免除了用户需要手动处理依赖关系和更新软件包等的困扰,具有更加方便、安全和易用等优点。
在命令行终端,复制下面的命令过去,按下回车键。
【1】通过以下命令配置 EMQX Apt 源:
curl -s https://assets.emqx.com/scripts/install-emqx-deb.sh | sudo bash
复制代码
【2】运行以下命令安装 EMQX:
sudo apt-get install emqx
复制代码
【3】运行以下命令启动 EMQX:
sudo systemctl start emqx
复制代码
过程如下:
4.3 EMQX 常用的命令
sudo systemctl emqx start 启动
sudo systemctl emqx stop 停止
sudo systemctl emqx restart 重启
复制代码
五、配置 EMQX 服务器
5.1 登录 EMQX 内置管理控制台
EMQX 提供了一个内置的管理控制台,即 EMQX Dashboard。方便用户通过 Web 页面就能轻松管理和监控 EMQX 集群,并配置和使用所需的各项功能。
在浏览器里输入: http://122.112.225.194:18083
就可以访问 EMQX 的后台管理页面。可以管理以连接的客户端或检查运行状态。
这里面的 IP 地址,就是自己 ECS 云服务器的公网 IP 地址。
打开浏览器后,输入地址后打开的效果:
默认用户名和密码:
第一次登录会提示你修改新密码,如果不想设置,也可以选择跳过(公网服务器部署,还是要修改密码安全些)。
下面修改新密码:
登录成功的页面显示如下:
5.2 MQTT 配置
这里可以配置 MQTT 的一些参数,根据自己的需求进行配置。
5.3 测试 MQTT 通信
新建一个客户端,点击连接。
连接之后,然后点击订阅,和发布,如果下面消息能正常的接收。说明 MQTT 服务器通信是已经正常,没问题了。
并且在这个页面也可以看到主题发布
和主题订阅
的格式。
5.4 MQTT 客户端登录服务器测试
接下来就打开我们自己的 MQTT 客户端登录 MQTT 服务器进行测试数据的通信。
端口选择: 1883
根据软件参数填入参数,登录,进行主题的发布和订阅。
说明: 目前还没有配置客户端认证,现在只要 IP 和端口输入正确,MQTT 三元组可以随便输入,都可以登录上服务器的,服务器没有对三元组做校验。
EMQ X 默认配置中启用了匿名认证,任何客户端都能接入 EMQX。没有启用认证插件或认证插件没有显式允许/拒绝(ignore)连接请求时,EMQX 将根据匿名认证启用情况决定是否允许客户端连接。
然后打开EMQX
的管理后台,可以看到我们的设备已经登录服务器了,名字为test1
。
在订阅主题的页面也可以看到我们客户端设备订阅的主题。
5.5 客户端认证配置
EMQX 默认配置中启用了匿名认证,任何客户端都能接入 EMQX。没有启用认证插件或认证插件没有显式允许/拒绝(ignore)连接请求时,EMQX 将根据匿名认证启用情况决定是否允许客户端连接。
在正式产品里肯定是要启用认证的,不然任何设备都能接入。
下面就介绍如何配置 客户端认证。
【1】打开客户端认证页面
【2】选择密码认证
【3】选择内置数据库
【4】设置认证方式(都可以默认,不用改),直接点击创建。
【5】创建成功后,点击用户管理
【6】添加用户
【7】添加成功
【8】添加完毕之后,打开 MQTT 客户端可以进行测试。
登录的时候,MQTT 用户名和密码必须输入正确,按照上一步添加的信息进行如实填写,否则是无法登录服务器的。
5.6 客户端授权配置
客户端授权页面可以配置每个客户端(设备)的主题发布,订阅权限。限制它是否可以发布主题,订阅主题。 如果有需要就可以进行配置。
http://127.0.0.1:18083/#/authorization/detail/built_in_database?tab=users
【1】创建数据源
【2】选择内置数据库
【3】完成创建
【4】点击权限管理
【5】选择客户端 ID,点击添加
【6】配置权限
5.7 数据转发(集成)
在集成选项里,可以对设备数据处理。 比如:转发到自己的 HTTP 服务器,转发到自己其他的 MQTT 服务器,创建规则,某些事件触发某些动作等等。
选择数据桥接。
可以把数据发送端自己的 HTTP 服务器,或者发送到其他的 MQTT 服务器。
选择 HTTP 服务 (如果自己有 HTTP 服务器,可以将数据转发给自己的 HTTP 服务器)。
七、MQTT 客户端消息互发测试
7.1 添加 2 个设备
为了方便测试设备间互相订阅主题,数据收发,在客户端认证页面至少添加 2 个设备。我这里分别添加了test1
和test2
。
7.2 设备间测试
设备 A 订阅设备 B 的主题,设备 B 订阅设备 A 的主题,实现数据互发。
设备 A 的 MQTT 信息:
MQTT服务器地址:122.112.225.194
MQTT服务器端口号:1883
MQTT客户端ID:AAA
MQTT用户名:test1
MQTT登录密码:12345678
订阅主题:BBB/#
发布主题:AAA/1
发布的消息:{ "msg": "我是AAA设备" }
复制代码
设备 B 的 MQTT 信息:
MQTT服务器地址:122.112.225.194
MQTT服务器端口号:1883
MQTT客户端ID:BBB
MQTT用户名:test2
MQTT登录密码:12345678
订阅主题:AAA/#
发布主题:BBB/1
发布的消息:{ "msg": "我是BBB设备" }
复制代码
八、STM32 硬件端开发
8.1 BC26 模块的 AT 指令调试过程
BC20/BC26 开启 GPS、连接 MQTT 服务器的 AT 指令发送流程。
(1)查询模块是否正常
(2)获取卡号,查询卡是否插好
(3)激活网络
(4)获取网络激活状态
(5)查询网络质量
(6)检查网络状态
AT+CEREG=? //检查网络状态
+CEREG: 0,1 //找网成功
OK
复制代码
(7)激活 GPS
激活GPS,要等一段时间
AT+QGNSSC=1
OK
复制代码
(8)查询 GPS 激活状态
查询激活状态,1表示成功激活
AT+QGNSSC?
+QGNSSC: 1
OK
复制代码
(9)获取一次 GPS 定位语句
AT+QGNSSRD="NMEA/RMC"
+QGNSSRD: $GNRMC,120715.00,A,3150.78179,N,11711.93433,E,0.000,,310818,,,A,V*19
OK
复制代码
(10)连接 MQTT 服务器
AT+QMTOPEN=0,"a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com",1883
OK
+QMTOPEN: 0,0
复制代码
(11)登录 MQTT 服务器
命令格式: AT+QMTCONN=<tcpconnectID>,<clientID>,<username>,<password>
AT+QMTCONN=0,"6210e8acde9933029be8facf_dev1_0_0_2022021913","6210e8acde9933029be8facf_dev1","6cea55404b463e666cd7a6060daba745bbaa17fe7078dfef45f8151cdf19673d"
OK
+QMTCONN: 0,0,0
复制代码
(12)订阅主题
命令格式: AT+QMTSUB=<tcpconnectID>,<msgID>,"<topic1>”,<qos1>[,"<topic2>”,<qos2>…]
AT+QMTSUB=0,1,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/messages/down",2
OK
+QMTSUB: 0,1,0,2
复制代码
(13)发布主题
命令格式:AT+QMTPUB=<tcpconnectID>,<msgID>,<qos>,<retain>,"<topic>","<msg>"
先发送指令:
AT+QMTPUB=0,0,0,0,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/repor"
等待返回 ">"
接着发送数据.不需要加回车。
"{"services": [{"service_id": "gps","properties":{"longitude":12.345,"latitude":33.345}}]}"
数据发送完毕,再发送结束符。 十六进制的值--0x1a 。某些串口调试助手可以适应ctrl+z 快捷键输入0xA
等待模块返回"OK",到此数据发送完成。
OK
+QMTPUB: 0,0,0
复制代码
8.2 BH1750 传感器
下面贴出的是 通过 I2C 接口与 BH1750 光照传感器通信,读取光敏值并通过串口打印的代码:
#include "stm32f1xx_hal.h"
#include "stdio.h"
I2C_HandleTypeDef hi2c1;
UART_HandleTypeDef huart1;
#define BH1750_ADDRESS (0x23 << 1) // BH1750地址
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_I2C1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_I2C1_Init();
uint8_t data[2];
uint16_t lux;
while (1)
{
HAL_I2C_Master_Transmit(&hi2c1, BH1750_ADDRESS, (uint8_t[]){0x10}, 1, HAL_MAX_DELAY); // 设置单次高分辨率模式
HAL_Delay(180); // 等待传感器测量完成
HAL_I2C_Master_Receive(&hi2c1, BH1750_ADDRESS, data, 2, HAL_MAX_DELAY); // 读取光照值
lux = (data[0] << 8) | data[1];
char buffer[20];
sprintf(buffer, "Lux: %d\r\n", lux);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY); // 通过串口打印光照值
HAL_Delay(1000); // 延时1秒
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
复制代码
8.3 DHT11 温湿度模块
下面贴出的是 DHT11 温湿度传感器读取环境温湿度数据并通过串口打印的代码:
#include "stm32f1xx_hal.h"
#include "stdio.h"
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart1;
#define DHT11_GPIO_PORT GPIOA
#define DHT11_GPIO_PIN GPIO_PIN_0
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_TIM2_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM2_Init();
HAL_TIM_Base_Start(&htim2);
while (1)
{
HAL_TIM_Base_Start(&htim2);
HAL_Delay(2000); // 等待2秒
// 发送开始信号
HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_RESET);
HAL_Delay(18);
HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET);
HAL_Delay(20);
// 设置引脚为输入
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
// 等待 DHT11 响应
while (!HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN))
;
while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN))
;
// 读取数据
uint8_t data[5] = {0};
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 8; j++)
{
while (!HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN))
;
HAL_TIM_Base_Start(&htim2);
while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN))
;
if (HAL_TIM_Base_GetCounter(&htim2) > 40)
data[i] |= (1 << (7 - j));
}
}
// 计算温度和湿度
uint8_t humidity = data[0];
uint8_t temperature = data[2];
char buffer[50];
sprintf(buffer, "Temperature: %d°C, Humidity: %d%%\r\n", temperature, humidity);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY); // 通过串口打印温湿度数据
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_TIM2_Init(void)
{
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 65535;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
复制代码
8.4 土壤湿度传感器
通过 ADC 模块读取土壤湿度并通过串口打印的代码:
#include "stm32f1xx_hal.h"
#include "stdio.h"
ADC_HandleTypeDef hadc1;
UART_HandleTypeDef huart1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_ADC1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_ADC1_Init();
uint16_t adc_value;
while (1)
{
HAL_ADC_Start(&hadc1); // 启动 ADC 转换
if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
{
adc_value = HAL_ADC_GetValue(&hadc1); // 读取 ADC 值
char buffer[50];
sprintf(buffer, "Soil Moisture: %d\r\n", adc_value);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY); // 通过串口打印土壤湿度数据
}
HAL_Delay(1000); // 延时1秒
}
}
void SystemClock_Config(void)
{
// 略,根据实际情况配置系统时钟
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_0; // 修改为实际连接的通道
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_13CYCLES_5; // 根据实际情况调整采样时间
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
复制代码
九、总结
本智慧农业管理系统设计与实现项目基于 STM32F103RCT6 微控制器为核心,通过集成 DHT11 温湿度传感器、BH1750 光照强度传感器和土壤湿度检测传感器等设备,构建了一套全面的农田环境监测系统。当环境参数超出预设阈值时,系统能够实时报警并自动或提醒进行灌溉、施肥等操作,同时利用蜂鸣器发出声音警报。
在远程控制方面,系统采用 NBIoT-BC26 模块实现了无线通信功能,将采集到的数据传输至云端 MQTT 服务器,并通过 EMQX 开源框架搭建的服务器处理数据。用户可通过微信小程序随时随地查看农田环境的各项实时数据,实现对农作物生长环境的远程监控,并能便捷地执行手动灌溉、开启补光灯等远程控制操作。
本项目的成功实施,不仅有效提升了农业生产过程中的智能化水平,降低了人工管理成本,而且为实现精准农业和智慧农业提供了有力的技术支持。未来,随着物联网技术、云计算和人工智能技术的进一步发展,这套智慧农业管理系统将有望在更多领域推广使用,助力我国现代农业朝着更高效、智能、可持续的方向迈进。
点击关注,第一时间了解华为云新鲜技术~
评论