基于物联网设计的大棚栽培监控系统

一、前言

1.1 项目介绍

【1】项目开发背景

随着现代农业技术的快速发展,传统的大棚种植模式面临着劳动力成本高、环境控制精度差、管理效率低下等诸多挑战。在传统的大棚管理方式中,农民需要人工定时巡查温湿度、光照、土壤水分等关键环境参数,并手动操作通风、灌溉、补光等设备,这种方式不仅耗费大量人力,而且往往难以及时发现环境异常变化,导致作物生长受影响,甚至造成减产或品质下降。此外,传统大棚缺乏对鸟类等动物的有效驱赶手段,小动物侵入大棚啃食作物或传播病害的问题长期困扰着种植户。因此,设计一套能够自动感知、智能决策、远程控制的智能化大棚栽培监控系统,成为提升农业生产效率和作物品质的迫切需求。

物联网技术的蓬勃发展为智慧农业提供了坚实的技术支撑。通过将传感器、无线通信、云计算和移动互联网等前沿技术应用于农业生产环境,可以实现对大棚内环境参数的实时采集、智能分析与自动调控。温湿度传感器、光照传感器、土壤湿度传感器等设备能够精准感知作物生长环境的细微变化,主控芯片根据预设的阈值规则自动控制风扇、水泵、补光灯等执行机构,使环境始终维持在适宜作物生长的范围内。同时,借助ESP8266模块将数据上传至云端物联网平台,种植户可以通过手机微信小程序随时随地查看大棚环境数据,远程调节控制参数,真正实现"掌上管理、指尖调控"。这种技术融合不仅大幅降低了人工巡检的劳动强度,更实现了精准化、自动化的栽培管理。

本系统特别设计了自动模式与手动模式两种工作方式,既满足了无人值守时的全自动运行需求,也为有经验的种植户提供了人工干预的灵活性。在此基础上,系统还引入了植物种类参数自动匹配功能,用户只需在手机端选择所种植的作物类型,系统便会自动加载该作物最佳生长环境对应的温湿度、光照强度、土壤湿度等阈值参数,无需用户自行查阅资料或反复调试,大大降低了智能设备的使用门槛,使得不同种植经验的用户都能轻松实现科学栽培。此外,针对大棚常见的鸟类及动物侵扰问题,系统集成了语音播报模块,可定时播放驱赶声音,形成物理驱赶与智能控制相结合的综合防护方案。

本项目选用的STM32F103C8T6主控芯片具有处理能力强、功耗低、外设丰富等优点,能够稳定可靠地完成多传感器数据采集和执行机构的驱动控制。SHT30温湿度传感器、BH1750光照传感器以及土壤湿度传感器的组合,覆盖了大棚环境监测的核心指标。ESP8266模块负责与OneNet物联网平台建立MQTT连接,实现数据上云和远程指令下发。微信小程序作为上位机,调用OneNet平台提供的API接口获取设备数据并展示,同时支持用户下发控制指令。整个系统从硬件选型到软件设计均充分考虑了农业应用场景的实际需求,具备成本可控、扩展性强、易于部署等特点,为中小型种植户提供了一套切实可行的智能化大棚解决方案。

开发需要使用的功能,模块代码,已经上网到网盘:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink

【2】设计实现的功能

(1)支持空气温度和空气湿度检测。当温度超过阈值,打开风扇通风降温,反之关闭。

(2)支持环境光强检测。当光照强度低于阈值,打开灯光补光,反之关闭。

(3)支持土壤湿度检测。当含水量低于阈值,打开水泵灌溉浇水;反之则关闭水泵。

(4)支持鸟儿或其他动物驱赶。设置固定时间,每隔固定时间段打开喇叭驱赶可能进入大棚的鸟或其他动物。

(5)支持数据上云。本地采集的所有数据,利用ESP8266-WIFI模块上传到OneNet物联网平台。设计微信小程序,调用OneNet物联网平台开放的API接口,获取设备上传的数据,进行实时展示。

(6)支持远程控制。微信小程序上可以手动调节各项参数的阈值,手动控制风扇、灯光、水泵开关。

(7)支持自动模式与手动模式切换。自动模式下,可以根据阈值自动调节;手动模式下可以手动控制风扇、灯光、水泵开关。

(8)支持参数自动匹配。手机APP上可以选择不同植物种类,自动调整预设的参数,自动配置相应的栽培参数。

【3】项目硬件模块组成

(1)主控芯片:STM32F103C8T6

(2)环境温湿度检测模块:SHT30

(3)显示模块:0.96寸IIC协议OLED显示屏

(4)土壤湿度检测模块:ADC模拟量接口的土壤湿度检测传感器(电压反馈感应水分含量)

(5)联网模块:ESP8266模块

(6)灌溉电机模块:5V直流抽水小电机,采用MOS管充当电子开关进行驱动控制

(7)系统控制板电源模块:Type-c接口供电

(8)驱鸟喇叭模块:CH9300-FL(16M内存)语音播报模块,支持电脑拷贝语音文件存储,单片机采用IO口高低电平方式触发模块内部语音播报

(9)环境光强检测模块:BH1750传感器

(10)补光灯模块:USB接口的LED灯,采用MOS管充当电子开关进行驱动控制

(11)通风风扇模块:5V小风扇,采用MOS管充当电子开关进行驱动控制

【4】设计意义

本系统通过物联网技术实现了大棚内空气温湿度、光照强度、土壤湿度等关键环境参数的自动检测与智能调控,能够显著降低传统人工巡检和手动操作设备所带来的劳动力成本。种植户无需频繁进入大棚查看环境状况,系统会根据预设阈值自动控制风扇、水泵、补光灯等执行机构,使管理更加高效便捷。同时,系统具备数据上云功能,用户可通过微信小程序远程查看实时数据,即使不在现场也能掌握大棚运行状态,有效解决了农业生产中"人不能时刻在场"的管理难题。

本系统通过精确的传感器检测和自动化的设备控制,能够将大棚内的温湿度、光照、土壤水分等环境参数维持在适宜作物生长的范围内,避免因人为疏忽或反应不及时导致的温度过高、干旱缺水或光照不足等问题。自动模式下的阈值控制机制保证了环境调节的及时性和准确性,而参数自动匹配功能允许用户根据不同植物种类一键加载最佳栽培参数,使缺乏专业种植经验的用户也能按照科学标准进行栽培管理,从而提升作物的生长质量和产量。此外,定时驱赶动物的功能减少了大棚内作物被鸟类或其他动物啃食的损失,进一步保障了种植收益。

本系统将传感器技术、嵌入式控制技术、无线通信技术、云平台技术和移动互联网技术融合于一体,构建了一个完整的物联网智能农业应用案例。选用STM32F103C8T6主控芯片搭配SHT30、BH1750等传感器,通过ESP8266模块连接OneNet云平台,再以微信小程序作为用户交互界面,形成了从数据采集、本地控制、云端传输到远程监控的完整闭环。该设计方案具有成本可控、模块化程度高、可扩展性强等特点,为农业物联网领域的教学演示、科研实验以及实际农业项目开发提供了可借鉴的技术路径和实现方案。

【5】市面上同类产品研究现状

在当前智慧农业市场中,面向大棚种植的监控类产品种类繁多,功能定位和技术路径各有侧重。以国内较为知名的企业产品为例,托普云农推出的农业物联网监控系统,集成了空气温湿度、光照强度、土壤水分、CO₂浓度等多种传感器,通过GPRS或4G方式将数据上传至其自研云平台,用户可通过电脑端或手机App查看实时数据和历史曲线,并支持远程控制卷帘、风机、滴灌等设备。该系统功能全面,但设备成本和后期流量费用相对较高,主要面向大型农业园区和政府示范项目。类似地,京东植物工厂采用的智能环控系统,实现了从播种到采收全流程的自动化管理,环境调控精度高,但其投入成本较大,普通农户难以承受。相比之下,市面上也存在着面向家庭阳台或小型大棚的简易产品,例如小米生态链下的花花草草监测仪,可检测土壤湿度、光照强度和温度,通过蓝牙连接手机App进行数据查看,但其仅具备监测功能,缺乏对风扇、水泵、补光灯等执行机构的自动控制能力,无法实现闭环调节。

