基于物联网设计的蜂箱智能监测系统设计

一、前言

1.1 项目介绍

【1】项目开发背景

随着现代农业向智能化、精细化方向的不断演进,传统养蜂业面临的挑战日益凸显。传统的养蜂方式高度依赖养蜂人的经验和定期的人工开箱检查,这种模式不仅耗费大量人力物力,而且频繁的开箱操作会严重干扰蜂群的正常生活,甚至可能引起蜂群应激反应,影响蜂蜜产量与蜂群健康。此外,对于病虫害、天敌入侵、分蜂预兆等突发状况,传统管理方式往往响应迟缓,容易错失最佳干预时机,给养蜂业带来显著的经济损失。

在此背景下,将物联网(IoT)技术与现代传感技术相结合,应用于传统养蜂业的数字化转型,成为提升养蜂效率与科学管理水平的关键路径。物联网技术能够实现对环境和设备状态的远程、实时、无干扰监测,通过数据分析为生产决策提供科学依据。将这一技术应用于蜂箱,可以打破传统养蜂业在空间和时间上的限制,实现对蜂群状态的全天候、数据化、可视化管理,从而推动养蜂业从"经验养蜂"向"科学养蜂"的现代化升级。

本项目设计并实现一套基于物联网的蜂箱智能监测系统,以应对传统养蜂模式的痛点。该系统通过集成多种传感器,实时采集蜂箱内外的关键环境参数,如温度、湿度、重量、声音、震动及光照强度等。这些数据通过4G或Wi-Fi等无线通信方式,实时上传至云端服务器。养蜂管理者可以通过上位机软件远程访问这些数据,直观地了解蜂群的健康状况、活动规律以及蜂蜜的产出情况,并通过系统设定的报警阈值,及时获取如失重(可能的分蜂或饥饿)、超重(蜂蜜已满)、异常震动(天敌入侵)等预警信息,从而实现精准、高效、低干扰的现代化蜂群管理,最终达到降本增效、提升蜂蜜产量与品质的目的。

【2】设计实现的功能

当前项目使用的相关软件工具、模块源码已经上传:ccnr8sukk85n.feishu.cn/wiki/QjY8we...

(1)使用 STM32F103C8T6 完成温湿度、重量、声音、震动等传感器的采集与数字处理。

(2)通过 HX711 完成蜂箱重量传感器(称重传感器)的高精度采集与校准。

(3)使用一个 DHT11 完成蜂箱内部温湿度精准测量,实现数据补偿与滤波。

(4)使用一个 DHT11 完成外部环境温湿度测量,上传对比分析数据。

(5)使用 MAX9814 麦克风模块采集蜂箱声学信息,用于蜂群活力判断。

(6)使用 SW420 震动模块采集蜂箱震动情况,实现异常震动报警。

(7)使用 BH1750 光照传感器测量外界光照强度,辅助判断蜂群活动周期。

(8)使用 OLED 0.96 显示屏显示实时的蜂箱内部温湿度、重量、光照等信息。

(9)使用 Air780E 模块实现 4G LTE 物联网数据通信或选择 ESP8266-WIFI,上传数据至华为云。

(10)通过 MQTT 协议实现蜂箱数据上报、阈值下发和报警推送。

(11)上位机采用 Qt(C++)实现数据接收可视化显示。

【3】项目硬件模块组成

(1)主控模块:STM32F103C8T6 核心板

(2)内部温湿度传感器:DHT11

(3)外部环境温湿度传感器:DHT11

(4)重量传感器:10kg 称重传感器 + HX711 放大器

(5)声音采集模块:MAX9814 自动增益麦克风模块

(6)震动传感器:SW420 震动检测模块

(7)光照采集模块:BH1750

(8)通信模块:Air780E(4G LTE Cat1) 或ESP8266-01S

(9)显示模块:OLED 0.96 I2C 显示屏

(10)电源模块:DC-DC 5V/3.3V 稳压模块(AMS1117)

(11)电池:18650 锂电池 + 保护板 + 太阳能板(户外部署)

【4】设计意义

本项目设计的智能蜂箱监测系统,其核心意义在于利用物联网和传感技术,将传统养蜂模式从依赖人力和经验的粗放式管理,转变为一种非侵入式、数据化的精细化管理模式。它解决了频繁开箱检查对蜂群造成的干扰问题,实现了对蜂箱内部状态的持续、无声监测,为养蜂业的现代化转型提供了切实可行的技术路径。

该设计的深远意义体现在它为养蜂决策提供了科学的数据支持。通过集成温度、湿度、重量、声音、光照和震动等多种传感器,系统能够构建一个关于蜂群健康、活力、繁殖和生产状态的多维度数据模型。养蜂人不再仅仅依赖主观经验来判断蜂群状况,而是可以根据实时、客观的数据进行分析。例如,通过对比箱内外温湿度和光照强度,可以更准确地评估蜂群的活动节律;通过分析声音频率,可以预判分蜂或病态等异常情况,从而将"经验养蜂"提升到"科学养蜂"的全新水平。

此系统显著提升了养蜂管理的效率与响应速度。借助4G或Wi-Fi无线通信模块,所有采集的数据都能实时传输至云服务器,并通过上位机软件进行可视化呈现。这意味着管理者可以随时随地远程监控蜂箱,极大地降低了巡查成本和劳动强度。更重要的是,基于重量和震动等关键指标的阈值报警功能,使得系统能够主动预警潜在风险,如蜂蜜满溢、蜂群失重(可能由分蜂或饥荒导致)或外部侵扰,使管理者能够第一时间采取干预措施,变被动管理为主动预防,有效规避经济损失。

本设计的现实意义还在于其高度的实用性和可部署性。系统提供了4G和Wi-Fi两种通信方案,使其能够灵活适应不同场地的网络条件,无论是偏远山区还是家庭农场。结合太阳能板和锂电池的供电方案,确保了设备在户外环境下的长期稳定运行。同时,本地OLED显示屏的设计也为现场维护和快速诊断提供了便利。这套完整的软硬件解决方案不仅是一个技术验证原型,更是一个具备实际应用价值、能够直接服务于养蜂生产、降低管理门槛、提升产业效益的智能化工具。

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

案例一:国内的先行者------天府蜂谷"大蜂慧"智能蜂箱

四川天府蜂谷科技有限公司是国内智慧养蜂领域的代表企业之一。 他们的核心产品是"大蜂慧"AI养蜂系统,该系统深度融合了物联网与大数据技术。

  • 核心功能:这套系统与您的设计非常相似,同样通过在蜂箱底部安装智能传感器,实时采集蜂箱的重量、蜜蜂出入蜂箱的次数(即"出勤率")等数据。 这些数据通过无线网络传输到云端,用户可以通过手机App随时查看,实现了远程、无干扰的蜂箱状态监控。
  • 技术亮点:天府蜂谷强调其"蜜蜂多维大数据平台",该平台不仅收集数据,还利用AI进行计算和分析,为养蜂户提供精准的数据服务和异常预警。 例如,通过分析蜂箱重量变化曲线,可以判断蜂蜜是否成熟,何时可以取蜜;通过监测蜜蜂出勤数据,可以评估蜂群的健康与活力。
  • 解决的痛点:该系统显著减少了传统养蜂中频繁的人工开箱检查,降低了70%的劳动量,同时减少了对蜂群50%的干扰。 对于养蜂大户来说,原本需要多人多天完成的巡查工作,现在只需少数人通过手机即可完成,大幅提升了管理效率。 此外,该公司还关注到了野外蜂场供电和网络覆盖的难题,并通过太阳能光伏板和优化的低功耗设计来应对。
案例二:国际视野下的创新者------以色列Beewise公司的"BeeHome"

Beewise是一家备受关注的以色列农业科技初创公司,他们推出的"BeeHome"产品,在智能化和自动化方面迈出了更大的一步。

  • 核心功能:"BeeHome"不仅仅是一个监测设备,它更像一个机器人管家。它在一个大型箱体中容纳多个蜂群,并通过内置的机器人、计算机视觉和AI技术,实现24/7的全天候监测和自动干预。
  • 技术亮点:其最大的特色是"机器人养蜂"。系统通过摄像头和传感器监测蜂群,当AI算法发现问题时(如病虫害、蜂王问题、分蜂预兆等),机器人会自动采取行动,例如施药、调整环境或合并蜂群,全程无需人工介入。 Beewise声称这种方式能显著降低高达35%的蜂群崩溃率。
    • 商业模式:Beewise的模式不仅是销售硬件,更侧重于提供授粉服务。他们与农场主合作,通过其高效的蜂群管理技术,确保作物授粉的质量和效率,解决了因蜂群数量下降导致的授粉服务价格上涨和供应不稳定的问题。
案例三:功能全面的解决方案提供商------慧养蜂

"慧养蜂"是中国农业科学院蜜蜂研究所背景下的一个品牌,提供从硬件到软件的一整套智慧蜂业解决方案。

  • 产品系列化:他们根据不同用户需求,推出了基础版、标准版、高级版等不同配置的智能蜂箱。 基础版侧重于内部环境监测和无线传输,由太阳能供电,易于部署。 而标准版则增加了视频监控和更精确的蜜蜂计数功能。
  • 平台化服务:除了智能硬件,他们还提供蜂产品质量安全追溯平台、蜂产业大数据管理决策平台和蜂业公共服务平台等。 这表明市场趋势正从单一的硬件销售,转向提供全产业链的数字化服务,包括生产管理、产品溯源和信息共享。
总结与共性

综合来看,市面上的同类产品在核心思路上通过传感器采集数据,实现远程监控和智能预警。 重量、温度、湿度 是所有产品的标配监测指标。 声音监测也逐渐成为主流,用于分析蜂群的"脉动"和状态。 在通信方面,同样采用4G/LTE或Wi-Fi等方案将数据上传至云平台。

【6】摘要

