一、前言
1.1 项目介绍
【1】项目开发背景
竹荪是一种珍贵的食用菌,具有较高的营养和经济价值,但其生长过程对环境条件要求极为苛刻。竹荪在生长过程中对温度、湿度、光照和土壤水分等参数的依赖度极高,稍有不当就会影响产量与品质。传统竹荪种植方式大多依赖人工经验判断与手动控制,管理效率低下,难以实时精确掌握种植环境的变化,且容易因环境调控不及时而造成资源浪费或产量损失。随着智能农业与物联网技术的不断发展,基于传感器与嵌入式控制系统的环境监控方案逐渐成为现代化农业发展的必然趋势。
本项目以STM32单片机为核心控制器,结合多种环境传感器与智能控制模块,旨在构建一个适用于竹荪种植的智能环境监控系统。系统可实时采集温度、湿度、光照、土壤水分等多项环境参数,并根据设定的阈值自动调节灌溉、通风、补光、加热等设备,实现自动化、精准化的环境控制。此外,系统还通过4G通信模块与华为云物联网平台进行数据交互,用户可通过APP上位机实现远程监控与控制,从而突破传统农业对人工值守和现场操作的依赖,实现智能化种植管理。
通过该项目的设计与实现,不仅能够提高竹荪种植的自动化水平,降低人工成本,还能实现对种植环境的科学化管理,保障竹荪的高品质与稳定产出。同时,本项目的研究对于其他温室类食用菌或农作物的智能种植也具有一定的参考价值和推广意义。该系统融合了嵌入式控制技术、物联网通信技术、自动控制理论及人机交互设计,具有较强的综合性与工程实践价值,符合当前智慧农业的发展方向和高校工程类专业的教学目标。
【2】设计实现的功能
(1)土壤水分检测功能:系统通过土壤湿度传感器实时采集竹荪种植土壤的含水量数据,用于判断是否需要进行自动灌溉。
(2)环境光照强度检测功能:利用BH1750光照传感器检测棚内光照强度,为卷帘机遮阳和补光灯控制提供依据。
(3)环境温湿度检测功能:采用SHT30传感器实时监测竹荪种植棚内的温度和湿度参数,作为通风与加热控制的依据。
(4)LCD显示功能:通过1.44寸SPI接口LCD显示屏实时显示温度、湿度、光照、土壤湿度等传感器数据及设备的运行状态(如风扇、水泵、卷帘机、加热模块等开关状态)。
(5)灌溉控制功能:系统通过继电器控制水泵,实现灌溉功能。支持本地按键手动控制、APP远程手动控制及根据土壤湿度自动灌溉三种模式。
(6)通风控制功能:系统通过继电器控制通风风扇,自动模式下当温度超过设定阈值时自动启动风扇进行降温。
(7)卷帘机控制功能:系统通过步进电机驱动卷帘机,自动模式下根据光照强度自动放下或收起草帘;手动模式下可通过APP远程控制卷放。
(8)补光灯控制功能:系统通过继电器控制补光LED灯,自动模式下根据环境亮度自动开启或关闭;手动模式下支持本地按键及APP远程控制。
(9)加热控制功能:系统通过继电器控制PTC陶瓷加热模块,自动模式下当温度低于设定值时自动启动加热;手动模式下可通过APP远程开启或关闭。
(10)控制模式切换功能:系统支持“自动模式”和“手动模式”两种控制方式,可通过APP上位机远程切换。
(11)上位机远程控制与监测功能:通过4G模块连接华为云物联网平台,实现数据上云,用户可通过Qt设计的上位机APP(Windows与Android版本)实时查看监测数据,并进行远程控制。
(12)数据上传与云端交互功能:所有采集的环境数据均实时上传至华为云物联网平台,云端数据可供APP上位机显示与分析,实现设备的远程可视化管理。
【3】项目硬件模块组成
(1)主控模块:采用STM32F103RCT6作为核心控制单元,负责系统的数据采集、逻辑判断、设备控制及通信管理,是整个系统的核心部分。
(2)土壤湿度检测模块:采用ADC模拟量接口的土壤湿度传感器,用于检测土壤含水量,为自动灌溉控制提供依据。
(3)光照强度检测模块:采用BH1750数字光照传感器,通过I²C总线与主控通信,用于检测环境光照强度。
(4)温湿度检测模块:采用SHT30温湿度传感器,通过I²C接口与主控通信,用于实时检测环境温度与湿度。
(5)LCD显示模块:采用1.44寸SPI接口LCD显示屏,用于实时显示传感器采集的数据和系统运行状态。
(6)灌溉控制模块:采用继电器驱动5V水泵电机,通过控制水泵开启或关闭实现自动或手动灌溉。
(7)通风风扇控制模块:采用继电器控制5V小风扇的启停,用于在温度过高时进行通风降温。
(8)卷帘机控制模块:采用5V 28BYJ-48步进电机及驱动电路,实现草帘的自动卷放,用于调节光照强度。
(9)补光灯控制模块:采用白色LED灯模块,通过继电器进行开关控制,用于在光照不足时提供补光。
(10)加热控制模块:采用5V PTC陶瓷加热模块,通过继电器控制启停,用于在低温环境下自动加热。
(11)通信模块:采用Air780E-4G模块,实现与华为云物联网平台的无线数据传输及远程控制通信。
(12)按键控制模块:设置本地物理按键,用于手动控制各功能设备以及模式切换。
(13)电源模块:系统统一由外部5V 2A电源供电,提供主控板及各外设模块所需的工作电压。
【4】设计意义
竹荪是一种高经济价值和高营养价值的珍稀食用菌,其生长环境对温度、湿度、光照和土壤水分等因素有着严格要求。传统的竹荪种植主要依赖人工经验管理,通过人工观测环境条件并手动调节灌溉、通风或遮阳,这种方式不仅劳动强度大,而且实时性和精准性较差,容易造成资源浪费或环境波动,从而影响竹荪的品质和产量。因此,将智能控制与环境监测技术引入竹荪种植过程,具有重要的现实意义和推广价值。
本项目以STM32单片机为核心控制器,结合温湿度、光照、土壤水分等多种传感器,构建了一个集环境监测、自动调控与远程管理为一体的智能竹荪种植环境监控系统。通过对环境参数的实时采集与分析,系统能够根据设定阈值自动控制灌溉、通风、遮阳、补光和加热等关键环节,显著提高了竹荪种植的智能化和自动化水平。这样不仅可以优化生长环境,提升竹荪的出菇率和品质,还能节约水、电等资源,实现节能高效的生产管理。
同时,本系统还通过4G模块将采集的数据上传至华为云物联网平台,用户可通过APP上位机进行远程监测与控制,实现从传统农业到智慧农业的转变。该设计充分融合了嵌入式控制技术、传感器检测技术、物联网通信技术和上位机人机交互设计,不仅具有良好的工程应用前景,也能为农业智能化控制系统的教学与研究提供实践平台。
综上所述,本项目的设计实现了竹荪种植环境监控的自动化、智能化与信息化,促进了农业生产方式的技术升级,具有重要的科研意义和实际应用价值。
【5】摘要
本设计以STM32F103RCT6单片机为核心控制器,设计并实现了一套基于STM32的竹荪种植环境监控系统。系统通过多种传感器对竹荪生长环境中的温度、湿度、光照强度及土壤水分含量进行实时监测,并结合继电器和步进电机等执行机构,实现对灌溉、通风、补光、遮阳及加热等设备的智能控制。系统支持自动与手动两种工作模式,自动模式下可根据设定阈值进行环境调节,手动模式下可通过本地按键或上位机APP远程控制。通过Air780E 4G通信模块,系统将采集的环境数据上传至华为云物联网平台,实现远程监测与管理功能。上位机端采用Qt框架设计,支持Windows和Android双版本APP,使用户可实时查看数据、切换模式并远程控制设备。该系统实现了竹荪种植环境的智能化与可视化管理,具有良好的推广应用价值与实践意义。
关键字:
STM32;竹荪种植;环境监控;物联网;自动控制;华为云;Qt
1.2 设计思路
本系统的整体设计思路是以STM32F103RCT6单片机为核心控制平台,通过多种传感器对竹荪种植环境中的关键参数进行实时监测,并根据预设的阈值实现自动控制。同时,结合4G物联网通信模块与上位机APP,实现对整个系统的远程监测与管理,从而构建一个集“感知—决策—执行—反馈”于一体的智能竹荪种植环境监控系统。
在硬件设计方面,系统以模块化为原则,主要包括传感器采集模块、控制执行模块、通信模块、显示模块和电源模块等。传感器模块由SHT30温湿度传感器、BH1750光照传感器及ADC型土壤湿度传感器组成,用于采集环境数据。控制执行部分包括继电器驱动模块与步进电机驱动模块,分别用于控制水泵灌溉、通风风扇、补光灯、加热模块及卷帘机。LCD显示模块用于显示系统的实时运行状态和各项监测数据,使用户能够直观了解现场环境变化。通信模块采用Air780E 4G模块,实现与华为云物联网平台的数据交互。
在软件设计方面,系统主要分为单片机端控制程序与上位机APP程序两部分。STM32端采用C语言编程,并通过寄存器方式进行底层驱动设计,保证系统运行的高效性与稳定性。主控程序通过定时采样传感器数据,并根据自动控制逻辑判断是否触发相应设备,同时通过串口与4G模块进行数据通信,将采集的数据上传至云端。上位机APP采用Qt框架编写,提供Windows与Android双版本界面,用户可实时查看温湿度、光照、土壤湿度等数据,并可进行模式切换、设备控制等操作。
在系统运行过程中,手动模式和自动模式可灵活切换。手动模式下,用户可以通过本地按键或上位机APP直接控制各设备;自动模式下,系统根据环境参数与设定阈值自动决策,实现无人值守的智能控制。通过云平台的接入,系统不仅实现了远程监测与管理,还为后期数据分析与农业智能优化提供了基础。
整体而言,本设计在硬件结构上注重稳定性与扩展性,在软件逻辑上注重实时性与交互性,充分体现了现代农业“智能感知、自动控制、远程监控”的发展方向,具有较强的实践应用与推广价值。
1.3 系统功能总结
| 功能编号 | 功能名称 | 功能说明 | 控制方式 | 执行元件 / 模块 |
|---|---|---|---|---|
| 1 | 土壤水分检测 | 实时检测竹荪种植土壤的含水量,为自动灌溉提供数据依据 | 自动采集 | 土壤湿度传感器(ADC接口) |
| 2 | 光照强度检测 | 采集棚内环境光照强度数据,用于卷帘与补光控制 | 自动采集 | BH1750光照传感器(I²C接口) |
| 3 | 温湿度检测 | 检测竹荪种植棚内温度与湿度,为通风与加热提供控制依据 | 自动采集 | SHT30温湿度传感器(I²C接口) |
| 4 | LCD数据显示 | 显示传感器采集的数据及各设备的运行状态 | 自动更新 | 1.44寸SPI接口LCD显示屏 |
| 5 | 灌溉控制 | 控制水泵进行灌溉,可手动控制或根据土壤湿度自动执行 | 自动/手动 | 继电器 + 5V水泵电机 |
| 6 | 通风风扇控制 | 当温度高于阈值时自动开启通风风扇降温 | 自动/手动 | 继电器 + 5V小风扇 |
| 7 | 卷帘机控制 | 根据光照强度自动卷放草帘,或通过APP远程手动控制 | 自动/手动 | 5V 28BYJ-48步进电机 |
| 8 | 补光灯控制 | 光照不足时自动开启补光灯,光照充足时关闭 | 自动/手动 | 继电器 + 白色LED灯模块 |
| 9 | 加热控制 | 当温度低于设定值时自动启动加热模块,维持适宜温度 | 自动/手动 | 继电器 + 5V PTC加热模块 |
| 10 | 模式切换 | 实现“手动模式”与“自动模式”之间的自由切换 | 远程控制 | 本地按键 / APP上位机 |
| 11 | 数据上传 | 将采集到的环境数据实时上传至云端,实现远程监测 | 自动上传 | Air780E 4G模块 + 华为云物联网平台 |
| 12 | 上位机远程控制 | 用户通过APP上位机远程查看数据、控制设备、切换模式 | 手动远程 | Qt上位机(Windows/Android版本) |
1.4 开发工具的选择
【1】设备端开发
硬件设备端的开发主要依赖于C语言,利用该语言直接操作硬件寄存器,确保系统运行的高效性和低延迟。C语言在嵌入式开发中具有广泛的应用,它能够直接访问硬件,满足对资源消耗和响应速度的严格要求。为了编写高效、稳定的代码,开发工具选择了Keil uVision 5作为主要的开发环境。Keil是一个专业的嵌入式开发工具,广泛应用于基于ARM架构的微控制器(如STM32)开发。Keil提供了完善的调试、编译和仿真支持,能够帮助在软件开发过程中高效地进行调试、单步执行以及断点设置,确保开发的稳定性和高效性。
STM32F103RCT6是项目中使用的主控芯片,它基于ARM Cortex-M3架构,拥有强大的计算能力和丰富的外设接口。在硬件编程中,寄存器级编程是常用的方式,这要求开发者对芯片的硬件寄存器有深入的理解。在Keil环境中,通过STM32的寄存器直接控制GPIO、ADC、I2C、SPI等硬件接口,以满足各个硬件模块(如传感器、执行器、显示屏等)与主控芯片的交互。使用寄存器编程能够提供更高效、精确的控制,避免了外部库的开销,同时也能深入调控硬件特性,提升系统性能。
【2】上位机开发
本项目的上位机开发基于Qt 5框架,使用**C**作为主要编程语言。Qt是一个跨平台的应用开发框架,广泛用于开发GUI应用程序。Qt提供了丰富的GUI组件和工具,能够高效地实现图形界面的设计与开发。C则作为Qt的底层语言,具有高效的性能和良好的控制力,非常适合用于处理设备与系统之间的数据交互、通信协议的实现和复杂的计算任务。在项目中,Qt被用于开发Windows平台的桌面应用程序以及Android平台的手机APP。Qt框架的跨平台特性使得开发者能够使用同一套代码在不同操作系统上进行构建和部署,大大提高了开发效率。
为了方便开发和调试,上位机的开发采用了Qt Creator作为主要的集成开发环境(IDE)。Qt Creator是一款由Qt官方提供的开发工具,专为Qt应用程序开发设计,支持C++、QML和JavaScript等语言。Qt Creator提供了丰富的功能,如代码编辑、调试、构建、版本控制集成等,能够显著提升开发者的生产力。在本项目中,Qt Creator为开发者提供了自动化构建、界面设计工具(如Qt Designer)和调试工具(如QDebug和QML调试工具),使得开发过程更加高效和流畅。
上位机与硬件设备端的通信采用了基于TCP/IP协议的数据传输方式。为了实现这一功能,Qt提供了丰富的网络编程支持,尤其是QTcpSocket和QTcpServer类,使得上位机能够轻松地与硬件设备建立TCP连接,进行数据收发。上位机通过WIFI连接ESP8266-WIFI模块,ESP8266模块创建TCP服务器,上位机应用则作为客户端连接到服务器,进行实时的数据传输与控制命令的下发。
为了满足不同用户的需求,本项目需要支持Windows平台的桌面应用和Android平台的移动APP。Qt的跨平台特性使得开发人员能够在一个代码库下完成多平台应用的开发和移植。开发者仅需要编写一次应用逻辑和用户界面,就可以通过Qt的跨平台构建工具生成Windows和Android两个平台的可执行文件。此外,Qt提供了丰富的文档和社区支持,帮助开发者解决平台差异和兼容性问题,确保应用在不同平台上都能稳定运行。
总体而言,上位机开发环境采用了Qt 5框架和C++语言,结合Qt Creator集成开发环境,提供了一个高效、稳定、跨平台的开发工具链。通过Qt强大的GUI设计、网络通信、多线程支持以及数据库管理功能,开发者能够轻松实现与硬件设备的交互、控制设备、处理传感器数据,并为用户提供直观、流畅的操作体验。
1.6 系统框架图
应用服务层网络传输层感知控制层 - STM32F103RCT6 主控制器本地人机交互执行器控制模块数据采集模块
MQTT/JSON over TCP
下发控制命令
驱动
驱动
驱动
脉冲驱动
I2C/光照数据
I2C/温湿度数据
ADC/土壤湿度
GPIO/按键事件
封装传感器数据/设备状态
订阅数据
显示数据
操作指令(模式切换/手动控制)
发布指令/MQTT
解析AT指令
GPIO/控制信号
GPIO/控制信号
GPIO/控制信号
自动控制逻辑(比较阈值)
刷新显示
Qt上位机应用Windows PC 上位机Android 手机 APPBH1750光照传感器SHT30温湿度传感器土壤湿度传感器模拟ADC继电器组水泵通风风扇PTC加热器步进电机驱动器卷帘机补光灯LED模块按键LCD显示屏4G通信模块Air780E移动网络/以太网华为云物联网平台设备影子/物模型/MQTT用户
1.7 模块的技术详情介绍
本项目是一个集传感器数据采集、本地自动控制、远程云端监控于一体的综合性物联网系统。其技术核心可分为STM32硬件层、云端服务层和Qt应用层三大板块。
【1】STM32硬件控制层 (寄存器编程)
核心微控制器:STM32F103RCT6
• 核心地位:作为整个系统的“大脑”,负责所有输入信号的采集、逻辑判断和输出设备的控制。•
技术详情:•
内核:ARM Cortex-M3,最高运行频率72MHz,为复杂的多任务逻辑控制提供了足够的算力。•
存储:具有256KB的Flash和48KB的SRAM,足以容纳所有寄存器配置代码、业务逻辑代码以及临时数据。•
外设资源:
多路ADC:用于采集土壤湿度传感器的模拟电压信号。•
多个定时器:用于产生PWM信号精确控制步进电机(卷帘机),以及为系统提供精确的延时和计时。•
多个USART:一个用于连接4G模块(AT指令通信),一个可用于调试信息打印。•
SPI接口:用于驱动SPI协议的LCD显示屏。•
I2C接口:用于与BH1750(光照传感器)和SHT30(温湿度传感器)通信。•
GPIO:大量通用输入输出口,用于控制继电器、读取按键状态等。
• 编程方式:采用寄存器直接编程。这种方式不依赖HAL或标准库,直接读写芯片内部的存储器映射寄存器来配置和控制外设。其优点是代码执行效率最高、对硬件理解最深入、生成的二进制文件体积最小;缺点是开发难度大、可读性较差、移植不便,非常考验开发者对STM32芯片手册的理解能力。
【2】传感器与执行器模块
1. 环境信息采集模块
光照强度传感器:BH1750•
通信协议:I2C。•
技术特点:是一款数字型环境光强度传感器。它直接输出数字量,无需STM32进行额外的AD转换,抗干扰能力强。测量范围广(0-65535 lx),精度高,且能够排除红外线的影响,更接近人眼感知的亮度。•
在系统中的作用:为“自动卷帘”和“自动补光”功能提供关键数据依据。
• 温湿度传感器:SHT30
通信协议:I2C。•
技术特点:是Sensirion公司推出的新一代数字温湿度传感器,具有高精度(±1.5%RH, ±0.2°C)和极高的可靠性。内部集成信号处理单元,直接输出校准后的数字温度与湿度值,简化了STM32的软件设计。•
在系统中的作用:为“自动通风”和“自动加热”功能提供数据依据,同时将温湿度数据上传至云端。
• 土壤湿度传感器:模拟量传感器
接口类型:模拟电压输出。•
技术特点:传感器探针之间的电阻值随土壤湿度变化,内部电路将其转换为0-3.3V(或5V)的模拟电压。STM32通过其内部的
ADC 将此模拟信号转换为数字值。•
在系统中的作用:核心的灌溉判断依据。STM32将ADC读取到的数值与设定的阈值进行比较,决定是否启动水泵。
2. 执行器与控制模块
卷帘机控制:28BYJ-48步进电机 + ULN2003驱动板•
控制方式:STM32的GPIO口输出脉冲序列,通过ULN2003驱动板放大电流来驱动步进电机。•
技术特点:这是一种5V供电的四相八拍步进电机。通过控制各相绕组的通电顺序和频率,可以精确控制电机的旋转角度和速度。STM32使用
定时器产生精确的时序脉冲来实现正转(卷帘)和反转(放帘)。•
在系统中的作用:实现草帘的自动卷放,控制棚内光照。
• 灌溉、通风、加热控制:继电器模块
控制方式:STM32的GPIO输出高/低电平信号(3.3V)来控制继电器的线圈吸合与断开。•
技术原理:继电器是一种电控开关,利用小电流(STM32的GPIO电流)控制大电流(水泵、风扇、加热片的5V/220V电路)的通断。起到了隔离和保护STM32的作用。•
在系统中的作用:
灌溉:控制5V水泵的电源。•
通风:控制5V风扇的电源。•
加热:控制PTC加热片的电源。
• 补光灯控制:白色LED模块
控制方式:通常也是通过GPIO连接一个MOS管或三极管来驱动。输出高电平时,LED亮;输出低电平时,LED灭。
• 人机交互模块
LCD显示屏:1.44寸SPI TFT屏
通信协议:SPI。•
技术特点:SPI协议是一种高速全双工的通信协议,占用IO口少,刷新速率快,非常适合驱动小型显示屏。STM32通过SPI接口向屏幕的控制器发送指令和像素数据,实时显示所有传感器数据和设备状态。
• 本地按键:简单的GPIO输入功能,用于手动触发某些操作(如切换显示页面、手动开关设备)。
【3】通信与云端模块
4G联网模块:Air780E
通信协议:USART(串口),使用AT指令集
技术详情:• Air780E是一款支持4G Cat.1的通信模块。STM32通过串口向其发送特定的AT命令,模块即可执行联网、数据传输等操作。•
工作流程:STM32将传感器数据按照华为云物联网平台的物模型协议 封装成JSON字符串,然后通过串口发送 AT+MQTTSEND 等指令,将数据发布到云平台指定的Topic。同样,STM32会一直监听串口,接收来自云平台的指令(如APP下发的控制命令),并解析执行。
• 在系统中的作用:实现设备与互联网的桥梁,是“设备上云”的关键。
• 云服务平台:华为云物联网平台
技术角色:作为整个系统的“云端服务器”和“数据中枢”。•
核心技术:
物模型:定义了设备的功能,包括属性(如温度、湿度)、命令(如开关灯)和事件。STM32和APP都遵循此模型进行数据交互,实现了双方的解耦。•
MQTT协议:一种轻量级的、基于发布/订阅模式的物联网通信协议。设备发布消息到Topic,平台将消息转发给订阅了该Topic的应用(如APP)。•
设备影子:存储设备的最新状态。即使设备离线,当APP查询状态时,平台也会返回设备最后一次上报的状态,保证了用户体验。
【4】Qt上位机应用层 (C++)
开发框架:Qt 5•
编程语言:C++•
技术详情:
跨平台特性:利用Qt“一次编写,到处编译”的特性,使用同一套C++代码基础,通过不同的编译工具链,分别生成适用于Windows 的桌面应用程序和适用于Android 的手机APP。•
网络通信:使用Qt的QNetworkAccessManager等网络模块,通过HTTPS/MQTT等协议与华为云物联网平台的API进行通信,实现数据的获取(订阅)和指令的下发(发布)。•
用户界面:使用Qt Widgets(对于Windows)或QML(对于Android,以获得更佳的移动端体验)来构建直观的图形界面,实时显示环境数据,并以按钮、开关等形式提供远程控制功能。•
数据解析:解析从华为云平台接收到的JSON格式数据,并更新UI;同时,将用户的操作(如点击“浇水”按钮)封装成符合物模型的JSON命令,发送给云端,再由云端下发给STM32设备。
二、部署华为云物联网平台
华为云官网: https://www.huaweicloud.com/
打开官网,搜索物联网,就能快速找到 设备接入IoTDA。
2.1 物联网平台介绍
华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。
使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。
物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。
设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。
业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。
2.2 开通物联网服务
地址: https://www.huaweicloud.com/product/iothub.html
开通免费单元。
点击立即创建。
正在创建标准版实例,需要等待片刻。
创建完成之后,点击详情。 可以看到标准版实例的设备接入端口和地址。
下面框起来的就是端口号和域名
点击实例名称,可以查看当前免费单元的配置情况。
开通之后,点击接入信息,也能查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。
总结:
端口号: MQTT (1883)| MQTTS (8883)
接入地址: dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com
根据域名地址得到IP地址信息:
打开Windows电脑的命令行控制台终端,使用ping 命令。ping一下即可。
Microsoft Windows [版本 10.0.19045.5011]
(c) Microsoft Corporation。保留所有权利。
C:UsersLenovo>ping dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com
正在 Ping dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com [117.78.5.125] 具有 32 字节的数据:
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
117.78.5.125 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 37ms,最长 = 37ms,平均 = 37ms
C:UsersLenovo>
MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口合适。
2.3 创建产品
链接:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-dev/all-product?instanceId=03c5c68c-e588-458c-90c3-9e4c640be7af
(1)创建产品
(2)填写产品信息
根据自己产品名字填写,下面的设备类型选择自定义类型。
(3)产品创建成功
创建完成之后点击查看详情。
(4)添加自定义模型
产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。
模型简单来说: 就是存放设备上传到云平台的数据。
你可以根据自己的产品进行创建。
比如:
烟雾可以叫 MQ2
温度可以叫 Temperature
湿度可以叫 humidity
火焰可以叫 flame
其他的传感器自己用单词简写命名即可。 这就是你的单片机设备端上传到服务器的数据名字。
先点击自定义模型。
再创建一个服务ID。
接着点击新增属性。
2.4 添加设备
产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。
(1)注册设备
(2)根据自己的设备填写
(3)保存设备信息
创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。
(4)设备创建完成
(5)设备详情
2.5 MQTT协议主题订阅与发布
(1)MQTT协议介绍
当前的设备是采用MQTT协议与华为云平台进行通信。
MQTT是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT是专门针对物联网开发的轻量级传输协议。MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。目前MQTT拥有各种平台和设备上的客户端,已经形成了初步的生态系统。
MQTT是一种消息队列协议,使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合,相对于其他协议,开发更简单;MQTT协议是工作在TCP/IP协议上;由TCP/IP协议提供稳定的网络连接;所以,只要具备TCP协议栈的网络设备都可以使用MQTT协议。 本次设备采用的ESP8266就具备TCP协议栈,能够建立TCP连接,所以,配合STM32代码里封装的MQTT协议,就可以与华为云平台完成通信。
华为云的MQTT协议接入帮助文档在这里: https://support.huaweicloud.com/devg-iothub/iot_02_2200.html
业务流程:
(2)华为云平台MQTT协议使用限制
| 描述 | 限制 |
|---|---|
| 支持的MQTT协议版本 | 3.1.1 |
| 与标准MQTT协议的区别 | 支持Qos 0和Qos 1支持Topic自定义不支持QoS2不支持will、retain msg |
| MQTTS支持的安全等级 | 采用TCP通道基础 + TLS协议(最高TLSv1.3版本) |
| 单帐号每秒最大MQTT连接请求数 | 无限制 |
| 单个设备每分钟支持的最大MQTT连接数 | 1 |
| 单个MQTT连接每秒的吞吐量,即带宽,包含直连设备和网关 | 3KB/s |
| MQTT单个发布消息最大长度,超过此大小的发布请求将被直接拒绝 | 1MB |
| MQTT连接心跳时间建议值 | 心跳时间限定为30至1200秒,推荐设置为120秒 |
| 产品是否支持自定义Topic | 支持 |
| 消息发布与订阅 | 设备只能对自己的Topic进行消息发布与订阅 |
| 每个订阅请求的最大订阅数 | 无限制 |
(3)主题订阅格式
帮助文档地址:https://support.huaweicloud.com/devg-iothub/iot_02_2200.html
对于设备而言,一般会订阅平台下发消息给设备 这个主题。
设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。
如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。
以当前设备为例,最终订阅主题的格式如下:
$oc/devices/{device_id}/sys/messages/down
最终的格式:
$oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down
(4)主题发布格式
对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。
这个操作称为:属性上报。
帮助文档地址:https://support.huaweicloud.com/usermanual-iothub/iot_06_v5_3010.html
根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:
发布的主题格式:
$oc/devices/{device_id}/sys/properties/report
最终的格式:
$oc/devices/663cb18871d845632a0912e7_dev1/sys/properties/report
发布主题时,需要上传数据,这个数据格式是JSON格式。
上传的JSON数据格式如下:
{
"services": [
{
"service_id": <填服务ID>,
"properties": {
"<填属性名称1>": <填属性值>,
"<填属性名称2>": <填属性值>,
..........
}
}
]
}
根据JSON格式,一次可以上传多个属性字段。 这个JSON格式里的,服务ID,属性字段名称,属性值类型,在前面创建产品的时候就已经介绍了,不记得可以翻到前面去查看。
根据这个格式,组合一次上传的属性数据:
{"services": [{"service_id": "stm32","properties":{"你的字段名字1":30,"你的字段名字2":10,"你的字段名字3":1,"你的字段名字4":0}}]}
2.6 MQTT三元组
MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。
接下来介绍,华为云平台的MQTT三元组参数如何得到。
(1)MQTT服务器地址
要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。
帮助文档地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home
MQTT协议的端口支持1883和8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用1883端口进连接的。
根据上面的域名和端口号,得到下面的IP地址和端口号信息: 如果设备支持填写域名可以直接填域名,不支持就直接填写IP地址。 (IP地址就是域名解析得到的)
华为云的MQTT服务器地址:117.78.5.125
华为云的MQTT端口号:1883
如何得到IP地址?如何域名转IP? 打开Windows的命令行输入以下命令。
ping ad635970a1.st1.iotda-device.cn-north-4.myhuaweicloud.com
(2)生成MQTT三元组
华为云提供了一个在线工具,用来生成MQTT鉴权三元组: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。
下面是打开的页面:
填入设备的信息: (上面两行就是设备创建完成之后保存得到的)
直接得到三元组信息。
得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。
ClientId 663cb18871d845632a0912e7_dev1_0_0_2024050911
Username 663cb18871d845632a0912e7_dev1
Password 71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac237
2.7 模拟设备登录测试
经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。
MQTT软件下载地址【免费】: https://download.csdn.net/download/xiaolong1126626497/89928772
(1)填入登录信息
打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。
(2)打开网页查看
完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。
点击详情页面,可以看到上传的数据:
到此,云平台的部署已经完成,设备已经可以正常上传数据了。
(3)MQTT登录测试参数总结
MQTT服务器: 117.78.5.125
MQTT端口号: 183
//物联网服务器的设备信息
#define MQTT_ClientID "663cb18871d845632a0912e7_dev1_0_0_2024050911"
#define MQTT_UserName "663cb18871d845632a0912e7_dev1"
#define MQTT_PassWord "71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac237"
//订阅与发布的主题
#define SET_TOPIC "$oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down" //订阅
#define POST_TOPIC "$oc/devices/663cb18871d845632a0912e7_dev1/sys/properties/report" //发布
发布的数据:
{"services": [{"service_id": "stm32","properties":{"你的字段名字1":30,"你的字段名字2":10,"你的字段名字3":1,"你的字段名字4":0}}]}
2.8 创建IAM账户
创建一个IAM账户,因为接下来开发上位机,需要使用云平台的API接口,这些接口都需要token进行鉴权。简单来说,就是身份的认证。 调用接口获取Token时,就需要填写IAM账号信息。所以,接下来演示一下过程。
地址: https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users
**【1】获取项目凭证 ** 点击左上角用户名,选择下拉菜单里的我的凭证
项目凭证:
28add376c01e4a61ac8b621c714bf459
【2】创建IAM用户
鼠标放在左上角头像上,在下拉菜单里选择统一身份认证。
点击左上角创建用户。
创建成功:
【3】创建完成
用户信息如下:
主用户名 l19504562721
IAM用户 ds_abc
密码 DS12345678
2.9 获取影子数据
帮助文档:https://support.huaweicloud.com/api-iothub/iot_06_v5_0079.html
设备影子介绍:
设备影子是一个用于存储和检索设备当前状态信息的JSON文档。
每个设备有且只有一个设备影子,由设备ID唯一标识
设备影子仅保存最近一次设备的上报数据和预期数据
无论该设备是否在线,都可以通过该影子获取和设置设备的属性
简单来说:设备影子就是保存,设备最新上传的一次数据。
我们设计的软件里,如果想要获取设备的最新状态信息,就采用设备影子接口。
如果对接口不熟悉,可以先进行在线调试:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow
在线调试接口,可以请求影子接口,了解请求,与返回的数据格式。
调试完成看右下角的响应体,就是返回的影子数据。
设备影子接口返回的数据如下:
{
"device_id": "663cb18871d845632a0912e7_dev1",
"shadow": [
{
"service_id": "stm32",
"desired": {
"properties": null,
"event_time": null
},
"reported": {
"properties": {
"DHT11_T": 18,
"DHT11_H": 90,
"BH1750": 38,
"MQ135": 70
},
"event_time": "20240509T113448Z"
},
"version": 3
}
]
}
调试成功之后,可以得到访问影子数据的真实链接,接下来的代码开发中,就采用Qt写代码访问此链接,获取影子数据,完成上位机开发。
链接如下:
https://ad635970a1.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/28add376c01e4a61ac8b621c714bf459/devices/663cb18871d845632a0912e7_dev1/shadow
三、STM32代码设计
3.1 硬件连线说明
在开始连接前,必须规划好电源,这是系统稳定的基础。
总输入:外部 5V/2A 电源适配器。•
5V 域:直接为 4G模块、继电器线圈、步进电机驱动板、水泵、风扇、加热片 供电。
注意:大功率设备(水泵、加热片)必须通过继电器的触点供电,而非直接连接到STM32。
3.3V 域:通过AMS1117-3.3V稳压芯片从5V降压得到,为
STM32、所有传感器(BH1750, SHT30, 土壤湿度)、LCD显示屏
- 供电。
| 模块 | 功能 | STM32引脚 | 备注 |
|---|---|---|---|
| 电源 | 3.3V | - | 为MCU和传感器供电 |
| 土壤湿度 | ADC | PA0 | ADC1_IN0 |
| I2C1 | SCL | PB6 | BH1750, SHT30共用 |
| I2C1 | SDA | PB7 | BH1750, SHT30共用 |
| 步进电机 | IN1 | PA1 | TIM2_CH2 |
| 步进电机 | IN2 | PA2 | TIM2_CH3 |
| 步进电机 | IN3 | PA3 | TIM2_CH4 |
| 步进电机 | IN4 | PA4 | 修改为TIM2_CH1,需重映射 |
| 继电器 | 水泵 | PA5 | |
| 继电器 | 风扇 | PA6 | |
| 继电器 | 加热 | PA7 | |
| 继电器 | 补光灯 | PA8 | |
| LCD (SPI2) | SCLK | PB13 | SPI2_SCK |
| LCD (SPI2) | MOSI | PB15 | SPI2_MOSI |
| LCD (SPI2) | CS | PB12 | GPIO Output |
| LCD | RES | PC0 | GPIO Output |
| LCD | DC | PC1 | GPIO Output |
| 4G模块 | RXD | PA2 | USART2_TX |
| 4G模块 | TXD | PA3 | USART2_RX |
| 4G模块 | PWRKEY | PC3 | GPIO Output |
| 按键 | 模式切换 | PC5 | GPIO Input Pull-up |
| 按键 | 手动灌溉 | PC6 | GPIO Input Pull-up |
| 按键 | 手动卷帘 | PC7 | GPIO Input Pull-up |
| 按键 | 手动补光 | PC8 | GPIO Input Pull-up |
| 调试 | SWDIO | PA13 | 程序下载调试 |
| 调试 | SWCLK | PA14 | 程序下载调试 |
3.2 传感器代码
项目头文件定义
首先创建一个头文件 sensor_config.h 来统一定义所有引脚和参数:
#ifndef __SENSOR_CONFIG_H
#define __SENSOR_CONFIG_H
#include "stm32f10x.h"
// 系统参数
#define SYS_MODE_AUTO 0
#define SYS_MODE_MANUAL 1
// 继电器控制引脚
#define PUMP_PIN GPIO_Pin_5 // PA5
#define FAN_PIN GPIO_Pin_6 // PA6
#define HEATER_PIN GPIO_Pin_7 // PA7
#define LIGHT_PIN GPIO_Pin_8 // PA8
// 步进电机控制引脚
#define STEPPER_IN1 GPIO_Pin_1 // PA1
#define STEPPER_IN2 GPIO_Pin_2 // PA2
#define STEPPER_IN3 GPIO_Pin_3 // PA3
#define STEPPER_IN4 GPIO_Pin_4 // PA4
// 按键引脚
#define KEY_MODE_PIN GPIO_Pin_5 // PC5
#define KEY_WATER_PIN GPIO_Pin_6 // PC6
#define KEY_CURTAIN_PIN GPIO_Pin_7 // PC7
#define KEY_LIGHT_PIN GPIO_Pin_8 // PC8
// LCD SPI引脚
#define LCD_CS_PIN GPIO_Pin_12 // PB12
#define LCD_DC_PIN GPIO_Pin_1 // PC1
#define LCD_RES_PIN GPIO_Pin_0 // PC0
// 4G模块引脚
#define LTE_PWRKEY_PIN GPIO_Pin_3 // PC3
// 传感器地址
#define BH1750_ADDR 0x23
#define SHT30_ADDR 0x44
// 控制阈值
#define SOIL_MOISTURE_THRESHOLD 50 // 土壤湿度阈值%
#define TEMP_HIGH_THRESHOLD 30 // 温度高阈值°C
#define TEMP_LOW_THRESHOLD 15 // 温度低阈值°C
#define LIGHT_HIGH_THRESHOLD 5000 // 光照高阈值lux
#define LIGHT_LOW_THRESHOLD 1000 // 光照低阈值lux
// 全局变量声明
extern uint8_t system_mode;
extern uint16_t soil_moisture;
extern float temperature, humidity;
extern uint16_t light_intensity;
#endif
1. GPIO初始化代码
#include "sensor_config.h"
// GPIO初始化函数
void GPIO_Init_All(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |
RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;
// 继电器控制引脚 (PA5, PA6, PA7, PA8) - 推挽输出
GPIOA->CRL &= ~(GPIO_CRL_MODE5 | GPIO_CRL_CNF5 |
GPIO_CRL_MODE6 | GPIO_CRL_CNF6 |
GPIO_CRL_MODE7 | GPIO_CRL_CNF7);
GPIOA->CRL |= (GPIO_CRL_MODE5_0 | GPIO_CRL_MODE6_0 |
GPIO_CRL_MODE7_0);
GPIOA->CRH &= ~(GPIO_CRH_MODE8 | GPIO_CRH_CNF8);
GPIOA->CRH |= GPIO_CRH_MODE8_0;
// 步进电机引脚 (PA1, PA2, PA3, PA4) - 推挽输出
GPIOA->CRL &= ~(GPIO_CRL_MODE1 | GPIO_CRL_CNF1 |
GPIO_CRL_MODE2 | GPIO_CRL_CNF2 |
GPIO_CRL_MODE3 | GPIO_CRL_CNF3 |
GPIO_CRL_MODE4 | GPIO_CRL_CNF4);
GPIOA->CRL |= (GPIO_CRL_MODE1_0 | GPIO_CRL_MODE2_0 |
GPIO_CRL_MODE3_0 | GPIO_CRL_MODE4_0);
// 按键引脚 (PC5, PC6, PC7, PC8) - 上拉输入
GPIOC->CRL &= ~(GPIO_CRL_MODE5 | GPIO_CRL_CNF5 |
GPIO_CRL_MODE6 | GPIO_CRL_CNF6 |
GPIO_CRL_MODE7 | GPIO_CRL_CNF7);
GPIOC->CRL |= (GPIO_CRL_CNF5_1 | GPIO_CRL_CNF6_1 |
GPIO_CRL_CNF7_1);
GPIOC->CRH &= ~(GPIO_CRH_MODE8 | GPIO_CRH_CNF8);
GPIOC->CRH |= GPIO_CRH_CNF8_1;
// 上拉使能
GPIOC->ODR |= KEY_MODE_PIN | KEY_WATER_PIN |
KEY_CURTAIN_PIN | KEY_LIGHT_PIN;
// LCD控制引脚 (PC0, PC1, PB12) - 推挽输出
GPIOC->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0 |
GPIO_CRL_MODE1 | GPIO_CRL_CNF1);
GPIOC->CRL |= (GPIO_CRL_MODE0_0 | GPIO_CRL_MODE1_0);
GPIOB->CRH &= ~(GPIO_CRH_MODE12 | GPIO_CRH_CNF12);
GPIOB->CRH |= GPIO_CRH_MODE12_0;
// 4G模块PWRKEY引脚 (PC3) - 推挽输出
GPIOC->CRL &= ~(GPIO_CRL_MODE3 | GPIO_CRL_CNF3);
GPIOC->CRL |= GPIO_CRL_MODE3_0;
}
// 继电器控制函数
void Relay_Control(uint16_t relay_pin, uint8_t state)
{
if(state) {
GPIOA->BSRR = relay_pin; // 置高 - 关闭继电器
} else {
GPIOA->BRR = relay_pin; // 置低 - 打开继电器
}
}
2. ADC土壤湿度传感器代码
#include "sensor_config.h"
// ADC初始化
void ADC1_Init(void)
{
// 开启ADC1和GPIOA时钟
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_IOPAEN;
// 配置PA0为模拟输入
GPIOA->CRL &= ~GPIO_CRL_CNF0;
GPIOA->CRL &= ~GPIO_CRL_MODE0;
// ADC校准
ADC1->CR2 |= ADC_CR2_ADON; // 开启ADC
delay_ms(1);
ADC1->CR2 |= ADC_CR2_CAL; // 开始校准
while(ADC1->CR2 & ADC_CR2_CAL); // 等待校准完成
// 配置ADC
ADC1->CR1 = 0x00; // 独立模式
ADC1->CR2 = 0x00;
ADC1->CR2 |= ADC_CR2_CONT; // 连续转换模式
ADC1->CR2 &= ~ADC_CR2_ALIGN; // 右对齐
// 采样时间配置 (239.5周期)
ADC1->SMPR2 |= ADC_SMPR2_SMP0; // 通道0采样时间
// 规则序列配置
ADC1->SQR1 = 0x00; // 1个转换
ADC1->SQR3 = 0x00; // 通道0
ADC1->CR2 |= ADC_CR2_ADON; // 开启ADC
delay_ms(1);
ADC1->CR2 |= ADC_CR2_ADON; // 再次开启开始转换
}
// 读取土壤湿度值 (0-100%)
uint16_t Read_Soil_Moisture(void)
{
uint16_t adc_value;
float moisture_percent;
ADC1->CR2 |= ADC_CR2_ADON; // 开始转换
while(!(ADC1->SR & ADC_SR_EOC)); // 等待转换完成
adc_value = ADC1->DR; // 读取转换结果
// 将ADC值转换为湿度百分比 (需要根据实际传感器校准)
// 假设ADC值范围 0-4095,湿度范围 0-100%
// 实际应用中需要根据干湿状态校准这两个值
uint16_t dry_value = 3500; // 干燥时的ADC值
uint16_t wet_value = 1500; // 湿润时的ADC值
if(adc_value >= dry_value) {
moisture_percent = 0;
} else if(adc_value <= wet_value) {
moisture_percent = 100;
} else {
moisture_percent = 100.0 * (dry_value - adc_value) / (dry_value - wet_value);
}
return (uint16_t)moisture_percent;
}
3. I2C通信基础代码 (BH1750 & SHT30)
#include "sensor_config.h"
// I2C1初始化 (PB6-SCL, PB7-SDA)
void I2C1_Init(void)
{
// 开启I2C1和GPIOB时钟
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;
// 配置PB6(SCL), PB7(SDA)为开漏输出
GPIOB->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_MODE6 |
GPIO_CRL_CNF7 | GPIO_CRL_MODE7);
GPIOB->CRL |= (GPIO_CRL_CNF6_0 | GPIO_CRL_MODE6_0 |
GPIO_CRL_CNF7_0 | GPIO_CRL_MODE7_0);
// I2C配置
I2C1->CR1 &= ~I2C_CR1_PE; // 禁用I2C
// 时钟配置: 36MHz PCLK1, 目标100kHz
I2C1->CR2 = 0x24; // 36MHz
I2C1->CCR = 180; // CCR = 36M / (2 * 100k) = 180
I2C1->TRISE = 37; // TRISE = 36M / 1000k + 1 = 37
I2C1->CR1 |= I2C_CR1_PE; // 启用I2C
}
// I2C起始条件
void I2C_Start(void)
{
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
}
// I2C停止条件
void I2C_Stop(void)
{
I2C1->CR1 |= I2C_CR1_STOP;
while(I2C1->SR2 & I2C_SR2_MSL);
}
// I2C发送地址
void I2C_Address(uint8_t address, uint8_t direction)
{
I2C1->DR = (address << 1) | direction;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2; // 读取SR2清除ADDR标志
}
// I2C发送数据
void I2C_Write(uint8_t data)
{
I2C1->DR = data;
while(!(I2C1->SR1 & I2C_SR1_TXE));
}
// I2C读取数据 (带ACK)
uint8_t I2C_Read_ACK(void)
{
I2C1->CR1 |= I2C_CR1_ACK;
while(!(I2C1->SR1 & I2C_SR1_RXNE));
return I2C1->DR;
}
// I2C读取数据 (不带ACK)
uint8_t I2C_Read_NACK(void)
{
I2C1->CR1 &= ~I2C_CR1_ACK;
while(!(I2C1->SR1 & I2C_SR1_RXNE));
return I2C1->DR;
}
4. BH1750光照传感器代码
#include "sensor_config.h"
// BH1750初始化
void BH1750_Init(void)
{
I2C_Start();
I2C_Address(BH1750_ADDR, 0); // 写模式
I2C_Write(0x01); // 上电
I2C_Stop();
delay_ms(10);
I2C_Start();
I2C_Address(BH1750_ADDR, 0);
I2C_Write(0x10); // 高分辨率模式,连续测量
I2C_Stop();
}
// 读取光照强度 (lux)
uint16_t BH1750_Read_Light(void)
{
uint8_t data[2];
uint16_t lux;
I2C_Start();
I2C_Address(BH1750_ADDR, 1); // 读模式
data[0] = I2C_Read_ACK(); // 高字节
data[1] = I2C_Read_NACK(); // 低字节
I2C_Stop();
lux = (data[0] << 8) | data[1];
lux = (lux * 10) / 12; // 转换为lux值
return lux;
}
5. SHT30温湿度传感器代码
#include "sensor_config.h"
// SHT30初始化
void SHT30_Init(void)
{
// 发送测量命令 (高重复性)
I2C_Start();
I2C_Address(SHT30_ADDR, 0); // 写模式
I2C_Write(0x2C); // 命令高字节
I2C_Write(0x06); // 命令低字节
I2C_Stop();
delay_ms(20); // 等待测量完成
}
// 读取温湿度
void SHT30_Read_Temp_Humidity(float *temp, float *humidity)
{
uint8_t data[6];
uint16_t raw_temp, raw_humidity;
// 读取数据
I2C_Start();
I2C_Address(SHT30_ADDR, 1); // 读模式
for(int i = 0; i < 5; i++) {
data[i] = I2C_Read_ACK();
}
data[5] = I2C_Read_NACK();
I2C_Stop();
// 解析温度数据
raw_temp = (data[0] << 8) | data[1];
*temp = -45.0 + 175.0 * ((float)raw_temp / 65535.0);
// 解析湿度数据
raw_humidity = (data[3] << 8) | data[4];
*humidity = 100.0 * ((float)raw_humidity / 65535.0);
}
6. 步进电机控制代码
#include "sensor_config.h"
// 步进电机步序 (4相8拍)
const uint16_t stepper_sequence[8] = {
0x0001, // IN1
0x0003, // IN1 + IN2
0x0002, // IN2
0x0006, // IN2 + IN3
0x0004, // IN3
0x000C, // IN3 + IN4
0x0008, // IN4
0x0009 // IN4 + IN1
};
uint8_t current_step = 0;
// 步进电机初始化
void Stepper_Init(void)
{
// 引脚已在GPIO初始化中配置
// 初始状态全部拉低
GPIOA->BRR = STEPPER_IN1 | STEPPER_IN2 | STEPPER_IN3 | STEPPER_IN4;
}
// 步进电机单步控制
void Stepper_Step(uint8_t direction) // 0: 正转(卷帘), 1: 反转(放帘)
{
// 清除所有步进电机引脚
GPIOA->BRR = STEPPER_IN1 | STEPPER_IN2 | STEPPER_IN3 | STEPPER_IN4;
// 根据方向设置当前步序
if(direction == 0) {
// 正转
GPIOA->BSRR = stepper_sequence[current_step];
current_step = (current_step + 1) % 8;
} else {
// 反转
GPIOA->BSRR = stepper_sequence[current_step];
current_step = (current_step == 0) ? 7 : (current_step - 1);
}
}
// 控制卷帘机
void Control_Curtain(uint8_t action) // 0: 收帘, 1: 放帘, 2: 停止
{
static uint8_t curtain_state = 0; // 0: 收起, 1: 放下
if(action == 0 && curtain_state == 1) {
// 收帘
for(int i = 0; i < 512; i++) { // 假设512步完成收帘
Stepper_Step(0);
delay_ms(2);
}
curtain_state = 0;
} else if(action == 1 && curtain_state == 0) {
// 放帘
for(int i = 0; i < 512; i++) { // 假设512步完成放帘
Stepper_Step(1);
delay_ms(2);
}
curtain_state = 1;
}
// 停止状态不做操作
}
7. 4G模块通信代码
#include "sensor_config.h"
#include <stdio.h>
#include <string.h>
// USART2初始化 (PA2-TX, PA3-RX)
void USART2_Init(void)
{
// 开启USART2和GPIOA时钟
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// 配置PA2为复用推挽输出(TX), PA3为浮空输入(RX)
GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_MODE2 |
GPIO_CRL_CNF3 | GPIO_CRL_MODE3);
GPIOA->CRL |= (GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2_0 |
GPIO_CRL_CNF3_0);
// USART配置: 115200, 8N1
USART2->BRR = 0x0139; // 36MHz / 115200 = 312.5
USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
// 发送字符
void USART2_SendChar(char c)
{
while(!(USART2->SR & USART_SR_TXE));
USART2->DR = c;
}
// 发送字符串
void USART2_SendString(char *str)
{
while(*str) {
USART2_SendChar(*str++);
}
}
// 接收字符
char USART2_ReceiveChar(void)
{
while(!(USART2->SR & USART_SR_RXNE));
return USART2->DR;
}
// 4G模块初始化
void LTE_Init(void)
{
// 开机序列
GPIOC->BRR = LTE_PWRKEY_PIN; // 拉低PWRKEY
delay_ms(3000); // 保持3秒
GPIOC->BSRR = LTE_PWRKEY_PIN; // 释放PWRKEY
delay_ms(10000); // 等待模块启动
// 发送AT指令测试
USART2_SendString("ATrn");
delay_ms(1000);
// 配置MQTT参数 (需要根据华为云平台具体配置修改)
USART2_SendString("AT+MQTTCFG="your_server",1883,"your_device_id",60,0,"your_username","your_password"rn");
delay_ms(2000);
// 连接MQTT服务器
USART2_SendString("AT+MQTTCONN=1,120,0rn");
delay_ms(5000);
}
// 发送传感器数据到云端
void Send_Data_To_Cloud(void)
{
char buffer[256];
sprintf(buffer, "{"temp":%.1f,"humi":%.1f,"soil":%d,"light":%d}",
temperature, humidity, soil_moisture, light_intensity);
USART2_SendString("AT+MQTTPUB="sensor/data",0,0,0,");
USART2_SendString(buffer);
USART2_SendString("rn");
}
8. 主控制逻辑代码
#include "sensor_config.h"
#include "delay.h" // 需要实现delay函数
// 全局变量定义
uint8_t system_mode = SYS_MODE_AUTO;
uint16_t soil_moisture = 0;
float temperature = 0, humidity = 0;
uint16_t light_intensity = 0;
// 系统初始化
void System_Init(void)
{
// 配置系统时钟
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_HPRE) | RCC_CFGR_HPRE_DIV1;
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_PPRE1) | RCC_CFGR_PPRE1_DIV2;
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_PPRE2) | RCC_CFGR_PPRE2_DIV1;
// 开启所有需要的时钟
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
GPIO_Init_All();
ADC1_Init();
I2C1_Init();
USART2_Init();
BH1750_Init();
SHT30_Init();
Stepper_Init();
LTE_Init();
// LCD初始化 (需要根据具体LCD型号实现)
// LCD_Init();
}
// 自动控制逻辑
void Auto_Control_Logic(void)
{
// 自动灌溉控制
if(soil_moisture < SOIL_MOISTURE_THRESHOLD) {
Relay_Control(PUMP_PIN, 0); // 打开水泵
} else {
Relay_Control(PUMP_PIN, 1); // 关闭水泵
}
// 自动通风控制
if(temperature > TEMP_HIGH_THRESHOLD) {
Relay_Control(FAN_PIN, 0); // 打开风扇
} else {
Relay_Control(FAN_PIN, 1); // 关闭风扇
}
// 自动加热控制
if(temperature < TEMP_LOW_THRESHOLD) {
Relay_Control(HEATER_PIN, 0); // 打开加热
} else {
Relay_Control(HEATER_PIN, 1); // 关闭加热
}
// 自动卷帘控制
if(light_intensity > LIGHT_HIGH_THRESHOLD) {
Control_Curtain(1); // 放下草帘
} else if(light_intensity < LIGHT_LOW_THRESHOLD) {
Control_Curtain(0); // 收起草帘
}
// 自动补光灯控制
if(light_intensity < LIGHT_LOW_THRESHOLD) {
Relay_Control(LIGHT_PIN, 0); // 打开补光灯
} else {
Relay_Control(LIGHT_PIN, 1); // 关闭补光灯
}
}
// 按键扫描函数
void Key_Scan(void)
{
static uint8_t key_flag[4] = {0};
// 模式切换键
if(!(GPIOC->IDR & KEY_MODE_PIN)) {
if(!key_flag[0]) {
key_flag[0] = 1;
system_mode = !system_mode; // 切换模式
}
} else {
key_flag[0] = 0;
}
// 手动灌溉键 (仅在手动模式下有效)
if(system_mode == SYS_MODE_MANUAL && !(GPIOC->IDR & KEY_WATER_PIN)) {
if(!key_flag[1]) {
key_flag[1] = 1;
Relay_Control(PUMP_PIN, 0); // 打开水泵
delay_ms(5000); // 灌溉5秒
Relay_Control(PUMP_PIN, 1); // 关闭水泵
}
} else {
key_flag[1] = 0;
}
// 其他按键类似实现...
}
int main(void)
{
System_Init();
while(1) {
// 读取所有传感器数据
soil_moisture = Read_Soil_Moisture();
light_intensity = BH1750_Read_Light();
SHT30_Read_Temp_Humidity(&temperature, &humidity);
// 按键扫描
Key_Scan();
// 根据模式执行控制逻辑
if(system_mode == SYS_MODE_AUTO) {
Auto_Control_Logic();
}
// 发送数据到云端 (每30秒发送一次)
static uint32_t cloud_timer = 0;
if(cloud_timer >= 30000) {
Send_Data_To_Cloud();
cloud_timer = 0;
}
// 更新LCD显示
// Update_LCD_Display();
delay_ms(100);
}
}
延迟函数实现
// 简单的毫秒延迟函数
void delay_ms(uint32_t ms)
{
// 配置SysTick定时器
SysTick->LOAD = 72000 - 1; // 72MHz/1000 = 72000
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
for(uint32_t i = 0; i < ms; i++) {
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
}
SysTick->CTRL = 0;
}
代码使用说明
-
- 1.
寄存器编程
-
- :所有代码都直接操作STM32寄存器,符合您的要求2.
-
- :每个传感器都有独立的初始化和读取函数3.
自动控制
-
- :实现了完整的自动控制逻辑4.
云端通信
-
- :包含4G模块与华为云通信的基础框架5.
需要完善
- :
-
- • LCD显示驱动需要根据具体屏幕型号实现• 华为云MQTT参数需要根据实际账户配置• 部分阈值参数需要根据实际种植环境调整
3.3 项目核心代码
1. 主头文件 main.h
#ifndef __MAIN_H
#define __MAIN_H
#include "stm32f10x.h"
// 系统状态定义
typedef enum {
SYS_MODE_AUTO = 0,
SYS_MODE_MANUAL = 1
} SystemMode;
typedef enum {
CURTAIN_UP = 0, // 收帘
CURTAIN_DOWN = 1, // 放帘
CURTAIN_STOP = 2 // 停止
} CurtainState;
// 系统配置参数
typedef struct {
uint16_t soil_moisture_threshold;
float temp_high_threshold;
float temp_low_threshold;
uint16_t light_high_threshold;
uint16_t light_low_threshold;
} SystemConfig;
// 全局变量声明
extern SystemMode system_mode;
extern CurtainState curtain_state;
extern SystemConfig sys_config;
extern uint16_t soil_moisture;
extern float temperature;
extern float humidity;
extern uint16_t light_intensity;
extern uint32_t system_tick;
// 函数声明
void System_Clock_Init(void);
void System_Init_All(void);
void System_Tick_Handler(void);
void Read_All_Sensors(void);
void Auto_Control_Handler(void);
void Manual_Control_Handler(void);
void Key_Scan_Handler(void);
void LCD_Display_Handler(void);
void Cloud_Data_Handler(void);
void Delay_Init(void);
void delay_ms(uint32_t ms);
#endif
2. 系统配置和全局变量 system_config.c
#include "main.h"
// 全局变量定义
SystemMode system_mode = SYS_MODE_AUTO;
CurtainState curtain_state = CURTAIN_UP;
SystemConfig sys_config = {
.soil_moisture_threshold = 50,
.temp_high_threshold = 30.0,
.temp_low_threshold = 15.0,
.light_high_threshold = 5000,
.light_low_threshold = 1000
};
uint16_t soil_moisture = 0;
float temperature = 0.0;
float humidity = 0.0;
uint16_t light_intensity = 0;
uint32_t system_tick = 0;
volatile uint32_t delay_tick = 0;
// 设备状态
uint8_t pump_status = 0;
uint8_t fan_status = 0;
uint8_t heater_status = 0;
uint8_t light_status = 0;
uint8_t curtain_status = 0;
3. 系统时钟和延时 system_core.c
#include "main.h"
// 系统时钟初始化
void System_Clock_Init(void)
{
// 启用外部晶振
RCC->CR |= RCC_CR_HSEON;
while(!(RCC->CR & RCC_CR_HSERDY));
// 配置PLL: HSE * 9 = 72MHz
RCC->CFGR |= RCC_CFGR_PLLSRC;
RCC->CFGR |= RCC_CFGR_PLLMULL9;
// 启用PLL
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
// 配置Flash延迟
FLASH->ACR |= FLASH_ACR_LATENCY_2;
// 切换系统时钟到PLL
RCC->CFGR |= RCC_CFGR_SW_PLL;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
// 配置AHB, APB1, APB2分频
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB = 72MHz
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // APB1 = 36MHz
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; // APB2 = 72MHz
}
// SysTick初始化 for delay
void Delay_Init(void)
{
// 配置SysTick为1ms中断
SysTick->LOAD = 72000 - 1; // 72MHz/1000 = 72000
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
}
// 毫秒延时函数
void delay_ms(uint32_t ms)
{
delay_tick = ms;
while(delay_tick != 0);
}
// SysTick中断处理
void SysTick_Handler(void)
{
if(delay_tick > 0) delay_tick--;
system_tick++;
}
4. 完整的GPIO初始化 gpio_config.c
#include "main.h"
void GPIO_Init_All(void)
{
// 开启所有GPIO时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |
RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN |
RCC_APB2ENR_AFIOEN;
// 继电器控制引脚 (PA5, PA6, PA7, PA8) - 推挽输出
// PA5 - 水泵
GPIOA->CRL &= ~(0xF << 20);
GPIOA->CRL |= (0x1 << 20); // 输出模式,最大速度10MHz
GPIOA->CRL |= (0x0 << 22); // 推挽输出
// PA6 - 风扇
GPIOA->CRL &= ~(0xF << 24);
GPIOA->CRL |= (0x1 << 24);
GPIOA->CRL |= (0x0 << 26);
// PA7 - 加热
GPIOA->CRL &= ~(0xF << 28);
GPIOA->CRL |= (0x1 << 28);
GPIOA->CRL |= (0x0 << 30);
// PA8 - 补光灯
GPIOA->CRH &= ~(0xF << 0);
GPIOA->CRH |= (0x1 << 0);
GPIOA->CRH |= (0x0 << 2);
// 初始状态:所有继电器关闭
GPIOA->BSRR = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
// 步进电机引脚 (PA1, PA2, PA3, PA4) - 推挽输出
// PA1
GPIOA->CRL &= ~(0xF << 4);
GPIOA->CRL |= (0x1 << 4);
GPIOA->CRL |= (0x0 << 6);
// PA2
GPIOA->CRL &= ~(0xF << 8);
GPIOA->CRL |= (0x1 << 8);
GPIOA->CRL |= (0x0 << 10);
// PA3
GPIOA->CRL &= ~(0xF << 12);
GPIOA->CRL |= (0x1 << 12);
GPIOA->CRL |= (0x0 << 14);
// PA4
GPIOA->CRL &= ~(0xF << 16);
GPIOA->CRL |= (0x1 << 16);
GPIOA->CRL |= (0x0 << 18);
// 初始状态:步进电机引脚全部低电平
GPIOA->BRR = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
// 按键引脚 (PC5, PC6, PC7, PC8) - 上拉输入
// PC5 - 模式切换
GPIOC->CRL &= ~(0xF << 20);
GPIOC->CRL |= (0x8 << 20); // 上拉/下拉输入模式
// PC6 - 手动灌溉
GPIOC->CRL &= ~(0xF << 24);
GPIOC->CRL |= (0x8 << 24);
// PC7 - 手动卷帘
GPIOC->CRL &= ~(0xF << 28);
GPIOC->CRL |= (0x8 << 28);
// PC8 - 手动补光
GPIOC->CRH &= ~(0xF << 0);
GPIOC->CRH |= (0x8 << 0);
// 使能上拉
GPIOC->BSRR = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
// LCD控制引脚
// PC0 - RESET, PC1 - DC 推挽输出
GPIOC->CRL &= ~(0xFF << 0);
GPIOC->CRL |= (0x11 << 0); // 推挽输出,最大速度10MHz
// PB12 - CS 推挽输出
GPIOB->CRH &= ~(0xF << 16);
GPIOB->CRH |= (0x1 << 16);
GPIOB->CRH |= (0x0 << 18);
// 4G模块PWRKEY (PC3) - 推挽输出
GPIOC->CRL &= ~(0xF << 12);
GPIOC->CRL |= (0x1 << 12);
GPIOC->CRL |= (0x0 << 14);
// 初始状态:PWRKEY高电平
GPIOC->BSRR = GPIO_Pin_3;
}
// 继电器控制函数
void Relay_Control(uint16_t relay_pin, uint8_t state)
{
if(state) {
GPIOA->BSRR = relay_pin; // 置高 - 关闭继电器
} else {
GPIOA->BRR = relay_pin; // 置低 - 打开继电器
}
}
5. 传感器数据读取 sensor_reader.c
#include "main.h"
// 读取所有传感器数据
void Read_All_Sensors(void)
{
static uint32_t sensor_timer = 0;
// 每1秒读取一次传感器
if(system_tick - sensor_timer >= 1000) {
sensor_timer = system_tick;
// 读取土壤湿度
soil_moisture = Read_Soil_Moisture();
// 读取光照强度
light_intensity = BH1750_Read_Light();
// 读取温湿度
SHT30_Read_Temp_Humidity(&temperature, &humidity);
// 更新设备状态
pump_status = (GPIOA->IDR & GPIO_Pin_5) ? 0 : 1;
fan_status = (GPIOA->IDR & GPIO_Pin_6) ? 0 : 1;
heater_status = (GPIOA->IDR & GPIO_Pin_7) ? 0 : 1;
light_status = (GPIOA->IDR & GPIO_Pin_8) ? 0 : 1;
}
}
6. 自动控制逻辑 auto_control.c
#include "main.h"
void Auto_Control_Handler(void)
{
static uint32_t control_timer = 0;
// 每2秒执行一次自动控制
if(system_tick - control_timer >= 2000) {
control_timer = system_tick;
// 自动灌溉控制
if(soil_moisture < sys_config.soil_moisture_threshold) {
Relay_Control(GPIO_Pin_5, 0); // 打开水泵
pump_status = 1;
} else {
Relay_Control(GPIO_Pin_5, 1); // 关闭水泵
pump_status = 0;
}
// 自动通风控制
if(temperature > sys_config.temp_high_threshold) {
Relay_Control(GPIO_Pin_6, 0); // 打开风扇
fan_status = 1;
} else {
Relay_Control(GPIO_Pin_6, 1); // 关闭风扇
fan_status = 0;
}
// 自动加热控制
if(temperature < sys_config.temp_low_threshold) {
Relay_Control(GPIO_Pin_7, 0); // 打开加热
heater_status = 1;
} else {
Relay_Control(GPIO_Pin_7, 1); // 关闭加热
heater_status = 0;
}
// 自动卷帘控制
if(light_intensity > sys_config.light_high_threshold && curtain_state != CURTAIN_DOWN) {
Control_Curtain(CURTAIN_DOWN); // 放下草帘
curtain_state = CURTAIN_DOWN;
} else if(light_intensity < sys_config.light_low_threshold && curtain_state != CURTAIN_UP) {
Control_Curtain(CURTAIN_UP); // 收起草帘
curtain_state = CURTAIN_UP;
}
// 自动补光灯控制
if(light_intensity < sys_config.light_low_threshold) {
Relay_Control(GPIO_Pin_8, 0); // 打开补光灯
light_status = 1;
} else {
Relay_Control(GPIO_Pin_8, 1); // 关闭补光灯
light_status = 0;
}
}
}
7. 手动控制和按键处理 manual_control.c
#include "main.h"
void Manual_Control_Handler(void)
{
// 手动模式下,自动控制逻辑不执行
// 设备状态通过按键或APP控制
// 这里可以添加手动模式下的特殊处理
}
void Key_Scan_Handler(void)
{
static uint32_t key_timer = 0;
static uint8_t key_flag[4] = {0};
// 每50ms扫描一次按键
if(system_tick - key_timer < 50) return;
key_timer = system_tick;
// 模式切换键 (PC5)
if(!(GPIOC->IDR & GPIO_Pin_5)) {
if(!key_flag[0]) {
key_flag[0] = 1;
system_mode = (system_mode == SYS_MODE_AUTO) ? SYS_MODE_MANUAL : SYS_MODE_AUTO;
// 切换到手动模式时,关闭所有自动设备
if(system_mode == SYS_MODE_MANUAL) {
Relay_Control(GPIO_Pin_5, 1); // 关闭水泵
Relay_Control(GPIO_Pin_6, 1); // 关闭风扇
Relay_Control(GPIO_Pin_7, 1); // 关闭加热
Relay_Control(GPIO_Pin_8, 1); // 关闭补光灯
}
}
} else {
key_flag[0] = 0;
}
// 手动灌溉键 (PC6) - 仅在手动模式下有效
if(system_mode == SYS_MODE_MANUAL && !(GPIOC->IDR & GPIO_Pin_6)) {
if(!key_flag[1]) {
key_flag[1] = 1;
Relay_Control(GPIO_Pin_5, 0); // 打开水泵
delay_ms(5000); // 灌溉5秒
Relay_Control(GPIO_Pin_5, 1); // 关闭水泵
}
} else {
key_flag[1] = 0;
}
// 手动卷帘控制 (PC7) - 仅在手动模式下有效
if(system_mode == SYS_MODE_MANUAL && !(GPIOC->IDR & GPIO_Pin_7)) {
if(!key_flag[2]) {
key_flag[2] = 1;
if(curtain_state == CURTAIN_UP) {
Control_Curtain(CURTAIN_DOWN); // 放下草帘
curtain_state = CURTAIN_DOWN;
} else {
Control_Curtain(CURTAIN_UP); // 收起草帘
curtain_state = CURTAIN_UP;
}
}
} else {
key_flag[2] = 0;
}
// 手动补光灯控制 (PC8) - 仅在手动模式下有效
if(system_mode == SYS_MODE_MANUAL && !(GPIOC->IDR & GPIO_Pin_8)) {
if(!key_flag[3]) {
key_flag[3] = 1;
if(light_status) {
Relay_Control(GPIO_Pin_8, 1); // 关闭补光灯
light_status = 0;
} else {
Relay_Control(GPIO_Pin_8, 0); // 打开补光灯
light_status = 1;
}
}
} else {
key_flag[3] = 0;
}
}
8. 云端数据通信 cloud_communication.c
#include "main.h"
#include <stdio.h>
#include <string.h>
void Cloud_Data_Handler(void)
{
static uint32_t cloud_timer = 0;
char tx_buffer[256];
// 每30秒上传一次数据到云端
if(system_tick - cloud_timer >= 30000) {
cloud_timer = system_tick;
// 构建JSON数据
sprintf(tx_buffer,
"{"temp":%.1f,"humi":%.1f,"soil":%d,"light":%d,"
""pump":%d,"fan":%d,"heater":%d,"light_ctrl":%d,"
""curtain":%d,"mode":%d}",
temperature, humidity, soil_moisture, light_intensity,
pump_status, fan_status, heater_status, light_status,
curtain_state, system_mode);
// 发送到4G模块 (需要根据实际的AT指令调整)
USART2_SendString("AT+MQTTPUB="sensor/data",1,0,0,");
USART2_SendString(tx_buffer);
USART2_SendString("rn");
}
// 处理接收到的云端指令 (简化处理)
if(USART2_Available()) {
char rx_buffer[128];
USART2_ReceiveString(rx_buffer, sizeof(rx_buffer));
// 解析云端指令 (这里需要根据实际协议实现)
// 示例: {"cmd":"pump","value":1}
if(strstr(rx_buffer, "pump") != NULL) {
if(strstr(rx_buffer, "1") != NULL) {
Relay_Control(GPIO_Pin_5, 0); // 打开水泵
} else {
Relay_Control(GPIO_Pin_5, 1); // 关闭水泵
}
}
// 其他指令处理...
}
}
// 检查串口是否有数据
uint8_t USART2_Available(void)
{
return (USART2->SR & USART_SR_RXNE);
}
// 接收字符串
void USART2_ReceiveString(char *buffer, uint16_t max_len)
{
uint16_t i = 0;
while(i < max_len - 1) {
if(USART2_Available()) {
buffer[i] = USART2_ReceiveChar();
if(buffer[i] == 'n' || buffer[i] == 'r') {
break;
}
i++;
}
}
buffer[i] = '';
}
9. LCD显示处理 lcd_display.c
#include "main.h"
// 简化的LCD显示处理 (实际需要根据具体LCD驱动实现)
void LCD_Display_Handler(void)
{
static uint32_t lcd_timer = 0;
static uint8_t display_page = 0;
// 每1秒更新一次显示
if(system_tick - lcd_timer >= 1000) {
lcd_timer = system_tick;
// 清屏
// LCD_Clear();
// 显示传感器数据
// LCD_SetCursor(0, 0);
// LCD_Printf("Temp:%.1fC Humi:%.1f%%", temperature, humidity);
// LCD_SetCursor(0, 1);
// LCD_Printf("Soil:%d%% Light:%d", soil_moisture, light_intensity);
// LCD_SetCursor(0, 2);
if(system_mode == SYS_MODE_AUTO) {
// LCD_Printf("Mode:Auto");
} else {
// LCD_Printf("Mode:Manual");
}
// LCD_SetCursor(0, 3);
// LCD_Printf("P:%d F:%d H:%d L:%d", pump_status, fan_status, heater_status, light_status);
}
}
10. 完整的系统初始化 system_init.c
#include "main.h"
void System_Init_All(void)
{
// 系统时钟初始化
System_Clock_Init();
// 延时初始化
Delay_Init();
// GPIO初始化
GPIO_Init_All();
// 外设初始化
ADC1_Init();
I2C1_Init();
USART2_Init();
// 传感器初始化
BH1750_Init();
SHT30_Init();
// 执行器初始化
Stepper_Init();
// 通信模块初始化
LTE_Init();
// LCD初始化 (需要根据具体LCD实现)
// LCD_Init();
// 初始延迟,等待系统稳定
delay_ms(1000);
}
11. 主函数 main.c
#include "main.h"
int main(void)
{
// 系统初始化
System_Init_All();
// 开机提示
USART2_SendString("System Startedrn");
USART2_SendString("Bamboo Fungus Monitoring System Readyrn");
while(1) {
// 系统滴答处理
System_Tick_Handler();
// 读取所有传感器数据
Read_All_Sensors();
// 按键扫描处理
Key_Scan_Handler();
// 根据模式执行控制逻辑
if(system_mode == SYS_MODE_AUTO) {
Auto_Control_Handler();
} else {
Manual_Control_Handler();
}
// LCD显示更新
LCD_Display_Handler();
// 云端数据处理
Cloud_Data_Handler();
// 系统休眠或延时,降低功耗
delay_ms(10);
}
}
// 简单的系统滴答处理
void System_Tick_Handler(void)
{
// 系统滴答在SysTick中断中自动更新
// 这里可以添加基于系统滴答的定时任务调度
}
12. 中断向量表配置 (在启动文件中)
确保在启动文件 startup_stm32f10x_hd.s 中添加SysTick中断处理:
; 在启动文件的中断向量表中添加
__Vectors
DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
; ... 其他中断 ...
DCD SysTick_Handler ; SysTick Handler
; ... 其他中断 ...
编译配置说明
在Keil MDK中需要配置:
1. 目标设备: STM32F103RC
2. 优化等级: -O0 (便于调试)
3. 预处理器定义: USE_STDPERIPH_DRIVER, STM32F10X_HD
4. 包含路径: 添加所有头文件路径
5. 链接器配置: 根据Flash和RAM大小配置
3.4 程序下载
也有视频教程:
讲解如何编译代码,下载STM32程序: https://www.bilibili.com/video/BV1Cw4m1e7Yc
打STM32的keil工程,编译代码、然后,使用USB线将开发板的左边的USB口(串口1)与电脑的USB连接,打开程序下载软件下载程序。
具体下载过程看下面图:
打开程序下载软件:[软件就在资料包里的软件工具目录下]
3.5 程序正常运行效果
设备运行过程中会通过串口打印调试信息,我们可以通过串口打印了解程序是否正常。
程序下载之后,可以打开串口调试助手查看程序运行的状态信息。[软件就在资料包里的软件工具目录下]
3.6 取模软件的使用
显示屏上会显示中文,字母,数字等数据,可以使用下面的取模软件进行取模设置。
[软件就在资料包里的软件工具目录下]
打开软件之后:
四、上位机开发
4.1 Qt开发环境安装
Qt的中文官网: https://www.qt.io/zh-cn/
QT5.12.6的下载地址:https://download.qt.io/archive/qt/5.12/5.12.6
打开下载链接后选择下面的版本进行下载:
如果下载不了,可以在网盘里找到安装包下载: 飞书文档记录的网盘地址:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink
软件安装时断网安装,否则会提示输入账户。
安装的时候,第一个复选框里的编译器可以全选,直接点击下一步继续安装。
选择编译器: (一定要看清楚了)
4.2 新建上位机工程
前面2讲解了需要用的API接口,接下来就使用Qt设计上位机,设计界面,完成整体上位机的逻辑设计。
【1】新建工程
【2】设置项目的名称。
【3】选择编译系统
【4】选择默认继承的类
【5】选择编译器
【6】点击完成
【7】工程创建完成
4.3 切换编译器
在左下角是可以切换编译器的。 可以选择用什么样的编译器编译程序。
目前新建工程的时候选择了2种编译器。 一种是mingw32这个编译Windows下运行的程序。 一种是Android编译器,可以生成Android手机APP。
不过要注意:Android的编译器需要配置一些环境才可以正常使用,这个大家可以网上找找教程配置一下就行了。
windows的编译器就没有这么麻烦,安装好Qt就可以编译使用。
下面我这里就选择的 mingw32这个编译器,编译Windows下运行的程序。
4.4 编译测试功能
创建完毕之后,编译测试一下功能是否OK。
点击左下角的绿色三角形按钮。
正常运行就可以看到弹出一个白色的框框。这就表示工程环境没有问题了。 接下来就可以放心的设计界面了。
4.5 设计UI界面与工程配置
【1】打开UI文件
打开默认的界面如下:
【2】开始设计界面
根据自己需求设计界面。
4.6 设计代码
1. 主窗口头文件 (mainwindow.h)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QMqttClient>
#include <QChart>
#include <QChartView>
#include <QLineSeries>
#include <QValueAxis>
QT_CHARTS_USE_NAMESPACE
namespace Ui {
class MainWindow;
}
// 设备数据结构体
struct DeviceData {
float soilMoisture; // 土壤湿度
float temperature; // 温度
float humidity; // 湿度
float lightIntensity; // 光照强度
bool pumpStatus; // 水泵状态
bool fanStatus; // 风扇状态
bool curtainStatus; // 卷帘状态
bool lightStatus; // 补光灯状态
bool heaterStatus; // 加热器状态
bool autoMode; // 自动模式
float tempThresholdHigh; // 温度上限阈值
float tempThresholdLow; // 温度下限阈值
float lightThreshold; // 光照阈值
float moistureThreshold; // 湿度阈值
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
// MQTT相关槽函数
void onMqttConnected();
void onMqttDisconnected();
void onMqttMessageReceived(const QByteArray &message, const QString &topic);
// 控制按钮槽函数
void on_pumpControl_clicked();
void on_fanControl_clicked();
void on_curtainControl_clicked();
void on_lightControl_clicked();
void on_heaterControl_clicked();
void on_modeSwitch_clicked();
// 阈值设置槽函数
void on_tempHighThreshold_valueChanged(double arg1);
void on_tempLowThreshold_valueChanged(double arg1);
void on_lightThreshold_valueChanged(double arg1);
void on_moistureThreshold_valueChanged(double arg1);
// 定时器槽函数
void updateDataDisplay();
void updateChart();
private:
Ui::MainWindow *ui;
QMqttClient *m_mqttClient;
QTimer *m_dataTimer;
QTimer *m_chartTimer;
DeviceData m_deviceData;
// 图表相关
QChart *m_temperatureChart;
QLineSeries *m_temperatureSeries;
QChart *m_humidityChart;
QLineSeries *m_humiditySeries;
QChart *m_lightChart;
QLineSeries *m_lightSeries;
// 数据历史记录
QVector<QPointF> m_tempData;
QVector<QPointF> m_humidityData;
QVector<QPointF> m_lightData;
int m_dataCount;
void setupMQTT();
void setupCharts();
void setupUI();
void publishControlCommand(const QString &command, const QVariant &value);
void updateStatusIndicators();
};
#endif // MAINWINDOW_H
2. 主窗口实现文件 (mainwindow.cpp)
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
#include <QMessageBox>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonParseError>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
m_dataCount(0)
{
ui->setupUi(this);
// 初始化设备数据
memset(&m_deviceData, 0, sizeof(DeviceData));
m_deviceData.autoMode = true;
m_deviceData.tempThresholdHigh = 28.0f;
m_deviceData.tempThresholdLow = 18.0f;
m_deviceData.lightThreshold = 500.0f;
m_deviceData.moistureThreshold = 60.0f;
setupUI();
setupCharts();
setupMQTT();
// 设置定时器
m_dataTimer = new QTimer(this);
connect(m_dataTimer, &QTimer::timeout, this, &MainWindow::updateDataDisplay);
m_dataTimer->start(2000); // 2秒更新一次数据显示
m_chartTimer = new QTimer(this);
connect(m_chartTimer, &QTimer::timeout, this, &MainWindow::updateChart);
m_chartTimer->start(10000); // 10秒更新一次图表
}
MainWindow::~MainWindow()
{
delete ui;
if(m_mqttClient) {
m_mqttClient->disconnectFromHost();
delete m_mqttClient;
}
}
void MainWindow::setupUI()
{
// 设置窗口标题
setWindowTitle("竹荪种植环境监控系统");
// 初始化阈值显示
ui->tempHighThreshold->setValue(m_deviceData.tempThresholdHigh);
ui->tempLowThreshold->setValue(m_deviceData.tempThresholdLow);
ui->lightThreshold->setValue(m_deviceData.lightThreshold);
ui->moistureThreshold->setValue(m_deviceData.moistureThreshold);
// 设置模式显示
ui->modeLabel->setText("自动模式");
ui->modeLabel->setStyleSheet("color: green; font-weight: bold;");
}
void MainWindow::setupMQTT()
{
m_mqttClient = new QMqttClient(this);
// 配置华为云MQTT连接参数(需要替换为实际参数)
m_mqttClient->setHostname("你的华为云MQTT地址");
m_mqttClient->setPort(1883);
m_mqttClient->setClientId("你的客户端ID");
m_mqttClient->setUsername("你的用户名");
m_mqttClient->setPassword("你的密码");
connect(m_mqttClient, &QMqttClient::connected, this, &MainWindow::onMqttConnected);
connect(m_mqttClient, &QMqttClient::disconnected, this, &MainWindow::onMqttDisconnected);
connect(m_mqttClient, &QMqttClient::messageReceived, this, &MainWindow::onMqttMessageReceived);
// 连接MQTT服务器
m_mqttClient->connectToHost();
}
void MainWindow::setupCharts()
{
// 温度图表设置
m_temperatureChart = new QChart();
m_temperatureSeries = new QLineSeries();
m_temperatureSeries->setName("温度(°C)");
m_temperatureChart->addSeries(m_temperatureSeries);
m_temperatureChart->setTitle("温度变化趋势");
m_temperatureChart->createDefaultAxes();
m_temperatureChart->axisX()->setTitleText("时间");
m_temperatureChart->axisY()->setTitleText("温度(°C)");
m_temperatureChart->legend()->setVisible(true);
ui->temperatureChartView->setChart(m_temperatureChart);
ui->temperatureChartView->setRenderHint(QPainter::Antialiasing);
// 湿度图表设置
m_humidityChart = new QChart();
m_humiditySeries = new QLineSeries();
m_humiditySeries->setName("湿度(%)");
m_humidityChart->addSeries(m_humiditySeries);
m_humidityChart->setTitle("湿度变化趋势");
m_humidityChart->createDefaultAxes();
m_humidityChart->axisX()->setTitleText("时间");
m_humidityChart->axisY()->setTitleText("湿度(%)");
m_humidityChart->legend()->setVisible(true);
ui->humidityChartView->setChart(m_humidityChart);
ui->humidityChartView->setRenderHint(QPainter::Antialiasing);
// 光照图表设置
m_lightChart = new QChart();
m_lightSeries = new QLineSeries();
m_lightSeries->setName("光照强度(lux)");
m_lightChart->addSeries(m_lightSeries);
m_lightChart->setTitle("光照强度变化趋势");
m_lightChart->createDefaultAxes();
m_lightChart->axisX()->setTitleText("时间");
m_lightChart->axisY()->setTitleText("光照强度(lux)");
m_lightChart->legend()->setVisible(true);
ui->lightChartView->setChart(m_lightChart);
ui->lightChartView->setRenderHint(QPainter::Antialiasing);
}
void MainWindow::onMqttConnected()
{
ui->statusLabel->setText("已连接到华为云");
ui->statusLabel->setStyleSheet("color: green;");
// 订阅设备数据主题
QString subscribeTopic = "你的设备数据主题";
m_mqttClient->subscribe(subscribeTopic);
}
void MainWindow::onMqttDisconnected()
{
ui->statusLabel->setText("连接断开");
ui->statusLabel->setStyleSheet("color: red;");
}
void MainWindow::onMqttMessageReceived(const QByteArray &message, const QString &topic)
{
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(message, &error);
if (error.error != QJsonParseError::NoError) {
qDebug() << "JSON解析错误:" << error.errorString();
return;
}
QJsonObject json = doc.object();
// 解析传感器数据
if (json.contains("soilMoisture")) {
m_deviceData.soilMoisture = json["soilMoisture"].toDouble();
}
if (json.contains("temperature")) {
m_deviceData.temperature = json["temperature"].toDouble();
}
if (json.contains("humidity")) {
m_deviceData.humidity = json["humidity"].toDouble();
}
if (json.contains("lightIntensity")) {
m_deviceData.lightIntensity = json["lightIntensity"].toDouble();
}
if (json.contains("pumpStatus")) {
m_deviceData.pumpStatus = json["pumpStatus"].toBool();
}
if (json.contains("fanStatus")) {
m_deviceData.fanStatus = json["fanStatus"].toBool();
}
if (json.contains("curtainStatus")) {
m_deviceData.curtainStatus = json["curtainStatus"].toBool();
}
if (json.contains("lightStatus")) {
m_deviceData.lightStatus = json["lightStatus"].toBool();
}
if (json.contains("heaterStatus")) {
m_deviceData.heaterStatus = json["heaterStatus"].toBool();
}
if (json.contains("autoMode")) {
m_deviceData.autoMode = json["autoMode"].toBool();
}
updateDataDisplay();
}
void MainWindow::updateDataDisplay()
{
// 更新传感器数据显示
ui->soilMoistureLabel->setText(QString::number(m_deviceData.soilMoisture, 'f', 1) + " %");
ui->temperatureLabel->setText(QString::number(m_deviceData.temperature, 'f', 1) + " °C");
ui->humidityLabel->setText(QString::number(m_deviceData.humidity, 'f', 1) + " %");
ui->lightIntensityLabel->setText(QString::number(m_deviceData.lightIntensity, 'f', 1) + " lux");
// 更新设备状态显示
updateStatusIndicators();
// 更新模式显示
if (m_deviceData.autoMode) {
ui->modeLabel->setText("自动模式");
ui->modeLabel->setStyleSheet("color: green; font-weight: bold;");
ui->modeSwitch->setText("切换到手动模式");
} else {
ui->modeLabel->setText("手动模式");
ui->modeLabel->setStyleSheet("color: blue; font-weight: bold;");
ui->modeSwitch->setText("切换到自动模式");
}
}
void MainWindow::updateChart()
{
QDateTime currentTime = QDateTime::currentDateTime();
qint64 timestamp = currentTime.toMSecsSinceEpoch();
// 更新温度图表
m_temperatureSeries->append(timestamp, m_deviceData.temperature);
// 限制数据点数,保持图表清晰
if (m_temperatureSeries->count() > 100) {
m_temperatureSeries->remove(0);
}
// 更新湿度图表
m_humiditySeries->append(timestamp, m_deviceData.humidity);
if (m_humiditySeries->count() > 100) {
m_humiditySeries->remove(0);
}
// 更新光照图表
m_lightSeries->append(timestamp, m_deviceData.lightIntensity);
if (m_lightSeries->count() > 100) {
m_lightSeries->remove(0);
}
// 更新图表显示范围
m_temperatureChart->axisX()->setRange(timestamp - 3600000, timestamp); // 显示最近1小时
m_humidityChart->axisX()->setRange(timestamp - 3600000, timestamp);
m_lightChart->axisX()->setRange(timestamp - 3600000, timestamp);
}
void MainWindow::updateStatusIndicators()
{
// 更新水泵状态
if (m_deviceData.pumpStatus) {
ui->pumpIndicator->setStyleSheet("background-color: green; border-radius: 10px;");
ui->pumpControl->setText("关闭水泵");
} else {
ui->pumpIndicator->setStyleSheet("background-color: red; border-radius: 10px;");
ui->pumpControl->setText("开启水泵");
}
// 更新风扇状态
if (m_deviceData.fanStatus) {
ui->fanIndicator->setStyleSheet("background-color: green; border-radius: 10px;");
ui->fanControl->setText("关闭风扇");
} else {
ui->fanIndicator->setStyleSheet("background-color: red; border-radius: 10px;");
ui->fanControl->setText("开启风扇");
}
// 更新卷帘状态
if (m_deviceData.curtainStatus) {
ui->curtainIndicator->setStyleSheet("background-color: green; border-radius: 10px;");
ui->curtainControl->setText("收起卷帘");
} else {
ui->curtainIndicator->setStyleSheet("background-color: red; border-radius: 10px;");
ui->curtainControl->setText("放下卷帘");
}
// 更新补光灯状态
if (m_deviceData.lightStatus) {
ui->lightIndicator->setStyleSheet("background-color: green; border-radius: 10px;");
ui->lightControl->setText("关闭补光灯");
} else {
ui->lightIndicator->setStyleSheet("background-color: red; border-radius: 10px;");
ui->lightControl->setText("开启补光灯");
}
// 更新加热器状态
if (m_deviceData.heaterStatus) {
ui->heaterIndicator->setStyleSheet("background-color: green; border-radius: 10px;");
ui->heaterControl->setText("关闭加热器");
} else {
ui->heaterIndicator->setStyleSheet("background-color: red; border-radius: 10px;");
ui->heaterControl->setText("开启加热器");
}
}
// 控制按钮槽函数实现
void MainWindow::on_pumpControl_clicked()
{
bool newStatus = !m_deviceData.pumpStatus;
publishControlCommand("pump", newStatus);
}
void MainWindow::on_fanControl_clicked()
{
bool newStatus = !m_deviceData.fanStatus;
publishControlCommand("fan", newStatus);
}
void MainWindow::on_curtainControl_clicked()
{
bool newStatus = !m_deviceData.curtainStatus;
publishControlCommand("curtain", newStatus);
}
void MainWindow::on_lightControl_clicked()
{
bool newStatus = !m_deviceData.lightStatus;
publishControlCommand("light", newStatus);
}
void MainWindow::on_heaterControl_clicked()
{
bool newStatus = !m_deviceData.heaterStatus;
publishControlCommand("heater", newStatus);
}
void MainWindow::on_modeSwitch_clicked()
{
bool newMode = !m_deviceData.autoMode;
publishControlCommand("autoMode", newMode);
}
void MainWindow::on_tempHighThreshold_valueChanged(double arg1)
{
m_deviceData.tempThresholdHigh = arg1;
publishControlCommand("tempHighThreshold", arg1);
}
void MainWindow::on_tempLowThreshold_valueChanged(double arg1)
{
m_deviceData.tempThresholdLow = arg1;
publishControlCommand("tempLowThreshold", arg1);
}
void MainWindow::on_lightThreshold_valueChanged(double arg1)
{
m_deviceData.lightThreshold = arg1;
publishControlCommand("lightThreshold", arg1);
}
void MainWindow::on_moistureThreshold_valueChanged(double arg1)
{
m_deviceData.moistureThreshold = arg1;
publishControlCommand("moistureThreshold", arg1);
}
void MainWindow::publishControlCommand(const QString &command, const QVariant &value)
{
if (!m_mqttClient || m_mqttClient->state() != QMqttClient::Connected) {
QMessageBox::warning(this, "警告", "未连接到MQTT服务器");
return;
}
QJsonObject json;
json[command] = QJsonValue::fromVariant(value);
QJsonDocument doc(json);
QString publishTopic = "你的控制命令主题";
m_mqttClient->publish(publishTopic, doc.toJson());
}
-
- 3.
主函数 (main.cpp)
#include "mainwindow.h"
#include <QApplication>
#include <QStyleFactory>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置应用程序样式
a.setStyle(QStyleFactory::create("Fusion"));
// 设置应用程序信息
a.setApplicationName("竹荪种植环境监控系统");
a.setApplicationVersion("1.0");
a.setOrganizationName("YourCompany");
MainWindow w;
w.show();
return a.exec();
}
4. 项目文件 (BambooMushroomMonitor.pro)
QT += core gui network mqtt charts
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# 支持Android和Windows平台
android {
QT += androidextras
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
}
win32 {
# Windows特定配置
}
# 设置目标文件名
TARGET = BambooMushroomMonitor
TEMPLATE = app
# 源代码文件
SOURCES +=
main.cpp
mainwindow.cpp
HEADERS +=
mainwindow.h
FORMS +=
mainwindow.ui
# 安装规则
unix {
target.path = /usr/local/bin
INSTALLS += target
}
# 发布设置
CONFIG(release, debug|release) {
DESTDIR = release
} else {
DESTDIR = debug
}
# 资源文件
RESOURCES +=
resources.qrc
5. UI界面设计 (mainwindow.ui 简化版)
由于UI文件较长,这里给出关键布局结构:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>800</height>
</rect>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="monitorTab">
<!-- 传感器数据显示区域 -->
<widget class="QLabel" name="soilMoistureLabel"/>
<widget class="QLabel" name="temperatureLabel"/>
<widget class="QLabel" name="humidityLabel"/>
<widget class="QLabel" name="lightIntensityLabel"/>
<!-- 设备状态指示器 -->
<widget class="QFrame" name="pumpIndicator"/>
<widget class="QFrame" name="fanIndicator"/>
<widget class="QFrame" name="curtainIndicator"/>
<widget class="QFrame" name="lightIndicator"/>
<widget class="QFrame" name="heaterIndicator"/>
<!-- 控制按钮 -->
<widget class="QPushButton" name="pumpControl"/>
<widget class="QPushButton" name="fanControl"/>
<widget class="QPushButton" name="curtainControl"/>
<widget class="QPushButton" name="lightControl"/>
<widget class="QPushButton" name="heaterControl"/>
<widget class="QPushButton" name="modeSwitch"/>
</widget>
<widget class="QWidget" name="chartTab">
<!-- 图表显示区域 -->
<widget class="QChartView" name="temperatureChartView"/>
<widget class="QChartView" name="humidityChartView"/>
<widget class="QChartView" name="lightChartView"/>
</widget>
<widget class="QWidget" name="settingsTab">
<!-- 阈值设置区域 -->
<widget class="QDoubleSpinBox" name="tempHighThreshold"/>
<widget class="QDoubleSpinBox" name="tempLowThreshold"/>
<widget class="QDoubleSpinBox" name="lightThreshold"/>
<widget class="QDoubleSpinBox" name="moistureThreshold"/>
</widget>
</widget>
</item>
</layout>
</widget>
<!-- 状态栏 -->
<widget class="QStatusBar" name="statusBar">
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>
6. Android Manifest配置 (AndroidManifest.xml)
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourcompany.bamboomushroommonitor"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application android:label="竹荪监控"
android:icon="@drawable/icon">
<activity android:name="org.qtproject.qt5.android.bindings.QtActivity"
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
android:label="竹荪监控"
android:launchMode="singleTop"
android:screenOrientation="unspecified">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
使用说明
1. 配置华为云连接参数: 修改setupMQTT()函数中的MQTT连接参数 设置正确的主题名称
2. 编译运行:
qmake BambooMushroomMonitor.pro
make
./BambooMushroomMonitor
3. Android版本编译:
android-configure
make apk
4. 主要功能: 实时数据显示和图表展示 手动/自动模式切换 远程设备控制 阈值设置 数据历史记录
1066