在智能灌溉领域,市场上出现了多款基于土壤湿度检测的自动浇水产品。例如,国内外电商平台上常见的电池供电式自动浇水器,内置土壤湿度传感器和小型水泵,当检测到土壤偏干时自动启动水泵浇水。这类产品价格低廉、安装简单,但大多数不具备联网功能,用户无法远程查看土壤湿度状态,也无法调整浇水阈值,功能较为单一。另一类产品如"花帮主"智能花盆,集成了温湿度传感器和储水模块,通过Wi-Fi连接云端,用户可在手机上查看植物状态并远程控制浇水,但其适用的场景多为单盆植物,难以扩展到大棚多点的环境监控需求。此外,部分中高端产品如绿植宝、植物管家等,虽然支持多个环境参数检测和手机端控制,但价格相对较高,且不同品牌之间的云端平台不互通,用户更换设备时数据无法迁移。

在动物驱赶方面,市面上的产品主要分为超声波驱鸟器和语音驱鸟器两大类。超声波驱鸟器通过发射高频声波刺激鸟类的听觉系统,使其感到不适而离开,代表产品有Solar Bird Repeller等,这类产品多采用太阳能供电和感应触发方式,适用于果园、农田等开阔场景,但其驱鸟效果受环境影响较大,且对某些适应性强的鸟类效果逐渐减弱。语音驱鸟器则通过预录的鞭炮声、鹰叫声、鸟类报警声等音频进行驱赶,如市面上常见的"智能语音驱鸟器",支持多段音频轮播和定时设置,有的产品还支持用户自行拷贝音频文件。然而,这些驱鸟设备通常作为独立产品存在,很少与大棚环境监控系统集成,导致用户需要分别管理环境调控设备和驱鸟设备,增加了使用复杂度和设备成本。

在远程监控与云平台结合方面,市场上已有不少产品采用"传感器+Wi-Fi模块+云平台+手机端"的成熟架构。例如,国内外一些开源硬件厂商推出的智能温室套件(如DFRobot的智能温室套件、Seeed Studio的Grove智能农场套件),基于Arduino或ESP8266开发,支持多种传感器接入,数据可上传至Thingspeak、Blynk等国外云平台或阿里云、OneNet等国内平台,用户通过手机App或小程序即可查看和控制。这类套件灵活性高、扩展性强,主要用于教学、创客和DIY爱好者群体,但作为完整产品销售时,其用户体验和系统稳定性与商业化成熟产品相比尚有差距。值得注意的是,中国移动OneNet平台近年来在教育领域和中小型企业中应用广泛,其免费接入和设备管理服务降低了物联网产品的开发门槛,不少基于OneNet的智能大棚项目在高校课程设计、毕业设计以及初创公司原型产品中得到了实现,但这些成果大多停留在样机或小规模试用阶段,尚未形成批量化、标准化的市售产品。

综合来看,市面上现有的大棚监控类产品存在两极分化现象:高端产品功能全面但成本较高,普通农户难以接受;低端产品价格低廉但功能单一,缺乏联网能力和远程控制功能。同时,将环境自动调控、远程控制、植物参数自动匹配以及动物驱赶功能集于一体的综合性产品较为少见。因此,设计一套成本可控、功能完备、操作简便且具备完整物联网能力的大棚栽培监控系统,既符合当前市场需求,也具有一定的技术创新价值。

【6】摘要

随着农业现代化进程的不断推进,传统大棚种植模式在环境调控及时性、精准性和人力成本等方面暴露出的问题日益突出。针对这一现状,本文设计并实现了一套基于物联网技术的大棚栽培监控系统。系统以STM32F103C8T6为主控芯片,集成SHT30温湿度传感器、BH1750光照传感器和土壤湿度传感器,实时采集大棚环境数据,并根据用户设定的阈值自动控制5V风扇、USB补光灯和水泵电机等执行机构,实现降温、补光和灌溉的智能调节。同时,系统搭载CH9300-FL语音播报模块,支持定时触发播放驱鸟声音,减少动物入侵对作物的损害。所有采集的环境数据通过ESP8266模块以MQTT协议上传至中国移动OneNet物联网云平台。上位机采用微信小程序开发,通过调用OneNet平台API接口实现数据的实时展示,并提供手动模式与自动模式切换、阈值远程调节、设备手动控制以及不同植物种类参数一键匹配等功能,满足不同种植场景和管理需求。该系统实现了本地自动控制与云端远程监控的有机结合,具有成本低、易部署、操作简便等特点,为中小型大棚种植提供了一套可行的智能化解决方案。

关键字

物联网;智慧农业;大棚监控;STM32;ESP8266;OneNet平台;微信小程序;传感器;自动控制

1.2 设计思路

本系统的整体设计思路围绕"感知-控制-传输-交互"四个层面展开,构建一个完整的物联网闭环系统。在感知层,系统通过SHT30温湿度传感器检测空气温湿度,通过BH1750光照传感器检测环境光强,通过ADC接口的土壤湿度传感器检测土壤含水量,三种传感器协同工作,覆盖了大棚栽培所需监测的核心环境参数。在控制层,STM32F103C8T6主控芯片作为核心处理单元,实时读取各传感器数据,并根据当前工作模式(自动模式或手动模式)以及用户设定的阈值,决策是否驱动风扇、补光灯、水泵等执行机构。执行机构的驱动采用MOS管作为电子开关,实现对5V直流小风扇、USB接口LED灯和5V直流抽水电机的通断控制,同时利用IO口高低电平触发CH9300-FL语音播报模块,在预设的固定时间段内播放驱鸟声音。在传输层,ESP8266模块通过Wi-Fi连接互联网,采用MQTT协议将本地采集的所有环境数据实时上传至中国移动OneNet物联网云平台,同时也通过订阅主题接收来自云平台下发的远程控制指令。在交互层,微信小程序作为上位机用户界面,调用OneNet平台开放的API接口获取设备上传的数据进行实时展示,用户可以通过小程序查看当前温湿度、光照强度、土壤湿度等参数,同时可以手动调节各项阈值、手动控制风扇灯光水泵的开关,并实现自动模式与手动模式之间的切换。

在系统工作流程的设计上,本系统提供自动模式和手动模式两种运行状态,用户可通过微信小程序自由切换。在自动模式下,系统独立运行本地闭环控制逻辑:STM32主控芯片不断读取各传感器数值,将实时值与预设阈值进行比较,当空气温度超过阈值时自动打开风扇,低于阈值时关闭;当光照强度低于阈值时自动打开补光灯,高于阈值时关闭;当土壤湿度低于阈值时自动打开水泵进行灌溉,达到阈值后关闭水泵。这一设计确保了无人值守时大棚环境的自动稳定调节。在手动模式下,系统不再根据阈值自动执行动作,而是等待用户通过微信小程序下达的具体指令,用户可分别控制风扇、补光灯和水泵的开启或关闭,满足特殊情况下的人工干预需求。此外,驱鸟功能独立于两种模式之外,按照用户设定的固定时间点和时间间隔,由主控芯片定时触发语音播报模块播放驱鸟声音。

在参数管理与用户交互方面,系统设计了参数自动匹配功能。微信小程序端预置了多种常见植物的最佳生长环境参数表,包括番茄、黄瓜、草莓等不同作物对应的适宜温湿度范围、光照强度阈值和土壤湿度阈值。用户在选择某种植物后,小程序会将该植物的预设参数通过云平台下发给STM32主控芯片,主控芯片更新本地的阈值配置,实现栽培参数的一键自动匹配,降低了用户设置参数的门槛和操作复杂度。同时,用户也可以通过小程序手动调整各项阈值,灵活适应不同生长阶段或不同品种的栽培需求。所有从云平台下发的参数变更和手动控制指令,均通过MQTT协议经ESP8266模块传输至主控芯片,主控芯片在接收到指令后更新本地状态并执行相应操作,实现了远程控制的实时性。

在硬件与软件的协同配合上,系统充分考虑了各模块的特性和接口匹配。STM32F103C8T6作为主控,其丰富的GPIO和I2C、ADC等外设资源能够很好地满足多传感器接入需求。SHT30和BH1750均采用I2C接口,与OLED显示屏共用同一I2C总线,节省了引脚资源。土壤湿度传感器输出模拟电压信号,连接至STM32的ADC引脚,通过电压值换算得到土壤水分含量。ESP8266模块通过串口与STM32通信,采用AT指令集或MQTT透传固件实现联网和数据传输。语音播报模块采用IO口电平触发方式,STM32只需输出高电平或低电平即可触发对应音轨的播放,控制方式简单可靠。电源部分采用Type-c接口为整个系统板供电,提供5V电压,再通过板载稳压电路为STM32和各传感器提供3.3V工作电压,保证系统稳定运行。通过上述硬件选型与软件设计的紧密配合,系统实现了从本地采集、自动控制、云端传输到远程交互的完整功能链条。