为解决传统养蜂业依赖人工、管理效率低下、干扰蜂群等问题,本项目设计并实现了一套基于物联网(IoT)的蜂箱智能监测系统。该系统以STM32F103C8T6单片机作为主控核心,集成DHT11温湿度传感器、HX711称重模块、MAX9814声音传感器、SW420震动传感器及BH1750光照传感器,实现对蜂箱内外环境、蜂群活力、蜂蜜产量及外部干扰等多维度信息的实时采集。系统支持通过Air780E(4G)或ESP8266(Wi-Fi)模块,利用MQTT协议将数据稳定上传至华为云IoTDA平台。同时,数据可在本地OLED屏幕上实时显示,并通过Qt开发的上位机软件进行远程可视化监控与报警阈值配置。本设计旨在实现对蜂箱的远程化、数据化、无干扰式管理,以提升养蜂的科学性与经济效益。


关键字

  • 物联网
  • 智能蜂箱
  • STM32
  • 传感器网络
  • MQTT
  • 华为云
  • Qt

1.2 设计思路

本项目的设计思路围绕模块化、实用性和可扩展性展开,构建一个完整的"感知-传输-应用"物联网解决方案。整体架构分为三个核心层次:底层的数据采集端、中层的网络传输层以及顶层的云平台与用户应用层。通过这种分层设计,将复杂的系统功能解耦,使每个部分都能独立开发、测试和优化,最终集成为一个高效、可靠的智能监测系统。

在数据采集端,设计的核心是选用STM32F103C8T6作为主控芯片。选择此芯片是基于其强大的性能、丰富的片上外设资源(如ADC、I2C、SPI等)以及广泛的社区支持,足以胜任同时处理多个传感器数据的任务。传感器的选择则严格对应功能需求:通过两个DHT11传感器分别采集蜂箱内部和外部的温湿度,形成内外环境对比;采用高精度的HX711模块配合称重传感器,实现对蜂箱重量的精确测量,从而间接反映蜂蜜产量和蜂群规模;利用MAX9814麦克风和SW420震动模块,分别从声学和物理震动两个维度监测蜂群状态及外部威胁;并辅以BH1750光照传感器判断蜂群的昼夜活动周期。所有传感器数据由STM32进行统一的采集、滤波和初步处理,并通过本地的OLED显示屏为现场人员提供即时的数据反馈。

在网络传输层,设计充分考虑了不同部署场景的网络可用性,提供了4G和Wi-Fi两种灵活的通信方案。对于偏远、无Wi-Fi覆盖的野外蜂场,选用Air780E模组接入4G LTE网络;对于家庭或有Wi-Fi网络的场景,则可选用成本更低的ESP8266模块。通信协议选择了轻量级的MQTT协议,它基于发布/订阅模式,非常适合资源受限的物联网设备。设备作为客户端,将采集到的各项数据发布到云服务器的指定主题(Topic),同时订阅配置主题,以接收云端下发的报警阈值等指令,实现了设备与云平台之间高效、低功耗的双向通信。

在云平台与应用层,系统以华为云IoTDA作为数据中枢。该平台负责设备的接入认证、数据流的接收、存储以及规则引擎的配置,为后续的数据分析和应用开发提供了坚实的基础。报警功能(如超重、失重、异常震动)的核心逻辑在云端实现,当上报的数据触发预设阈值时,平台能够主动推送报警信息。最终,面向用户的交互界面通过Qt(C++)开发的上位机软件实现。该软件通过订阅云平台相应的数据主题,将接收到的实时数据直观形式进行可视化展示,并能接收和显示报警通知,从而让管理者可以远程、清晰地掌握蜂箱的全部动态,实现科学决策。

1.3 系统功能总结

功能类别 具体功能描述 实现方式/涉及模块
数据采集 采集蜂箱内部的实时温度和湿度。 STM32F103C8T6 + 内部DHT11传感器
采集蜂箱外部环境的实时温度和湿度。 STM32F103C8T6 + 外部DHT11传感器
高精度采集蜂箱重量,反映蜂蜜量与蜂群规模。 10kg称重传感器 + HX711放大器 + STM32
采集蜂箱内部声音,用于分析蜂群状态。 MAX9814麦克风模块 + STM32
采集蜂箱入口震动,判断外部干扰或攻击。 SW420震动模块 + STM32
采集外部环境光照强度,辅助判断蜂群活动周期。 BH1750光照传感器 + STM32
数据传输 支持4G或Wi-Fi网络将数据上传至云服务器。 Air780E (4G LTE) 或 ESP8266 (Wi-Fi)
通过MQTT协议实现数据上报和指令下发。 MQTT协议栈 (在通信模块和STM32上实现)
本地交互 在蜂箱本地实时显示各项关键数据。 OLED 0.96寸I2C显示屏
远程监控 在上位机软件上实时可视化显示蜂箱数据。 Qt (C++) 开发的上位机软件
智能报警 支持服务器端配置报警阈值(温度、湿度、重量、声音)。 华为云IoTDA平台规则引擎
实现蜂蜜过载或异常减重时的报警。 基于重量数据和云端阈值配置
实现蜂箱异常震动时的报警。 基于震动数据和云端阈值配置

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提供了丰富的网络编程支持,尤其是QTcpSocketQTcpServer类,使得上位机能够轻松地与硬件设备建立TCP连接,进行数据收发。上位机通过WIFI连接ESP8266-WIFI模块,ESP8266模块创建TCP服务器,上位机应用则作为客户端连接到服务器,进行实时的数据传输与控制命令的下发。 为了满足不同用户的需求,本项目需要支持Windows平台的桌面应用和Android平台的移动APP。Qt的跨平台特性使得开发人员能够在一个代码库下完成多平台应用的开发和移植。开发者仅需要编写一次应用逻辑和用户界面,就可以通过Qt的跨平台构建工具生成Windows和Android两个平台的可执行文件。此外,Qt提供了丰富的文档和社区支持,帮助开发者解决平台差异和兼容性问题,确保应用在不同平台上都能稳定运行。

总体而言,上位机开发环境采用了Qt 5框架和C++语言,结合Qt Creator集成开发环境,提供了一个高效、稳定、跨平台的开发工具链。通过Qt强大的GUI设计、网络通信、多线程支持以及数据库管理功能,开发者能够轻松实现与硬件设备的交互、控制设备、处理传感器数据,并为用户提供直观、流畅的操作体验。

1.5 模块的技术详情介绍

(1) 主控模块:STM32F103C8T6 核心板

  • 功能:作为整个系统的"大脑",负责协调各传感器的数据采集、信号处理、逻辑判断以及控制通信模块与云端交互。
  • 特点:基于 ARM Cortex-M3 内核,拥有丰富的外设资源(如 ADC、I2C、USART 等),处理速度快,功耗较低,且开发生态成熟,性价比高,完全满足多传感器并行工作的需求。

(2) 温湿度传感器:DHT11

  • 功能:分别用于采集蜂箱内部和外部环境的温度与湿度数据。
  • 特点:采用单总线数字通信,集成度高,自带校准功能。虽然精度相对中等,但稳定性好、响应速度快且成本低廉,非常适合一般的环境温湿度监测场景。

(3) 重量检测模块:10kg 称重传感器 + HX711 放大器

  • 功能:实时测量蜂箱的总重量,通过重量变化监测蜂蜜产量、蜂群数量变化及异常失重情况。
  • 特点:HX711 是一款专为高精度电子秤设计的 24 位 A/D 转换芯片,具有极高的集成度和抗干扰能力。配合全桥式称重传感器,能实现微小重量变化的精准捕捉。

(4) 声音采集模块:MAX9814 自动增益麦克风

  • 功能:采集蜂箱内部的音频信号,用于后续分析蜂群的鸣叫频率和强度,以判断蜂群是否处于躁动或分蜂状态。
  • 特点:内置自动增益控制(AGC)功能,能够在周围环境声音过大时自动降低增益,声音过小时自动提高增益,从而保证采集到的蜂群声音信号幅度稳定,噪声更低。

(5) 震动传感器:SW420 震动检测模块

  • 功能:监测蜂箱受到的物理震动,用于检测是否有天敌入侵(如熊、獾)或人为破坏等外部攻击行为。
  • 特点:基于常闭型震动传感器设计,灵敏度可通过电位器调节。输出为数字开关量(0或1),能够直接触发单片机中断,响应迅速,误报率通过调节可控。

(6) 光照采集模块:BH1750

  • 功能:测量外界环境的光照强度(Lux),辅助系统判断当前的昼夜状态及天气情况,分析蜂群出勤与光照的关系。
  • 特点:采用 I2C 总线接口,能够直接输出数字光照值,无需复杂的计算和校准。其光谱响应特性接近人眼视觉,测量范围宽,受红外线影响小。

(7) 通信模块:Air780E (4G) 或 ESP8266 (Wi-Fi)

  • 功能:负责将单片机处理后的数据打包,通过 MQTT 协议发送至华为云 IoTDA 平台。
  • 特点
    • Air780E:支持 LTE Cat.1 网络,广域网覆盖,传输速率适中,适合没有 Wi-Fi 的野外偏远蜂场部署。
    • ESP8266:集成度高的 Wi-Fi 模块,成本极低,适合家庭农场或有无线网络覆盖的区域,开发便捷。

(8) 显示模块:OLED 0.96 I2C 显示屏

  • 功能:作为本地人机交互界面,实时轮播显示温湿度、重量、系统状态等关键信息,方便现场巡查。
  • 特点:自发光技术(无需背光),对比度高,可视角广,功耗极低。使用 I2C 接口,仅需占用单片机两个 I/O 口,节省资源。

(9) 电源系统:AMS1117 稳压与锂电池供电

  • 功能:为系统各模块提供稳定、纯净的电压(3.3V 和 5V),并保障户外持续运行。
  • 特点:AMS1117 系列具有低压差特性,输出电压稳定。配合 18650 锂电池和太阳能充电板,构成了可循环的绿色供电系统,解决了户外设备取电难的问题。

二、部署华为云物联网平台

华为云官网: www.huaweicloud.com/

打开官网,搜索物联网,就能快速找到 设备接入IoTDA

2.1 物联网平台介绍

华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。

使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。

物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。

设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。

业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。

2.2 开通物联网服务

地址: www.huaweicloud.com/product/iot...

开通免费单元。

点击立即创建

正在创建标准版实例,需要等待片刻。

创建完成之后,点击详情。 可以看到标准版实例的设备接入端口和地址。

下面框起来的就是端口号域名

点击实例名称,可以查看当前免费单元的配置情况。

开通之后,点击接入信息,也能查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。

总结:

scss 复制代码
端口号:   MQTT (1883)| MQTTS (8883)    
接入地址: dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com

根据域名地址得到IP地址信息:

打开Windows电脑的命令行控制台终端,使用ping 命令。ping一下即可。

ini 复制代码
Microsoft Windows [版本 10.0.19045.5011]
(c) Microsoft Corporation。保留所有权利。

C:\Users\Lenovo>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:\Users\Lenovo>

MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口合适

2.3 创建产品

链接:console.huaweicloud.com/iotdm/?regi...

(1)创建产品

(2)填写产品信息

根据自己产品名字填写,下面的设备类型选择自定义类型。

(3)产品创建成功

创建完成之后点击查看详情。

(4)添加自定义模型

产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。

模型简单来说: 就是存放设备上传到云平台的数据。

可以根据自己的产品进行创建。

比如:

cpp 复制代码
烟雾可以叫  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协议接入帮助文档在这里: support.huaweicloud.com/devg-iothub...

业务流程:

(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)主题订阅格式

帮助文档地址:support.huaweicloud.com/devg-iothub...

对于设备而言,一般会订阅平台下发消息给设备 这个主题。

设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。

如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。

cpp 复制代码
以当前设备为例,最终订阅主题的格式如下:
$oc/devices/{device_id}/sys/messages/down
    
最终的格式:
$oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down

(4)主题发布格式

对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。

这个操作称为:属性上报。

帮助文档地址:support.huaweicloud.com/usermanual-...

根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:

cpp 复制代码
发布的主题格式:
$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服务器,首先记得先知道服务器的地址是多少,端口是多少。

帮助文档地址:console.huaweicloud.com/iotdm/?regi...

MQTT协议的端口支持1883和8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用1883端口进连接的。

根据上面的域名和端口号,得到下面的IP地址和端口号信息: 如果设备支持填写域名可以直接填域名,不支持就直接填写IP地址。 (IP地址就是域名解析得到的)

cpp 复制代码
华为云的MQTT服务器地址:117.78.5.125
华为云的MQTT端口号:1883

如何得到IP地址?如何域名转IP? 打开Windows的命令行输入以下命令。

cpp 复制代码
ping  ad635970a1.st1.iotda-device.cn-north-4.myhuaweicloud.com

(2)生成MQTT三元组

华为云提供了一个在线工具,用来生成MQTT鉴权三元组: iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。

下面是打开的页面:

填入设备的信息: (上面两行就是设备创建完成之后保存得到的)

直接得到三元组信息。

得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。

cpp 复制代码
ClientId  663cb18871d845632a0912e7_dev1_0_0_2024050911
Username  663cb18871d845632a0912e7_dev1
Password  71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac237

2.7 模拟设备登录测试

经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。

MQTT软件下载地址【免费】: download.csdn.net/download/xi...

(1)填入登录信息

打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。

(2)打开网页查看

完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。

点击详情页面,可以看到上传的数据:

到此,云平台的部署已经完成,设备已经可以正常上传数据了。

(3)MQTT登录测试参数总结

cpp 复制代码
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账号信息。所以,接下来演示一下过程。

地址: console.huaweicloud.com/iam/?region...

**【1】获取项目凭证 ** 点击左上角用户名,选择下拉菜单里的我的凭证

项目凭证:

cpp 复制代码
28add376c01e4a61ac8b621c714bf459

【2】创建IAM用户

鼠标放在左上角头像上,在下拉菜单里选择统一身份认证

点击左上角创建用户

创建成功:

【3】创建完成

用户信息如下:

cpp 复制代码
主用户名  l19504562721
IAM用户  ds_abc
密码     DS12345678

2.9 获取影子数据

帮助文档:support.huaweicloud.com/api-iothub/...

设备影子介绍:

cpp 复制代码
设备影子是一个用于存储和检索设备当前状态信息的JSON文档。
每个设备有且只有一个设备影子,由设备ID唯一标识
设备影子仅保存最近一次设备的上报数据和预期数据
无论该设备是否在线,都可以通过该影子获取和设置设备的属性

简单来说:设备影子就是保存,设备最新上传的一次数据。

我们设计的软件里,如果想要获取设备的最新状态信息,就采用设备影子接口。

如果对接口不熟悉,可以先进行在线调试:apiexplorer.developer.huaweicloud.com/apiexplorer...

在线调试接口,可以请求影子接口,了解请求,与返回的数据格式。

调试完成看右下角的响应体,就是返回的影子数据。

设备影子接口返回的数据如下:

cpp 复制代码
{
 "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写代码访问此链接,获取影子数据,完成上位机开发。

链接如下:

cpp 复制代码
https://ad635970a1.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/28add376c01e4a61ac8b621c714bf459/devices/663cb18871d845632a0912e7_dev1/shadow

三、Qt开发入门与环境搭建

当前项目的上位机是采用Qt开发的,这一章节主要是介绍Qt开发环境的安装,以及Qt开发环境入门的使用。如果Qt没有任何基础,建议仔细看一遍。

3.1 Qt是什么?

Qt 是一个功能强大、跨平台的应用程序开发框架,主要用于创建图形用户界面(GUI)应用程序,但它不仅仅局限于GUI编程。它由挪威的奇趣科技(TrollTech)最初于1991年开发,并在后续的发展历程中经历了多次所有权变更,包括诺基亚和Digia等公司接手,现在Qt属于The Qt Company所有。

Qt 主要特点和优势包括:

(1)跨平台:Qt 支持多种操作系统,开发者可以使用同一份源代码在不同平台上编译运行,如Windows、Linux、macOS、Android以及各种嵌入式系统(如RTOS),实现"一次编写,到处编译"。

(2)C++ 开发:Qt 的核心是基于C++编程语言构建,提供了一套丰富的类库,通过面向对象的设计方式简化了开发过程。

(3)图形用户界面:Qt 提供了完整的GUI组件集,包含窗口、按钮、标签、文本框等各种标准控件,以及布局管理器、样式表等功能,使得开发者能够高效地创建美观且功能完善的桌面应用或移动应用界面。

(4)工具链完整:Qt 包含一系列集成开发环境(IDE)和辅助工具,例如Qt Creator是一个全能的跨平台IDE,Qt Designer用于可视化拖拽设计UI界面,Qt Linguist支持国际化资源文件的翻译,还有Qt Assistant和大量文档资源方便开发者的使用。

(5)非GUI功能丰富:除了GUI功能外,Qt 还提供了众多非图形化功能模块,如网络通信、数据库访问、XML处理、多媒体处理(音频视频)、文件I/O、线程与并发处理、OpenGL和3D图形渲染等。

(6)元对象系统:Qt 使用元对象系统(Meta-Object System, MOC)实现了信号与槽机制(Signals and Slots),这是一种高级事件处理机制,允许在不同对象之间安全地进行异步通信。

(7)可扩展性与灵活性:Qt 架构高度灵活,支持插件体系结构,开发者可以根据需要自定义组件并轻松地集成到Qt应用中。

Qt 以其强大的跨平台能力和全面的功能集合成为许多企业和个人开发者选择用来开发高性能、高稳定性的应用程序的重要工具之一,被广泛应用于各类桌面软件、嵌入式设备、移动应用以及服务器端组件等领域。

3.2 Qt版本介绍

在Qt发行版本中将要涉及两个版本:Qt商业授权和Qt开源授权。

(1)Qt商业授权是设计商业软件的开发环境,这些商业软件使用了传统的商业来发布,它包含了一些更新的功能、技术上的支持和大量的解决方案,开发了使用于行业的一些特定的组件,有一些特殊的功能只在商业用户中使用。

(2)Qt开源授权是用来开发开源的软件,它提供了一些免费的支持,并遵循QPL协议。

开放源代码是免费的软件,不牵涉用户的某些权益。任何人都有使用开源软件和参与它的修改的机会,这就意味着其他的人同样可获得开发的代码。目前 Qt 的开源授权有两种,一种是 GPL 授权,另一种是 LGPL 授权。

3.3 Qt开发环境安装

Qt的中文官网: www.qt.io/zh-cn/![ima...

QT5.12.6的下载地址:download.qt.io/archive/qt/...

打开下载链接后选择下面的版本进行下载:

qt-opensource-windows-x86-5.12.6.exe 13-Nov-2019 07:28 3.7G Details

软件安装时断网安装,否则会提示输入账户。

如果下载不了,可以在网盘里找到安装包下载: 飞书文档记录的网盘地址:ccnr8sukk85n.feishu.cn/wiki/QjY8we...

安装的时候,第一个复选框里勾选一个mingw 32编译器即可,其他的不管默认就行,直接点击下一步继续安装。

选择MinGW 32-bit 编译器:

3.4 开发第一个QT程序

在QT开发过程中,可以手动编写代码也可以使用UI设计师直接拖拽控件的方式编写界面和布局,在实际的开发过程中一般是两种方式结合使用,提高开发效率。

本小节用一个简单的 "Hello QT" 程序介绍一下使用QtCreator新建工程的步骤

(1)打开QtCreator软件,选择New Project,新建一个工程。

(2)项目模板选择QT Widgets Application

(3)设置项目名称和存放路径

注意:QT项目路径和名称不能出现中文字符。

(4)编译工具套件选择

编译工具套件可以后面自己增加,比如增加Android的。套件是指 Qt 程序从编译链接到运行环境的全部工具和 Qt 类库的集合。

(5)设置生成的类信息

在类信息设置界面选择基类,目前有三种基类:QMainWindow,QWidget,QDialog。在基类里选择QMainWindow,类名和文件名会根据基类自动修改,一般不需要修改,默认即可。

(6)项目管理

在项目管理界面可以设置作为子项目,以及加入版本控制系统。这两个功能暂时用不到,都用默认的 ,然后点击 "完成"。

(7)创建完成

(8) 编辑代码

展开main.cpp文件,添加内容如下:

cpp 复制代码
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <QLabel>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //MainWindow w;
    //w.show();
    QLabel *label =new QLabel("Hello Qt!");
    label->setGeometry(400,100,100,20);
    label->show();
    return a.exec();
}

代码解析:

cpp 复制代码
1)	#include <QApplication>和 #include <QLabel>是QT的类声明头文件,对于每个QT类都有一个与该类同名的头文件,在这个头文件包含了对该类的定义。
2)	main(int argc, char *argv[]) :main函数的标准写法。
3)	QApplication a(argc, argv):创建一个QApplication对象,用于管理应用程序的资源,QApplication类的构造函数需要两个参数。
4)	QLabel *label =new QLabel("Hello Qt!") :创建QLabel窗口部件,QLabel是一个Qt提供的窗口部件,可以用来显示一行文本。
5)	label->setGeometry(400,100,100,20) : 设置控件显示的位置。
6)	label->show():使Qlabel创建的窗口可见,就是显示设置的文本。
7)	return a.exec():应用程序将控制权传递给QT,让程序进入消息循环。等待可能的菜单,工具条,鼠标等的输入,进行响应。