1.3 系统功能总结

功能模块 功能描述
空气温湿度检测与控制 通过SHT30传感器实时检测空气温度和湿度。当温度超过预设阈值时,自动打开风扇通风降温;当温度低于或等于阈值时,关闭风扇。
环境光强检测与补光控制 通过BH1750传感器实时检测环境光照强度。当光照强度低于预设阈值时,自动打开补光灯;当光照强度达到或超过阈值时,关闭补光灯。
土壤湿度检测与灌溉控制 通过ADC模拟量接口的土壤湿度传感器实时检测土壤含水量。当含水量低于预设阈值时,自动打开水泵进行灌溉浇水;当含水量达到或超过阈值时,关闭水泵。
动物驱赶功能 支持设置固定时间点,每隔固定时间段自动触发CH9300-FL语音播报模块,通过喇叭播放驱赶声音,防止鸟类或其他动物进入大棚。
数据上云 所有本地采集的温湿度、光照强度、土壤湿度等数据,通过ESP8266-WIFI模块利用MQTT协议上传到中国移动OneNet物联网平台。
微信小程序实时展示 设计微信小程序,调用OneNet物联网平台开放的API接口,获取设备上传的数据,进行环境参数的实时展示。
远程参数阈值调节 用户可在微信小程序上手动调节温度阈值、光照强度阈值、土壤湿度阈值等各项参数。
远程设备手动控制 用户可在微信小程序上手动控制风扇、补光灯、水泵开关的开启或关闭。
自动模式与手动模式切换 支持两种工作模式切换。自动模式下,系统根据预设阈值自动调节执行机构;手动模式下,用户通过小程序手动控制风扇、灯光、水泵。
参数自动匹配 微信小程序上支持选择不同植物种类,系统自动调整并配置相应的预设栽培参数(温湿度阈值、光照阈值、土壤湿度阈值等)。
OLED本地显示 通过0.96寸IIC协议OLED显示屏,本地实时显示空气温湿度、光照强度、土壤湿度等环境数据以及设备工作状态。

1.4 开发工具的选择

**项目开发使用的全部软件工具已经上传到网盘:**https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink

【1】设备端开发

硬件设备端的开发主要采用C语言,利用该语言直接操作硬件寄存器,确保系统运行的高效性和低延迟。C语言在嵌入式开发中具有广泛的应用,能够直接访问硬件,满足对资源消耗和响应速度的严格要求。为了编写高效、稳定的代码,开发工具选择Keil uVision 5作为主要的集成开发环境。Keil是一款专业的嵌入式开发工具,广泛应用于基于ARM架构的微控制器开发,提供了完善的编译、调试和仿真支持,能够帮助开发者高效地进行代码调试、单步执行以及断点设置,确保开发过程的稳定性和高效性。

本项目使用的主控芯片为STM32F103C8T6,它基于ARM Cortex-M3架构,具有强大的计算能力和丰富的外设接口。在硬件编程中,开发者需要对芯片的硬件寄存器有深入的理解,通过寄存器级编程直接控制GPIO、I2C、ADC、串口等硬件接口,以实现各传感器模块(SHT30、BH1750、土壤湿度传感器)、执行机构(风扇、补光灯、水泵)、显示模块(OLED显示屏)及通信模块(ESP8266)与主控芯片的正常交互。寄存器编程方式能够提供更高效、精确的控制,避免了库函数带来的额外开销,同时也能深入调控硬件特性,提升系统整体性能。

【2】上位机开发

本项目的上位机采用微信小程序进行开发,使用微信官方提供的微信小程序开发工具作为集成开发环境。微信小程序是一种不需要下载安装即可使用的轻量级应用,运行于微信生态系统内,具有开发周期短、跨平台兼容性好、用户使用门槛低等优点。微信小程序开发工具集成了代码编辑、实时预览、调试、真机测试、上传发布等完整功能链,支持WXML、WXSS、JavaScript等语言,并提供了丰富的组件和API接口,能够高效地实现用户界面设计与业务逻辑开发。

在上位机与云平台的通信方面,微信小程序通过调用中国移动OneNet物联网平台开放的API接口,实现设备数据的获取和远程控制指令的下发。OneNet平台提供了标准化的RESTful API接口,小程序端通过HTTPS协议发送HTTP请求,即可获取ESP8266模块上传至云端的温湿度、光照强度、土壤湿度等实时数据,并进行可视化展示。同时,用户在小程序上进行的阈值调节、手动设备控制、模式切换等操作,也通过调用OneNet平台的API接口或MQTT协议下发至设备端,实现对大棚环境调控设备的远程控制。

在植物参数自动匹配功能的实现上,微信小程序端预置了多种常见植物的最佳生长环境参数数据库。用户选择特定植物种类后,小程序自动调取对应的温湿度、光照强度、土壤湿度等阈值参数,并通过云平台下发至STM32主控芯片,完成栽培参数的一键自动配置。此外,小程序还支持自动模式与手动模式的切换界面设计,用户可直观地查看当前工作模式,并自由切换。

总体而言,上位机采用微信小程序开发工具,结合中国移动OneNet云平台的API接口,实现了数据实时展示、远程参数调节、远程设备控制、模式切换以及植物参数自动匹配等功能。微信小程序开发工具提供了便捷高效的开发环境,而OneNet平台则为数据传输和设备管理提供了稳定可靠的后端支撑,二者共同构成了本系统完整的用户交互与云服务链路。

1.5 模块的技术详情介绍

(1)主控芯片 STM32F103C8T6

STM32F103C8T6是意法半导体公司推出的一款基于ARM Cortex-M3内核的32位微控制器,属于STM32F1系列中的主流产品。该芯片主频最高可达72MHz,内置64KB的Flash存储器和20KB的SRAM,具有丰富的片上外设资源,包括多个通用定时器、ADC模数转换器、I2C总线接口、SPI接口、USART串口等。在本系统中,STM32F103C8T6作为核心控制单元,负责读取SHT30温湿度传感器、BH1750光照传感器和土壤湿度传感器的数据,控制OLED显示屏的信息刷新,驱动ESP8266模块进行网络通信,并根据工作模式和阈值判断结果,通过MOS管电子开关控制风扇、补光灯和水泵的执行动作,同时定时触发CH9300-FL语音播报模块实现驱鸟功能。

(2)环境温湿度检测模块 SHT30

SHT30是瑞士Sensirion公司推出的一款数字式温湿度传感器,属于SHT3x系列产品。该传感器采用I2C通信接口,具有体积小、功耗低、精度高等特点。温度测量范围为-40℃至125℃,精度可达±0.3℃;湿度测量范围为0至100%RH,精度可达±3%RH。SHT30内部集成了信号放大、模数转换和校准电路,可直接输出经过校正的数字信号,无需外部电路处理。在本系统中,SHT30通过I2C总线与STM32主控芯片连接,实时采集大棚内的空气温度和湿度数据,为温湿度自动控制提供决策依据。

(3)OLED显示屏 0.96寸 IIC协议

OLED显示屏是一种采用有机发光二极管技术的自发光显示模块,具有视角宽、对比度高、功耗低等优点。本系统选用的是0.96寸OLED显示屏,分辨率为128×64像素,采用IIC接口协议与主控芯片通信,仅需占用两个GPIO引脚即可完成显示控制。该模块无需背光源,每个像素点独立发光,在户外或强光环境下依然具有良好的可视性。在本系统中,OLED显示屏用于本地实时显示空气温湿度、光照强度、土壤湿度等环境参数以及风扇、灯光、水泵等工作状态,方便现场巡检人员查看。

(4)土壤湿度检测模块 ADC模拟量接口

本系统采用的土壤湿度检测模块是一种基于电压反馈原理的传感器,由不锈钢探针和信号转换电路组成。当探针插入土壤中时,土壤含水量的不同会导致探针之间的电阻值发生变化,从而改变模块输出的模拟电压值。该模块输出0至5V的模拟电压信号,含水量越高输出电压越低,反之输出电压越高。模块采用ADC模拟量接口,可直接连接至STM32的ADC输入引脚,通过电压值换算得到土壤水分含量百分比。在本系统中,该传感器用于实时监测大棚内土壤的含水量,为水泵灌溉控制提供判断依据。

(5)联网模块 ESP8266