(9)行程序

运行程序可以点击左下角的三角形符号或者按下快捷键Ctrl+R。

3.5 调试输出

QT中使用QDebug类输出调试信息。主要用于调试代码,类似于std::cout的替代品,支持QT的数据类型。使用前需要包含头文件。

调试输出的分类

qDebug 调试信息提示
qWarning 一般的警告提示
qCritical 严重错误提示
qFatal 致命错误提示

示例代码:

cpp 复制代码
qDebug("调试信息输出");
qWarning("一般警告信息输出");
qCritical("严重错误输出");
qFatal("致命错误输出");

qDebug输出的信息会打印到QT软件下边的输出面板。

在上节的HelloQt工程上加上调试输出代码,增加的main.cpp代码如下:

cpp 复制代码
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //MainWindow w;
    //w.show();
    qDebug()<<"QT调试信息输出";
    int data_int=8888;
    qDebug()<<data_int;
    float data_float=123.888;
    qDebug()<<data_float;
    return a.exec();
}

运行程序,观察输出的调试信息:

3.6 QT Creator常用的快捷键

掌握一些适用的快捷键,可以提高程序开发的效率。

(1)F1 键,快速切换光标选中的函数或者类的帮助信息,按一次半屏显示,按下两次全屏显示。

(2)F2 键,快速切换到光标选中的函数或者类的源码定义处。

(3)F4键,快速在源文件和头文件之间切换。

(4)Ctrl(按住)+ Tab,快速切换已打开的文件

(5)Ctrl+ I ,缩进光标选中行代码(自动与上层代码对齐)。

(6)Ctrl + / ,快速注释或者取消注释光标选中行。

(7)快速修改全局变量名

鼠标光标选中变量名,按下Ctrl+Shift+R,当变量名称出现红色框表示已经激活全局修改功能。修改一处,整个工程对应变量名称全部会修改。修改完毕之后,光标移开,再按下Ctrl+Shift+R保存修改。

(8)快速修改全局函数名

快捷方式与变量修改一样按下Ctrl+Shift+R,一处修改整个工程对应的函数名称也会跟着改。选中函数后,按下Ctrl+Shift+R后整个工程的对应的函数名会高亮,并且在软件下方弹出修改框。

3.7 QT帮助文档

Qt 帮助文档太多,难以全部翻译成中文,即使翻译了一部分,翻译花的时间太多,翻译更新的时效性也难以保证,最终还是得看英文帮助,QtCreator 集成了帮助系统,查找非常方便。

打开QtCreator,选择菜单栏的最左边的帮助选项,界面如下:

(1)查看Qlabel控件的帮助信息:

3.8 UI设计师使用

上节的Hello QT程序使用纯C++代码编写,这一节使用QT界面设计模式实现与上一节Hello QT程序一样的功能。仿照着上节新创建一个工程。双击打开mainwindow.ui文件,进入到UI设计界面。

(1)拖一个Label控件到编辑区,双击Label控件可以修改文本内容。

(2)运行程序可以点击左下角的三角形符号或者按下快捷键Ctrl+R。

(3)UI设计师界面功能介绍

3.9 按钮控件组

QT Creator UI设计师界面的按钮组截图如下:

以下是对按钮组控件的一些功能介绍:

(1)Push Button按压按钮:最普通的按钮,按(点击)按钮命令计算机执行一些动作,或者回答问题,比如windows开始菜单里的重启,注销,关机等按钮。

(2)Tool Button工具按钮:工具按钮是一个集合,一般集成在工具栏里。比如打开,保存,复制,粘贴,剪切等常用的操作。

(3)Radio Button单选按钮:单选按钮是两个以上的形式出现在一块,按钮之间有互斥关系,每次只能选中一个。比如:一个人的性别只能选择一个,不能同时是男性又是女性。

(4)Check Box复选框:复选框与单选按钮概念相反,复选框表示多个可以同时存在的选项,比如一个人可以同时拥有多个爱好,比如读书、看电影、爬山、游泳等。

(5)Command Link Button命令链接按钮:一般用来打开的窗口或者网页链接。

(6)Dialog Button Box标准按钮盒:标准按钮盒用于对话框程序;比如:常见的确认对话框有 "确定""取消"等标准按钮,Qt 将这些典型的按钮做成标准按钮盒,并将相应的信号加以封装,方便程序员使用。

3.10 布局控件组

开发一个图形界面应用程序,界面的布局影响到界面的美观。前面的程序中都是使用UI界面拖控件,如果有多个按钮,会出现大小难调整、位置难对齐等问题。Qt 提供的"布局管理"就很好的解决了控件摆放的问题。

以下是UI设计师界面的布局相关控件组:

功能介绍:

(1)Vertical Layout:垂直布局

(2)Horizontal Layout:水平布局

(3)Grid Layout:网格布局

(4)Form Layout:窗体中布局

(5)Horizontal Spacers:水平空格,在布局中用来占位。

(6)Vertical Spacer:垂直空格,在布局中用来占位。

3.11 基本布局控件

在UI设计界面添加一个布局控件,然后将需要布局的其他控件放入布局控件中即可完成布局,布局控件可以互相嵌套使用。(本节只介绍基本布局控件的使用)

以下是4种布局控件的效果:

3.12 UI设计师的布局功能

在UI设计界面的左上角有一排快捷的布局选项,使用时选中两个以上的控件,点击其中一种布局方式就可以切换布局。

以下为布局的简单示例图:

(1)为布局的选项。

(2)控件层次图,可以看到控件的布局摆放层次。

如果想要控制某个控件的固定大小,不随着布局改变大小,可以限定最大最小尺寸。选中控件鼠标右键-->大小限定->设置大小。

水平布局与垂直布局:

水平布局将控件按照水平方式摆放,垂直布局将控件按照垂直方式摆放。鼠标拖动红色布局框上的黑色方点,可以调整布局框的大小。随着布局框的尺寸变化,包含的控件高度不会变化,宽度会随着布局框变化。选中其中一个控件然后鼠标右键>点击大小限定,可以限定控件的最大和最小尺寸。

分裂器水平布局与垂直布局:

分裂器方式布局,包含控件的高度和宽度都会随着布局框的拉伸而改变。选中其中一个控件然后鼠标右键>点击大小限定,可以限定控件的最大和最小尺寸。

窗体中布局与栅格布局:

栅格(网格)布局器的基本单元是单元格,而窗体中布局(表单)的基本单元是行。随着布局框的尺寸变化,包含的控件高度不会变化,宽度会随着布局框变化。

设置主窗体布局方式:

设置主窗体的布局方式后,包含在主窗体内的控件会随着窗体的拉伸自动调整大小。

四、上位机开发

当前项目使用的相关软件工具、模块源码已经上传到网盘:ccnr8sukk85n.feishu.cn/wiki/QjY8we...

4.1 Qt开发环境安装

Qt的中文官网: www.qt.io/zh-cn/![ima...

QT5.12.6的下载地址:download.qt.io/archive/qt/...

打开下载链接后选择下面的版本进行下载:

如果下载不了,可以在网盘里找到安装包下载: 飞书文档记录的网盘地址:ccnr8sukk85n.feishu.cn/wiki/QjY8we...

软件安装时断网安装,否则会提示输入账户。

安装的时候,第一个复选框里的编译器可以全选,直接点击下一步继续安装。

选择编译器: (一定要看清楚了)

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. 纯代码构建 UI :不依赖 .ui 文件,只需要把 .h.cpp 文件复制到项目中即可,无需配置 UI 设计器。
  2. MQTT 通信 :由于 Qt 官方的 QtMqtt 模块在标准安装包中不包含(需要自行编译),为了保证兼容性和降低门槛,本代码示例假设已经配置好了 QtMqtt 环境
    • 如果的 Qt 环境没有 Mqtt 模块,需要先编译安装 QtMqtt,或者使用第三方库(如 QMQTT)。
  3. 数据协议:采用标准的 JSON 格式与 STM32 通信。
  4. 功能覆盖:包含了连接云平台、实时数据显示(温湿度、重量、光照等)、阈值下发设置、日志显示。

1. 项目配置文件 (BeehiveMonitor.pro)

.pro 文件中,必须添加 mqtt 模块(如果使用的是官方模块)。

pro 复制代码
QT       += core gui network mqtt

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# You can rename the target as you like
TARGET = BeehiveMonitor
TEMPLATE = app

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

2. 头文件 (mainwindow.h)

定义了界面元素和业务逻辑。

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMqttClient>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QTextEdit>
#include <QGroupBox>
#include <QGridLayout>
#include <QSpinBox>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDateTime>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    // MQTT Slots
    void onConnectClicked();
    void onSubscribeClicked();
    void onPublishThresholdClicked();
    void updateLogStateChange();
    void brokerDisconnected();
    void onMessageReceived(const QByteArray &message, const QMqttTopicName &topic);

private:
    // --- UI 初始化函数 ---
    void initUI();
    void setupMqtt();

    // --- MQTT 客户端 ---
    QMqttClient *m_client;

    // --- UI 控件 ---
    // 1. 连接设置区域
    QLineEdit *le_host;
    QLineEdit *le_port;
    QLineEdit *le_clientId;
    QLineEdit *le_username;
    QLineEdit *le_password;
    QLineEdit *le_sub_topic; // 订阅主题(接收数据)
    QLineEdit *le_pub_topic; // 发布主题(下发命令)
    QPushButton *btn_connect;
    QPushButton *btn_subscribe;

    // 2. 实时数据通过 Label 显示
    QLabel *lbl_temp_in;
    QLabel *lbl_humi_in;
    QLabel *lbl_temp_out;
    QLabel *lbl_humi_out;
    QLabel *lbl_weight;
    QLabel *lbl_sound;
    QLabel *lbl_light;
    QLabel *lbl_vibration; // 状态:正常/异常

    // 3. 阈值设置
    QSpinBox *sb_temp_max;
    QSpinBox *sb_humi_max;
    QDoubleSpinBox *sb_weight_min; // 比如失重报警
    QSpinBox *sb_sound_max;
    QPushButton *btn_set_threshold;

    // 4. 日志区域
    QTextEdit *txt_log;

    // --- 辅助函数 ---
    void log(const QString &msg);
};

#endif // MAINWINDOW_H

3. 源文件 (mainwindow.cpp)

实现了核心逻辑:解析 JSON、更新 UI、发送 MQTT 指令。

cpp 复制代码
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    initUI();
    setupMqtt();
}

MainWindow::~MainWindow()
{
}

void MainWindow::initUI()
{
    QWidget *centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);
    QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);

    // === 1. 连接配置区域 ===
    QGroupBox *grpSettings = new QGroupBox("华为云 MQTT 连接配置", this);
    QGridLayout *layoutSettings = new QGridLayout;

    layoutSettings->addWidget(new QLabel("服务器地址:"), 0, 0);
    le_host = new QLineEdit("iot-mqtts.cn-north-4.myhuaweicloud.com"); // 示例地址
    layoutSettings->addWidget(le_host, 0, 1);

    layoutSettings->addWidget(new QLabel("端口:"), 0, 2);
    le_port = new QLineEdit("1883");
    layoutSettings->addWidget(le_port, 0, 3);

    layoutSettings->addWidget(new QLabel("Client ID:"), 1, 0);
    le_clientId = new QLineEdit("Beehive_PC_Client");
    layoutSettings->addWidget(le_clientId, 1, 1);

    layoutSettings->addWidget(new QLabel("用户名:"), 1, 2);
    le_username = new QLineEdit();
    layoutSettings->addWidget(le_username, 1, 3);

    layoutSettings->addWidget(new QLabel("密码:"), 2, 0);
    le_password = new QLineEdit();
    le_password->setEchoMode(QLineEdit::Password);
    layoutSettings->addWidget(le_password, 2, 1);

    btn_connect = new QPushButton("连接服务器");
    layoutSettings->addWidget(btn_connect, 2, 2, 1, 2);

    layoutSettings->addWidget(new QLabel("订阅Topic (接收):"), 3, 0);
    le_sub_topic = new QLineEdit("/beehive/data"); // 需根据华为云规则修改
    layoutSettings->addWidget(le_sub_topic, 3, 1, 1, 2);
    
    btn_subscribe = new QPushButton("订阅");
    btn_subscribe->setEnabled(false);
    layoutSettings->addWidget(btn_subscribe, 3, 3);

    layoutSettings->addWidget(new QLabel("发布Topic (命令):"), 4, 0);
    le_pub_topic = new QLineEdit("/beehive/cmd"); // 需根据华为云规则修改
    layoutSettings->addWidget(le_pub_topic, 4, 1, 1, 3);

    grpSettings->setLayout(layoutSettings);
    mainLayout->addWidget(grpSettings);

    // === 2. 实时监控面板 ===
    QGroupBox *grpData = new QGroupBox("蜂箱实时监测数据", this);
    QGridLayout *layoutData = new QGridLayout;

    // 样式设置
    QString labelStyle = "QLabel { font-size: 18px; font-weight: bold; color: #2E8B57; }";
    
    // 初始化 Label
    lbl_temp_in = new QLabel("0.0 ℃"); lbl_temp_in->setStyleSheet(labelStyle);
    lbl_humi_in = new QLabel("0.0 %"); lbl_humi_in->setStyleSheet(labelStyle);
    lbl_temp_out = new QLabel("0.0 ℃"); lbl_temp_out->setStyleSheet(labelStyle);
    lbl_humi_out = new QLabel("0.0 %"); lbl_humi_out->setStyleSheet(labelStyle);
    lbl_weight = new QLabel("0.00 kg"); lbl_weight->setStyleSheet("QLabel { font-size: 18px; font-weight: bold; color: blue; }");
    lbl_sound = new QLabel("0 dB"); lbl_sound->setStyleSheet(labelStyle);
    lbl_light = new QLabel("0 Lx"); lbl_light->setStyleSheet(labelStyle);
    lbl_vibration = new QLabel("正常"); lbl_vibration->setStyleSheet("QLabel { font-size: 18px; font-weight: bold; color: green; }");

    layoutData->addWidget(new QLabel("内部温度:"), 0, 0); layoutData->addWidget(lbl_temp_in, 0, 1);
    layoutData->addWidget(new QLabel("内部湿度:"), 0, 2); layoutData->addWidget(lbl_humi_in, 0, 3);
    
    layoutData->addWidget(new QLabel("外部温度:"), 1, 0); layoutData->addWidget(lbl_temp_out, 1, 1);
    layoutData->addWidget(new QLabel("外部湿度:"), 1, 2); layoutData->addWidget(lbl_humi_out, 1, 3);

    layoutData->addWidget(new QLabel("蜂箱重量:"), 2, 0); layoutData->addWidget(lbl_weight, 2, 1);
    layoutData->addWidget(new QLabel("光照强度:"), 2, 2); layoutData->addWidget(lbl_light, 2, 3);

    layoutData->addWidget(new QLabel("声音强度:"), 3, 0); layoutData->addWidget(lbl_sound, 3, 1);
    layoutData->addWidget(new QLabel("震动状态:"), 3, 2); layoutData->addWidget(lbl_vibration, 3, 3);

    grpData->setLayout(layoutData);
    mainLayout->addWidget(grpData);

    // === 3. 阈值设置面板 ===
    QGroupBox *grpControl = new QGroupBox("报警阈值配置", this);
    QHBoxLayout *layoutControl = new QHBoxLayout;

    layoutControl->addWidget(new QLabel("最高温度:"));
    sb_temp_max = new QSpinBox(); sb_temp_max->setRange(0, 60); sb_temp_max->setValue(38);
    layoutControl->addWidget(sb_temp_max);

    layoutControl->addWidget(new QLabel("最小重量(kg):"));
    sb_weight_min = new QDoubleSpinBox(); sb_weight_min->setRange(0, 50); sb_weight_min->setValue(5.0);
    layoutControl->addWidget(sb_weight_min);

    layoutControl->addWidget(new QLabel("声音阈值(dB):"));
    sb_sound_max = new QSpinBox(); sb_sound_max->setRange(0, 120); sb_sound_max->setValue(80);
    layoutControl->addWidget(sb_sound_max);

    btn_set_threshold = new QPushButton("下发配置");
    btn_set_threshold->setEnabled(false);
    layoutControl->addWidget(btn_set_threshold);

    grpControl->setLayout(layoutControl);
    mainLayout->addWidget(grpControl);

    // === 4. 日志显示 ===
    txt_log = new QTextEdit();
    txt_log->setReadOnly(true);
    txt_log->setMaximumHeight(150);
    mainLayout->addWidget(txt_log);

    // 信号连接
    connect(btn_connect, &QPushButton::clicked, this, &MainWindow::onConnectClicked);
    connect(btn_subscribe, &QPushButton::clicked, this, &MainWindow::onSubscribeClicked);
    connect(btn_set_threshold, &QPushButton::clicked, this, &MainWindow::onPublishThresholdClicked);

    setWindowTitle("智能蜂箱监测系统 - 上位机");
    resize(800, 600);
}

void MainWindow::setupMqtt()
{
    m_client = new QMqttClient(this);
    
    // 状态改变信号
    connect(m_client, &QMqttClient::stateChanged, this, &MainWindow::updateLogStateChange);
    connect(m_client, &QMqttClient::disconnected, this, &MainWindow::brokerDisconnected);

    // 收到消息信号
    connect(m_client, &QMqttClient::messageReceived, this, &MainWindow::onMessageReceived);
}

void MainWindow::onConnectClicked()
{
    if (m_client->state() == QMqttClient::Disconnected) {
        m_client->setHostname(le_host->text());
        m_client->setPort(le_port->text().toInt());
        m_client->setClientId(le_clientId->text());
        m_client->setUsername(le_username->text());
        m_client->setPassword(le_password->text());

        m_client->connectToHost();
    } else {
        m_client->disconnectFromHost();
    }
}