ESP8266是一款由乐鑫公司推出的低功耗、高集成度的Wi-Fi模块,内置Tensilica L106 32位处理器,支持802.11 b/g/n协议,可作为独立的MCU运行,也可作为串口转Wi-Fi模块与外部主控配合使用。该模块支持TCP/IP协议栈,可通过AT指令集或MQTT透传固件实现网络连接和数据传输。在本系统中,ESP8266通过串口与STM32主控芯片通信,负责将本地采集的所有环境数据通过MQTT协议上传至中国移动OneNet物联网平台,同时订阅云平台下发的控制主题,接收远程指令并转发给STM32执行。

(6)灌溉电机 5V直流抽水小电机

灌溉执行机构采用5V直流抽水小电机,该电机体积小、功耗低、驱动力适合小规模灌溉场景。电机工作时可将水箱中的水通过水管输送到大棚土壤中,实现自动浇水功能。由于直流电机工作电流较大,不能直接用STM32的GPIO引脚驱动,因此采用MOS管作为电子开关进行控制。STM32通过输出高电平或低电平信号控制MOS管的导通与关断,从而实现对电机启停的精确控制。在本系统中,当土壤湿度传感器检测到含水量低于预设阈值时,系统自动打开水泵进行灌溉;当含水量达到阈值时关闭水泵。

(7)系统控制板电源 Type-c接口

本系统控制板的供电采用Type-C接口。Type-C接口是一种通用性强的USB接口规范,具有正反插均可、支持多种电压电流规格、结构坚固等优点。系统通过Type-C接口接入5V直流电源,为整个控制板供电。输入的5V电压一方面直接供给5V直流风扇、5V直流抽水小电机、USB接口LED灯等外设使用,另一方面通过板载稳压电路转换为3.3V电压,为STM32主控芯片、SHT30传感器、BH1750传感器、OLED显示屏和ESP8266模块提供工作电源。

(8)驱鸟喇叭模块 CH9300-FL

CH9300-FL是一款支持语音文件存储和播放的语音播报模块,内置16M内存空间,用户可通过电脑USB接口将驱鸟所需的音频文件(如鞭炮声、鹰叫声、鸟类警报声等)拷贝存储到模块内部。该模块采用IO口高低电平触发方式,每个音频文件对应一个触发引脚,STM32主控芯片通过输出高电平即可触发对应音轨的语音播报。在本系统中,用户可设置固定时间点,每隔固定时间段由STM32触发CH9300-FL模块播放预设的驱鸟声音,实现对可能进入大棚的鸟类或其他动物的驱赶功能。

(9)环境光强检测模块 BH1750

BH1750是一种数字式环境光强度传感器,采用I2C总线接口,内置16位模数转换器,可直接输出光照强度的数字测量值,测量范围为1至65535勒克斯,分辨率可达1勒克斯。该传感器对不同光谱的响应特性接近人眼的视觉敏感度,测量结果准确可靠。在本系统中,BH1750通过I2C总线与STM32主控芯片连接,实时检测大棚内的环境光照强度,为补光灯的自动控制提供判断依据。

(10)补光灯 USB接口LED灯

补光执行机构采用USB接口的LED灯,该灯具有功耗低、亮度高、安装方便等优点。USB接口为标准5V供电,可通过MOS管电子开关直接控制其通断电。在本系统中,当BH1750光照传感器检测到环境光强低于预设阈值时,系统自动导通MOS管,点亮LED灯进行补光;当光照强度达到或超过阈值时,系统关断MOS管,熄灭LED灯。

(11)通风风扇 5V小风扇

通风执行机构采用5V直流小风扇,用于大棚内的强制通风降温。该风扇工作电压为5V,启动电流较小,同样采用MOS管作为电子开关进行驱动控制。STM32主控芯片通过GPIO引脚输出PWM信号或高低电平信号控制MOS管的导通与关断,从而控制风扇的启停。在本系统中,当SHT30传感器检测到空气温度超过预设阈值时,系统自动打开风扇进行通风降温;当温度降至阈值以下时关闭风扇。

二、微信小程序开发环境搭建

2.1 下载开发工具

链接地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/log.html#stable-2.01.2510290

2.2 安装开发工具

双击安装。

点击下一步。

安装完毕了找到开发工具的位置。

如果桌面没有自动创建快捷方式。可以自己创建一个。

2.3 打开使用开发工具

进去之后,可以微信扫码登录。

关闭默认打开的工程。就会看到下面的页面。

选择 + 号。

2.4 新建工程

APPID可以注册一个,也可先使用测试号。 然后,选择小程序,模版选择基础,选择-JS-基础模版。

选择创建。

工程打开之后就可以点击预览测试。

微信扫码就可以看默认的效果了。

2.5 界面开发

三、OneNet平台开发

3.1 OneNet平台介绍

OneNET-中国移动物联网开放平台是由中国移动打造的PaaS物联网开放平台。平台能够帮助开发者轻松实现设备接入与设备连接,提供综合性的物联网解决方案,实现物联网设备的数据获取,数据存储,数据展现。

OneNET资源模型如下图:

  • 产品(product)
    用户的最大资源集为产品,产品下资源包括设备、设备数据、设备权限、数据触发服务以及基于设备数据的应用等多种资源,用户可以创建多个产品。
  • 设备(device)
    设备为真实终端在平台的映射,真实终端连接平台时,需要与平台设备建立一一对应关系,终端上传的数据被存储在数据流中,设备可以拥有一个或者多个数据流。
  • 数据流与数据点
    数据流用于存储设备的某一类属性数据,例如温度,湿度,坐标等信息;平台要求设备上传并存储数据时,必须以key-value的格式上传数据,其中key即为数据流名称,value为实际存储的数据点,value格式可以,int、float、string、json等多种自定义格式。
  • APIkey
    APIkey为用户进行API调用时的密钥,用户访问产品资源时,必须使用该产品目录下对应的APIkey。
  • 触发器(trigger)
    触发器为产品目录下的消息服务,可以进行基于数据流的简单逻辑判断并触发HTTP请求或者邮件。
  • 应用(application)
    应用编辑服务,支持用户以拖拽控件并关联设备数据流的方式,生成简易网页展示应用。

OneNET常用的术语解释如下:

术语 解释 别名&曾用名
产品 OneNET平台资源(包括设备,APIKey,触发器,应用等)的集合,一个产品对应唯一的masterkey、产品ID,设备注册码,一个产品下包含多个具备同一特征的设备,多个设备之间的唯一性由SN来区分 项目
产品ID pid,鉴权信息组之一,创建产品时由平台分配的唯一产品识别码,用于标识唯一个产品,作为设备登录鉴权参数之一 项目ID
APIKey 用于API调用时的鉴权参数 Master-APIkey:产品下唯一的管理员权限的APIKey,具有管理产品下所有设备的权限,在产品页面获取 Device-APIkey:设备级APIkey,具备与之关联的所有设备的访问权限,在设备详情获取
accessKey 安全性更高的访问密钥,用于访问平台时的隐性鉴权参数(非直接传输),通过参与计算并传输token的方式进行访问鉴权
token 安全性更高的鉴权参数,由多个参数组成,在通道中直接传输
注册码 产品下唯一,可用于真实设备调用注册设备时,作为API的鉴权参数之一
设备 归属于某一个产品下,是真实设备在平台的映射,用于和真实设备通过连接报文建立连接关系,平台资源分配的最小单位
鉴权信息组 由设备ID,产品ID,设备SN组成的平台内设备唯一的参数组合,真实设备进行设备连接时需要携带有这些参数进行鉴权(参数要求根据设备接入协议不同有一定差异)
设备ID 鉴权信息组之一,由平台分配的,在平台范围内设备唯一的识别号
SN 鉴权信息组之一,由硬件厂家自定义的设备唯一出厂序列号,创建/注册设备时作为设备参数,在产品内唯一,作为设备连接时的鉴权参数之一 auth_info 设备编号
数据流 设备属性,可为设备单项数据属性,例如温度=10;也可为设备数据属性的组合,例如坐标=x:10 y:20
数据流模板 产品下所有设备均具备的采集数据属性,例如空气质量检测仪均可以上报"PH2.5""甲醛浓度"等数据
数据点 设备每次上传到数据流中的数据
脚本 平台支持用户自定义数据解析规则,解析二进制/字符串格式的数据(仅适用于TCP+脚本接入方式)

3.2 创建产品

(1)登录账户

官网:https://open.iot.10086.cn/

进来先登录账号。

(2)选择物联网开放平台

(3)添加产品