void MainWindow::updateLogStateChange()
{
    const QString content = QDateTime::currentDateTime().toString() + ": State Change -> " + QString::number(m_client->state());
    log(content);

    if (m_client->state() == QMqttClient::Connected) {
        btn_connect->setText("断开连接");
        btn_subscribe->setEnabled(true);
        btn_set_threshold->setEnabled(true);
        log("成功连接至华为云服务器!");
    } else {
        btn_connect->setText("连接服务器");
        btn_subscribe->setEnabled(false);
        btn_set_threshold->setEnabled(false);
    }
}

void MainWindow::brokerDisconnected()
{
    log("已断开连接");
}

void MainWindow::onSubscribeClicked()
{
    QString topic = le_sub_topic->text();
    auto subscription = m_client->subscribe(topic);
    if (!subscription) {
        log("订阅失败: " + topic);
        return;
    }
    log("成功订阅主题: " + topic);
}

// *** 核心:处理接收到的 JSON 数据 ***
// 假设 STM32 发送的 JSON 格式如下:
// {
//    "temp_in": 28.5, "humi_in": 60,
//    "temp_out": 22.0, "humi_out": 45,
//    "weight": 10.5, "light": 1500,
//    "sound": 40, "vib": 0
// }
void MainWindow::onMessageReceived(const QByteArray &message, const QMqttTopicName &topic)
{
    log("收到数据 [" + topic.name() + "]: " + message);

    QJsonDocument doc = QJsonDocument::fromJson(message);
    if (doc.isNull() || !doc.isObject()) {
        log("JSON 解析失败");
        return;
    }

    QJsonObject obj = doc.object();

    // 更新内部温湿度
    if (obj.contains("temp_in")) lbl_temp_in->setText(QString::number(obj["temp_in"].toDouble(), 'f', 1) + " ℃");
    if (obj.contains("humi_in")) lbl_humi_in->setText(QString::number(obj["humi_in"].toDouble(), 'f', 1) + " %");

    // 更新外部温湿度
    if (obj.contains("temp_out")) lbl_temp_out->setText(QString::number(obj["temp_out"].toDouble(), 'f', 1) + " ℃");
    if (obj.contains("humi_out")) lbl_humi_out->setText(QString::number(obj["humi_out"].toDouble(), 'f', 1) + " %");

    // 更新重量
    if (obj.contains("weight")) lbl_weight->setText(QString::number(obj["weight"].toDouble(), 'f', 2) + " kg");

    // 更新光照
    if (obj.contains("light")) lbl_light->setText(QString::number(obj["light"].toInt()) + " Lx");

    // 更新声音
    if (obj.contains("sound")) lbl_sound->setText(QString::number(obj["sound"].toInt()) + " dB");

    // 更新震动状态
    if (obj.contains("vib")) {
        int vib = obj["vib"].toInt();
        if (vib == 1) {
            lbl_vibration->setText("⚠ 异常震动!");
            lbl_vibration->setStyleSheet("QLabel { font-size: 18px; font-weight: bold; color: red; }");
        } else {
            lbl_vibration->setText("正常");
            lbl_vibration->setStyleSheet("QLabel { font-size: 18px; font-weight: bold; color: green; }");
        }
    }
}

// *** 核心:下发阈值配置 ***
void MainWindow::onPublishThresholdClicked()
{
    QString topic = le_pub_topic->text();
    
    // 构建 JSON
    QJsonObject jobj;
    jobj["cmd"] = "set_threshold";
    jobj["temp_max"] = sb_temp_max->value();
    jobj["weight_min"] = sb_weight_min->value();
    jobj["sound_max"] = sb_sound_max->value();

    QJsonDocument doc(jobj);
    QByteArray payload = doc.toJson(QJsonDocument::Compact);

    if (m_client->publish(topic, payload) == -1) {
        log("指令下发失败");
    } else {
        log("指令下发成功: " + payload);
    }
}

void MainWindow::log(const QString &msg)
{
    txt_log->append(QDateTime::currentDateTime().toString("HH:mm:ss") + " " + msg);
}

4. 主程序入口 (main.cpp)

cpp 复制代码
#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

5. 使用说明与注意事项

1. 关于 QtMqtt 模块

本代码使用了 QMqttClient 类,这是 Qt 官方提供的 MQTT 封装。

  • Qt 5.10+ / Qt 6+ :需要手动编译 qtmqtt 库。
  • Linux (Ubuntu) :可以通过 sudo apt install qt6-mqtt-dev (针对 Qt6) 安装。
  • 如果无法编译 QtMqtt :需要将代码中的 QMqttClient 替换为第三方的 QMQTT 库(GitHub 上有很多单头文件的库)。
2. 华为云配置

为了成功连接华为云,需要在上位机软件界面填入正确的鉴权信息。

  • Server : iot-mqtts.cn-north-4.myhuaweicloud.com (请根据的华为云实际区域填写)。
  • Client ID : 格式为 device_id_0_0_timestamp
  • Username : device_id
  • Password : 需要使用 HMAC-SHA256 算法,通过 Device Secret 生成。华为云控制台提供了在线生成工具,或者可以写一个小工具生成后填入。
3. 通信协议对应

确保 STM32 端上传的 JSON 键名(Key)与 mainwindow.cpp 中解析的键名一致:

  • temp_in, humi_in
  • temp_out, humi_out
  • weight
  • light
  • sound
  • vib (0为正常,1为报警)

4.5 编译Windows上位机

点击软件左下角的绿色三角形按钮进行编译运行。

4.6 配置Android环境

如果想编译Android手机APP,必须要先自己配置好自己的Android环境。(搭建环境的过程可以自行百度搜索学习)

然后才可以进行下面的步骤。

【1】选择Android编译器

选择编译器。

切换编译器。

【2】创建Android配置文件

创建完成。

【3】配置Android图标与名称

根据自己的需求配置 Android图标与名称。

【3】编译Android上位机

Qt本身是跨平台的,直接选择Android的编译器,就可以将程序编译到Android平台。

然后点击构建。

成功之后,在目录下可以看到生成的apk文件,也就是Android手机的安装包,电脑端使用QQ发送给手机QQ,手机登录QQ接收,就能直接安装。

生成的apk的目录在哪里呢? 编译完成之后,在控制台会输出APK文件的路径。

知道目录在哪里之后,在Windows的文件资源管理器里,找到路径,具体看下图,找到生成的apk文件。

cpp 复制代码
File: D:/QtProject/build-333_QtProject-Android_for_arm64_v8a_Clang_Qt_5_12_6_for_Android_ARM64_v8a-Release/android-build//build/outputs/apk/debug/android-build-debug.apk

4.7 设备仿真调试

通过MQTT客户端模拟设备登录华为云服务器。进行设备联调,实现数据上传和下发测试。

五、STM32代码设计

当前项目使用的相关软件工具、模块源码已经上传到网盘:ccnr8sukk85n.feishu.cn/wiki/QjY8we...

5.1 硬件连线说明


1. I2C 总线设备(OLED 显示屏 + BH1750 光照传感器)

这两个模块都使用 I2C 通信协议,可以并联挂载在同一个 I2C 总线上(STM32 的 I2C1 接口)。

模块引脚 STM32 引脚 说明
OLED SCL PB6 I2C1_SCL
OLED SDA PB7 I2C1_SDA
OLED VCC 3.3V OLED 供电
OLED GND GND 共地
BH1750 SCL PB6 与 OLED 并联
BH1750 SDA PB7 与 OLED 并联
BH1750 VCC 3.3V or 5V 模块通常兼容
BH1750 GND GND 共地
BH1750 ADDR GND (或悬空) 地址选择脚,接地默认地址

2. 温湿度传感器(内部 & 外部)

采用单总线协议,分别占用两个普通 GPIO 口。

模块引脚 STM32 引脚 说明
DHT11(内) DATA PA4 内部温湿度数据脚
DHT11(内) VCC 3.3V or 5V 建议 3.3V 以匹配逻辑电平
DHT11(内) GND GND 共地
DHT11(外) DATA PA5 外部温湿度数据脚
DHT11(外) VCC 3.3V or 5V
DHT11(外) GND GND

注意:DHT11 的 DATA 引脚通常需要接一个 4.7kΩ - 10kΩ 的上拉电阻到 VCC(很多成品模块已内置)。


3. 重量监测模块(HX711 + 称重传感器)

HX711 是高精度 ADC,使用自定义的串行协议。

模块引脚 STM32 引脚 说明
HX711 DT (Data) PA6 数据引脚
HX711 SCK (Clock) PA7 时钟引脚
HX711 VCC 5V 建议接 5V 以提供更稳定的传感器激励电压
HX711 GND GND 共地

接线说明:称重传感器的 E+/E-/A+/A- 请严格按照传感器线色连接到 HX711 模块对应端口。


4. 声音采集模块(MAX9814)

输出模拟电压信号,需要连接 STM32 的 ADC 引脚。

模块引脚 STM32 引脚 说明
MAX9814 OUT PA1 连接 ADC1_IN1
MAX9814 VCC 3.3V or 5V
MAX9814 GND GND
MAX9814 GAIN (可选) 增益设置,悬空默认 60dB,或接 VCC/GND 调整

5. 震动监测模块(SW420)

通常输出数字开关量(0 或 1),连接普通 GPIO 即可。

模块引脚 STM32 引脚 说明
SW420 DO PB0 数字输出引脚 (Digital Out)
SW420 VCC 3.3V or 5V
SW420 GND GND

6. 通信模块(二选一:Air780E 或 ESP8266)

使用串口 (USART2) 进行通信。注意 TX 和 RX 要交叉连接。

方案 A: Air780E (4G Cat.1)
模块引脚 STM32 引脚 说明
Air780 TX PA3 STM32 的 RX2
Air780 RX PA2 STM32 的 TX2
Air780 VCC 5V / VBAT 4G 模块电流大,建议直接由电源模块供电,不要从 STM32 取电
Air780 GND GND 务必共地
方案 B: ESP8266-01S (Wi-Fi)
模块引脚 STM32 引脚 说明
ESP8266 TX PA3 STM32 的 RX2
ESP8266 RX PA2 STM32 的 TX2
ESP8266 3V3 3.3V 严禁接 5V,会烧毁
ESP8266 EN 3.3V 使能脚,必须接高电平
ESP8266 GND GND 共地

7. 电源管理模块

系统供电是稳定的关键。

  • 18650 锂电池 输出 (3.7V - 4.2V) 连接到 AMS1117-3.3AMS1117-5.0 输入端(或者使用升压/降压模块)。
  • STM32 供电:将稳压后的 3.3V 接到 STM32 的 3.3V 引脚,GND 接 GND。
  • 5V 总线:用于给 HX711、Air780E 供电。
  • 3.3V 总线:用于给 STM32、OLED、DHT11、ESP8266 供电。

总结引脚分配表

STM32 引脚 功能分配 备注
PA1 MAX9814 (ADC) 声音采集
PA2 USART2_TX 连接通信模块 RX
PA3 USART2_RX 连接通信模块 TX
PA4 GPIO DHT11 (内部)
PA5 GPIO DHT11 (外部)
PA6 GPIO HX711_DT (数据)
PA7 GPIO HX711_SCK (时钟)
PB0 GPIO SW420 (震动)
PB6 I2C1_SCL OLED & BH1750
PB7 I2C1_SDA OLED & BH1750
PA9/PA10 USART1 用于程序下载调试 (ST-Link 或 串口下载)

5.2 传感器代码

这是一个基于 STM32F103C8T6 的完整底层驱动代码集合。为了满足"寄存器方式,不采用库"的要求,代码直接操作寄存器(如 GPIOA->CRLRCC->APB2ENR 等)。

为了方便你直接使用,我将代码组织为几个核心部分。你可以将这些代码分别保存为 .c.h 文件,或者整合到一个 main.c 中(虽然不建议这样做,但为了展示方便,下面按功能模块列出)。

前提条件

  1. 开发环境需包含 CMSIS 核心头文件 stm32f10x.h(这是所有 STM32 开发的基础,定义了寄存器结构体)。
  2. 系统时钟默认配置为 72MHz。

1. 基础延时与系统配置 (System)

核心功能:提供微秒级延时(DHT11必须)和毫秒级延时。

c 复制代码
// system.c
#include "stm32f10x.h"

// 系统时钟初始化 (72MHz) - 通常启动文件已处理,这里确保外设时钟开启
void System_Init_Config(void) {
    // 开启 GPIOA, GPIOB, AFIO, USART1, USART2, ADC1 时钟
    RCC->APB2ENR |= (1<<2) | (1<<3) | (1<<0) | (1<<14) | (1<<9); 
    RCC->APB1ENR |= (1<<17); // 开启 USART2 时钟
}

// 简单的 SysTick 延时函数 (72MHz)
void Delay_us(uint32_t us) {
    SysTick->LOAD = 72 * us;         // 装载计数值
    SysTick->VAL = 0x00;             // 清空计数器
    SysTick->CTRL = 0x00000005;      // 使能,源时钟AHB
    while(!(SysTick->CTRL & 0x00010000)); // 等待计数归零
    SysTick->CTRL = 0x00000004;      // 关闭定时器
}

void Delay_ms(uint32_t ms) {
    while(ms--) {
        Delay_us(1000);
    }
}

2. GPIO 工具函数 (GPIO Utils)

由于寄存器操作配置 GPIO 模式比较繁琐,定义两个辅助函数用于 DHT11 等需要频繁切换输入/输出的引脚。

c 复制代码
// 模式定义: 0=Input, 1=Output_PP_50MHz
void GPIO_SetMode(GPIO_TypeDef* GPIOx, uint8_t Pin, uint8_t Mode) {
    uint32_t *cr_reg;
    uint8_t shift;
    
    if (Pin < 8) {
        cr_reg = &GPIOx->CRL;
        shift = Pin * 4;
    } else {
        cr_reg = &GPIOx->CRH;
        shift = (Pin - 8) * 4;
    }

    *cr_reg &= ~(0xF << shift); // 清除原有配置
    
    if (Mode == 1) { // Output Push-Pull 50MHz
        *cr_reg |= (0x3 << shift); 
    } else { // Input Pull-Up/Down (需配合ODR设置电平)
        *cr_reg |= (0x8 << shift); 
    }
}

3. DHT11 温湿度传感器驱动

硬件连接:PA4 (内部), PA5 (外部)