根据自己产品信息填写:

创建之后点击确定。

创建完成。

(4)产品ID

cpp 复制代码
产品ID: q0o902pdYT

3.3 创建设备

产品是属性抽象模型,产品下面的设备就表示具体的硬件设备,需要与具体的硬件关联。产品下可以创建很多的设。

(1)添加设备

(2)填写设备信息

创建完成。

创建完成。

(3)查看设备详情

添加完成之后,点击查看详情,查看设备的详细信息。

这里的产品ID、名称非常有用。后续MQTT登录参数需要使用。这里记录一下。

cpp 复制代码
产品ID:  q0o902pdYT
设备名称:dev1

3.4 添加数据流模板

(1)添加数据流模板

添加数据流模板。

(2)根据设备需求添加

复制代码
添加一个数据名称即可: 

data

开始创建:

(3)添加完毕

3.5 MQTT协议接入地址

当前智能鱼缸设备是采用MQTT协议与OneNet服务器进行通信。

MQTT物联网套件产品架构如下图所示:

接入地址说明:https://open.iot.10086.cn/doc/v5/develop/detail/248

在帮助文档页面,介绍了MQTT接入的地址和端口号。 当前设备是单片机,端口采用1883非加密端口。

地址与端口固定如下:

cpp 复制代码
固定的IP地址: 183.230.40.96
固定的端口号: 1883

3.6 MQTT主题订阅与发布

MQTT协议是一种消息列队传输协议,采用订阅、发布机制,订阅者只接收自己已经订阅的数据,非订阅数据则不接收,既保证了必要的数据的交换,又避免了无效数据造成的储存与处理。因此在工业物联网中得到广泛的应用。

(1)主题订阅

主题订阅是设备订阅平台的消息,如果设备想知道平台下发的消息,就需要订阅主题。

帮助文档: https://open.iot.10086.cn/doc/mqtt/book/device-develop/protocol.html

需要订阅什么数据,设备端按照下面的主题格式填写订阅即可。

如果想知道设备所有相关信息,直接订阅$sys/{产品ID}/{device-name}/#即可。 (其中的PID就是产品ID)

cpp 复制代码
$sys/q0o902pdYT/dev1/#

(2)主题发布

主题发布: 就是设备向平台上传数据。

帮助文档地址:https://open.iot.10086.cn/doc/mqtt/book/example/datapoints.html

文档里介绍了数据点上传的格式:

根据当前设备,总结的格式如下:

cpp 复制代码
发布主题: $sys/q0o902pdYT/dev1/dp/post/json

发布消息:
{"id":123,"dp":{"data":[{"v":{"light":82.6,"DS18B20":6.8,"motor_sw":1,"PH":6.5,"led_sw":1,"fan_sw":1,"soil_abc":12.1,"run_mode":1}}]}}

dp对象里面就是需要上传的数据点字段。 这个数据点的名字就是自己创建数据流模板的时候创建的。

数据点是标准的JSON结构:

cpp 复制代码
{
  "id": 123,
  "dp": {
    "data": [
      {
        "v": {
          "light": 82.6,
          "DS18B20": 6.8,
          "motor_sw": 1,
          "PH": 6.5,
          "led_sw": 1,
          "fan_sw": 1,
          "soil_abc": 12.1,
          "run_mode": 1
        }
      }
    ]
  }
}

3.7 MQTT三元组生成

设备登录OneNet采用的是MQTT协议,MQTT协议登录需要填写 登录信息:简称 MQTT三元组

(1)需要的参数

(2)密码生成规则

链接:https://open.iot.10086.cn/doc/v5/develop/detail/241

(3)编写生成密码的算法

这里算法代码采用Python编写。下面是生成密码的Python算法。

电脑先安装Python环境,然后将下面代码copy到文本里,文本后缀改为.py,就可以允许python代码了。

python 复制代码
import base64
import hmac
from urllib.parse import quote
import time

# 中国移动官方文档给出的核心秘钥计算算法
def token(id,access_key):  
    version = '2018-10-31'
    res = 'products/%s' % id  # 通过产品ID访问产品API
    # 用户自定义token过期时间
    et = str(int(time.time()) + 63072000) # 设置为2年有效时间
    # 签名方法,支持md5、sha1、sha256
    method = 'sha1'
    # 对access_key进行decode
    key = base64.b64decode(access_key)
    # 计算sign
    org = et + '\n' + method + '\n' + res + '\n' + version
    sign_b = hmac.new(key=key, msg=org.encode(), digestmod=method)
    sign = base64.b64encode(sign_b.digest()).decode()
    # value 部分进行url编码,method/res/version值较为简单无需编码
    sign = quote(sign, safe='')
    res = quote(res, safe='')
    # token参数拼接
    token = 'version=%s&res=%s&et=%s&method=%s&sign=%s' % (version, res, et, method, sign)
    return token

username    = "q0o902pdYT"                                           # 产品ID
accesskey   = "Y3lyQTRGbXlmVER5d0FENzFWY2djUlEzNVdGTWJRaEs="         # accessKey
password = token(username, accesskey)
print(password)

# 等待用户按下 Enter 键再退出
input("按 Enter 键退出程序...")

先安装好Python环境。

**将上面的文件保存为 xxx.py 文件,然后双击运行: ** 就得到了生成的key

cpp 复制代码
version=2018-10-31&res=products%2Fq0o902pdYT&et=1810299787&method=sha1&sign=V0IBGLij7hoVvD3Sj2PoW49pL9Q%3D

(4)MQTT登录参数总结

MQTT协议登录时,需要输入3个参数: MQTT-设备ID,MQTT-设备名称,MQTT-密码。

对应OneNet的参数:

cpp 复制代码
MQTT的设备ID  -----> 就是OneNet的设备名称

MQTT的设备名称-----> 就是OneNet的产品ID

MQTT的密码------------> 就是OneNet的密匙工具生成的密码

下面是对本次的设备做总结:

cpp 复制代码
IP地址: 183.230.40.96
端口号: 1883
clientId: dev1
username: q0o902pdYT
password: version=2018-10-31&res=products%2Fq0o902pdYT&et=1810299787&method=sha1&sign=V0IBGLij7hoVvD3Sj2PoW49pL9Q%3D
    
订阅主题:  $sys/q0o902pdYT/dev1/#


发布主题: $sys/q0o902pdYT/dev1/dp/post/json

发布消息:
{"id":123,"dp":{"data":[{"v":{"light":82.6,"DS18B20":6.8,"motor_sw":1,"PH":6.5,"led_sw":1,"fan_sw":1,"soil_abc":12.1,"run_mode":1}}]}}

3.8 MQTT工具登录测试

前面已经介绍了MQTT协议登录需要用到的参数,以及订阅主题、发布主题的格式,接下来我们通过MQTT工具模拟设备登录OneNet平台,完成数据交互测试。

简单来说: 就是用软件来模拟实际的硬件,登录onenet平台,上传数据,走一下这个流程。

(1)模拟设备登录

接下来根据软件的输入框提示,输入对应的参数,然后登录设备,订阅主题,发布主题即可完成一个流程的测试。

(2)登录OneNet控制台查看设备

在设备列表页面,可以看到设备已经在线了。

在设备详情页面可以看到设备模拟器刚才上传的数据:

在设备的数据流页面可以看到上传的数据。

查看历史数据。

到此,设备的云平台已经配置完毕。

3.9 查看用户ID和key

开发APP需要这个参数。

四、STM32代码设计

4.1 硬件连线说明

【1】OLED显示屏(0.96寸 IIC接口)

OLED显示屏用于本地显示环境参数和设备状态,采用IIC总线协议与主控芯片连接。具体接线如下:

模块引脚 连接至STM32引脚
VCC 3.3V
GND GND
SCL PB0
SDA PB1

【2】ESP8266模块

ESP8266模块负责Wi-Fi联网和数据上云,通过串口与STM32进行通信。具体接线如下:

模块引脚 连接至STM32引脚
RXD PA2(USART2_TX)
TXD PA3(USART2_RX)
GND GND
VCC 5V

【3】BH1750环境光强检测模块

BH1750用于检测大棚环境光照强度,采用IIC总线协议与主控芯片连接。具体接线如下:

模块引脚 连接至STM32引脚
SDA PB6
SCL PB7
GND GND
VCC 3.3V

【4】土壤湿度检测模块

土壤湿度检测模块通过模拟电压输出反映土壤含水量,连接至STM32的ADC引脚进行采集。具体接线如下:

模块引脚 连接至STM32引脚
GND GND
VCC 5V
AO PA1(ADC输入)

【5】SHT30温湿度检测模块

SHT30用于检测空气温度和湿度,采用IIC总线协议与主控芯片连接。具体接线如下:

模块引脚 连接至STM32引脚
GND GND
VCC 3.3V
SDA PA6
SCL PA7

【6】LED补光灯

LED补光灯用于光照不足时自动补光,采用MOS管电子开关驱动,由GPIO引脚控制。具体接线如下:

模块引脚 连接至STM32引脚
GND GND
VCC 5V
IO PA8

【7】灌溉电机(5V直流抽水小电机)

灌溉电机用于土壤缺水时自动浇水,采用MOS管电子开关驱动,由GPIO引脚控制。具体接线如下:

模块引脚 连接至STM32引脚
GND GND
VCC 5V
IO PB8

【8】通风风扇(5V小风扇)

通风风扇用于温度过高时强制通风降温,采用MOS管电子开关驱动,由GPIO引脚控制。具体接线如下:

模块引脚 连接至STM32引脚
GND GND
VCC 5V
IO PB9

【9】CH9300-FL语音播报模块(驱鸟功能)

CH9300-FL模块用于定时播放驱鸟声音,通过IO口高低电平触发播报。具体接线如下:

模块引脚 连接至STM32引脚
GND GND
VCC 5V
IO PB10

【10】LED指示灯

LED指示灯用于显示系统运行状态和网络上传状态。具体接线如下:

指示灯用途 连接至STM32引脚
程序运行指示灯 PC13
上传指示灯 PB4

【11】独立按键(5颗)

独立按键用于本地手动控制,按键按下时引脚接地(低电平有效)。具体接线如下:

按键编号 功能说明 连接至STM32引脚
K1 模式切换 PB12
K2 补光灯控制 PB13
K3 灌溉电机控制 PB14
K4 驱鸟功能控制 PB15
K5 通风风扇控制 PB11

按键公共端接线: 所有按键的另一端均连接至GND。


【12】接线汇总表

模块名称 引脚连接
OLED显示屏 VCC→3.3V, GND→GND, SCL→PB0, SDA→PB1
ESP8266模块 RXD→PA2, TXD→PA3, GND→GND, VCC→5V
BH1750光照传感器 SDA→PB6, SCL→PB7, GND→GND, VCC→3.3V
土壤湿度传感器 GND→GND, VCC→5V, AO→PA1
SHT30温湿度传感器 GND→GND, VCC→3.3V, SDA→PA6, SCL→PA7
LED补光灯 GND→GND, VCC→5V, IO→PA8
灌溉电机 GND→GND, VCC→5V, IO→PB8
通风风扇 GND→GND, VCC→5V, IO→PB9
CH9300-FL语音播报模块 GND→GND, VCC→5V, IO→PB10
程序运行指示灯 PC13
上传指示灯 PB4
K1按键(模式切换) PB12
K2按键(补光灯控制) PB13
K3按键(灌溉电机控制) PB14
K4按键(驱鸟功能控制) PB15
K5按键(通风风扇控制) PB11

4.2 传感器代码

1. SHT30 温湿度传感器驱动 (I2C软件模拟)

由于 SHT30 采用 I2C 接口,分配在 PA6(SDA)PA7(SCL),这里采用软件模拟 I2C 的方式,稳定可靠。

sht30.h

复制代码
#ifndef __SHT30_H
#define __SHT30_H

#include "stm32f10x.h"

// 引脚宏定义
#define SHT30_SCL_PIN      GPIO_Pin_7
#define SHT30_SCL_PORT     GPIOA
#define SHT30_SDA_PIN      GPIO_Pin_6
#define SHT30_SDA_PORT     GPIOA

// IO操作宏
#define SHT30_SCL_H        GPIO_SetBits(SHT30_SCL_PORT, SHT30_SCL_PIN)
#define SHT30_SCL_L        GPIO_ResetBits(SHT30_SCL_PORT, SHT30_SCL_PIN)
#define SHT30_SDA_H        GPIO_SetBits(SHT30_SDA_PORT, SHT30_SDA_PIN)
#define SHT30_SDA_L        GPIO_ResetBits(SHT30_SDA_PORT, SHT30_SDA_PIN)
#define SHT30_SDA_READ     GPIO_ReadInputDataBit(SHT30_SDA_PORT, SHT30_SDA_PIN)

// SHT30 常用命令
#define SHT30_ADDR         0x44 << 1 // ADDR引脚接地时的器件地址
#define SHT30_CMD_HIGH_H   0x2C      // 高常规模式
#define SHT30_CMD_HIGH_L   0x06

// 函数声明
void SHT30_Init(void);
uint8_t SHT30_Read_Data(float *temperature, float *humidity);

#endif

sht30.c

复制代码
#include "sht30.h"
#include "delay.h" // 需自行提供us级延时函数

static void SHT30_I2C_Delay(void) {
    delay_us(4);
}

static void SHT30_I2C_Start(void) {
    // 设置SDA为输出
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = SHT30_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SHT30_SDA_PORT, &GPIO_InitStructure);

    SHT30_SDA_H;
    SHT30_SCL_H;
    SHT30_I2C_Delay();
    SHT30_SDA_L;
    SHT30_I2C_Delay();
    SHT30_SCL_L;
}

static void SHT30_I2C_Stop(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = SHT30_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(SHT30_SDA_PORT, &GPIO_InitStructure);

    SHT30_SDA_L;
    SHT30_SCL_H;
    SHT30_I2C_Delay();
    SHT30_SDA_H;
    SHT30_I2C_Delay();
}

static void SHT30_I2C_SendByte(uint8_t byte) {
    uint8_t i;
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = SHT30_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(SHT30_SDA_PORT, &GPIO_InitStructure);

    for (i = 0; i < 8; i++) {
        if (byte & 0x80) SHT30_SDA_H;
        else SHT30_SDA_L;
        byte <<= 1;
        SHT30_I2C_Delay();
        SHT30_SCL_H;
        SHT30_I2C_Delay();
        SHT30_SCL_L;
    }
}

static uint8_t SHT30_I2C_WaitAck(void) {
    uint8_t ack;
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = SHT30_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
    GPIO_Init(SHT30_SDA_PORT, &GPIO_InitStructure);

    SHT30_SCL_H;
    SHT30_I2C_Delay();
    if (SHT30_SDA_READ) ack = 1; // NACK
    else ack = 0;               // ACK
    SHT30_SCL_L;
    SHT30_I2C_Delay();
    return ack;
}

static uint8_t SHT30_I2C_ReadByte(uint8_t ack) {
    uint8_t i, byte = 0;
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = SHT30_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(SHT30_SDA_PORT, &GPIO_InitStructure);

    for (i = 0; i < 8; i++) {
        SHT30_SCL_H;
        SHT30_I2C_Delay();
        byte <<= 1;
        if (SHT30_SDA_READ) byte |= 0x01;
        SHT30_SCL_L;
        SHT30_I2C_Delay();
    }

    // 发送应答
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(SHT30_SDA_PORT, &GPIO_InitStructure);
    if (ack) SHT30_SDA_H; // NACK
    else SHT30_SDA_L;     // ACK
    SHT30_SCL_H;
    SHT30_I2C_Delay();
    SHT30_SCL_L;
    return byte;
}

void SHT30_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin = SHT30_SCL_PIN | SHT30_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SHT30_SDA_PORT, &GPIO_InitStructure);

    SHT30_SCL_H;
    SHT30_SDA_H;
}

uint8_t SHT30_Read_Data(float *temperature, float *humidity) {
    uint8_t buf[6];
    uint16_t raw_temp, raw_humi;

    // 1. 发送测量命令
    SHT30_I2C_Start();
    SHT30_I2C_SendByte(SHT30_ADDR);
    if (SHT30_I2C_WaitAck()) return 1;
    SHT30_I2C_SendByte(SHT30_CMD_HIGH_H);
    if (SHT30_I2C_WaitAck()) return 1;
    SHT30_I2C_SendByte(SHT30_CMD_HIGH_L);
    if (SHT30_I2C_WaitAck()) return 1;
    SHT30_I2C_Stop();

    delay_ms(20); // 等待转换完成

    // 2. 读取测量结果
    SHT30_I2C_Start();
    SHT30_I2C_SendByte(SHT30_ADDR | 0x01); // 读操作
    if (SHT30_I2C_WaitAck()) return 1;

    buf[0] = SHT30_I2C_ReadByte(0); // Temp MSB
    buf[1] = SHT30_I2C_ReadByte(0); // Temp LSB
    buf[2] = SHT30_I2C_ReadByte(0); // Temp CRC (略过验证)
    buf[3] = SHT30_I2C_ReadByte(0); // Humi MSB
    buf[4] = SHT30_I2C_ReadByte(0); // Humi LSB
    buf[5] = SHT30_I2C_ReadByte(1); // Humi CRC NACK
    SHT30_I2C_Stop();

    // 3. 数据转换
    raw_temp = (buf[0] << 8) | buf[1];
    raw_humi = (buf[3] << 8) | buf[4];

    *temperature = -45.0f + 175.0f * (float)raw_temp / 65535.0f;
    *humidity = 100.0f * (float)raw_humi / 65535.0f;

    return 0; // 成功
}