c 复制代码
// 读取 DHT11 数据
// GPIOx: GPIOA, Pin: 4 or 5
// 返回值: 0-成功, 1-失败
uint8_t DHT11_Read_Data(GPIO_TypeDef* GPIOx, uint8_t Pin, uint8_t *temp, uint8_t *humi) {
    uint8_t buf[5];
    uint8_t i, j;
    
    // 1. 主机发送开始信号
    GPIO_SetMode(GPIOx, Pin, 1); // 输出模式
    GPIOx->BRR = (1 << Pin);     // 拉低
    Delay_ms(20);                // 保持至少18ms
    GPIOx->BSRR = (1 << Pin);    // 拉高
    Delay_us(30);
    
    // 2. 切换为输入,等待响应
    GPIO_SetMode(GPIOx, Pin, 0); // 输入模式
    
    // 检测响应信号(低电平80us -> 高电平80us)
    if (!(GPIOx->IDR & (1 << Pin))) {
        while(!(GPIOx->IDR & (1 << Pin))); // 等待变高
        while(GPIOx->IDR & (1 << Pin));    // 等待变低
        
        // 3. 开始接收 40bit 数据
        for(i = 0; i < 5; i++) {
            buf[i] = 0;
            for(j = 0; j < 8; j++) {
                while(!(GPIOx->IDR & (1 << Pin))); // 等待变为高电平(开始传输位)
                Delay_us(40); // 延时40us判断是0还是1
                
                if(GPIOx->IDR & (1 << Pin)) { // 如果还在高电平,则是1
                    buf[i] |= (1 << (7 - j));
                    while(GPIOx->IDR & (1 << Pin)); // 等待变为低电平
                }
            }
        }
        
        // 4. 校验
        if((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) {
            *humi = buf[0];
            *temp = buf[2];
            return 0;
        }
    }
    return 1;
}

4. HX711 重量传感器驱动

硬件连接:DT=PA6, SCK=PA7

c 复制代码
unsigned long HX711_Read(void) {
    unsigned long count = 0;
    unsigned char i;
    
    // PA6 Input, PA7 Output (在初始化中配置好)
    // DT=PA6, SCK=PA7
    
    GPIOA->BRR = (1 << 7); // SCK = 0
    
    // 等待 DT 变低,表示转换完成
    while(GPIOA->IDR & (1 << 6)); 
    
    for(i = 0; i < 24; i++) {
        GPIOA->BSRR = (1 << 7); // SCK = 1
        Delay_us(1);
        count = count << 1;
        GPIOA->BRR = (1 << 7);  // SCK = 0
        Delay_us(1);
        if(GPIOA->IDR & (1 << 6)) count++;
    }
    
    // 第25个脉冲,设置增益 128
    GPIOA->BSRR = (1 << 7); 
    Delay_us(1);
    GPIOA->BRR = (1 << 7); 
    Delay_us(1);
    
    return count ^ 0x800000; // 转换符号位
}

// 初始化 GPIO
void HX711_Init(void) {
    // PA6 Input (Floating/PU), PA7 Output
    GPIO_SetMode(GPIOA, 6, 0);
    GPIOA->ODR |= (1<<6); // Pull Up
    GPIO_SetMode(GPIOA, 7, 1);
}

5. 软件 I2C 驱动 (OLED & BH1750)

硬件连接:SCL=PB6, SDA=PB7

c 复制代码
#define SCL_H GPIOB->BSRR = (1 << 6)
#define SCL_L GPIOB->BRR  = (1 << 6)
#define SDA_H GPIOB->BSRR = (1 << 7)
#define SDA_L GPIOB->BRR  = (1 << 7)
#define READ_SDA (GPIOB->IDR & (1 << 7))

void I2C_GPIO_Config(void) {
    // 配置 PB6, PB7 为开漏输出 (Open-Drain, 50MHz)
    // CNF=01, MODE=11 -> 0x7
    GPIOB->CRL &= 0x00FFFFFF; 
    GPIOB->CRL |= 0x77000000; 
    SCL_H; SDA_H;
}

void I2C_Start(void) {
    SDA_H; SCL_H; Delay_us(4);
    SDA_L; Delay_us(4);
    SCL_L;
}

void I2C_Stop(void) {
    SCL_L; SDA_L; Delay_us(4);
    SCL_H; SDA_H; Delay_us(4);
}

void I2C_SendByte(uint8_t Byte) {
    uint8_t i;
    for(i = 0; i < 8; i++) {
        if(Byte & 0x80) SDA_H; else SDA_L;
        Byte <<= 1;
        Delay_us(2);
        SCL_H; Delay_us(2);
        SCL_L; Delay_us(2);
    }
    // 等待 ACK (简单处理,忽略返回值)
    SDA_H; SCL_H; Delay_us(2); SCL_L;
}

uint8_t I2C_ReadByte(void) {
    uint8_t i, val = 0;
    SDA_H; // 释放总线
    for(i = 0; i < 8; i++) {
        val <<= 1;
        SCL_H; Delay_us(2);
        if(READ_SDA) val++;
        SCL_L; Delay_us(2);
    }
    // 发送 NoACK (最后一位)
    SDA_H; SCL_H; Delay_us(2); SCL_L;
    return val;
}

6. BH1750 光照传感器驱动

c 复制代码
#define BH1750_ADDR 0x46 // ADDR接地时地址

void BH1750_Init(void) {
    I2C_Start();
    I2C_SendByte(BH1750_ADDR);
    I2C_SendByte(0x01); // Power On
    I2C_Stop();
}

void BH1750_Start(void) {
    I2C_Start();
    I2C_SendByte(BH1750_ADDR);
    I2C_SendByte(0x10); // H-Resolution Mode
    I2C_Stop();
}

uint16_t BH1750_Read(void) {
    uint16_t data;
    I2C_Start();
    I2C_SendByte(BH1750_ADDR | 1); // Read mode
    data = I2C_ReadByte();
    data = (data << 8) + I2C_ReadByte();
    I2C_Stop();
    return data; // 单位 Lux
}

7. MAX9814 (ADC) 驱动

硬件连接:PA1 (ADC1_IN1)

c 复制代码
void ADC_Config(void) {
    // PA1 模拟输入模式 (CNF=00, MODE=00)
    GPIOA->CRL &= 0xFFFFFF0F;
    
    // ADC1 时钟预分频 (Max 14MHz, PCLK2=72M, DIV6=12M)
    RCC->CFGR |= (2 << 14); 
    
    // 复位 ADC
    ADC1->CR2 |= (1 << 3);
    while(ADC1->CR2 & (1 << 3));
    
    // 校准 ADC
    ADC1->CR2 |= (1 << 2);
    while(ADC1->CR2 & (1 << 2));
    
    // 配置规则组: 序列1 为 通道1
    ADC1->SQR3 = 1; 
    
    // 开启 ADC
    ADC1->CR2 |= 1; 
}

uint16_t Get_Sound_Level(void) {
    ADC1->CR2 |= 1; // 再次置位启动转换
    while(!(ADC1->SR & (1 << 1))); // 等待 EOC
    return ADC1->DR;
}

8. USART2 通信驱动 (连接 Air780E/ESP8266)

硬件连接:PA2 (TX), PA3 (RX)

c 复制代码
void USART2_Config(uint32_t baud) {
    // PA2 (TX): AF_PP (1011 -> 0xB)
    GPIOA->CRL &= 0xFFFF00FF;
    GPIOA->CRL |= 0x00004B00; // PA2=AF_PP, PA3=Input Floating(0x4)
    
    // 波特率计算 (PCLK1 = 36MHz)
    // USARTDIV = 36000000 / 115200 = 312.5
    // Mantissa = 312 (0x138), Fraction = 0.5*16 = 8
    // BRR = 0x1388
    float div = 36000000.0 / baud;
    uint16_t mantissa = (uint16_t)div;
    uint16_t fraction = (uint16_t)((div - mantissa) * 16);
    USART2->BRR = (mantissa << 4) + fraction;
    
    // 使能 UE, TE, RE, RXNE中断
    USART2->CR1 = (1<<13) | (1<<3) | (1<<2) | (1<<5); 
    
    // 配置 NVIC
    NVIC_EnableIRQ(USART2_IRQn);
}

void USART2_SendChar(char ch) {
    while(!(USART2->SR & (1 << 7))); // 等待 TXE
    USART2->DR = ch;
}

void USART2_SendString(char *str) {
    while(*str) {
        USART2_SendChar(*str++);
    }
}

// 中断接收处理
void USART2_IRQHandler(void) {
    if(USART2->SR & (1 << 5)) { // RXNE
        uint8_t data = USART2->DR;
        // 在这里处理接收到的 MQTT 数据
    }
}

9. OLED 驱动 (简化版)

硬件连接:同 I2C

c 复制代码
#define OLED_ADDR 0x78 

void OLED_WriteCmd(uint8_t cmd) {
    I2C_Start();
    I2C_SendByte(OLED_ADDR);
    I2C_SendByte(0x00); // Co=0, D/C#=0
    I2C_SendByte(cmd);
    I2C_Stop();
}

void OLED_WriteData(uint8_t data) {
    I2C_Start();
    I2C_SendByte(OLED_ADDR);
    I2C_SendByte(0x40); // Co=0, D/C#=1
    I2C_SendByte(data);
    I2C_Stop();
}

void OLED_Init(void) {
    Delay_ms(100);
    // 常用初始化序列
    OLED_WriteCmd(0xAE); // Display Off
    OLED_WriteCmd(0x20); OLED_WriteCmd(0x02); // Set Memory Addressing Mode
    OLED_WriteCmd(0xB0); // Set Page Start Address
    OLED_WriteCmd(0xC8); // Set COM Output Scan Direction
    OLED_WriteCmd(0x00); // Set Low Column Address
    OLED_WriteCmd(0x10); // Set High Column Address
    OLED_WriteCmd(0x40); // Set Start Line Address
    OLED_WriteCmd(0x81); OLED_WriteCmd(0xCF); // Contrast
    OLED_WriteCmd(0xA1); // Set Segment Re-map
    OLED_WriteCmd(0xA6); // Normal Display
    OLED_WriteCmd(0xA8); OLED_WriteCmd(0x3F); // Multiplex Ratio
    OLED_WriteCmd(0xAF); // Display On
}

// 清屏
void OLED_Clear(void) {
    uint8_t i, n;
    for(i=0;i<8;i++) {
        OLED_WriteCmd(0xB0 + i);
        OLED_WriteCmd(0x00);
        OLED_WriteCmd(0x10);
        for(n=0;n<128;n++) OLED_WriteData(0x00);
    }
}

5.3 项目核心代码

10. 主函数 (Main Loop)

这是如何调度上述驱动的示例。

c 复制代码
// main.c 示例
int main(void) {
    uint8_t t_in, h_in, t_out, h_out;
    uint16_t light, sound;
    long weight;
    char buffer[128];

    System_Init_Config();
    I2C_GPIO_Config();
    ADC_Config();
    HX711_Init();
    USART2_Config(115200); // 连接 4G/Wifi
    
    OLED_Init();
    OLED_Clear();
    BH1750_Init();
    
    while(1) {
        // 1. 采集数据
        DHT11_Read_Data(GPIOA, 4, &t_in, &h_in);
        DHT11_Read_Data(GPIOA, 5, &t_out, &h_out);
        light = BH1750_Read();
        weight = HX711_Read() / 100; // 简单比例换算,实际需校准公式
        sound = Get_Sound_Level();
        
        // 2. 处理震动 (PB0)
        uint8_t vib = (GPIOB->IDR & 1);
        
        // 3. 格式化为 JSON 字符串 (需自行实现 sprintf 或引用 stdio.h)
        // {"temp_in":25,"weight":1000...}
        // sprintf(buffer, "{\"temp_in\":%d, \"humi_in\":%d, \"weight\":%ld}\r\n", t_in, h_in, weight);
        
        // 4. 通过串口发送给通信模块
        // USART2_SendString(buffer);
        
        // 5. OLED 显示 (需配合字库)
        
        Delay_ms(1000); // 1秒采集一次
    }
}
  1. 头文件包含 :所有 .c 文件都需要 #include "stm32f10x.h"
  2. 中断处理 :如果使用 USART 中断,请确保在启动代码(.s 文件)中没有注释掉 USART2_IRQHandler,或者你自己定义了它。
  3. HX711 校准:代码中返回的是原始 ADC 值,实际使用时需要减去皮重(空载值)并除以系数(Gap Value)才能得到克数。
  4. OLED 字库 :由于篇幅限制,这里没有提供 8x16 的点阵数组(F8X16[]

5.4 程序下载

也有视频教程:

讲解如何编译代码,下载STM32程序: www.bilibili.com/video/BV1Cw...

打STM32的keil工程,编译代码、然后,使用USB线将开发板的左边的USB口(串口1)与电脑的USB连接,打开程序下载软件下载程序。

具体下载过程看下面图:

打开程序下载软件:[软件就在资料包里的软件工具目录下]

5.5 程序正常运行效果

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

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

5.6 取模软件的使用

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

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

打开软件之后:

六、总结

本项目成功设计并构建了一套基于 STM32 与华为云平台的物联网蜂箱智能监测系统。系统以 STM32F103C8T6 为核心控制单元,有机融合了多维传感器网络(温湿度、重量、声音、震动、光照)、无线通信技术(4G/Wi-Fi)以及 Qt 上位机可视化技术,实现了从数据感知、边缘处理、远程传输到云端交互的完整闭环。

通过本设计,有效解决了传统养蜂业中依赖人工巡查、管理效率低且易干扰蜂群正常生活等核心痛点。系统不仅能够全天候实时监测蜂箱的内外环境与蜂群活力,还能通过重量分析与震动检测功能,对分蜂、蜂蜜满载、天敌入侵等异常情况进行及时预警。这种"非侵入式"的智能管理模式,大幅降低了养蜂人的劳动强度,为蜂群健康繁育和蜂蜜增产提供了科学、客观的数据支撑。总体而言,本系统硬件成本可控、功能实用且运行稳定,充分验证了物联网技术在智慧农业细分领域的应用潜力和实用价值,具有良好的推广前景。

相关推荐
QZQ5418838 分钟前
C++编译期计算
后端
饕餮争锋41 分钟前
Spring内置的Bean作用域介绍
java·后端·spring
CryptoRzz43 分钟前
美股 (US) 与 墨西哥 (Mexico) 股票数据接口集成指南
后端
张人大 Renda Zhang1 小时前
Java 虚拟线程 Virtual Thread:让“每请求一线程”在高并发时代复活
java·jvm·后端·spring·架构·web·虚拟线程
一勺菠萝丶1 小时前
解决 SLF4J 警告问题 - 完整指南
java·spring boot·后端
零日失眠者1 小时前
【文件管理系列】001:文件批量重命名工具
后端·shell
申阳2 小时前
Day 19:02. 基于 SpringBoot4 开发后台管理系统-项目初始化
前端·后端·程序员
Undoom2 小时前
openEuler iSula 容器引擎关键性能指标量化评测
后端
q_19132846952 小时前
基于Springboot2+Vue2的旅游景点购票系统
java·vue.js·spring boot·后端·mysql·毕业设计·计算机毕业设计