2. BH1750 光强传感器驱动 (I2C软件模拟)

引脚分配在 PB6(SDA)PB7(SCL)

bh1750.h

复制代码
#ifndef __BH1750_H
#define __BH1750_H

#include "stm32f10x.h"

#define BH1750_SCL_PIN      GPIO_Pin_7
#define BH1750_SCL_PORT     GPIOB
#define BH1750_SDA_PIN      GPIO_Pin_6
#define BH1750_SDA_PORT     GPIOB

#define BH1750_SCL_H        GPIO_SetBits(BH1750_SCL_PORT, BH1750_SCL_PIN)
#define BH1750_SCL_L        GPIO_ResetBits(BH1750_SCL_PORT, BH1750_SCL_PIN)
#define BH1750_SDA_H        GPIO_SetBits(BH1750_SDA_PORT, BH1750_SDA_PIN)
#define BH1750_SDA_L        GPIO_ResetBits(BH1750_SDA_PORT, BH1750_SDA_PIN)
#define BH1750_SDA_READ     GPIO_ReadInputDataBit(BH1750_SDA_PORT, BH1750_SDA_PIN)

#define BH1750_ADDR         0x23 << 1 // ADDR地接地
#define BH1750_CMD_ON       0x01      // 通电
#define BH1750_CMD_CH       0x10      // 高分辨率模式1lx, 连续测量

void BH1750_Init(void);
float BH1750_Read_Lux(void);

#endif

bh1750.c

复制代码
#include "bh1750.h"
#include "delay.h"

static void BH1750_I2C_Delay(void) { delay_us(4); }

static void BH1750_I2C_Start(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = BH1750_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BH1750_SDA_PORT, &GPIO_InitStructure);
    BH1750_SDA_H; BH1750_SCL_H; BH1750_I2C_Delay();
    BH1750_SDA_L; BH1750_I2C_Delay(); BH1750_SCL_L;
}

static void BH1750_I2C_Stop(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = BH1750_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(BH1750_SDA_PORT, &GPIO_InitStructure);
    BH1750_SDA_L; BH1750_SCL_H; BH1750_I2C_Delay(); BH1750_SDA_H; BH1750_I2C_Delay();
}

static void BH1750_I2C_SendByte(uint8_t byte) {
    uint8_t i;
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = BH1750_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(BH1750_SDA_PORT, &GPIO_InitStructure);
    for (i = 0; i < 8; i++) {
        if (byte & 0x80) BH1750_SDA_H; else BH1750_SDA_L;
        byte <<= 1; BH1750_I2C_Delay(); BH1750_SCL_H; BH1750_I2C_Delay(); BH1750_SCL_L;
    }
}

static uint8_t BH1750_I2C_WaitAck(void) {
    uint8_t ack;
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = BH1750_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(BH1750_SDA_PORT, &GPIO_InitStructure);
    BH1750_SCL_H; BH1750_I2C_Delay();
    if (BH1750_SDA_READ) ack = 1; else ack = 0;
    BH1750_SCL_L; BH1750_I2C_Delay();
    return ack;
}

static uint8_t BH1750_I2C_ReadByte(uint8_t ack) {
    uint8_t i, byte = 0;
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = BH1750_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(BH1750_SDA_PORT, &GPIO_InitStructure);
    for (i = 0; i < 8; i++) {
        BH1750_SCL_H; BH1750_I2C_Delay();
        byte <<= 1;
        if (BH1750_SDA_READ) byte |= 0x01;
        BH1750_SCL_L; BH1750_I2C_Delay();
    }
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(BH1750_SDA_PORT, &GPIO_InitStructure);
    if (ack) BH1750_SDA_H; else BH1750_SDA_L;
    BH1750_SCL_H; BH1750_I2C_Delay(); BH1750_SCL_L;
    return byte;
}

void BH1750_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin = BH1750_SCL_PIN | BH1750_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BH1750_SDA_PORT, &GPIO_InitStructure);

    BH1750_SCL_H; BH1750_SDA_H;

    // 初始化配置 BH1750
    BH1750_I2C_Start();
    BH1750_I2C_SendByte(BH1750_ADDR);
    BH1750_I2C_WaitAck();
    BH1750_I2C_SendByte(BH1750_CMD_ON);
    BH1750_I2C_WaitAck();
    BH1750_I2C_Stop();

    BH1750_I2C_Start();
    BH1750_I2C_SendByte(BH1750_ADDR);
    BH1750_I2C_WaitAck();
    BH1750_I2C_SendByte(BH1750_CMD_CH);
    BH1750_I2C_WaitAck();
    BH1750_I2C_Stop();
}

float BH1750_Read_Lux(void) {
    uint8_t h_byte, l_byte;
    uint16_t raw_lux;
    
    BH1750_I2C_Start();
    BH1750_I2C_SendByte(BH1750_ADDR | 0x01);
    if (BH1750_I2C_WaitAck()) return -1.0f;
    h_byte = BH1750_I2C_ReadByte(0);
    l_byte = BH1750_I2C_ReadByte(1);
    BH1750_I2C_Stop();

    raw_lux = (h_byte << 8) | l_byte;
    return (float)raw_lux / 1.2f; // 返回光照度(Lx)
}

3. 土壤湿度传感器驱动 (ADC单通道采集)

使用单片机 PA1(ADC1_IN1) 来采集模拟电压,通过映射转化为湿度百分比。

soil.h

复制代码
#ifndef __SOIL_H
#define __SOIL_H

#include "stm32f10x.h"

void Soil_Init(void);
float Soil_Read_Humidity(void);

#endif

soil.c

复制代码
#include "soil.c"

// 对应空气中干燥的ADC值(最大值)和完全浸入水中的ADC值(最小值),根据实测调整
#define SOIL_AIR_VAL   3500 
#define SOIL_WATER_VAL 1200

void Soil_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;

    // 开启 GPIOA 和 ADC1 时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
    // 配置ADC时钟分频 (72MHz/6 = 12MHz)
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    // PA1 配置为模拟输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // ADC配置
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    ADC_Cmd(ADC1, ENABLE); // 使能 ADC1

    // 校准 ADC1
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

static uint16_t Get_ADC_Value(void) {
    // 配置规则组通道 1, 采样周期 55.5 周期
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 启动转换
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 等待转换结束
    return ADC_GetConversionValue(ADC1);
}

float Soil_Read_Humidity(void) {
    uint16_t adc_sum = 0;
    float humidity = 0.0f;
    // 滑动平均滤波取10次样本
    for(uint8_t i=0; i<10; i++) {
        adc_sum += Get_ADC_Value();
    }
    uint16_t adc_avg = adc_sum / 10;

    // 电压反馈与水分含量成反比:越湿,电压/ADC值越低
    if(adc_avg >= SOIL_AIR_VAL) return 0.0f;
    if(adc_avg <= SOIL_WATER_VAL) return 100.0f;

    humidity = (float)(SOIL_AIR_VAL - adc_avg) / (SOIL_AIR_VAL - SOIL_WATER_VAL) * 100.0f;
    return humidity;
}

4. 执行机构驱动 (风扇、水泵、补光灯、指示灯、语音)

这些外设均由 GPIO 驱动。风扇(PB9)、水泵(PB8)、补光灯(PA8)采用 MOS 管(高电平导通,低电平截止)。 语音播报(PB10)采用高低电平触发机制。

actuator.h

复制代码
#ifndef __ACTUATOR_H
#define __ACTUATOR_H

#include "stm32f10x.h"

// 宏定义各个引脚
#define FAN_PIN          GPIO_Pin_9
#define FAN_PORT         GPIOB

#define PUMP_PIN         GPIO_Pin_8
#define PUMP_PORT        GPIOB

#define LED_LIGHT_PIN    GPIO_Pin_8
#define LED_LIGHT_PORT   GPIOA

#define BIRD_VOICE_PIN   GPIO_Pin_10
#define BIRD_VOICE_PORT  GPIOB

#define LED_RUN_PIN      GPIO_Pin_13
#define LED_RUN_PORT     GPIOC

#define LED_NET_PIN      GPIO_Pin_4
#define LED_NET_PORT     GPIOB

// 函数声明
void Actuator_Init(void);
void Set_Fan(BitAction state);
void Set_Pump(BitAction state);
void Set_Light(BitAction state);
void Trigger_Bird_Voice(void);
void Toggle_Run_LED(void);
void Set_Net_LED(BitAction state);

#endif

actuator.c

复制代码
#include "actuator.h"
#include "delay.h"

void Actuator_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;

    // 开启所有相关GPIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);
    // 如果PB4被默认作JNTRST,必须先禁用JTAG功能,释放为常规IO使用
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

    // 1. 配置 GPIOB 输出外设 (风扇、水泵、语音、网络灯)
    GPIO_InitStructure.GPIO_Pin = FAN_PIN | PUMP_PIN | BIRD_VOICE_PIN | LED_NET_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 2. 配置 GPIOA 输出外设 (补光灯)
    GPIO_InitStructure.GPIO_Pin = LED_LIGHT_PIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 3. 配置 GPIOC 输出外设 (运行指示灯)
    GPIO_InitStructure.GPIO_Pin = LED_RUN_PIN;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    // 状态初始化复位
    GPIO_ResetBits(FAN_PORT, FAN_PIN);         // 关风扇
    GPIO_ResetBits(PUMP_PORT, PUMP_PIN);       // 关水泵
    GPIO_ResetBits(LED_LIGHT_PORT, LED_LIGHT_PIN); // 关补光
    GPIO_SetBits(BIRD_VOICE_PORT, BIRD_VOICE_PIN); // 语音模块触发脚维持高电平
    GPIO_SetBits(LED_RUN_PORT, LED_RUN_PIN);   // PC13低电平点亮,初始关闭
    GPIO_SetBits(LED_NET_PORT, LED_NET_PIN);   // PB4低电平点亮,初始关闭
}

void Set_Fan(BitAction state) {
    GPIO_WriteBit(FAN_PORT, FAN_PIN, state);
}

void Set_Pump(BitAction state) {
    GPIO_WriteBit(PUMP_PORT, PUMP_PIN, state);
}

void Set_Light(BitAction state) {
    GPIO_WriteBit(LED_LIGHT_PORT, LED_LIGHT_PIN, state);
}

// 采用低脉冲信号触发CH9300-FL进行语音播报(具体高/低有效需结合您拷贝的模式设置,通常拉低延时再拉高触发)
void Trigger_Bird_Voice(void) {
    GPIO_ResetBits(BIRD_VOICE_PORT, BIRD_VOICE_PIN);
    delay_ms(200); // 维持200ms低电平触发脉冲
    GPIO_SetBits(BIRD_VOICE_PORT, BIRD_VOICE_PIN);
}

void Toggle_Run_LED(void) {
    GPIOC->ODR ^= LED_RUN_PIN;
}

void Set_Net_LED(BitAction state) {
    // 假设低电平点亮
    if(state == BIT_SET) GPIO_ResetBits(LED_NET_PORT, LED_NET_PIN);
    else GPIO_SetBits(LED_NET_PORT, LED_NET_PIN);
}

5. 独立按键驱动 (支持5路矩阵或独立按键)

按键分配:K1(PB12)K2(PB13)K3(PB14)K4(PB15)K5(PB11)。设计了软件消抖。

key.h

复制代码
#ifndef __KEY_H
#define __KEY_H

#include "stm32f10x.h"

#define KEY_MODE    1   // K1
#define KEY_LIGHT   2   // K2
#define KEY_PUMP    3   // K3
#define KEY_BIRD    4   // K4
#define KEY_FAN     5   // K5

void Key_Init(void);
uint8_t Key_Scan(void);

#endif

key.c

复制代码
#include "key.h"
#include "delay.h"

void Key_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // K1~K4 为 PB12~PB15, K5 为 PB11,配置为上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t Key_Scan(void) {
    // 静态变量保持上一次状态标志
    static uint8_t key_up = 1; 
    
    if (key_up && ((GPIO_ReadInputData(GPIOB) & 0xF800) != 0xF800)) { // 检测PB11~PB15是否有低电平出现
        delay_ms(20); // 软件消抖
        key_up = 0;
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0)      return KEY_MODE;
        else if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 0) return KEY_LIGHT;
        else if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0) return KEY_PUMP;
        else if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15) == 0) return KEY_BIRD;
        else if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) return KEY_FAN;
    } else if ((GPIO_ReadInputData(GPIOB) & 0xF800) == 0xF800) {
        key_up = 1; // 按键释放
    }
    return 0; // 无按键按下
}

4.3 程序下载

【STM32采用ST-LINK下载程序的过程】 https://www.bilibili.com/video/BV1ehCvBcEX2/?share_source=copy_web\&vd_source=347136f3e32fe297fc17177194ce0a8b

4.4 程序正常运行效果

设备运行过程中会通过串口打印调试信息,我们可以通过串口打印了解程序是否正常。

程序下载之后,可以打开串口调试助手查看程序运行的状态信息。[软件就在资料包里的软件工具目录下]

4.5 取模软件的使用

显示屏上会显示中文,字母,数字等数据,可以使用下面的取模软件进行取模设置。

软件就在资料包里的软件工具目录下

打开软件之后:

五、项目复刻与资料下载

如果对本项目感兴趣,可以看本篇文章末尾,CSDN博客的推广位置,访问进行下载复刻资料包。

下面是复刻项目公共教程:

【嘉立创导入工程与PCB下单制板的过程演示(2026)】 https://www.bilibili.com/video/BV1vz6RB1Egt/?share_source=copy_web\&vd_source=347136f3e32fe297fc17177194ce0a8b

【嘉立创如何一键下单购买元器件?】 https://www.bilibili.com/video/BV1fszyBHEuA/?share_source=copy_web\&vd_source=347136f3e32fe297fc17177194ce0a8b

【项目复刻_PCB焊接过程演示】 https://www.bilibili.com/video/BV1FmPVzYEVx/?share_source=copy_web\&vd_source=347136f3e32fe297fc17177194ce0a8b

【1】安装与使用keil软件:不会安装软件,不会打开工程,不会看STM32代码,不会编译,编译报错,找不到下载工具

https://blog.csdn.net/xiaolong1126626497/article/details/146037629

【2】安装QT:这是上位机代码查看,编译软件

https://blog.csdn.net/xiaolong1126626497/article/details/146185260

【3】STM32代码下载教程:

【STM32采用ST-LINK下载程序的过程】 https://www.bilibili.com/video/BV1ehCvBcEX2/?share_source=copy_web\&vd_source=347136f3e32fe297fc17177194ce0a8b

【5】项目开发使用的全部软件工具已经上传到网盘:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink

相关推荐
会周易的程序员17 小时前
AI 编程助手:从“猫弄乱的线团”到“击鼓传花”的 Bug 修复
c++·人工智能·物联网·架构·bug·iot
黎阳之光1 天前
黎阳之光:以视频孪生重构智慧防火,打造“天空地人智”一体化森林防火新范式
大数据·运维·人工智能·物联网·安全
zxsz_com_cn1 天前
设备预测性维护的核心价值与实施路径
人工智能·物联网
TDengine (老段)2 天前
TDengine 存储引擎概览 — TSDB 分层存储架构与数据流转全景
大数据·数据库·物联网·架构·时序数据库·tdengine·涛思数据
砍材农夫2 天前
物联网 基于netty控制报文结构(报文分类)
网络·物联网·struts
数字新视界2 天前
如何通过能耗管理系统实现园区能源使用优化?
物联网·数据中心·dcim·动环监控·新人首发
物联通信量讯说2 天前
AI-eSIM 开启智联新入口,量讯物联助力企业把握万物智联新机遇
物联网·iot·esim
珠海西格电力2 天前
零碳园区的碳排放指标计算的实操步骤
大数据·运维·人工智能·物联网·能源
Yvonne爱编码3 天前
基于 ESP32 的冷链物流工业物联网(IIoT)监控系统|全流程实战
物联网·iot·工业物联网