基于STM32的智慧农业管理系统设计与实现

news2024/11/25 15:23:45

文章目录

  • 一、前言
    • 1.1 项目介绍
      • 【1】项目功能
      • 【2】设计实现的功能
      • 【3】项目硬件模块组成
    • 1.2 设计思路
    • 1.3 传感器功能介绍
    • 1.4 开发工具的选择
  • 二、EMQX开源MQTT服务器框架
  • 三、购买ECS云服务器
    • 3.1 登录官网
    • 3.2 购买ECS服务器
    • 3.3 配置安全组
    • 3.4 安装FinalShell
    • 3.5 远程登录到云服务器终端
  • 四、Linux下安装EMQX
    • 4.1 官网地址
    • 4.2 通过Apt源安装
    • 4.3 EMQX常用的命令
  • 五、配置EMQX服务器
    • 5.1 登录EMQX内置管理控制台
    • 5.2 MQTT配置
    • 5.3 测试MQTT通信
    • 5.4 MQTT客户端登录服务器测试
    • 5.5 客户端认证配置
    • 5.6 客户端授权配置
    • 5.7 数据转发(集成)
  • 七、MQTT客户端消息互发测试
    • 7.1 添加2个设备
    • 7.2 设备间测试
  • 八、STM32硬件端开发
    • 8.1 BC26模块的AT指令调试过程
    • 8.2 BH1750传感器
    • 8.3 DHT11温湿度模块
    • 8.4 土壤湿度传感器
    • 8.5 硬件连线
    • 8.6 KEIL工程
    • 8.7 取模软件使用
    • 8.8 核心代码
    • 8.9 程序正常运行效果
    • 8.10 硬件技术问题
      • (1)STM32与物联网服务器的交互协议
      • (2)ESP8266-WIFI功能介绍
      • (3)DHT11温湿度传感器
  • 九、硬件选型
    • 【1】母对母杜邦线
    • 【2】洞洞板
    • 【3】USB下载线
    • 【4】STM32F103RCT6开发板
    • 【5】 DHT11温湿度传感器
    • 【6】BH1750光敏传感器
    • 【7】 ESP8266 WIFI
    • 【8】 继电器
    • 【9】土壤湿度传感器
  • 十、上位机开发
    • 10.1 说明
    • 10.2 查看全部的API接口
    • 10.3 创建API密匙
    • 10.4 测试API: 获取节点信息
    • 10.5 在线调试(获取主题列表)
    • 10.6 在线调试(发布主题)
    • 10.7 安装Qt环境
    • 10.8 新建Qt工程
    • 10.9 设计代码
      • 【1】获取服务器节点信息
      • 【2】获得订阅的主题列表
      • 【3】发布主题
  • 十一、制作过程
  • 十二、总结

一、前言

1.1 项目介绍

【1】项目功能

随着全球农业现代化进程的加快,以及物联网、人工智能等先进技术的发展与应用,智慧农业已经成为现代农业发展的新趋势。基于精准感知、智能控制和远程管理的智慧农业系统能够显著提升农作物生产效率,降低资源消耗,实现环境友好型可持续农业生产。

在当前背景下,我国正大力推进数字乡村建设,智慧农业管理系统作为其中的重要组成部分,对于提高农业生产精细化管理水平,解决传统农业中信息获取不及时、人工管理成本高、决策缺乏科学依据等问题具有重要作用。

本设计开发一套基于STM32F103RCT6主控芯片的智慧农业管理系统,通过集成DHT11温湿度传感器、BH1750光照强度传感器以及土壤湿度检测传感器,实时监测农田环境和作物生长状态,并在超出阈值时通过蜂鸣器报警,提醒管理人员进行灌溉、施肥等操作。同时,采用NBIoT通信技术(BC26模块)将采集到的数据上传至云端,利用EMQX开源MQTT服务器框架部署于华为云ECS服务器上的MQTT服务器,实现数据的远程展示与处理。

系统支持微信小程序远程控制功能,使得农户或管理者可以随时随地查看农田环境参数、接收预警信息,并能远程手动控制灌溉设备、补光灯等,大大提高了农业生产的智能化和便捷性。此项目的实施不仅有助于推动我国农业信息化水平的提升,也有利于农业资源的高效利用,对保障国家粮食安全、促进农业增效、农民增收具有重要意义。

img

image-20240228104600454

【2】设计实现的功能

(1)实时环境监测:系统通过集成的DHT11温湿度传感器、BH1750光照强度传感器以及土壤湿度检测传感器,实时监测农田环境中的温度、湿度、光照强度和土壤含水量等关键参数。当这些参数超过或低于预设阈值时,系统将自动触发蜂鸣器报警,提醒管理人员关注并采取相应措施。

(2)自动化管理与预警:根据土壤湿度传感器检测的数据,如果土壤湿度低于设定的适宜作物生长的含水量阀值,则系统会自动提醒管理者进行灌溉操作。同时,可以按照预设周期发送施肥提醒,以确保农作物在最佳时期得到充足的水分和养分供应。

(3)远程控制功能:利用NBIoT通信技术(BC26模块)将现场采集到的各项数据上传至云端MQTT服务器,并通过微信小程序实现远程访问和展示。用户可以通过微信小程序查看实时监测数据,以及对农田设备进行远程手动控制,如启动或关闭5V抽水泵进行灌溉,开启或关闭白色LED补光灯调节光照条件。

(4)数据上云与分析:基于EMQX开源MQTT服务器框架搭建的MQTT服务器,能够接收并处理STM32主控板传输的农业环境数据,并对接微信小程序平台,为用户提供直观易懂的数据图表和分析结果,便于农户或农业技术人员进行科学决策和精准管理。

【3】项目硬件模块组成

(1)主控模块: 采用STM32F103RCT6微控制器作为核心控制单元,负责整个系统的运行和管理。STM32F103RCT6具有丰富的外设接口、强大的处理能力和低功耗特性,能够实时处理传感器数据、执行逻辑判断,并通过无线通信模块发送和接收指令。

(2)环境监测模块:

  • 温湿度监测:使用DHT11温湿度传感器采集农田环境的温度和湿度信息。
  • 光照强度监测:采用BH1750光照强度传感器测量农田的光照强度。
  • 土壤湿度检测:使用土壤湿度检测传感器获取作物生长区域的土壤含水量数据。

(3)控制输出模块:

  • 补光灯控制:配置白色LED灯作为补光光源,根据光照强度监测结果,通过STM32主控板进行智能调节或远程手动控制。
  • 灌溉系统控制:采用5V抽水泵配合继电器实现灌溉功能,当土壤湿度低于预设阈值时,STM32主控板将控制继电器闭合,启动抽水泵进行灌溉;反之则停止灌溉。

(4)无线通信模块: 集成NBIoT-BC26模块,实现与云端服务器的数据交互。该模块具备广覆盖、低功耗、大连接的特点,可确保在各种复杂农业环境中稳定地传输数据至MQTT服务器。

(5)报警模块: 系统配备蜂鸣器用于异常情况报警,当环境参数超出设定范围时,主控板会驱动蜂鸣器发出声音警报。

1.2 设计思路

(1)系统需求分析:根据智慧农业管理的实际需求,确定需要监测的关键环境参数(温度、湿度、光照强度和土壤湿度),以及必要的控制功能(灌溉、补光灯控制等)。同时考虑远程监控与预警的需求,规划通过NBIoT通信技术实现数据上传及远程操控。

(2)硬件选型与设计:

  • 主控芯片选择STM32F103RCT6,因其具有丰富的外设接口、强大的处理能力和低功耗特性,能够满足系统实时数据采集与控制的要求。
  • 选用DHT11作为温湿度传感器,BH1750作为光照强度传感器,以及土壤湿度检测传感器,分别获取农田环境的基本信息。
  • 设计灌溉系统,使用5V抽水泵配合继电器控制灌溉,以响应土壤湿度的监测结果。
  • 采用白色LED灯作为补光光源,并接入主控板进行智能调节或远程控制。
  • 配备蜂鸣器用于异常情况报警。
  • 选用NBIoT-BC26模块确保无线通信稳定可靠,实现数据上云。

(3)软件架构设计:

  • 开发STM32的嵌入式软件程序,负责读取各传感器数据,执行逻辑判断,如环境参数超限时触发报警、根据土壤湿度自动或手动控制灌溉、周期性提醒施肥等操作。
  • 实现NBIoT通信协议栈,将现场采集的数据通过BC26模块发送至云端MQTT服务器。
  • 在云端部署EMQX开源MQTT服务器框架,接收并存储前端设备发送的数据。
  • 开发微信小程序客户端,对接MQTT服务器,展示农田环境的各项实时监测数据,提供远程手动控制界面。

1.3 传感器功能介绍

(1)DHT11温湿度传感器

  • 功能:用于实时监测农田环境中的温度和相对湿度。
  • 特点:DHT11是一种低成本、低功耗的数字式温湿度复合传感器,提供了一体化的解决方案。它能够直接输出经过校准的数字信号,便于微处理器直接读取,无需复杂的信号处理电路。

(2)BH1750光照强度传感器

  • 功能:测量农田或温室内的光照强度(照度),以判断当前光照条件是否满足作物生长需求。
  • 特点:BH1750是一款I²C接口的数字光照强度传感器,具有高精度和宽量程的特点,可精确检测光照强度,并支持多种分辨率模式切换以适应不同的应用场景。

(3)土壤湿度检测传感器

  • 功能:用于监测种植区域土壤的水分含量,作为决定灌溉与否的重要依据。
  • 特点:这类传感器通常采用电容式、电阻式或者频域反射(FDR)等原理来检测土壤湿度,通过转换为电信号变化,从而实现对土壤含水量的非破坏性测定。其特点是能反映土壤实际湿润状况,帮助实现精准灌溉。

(4)蜂鸣器报警模块

  • 功能:虽然不是传统意义上的传感器,但在本系统中作为报警装置使用,当环境参数超出预设阈值时,由主控芯片STM32控制蜂鸣器发出声音警报,提醒管理人员及时处理异常情况。

(5)5V抽水泵与继电器组合

  • 功能:抽水泵与继电器配合实现灌溉功能,继电器根据土壤湿度传感器的数据反馈控制抽水泵的开关状态,达到智能灌溉的目的。
  • 特点:继电器作为电子开关,可以远程控制大电流设备如抽水泵的通断,实现小电流控制大电流,同时隔离了主控制器与负载之间的电气连接,提高了系统的安全性。

(6)NBIoT-BC26模块

  • 功能:作为物联网通信组件,负责将采集到的各种数据无线传输至云端服务器,同时也接收来自云端的控制指令,实现远程数据交互和控制。
  • 特点:NBIoT(窄带物联网)技术具有低功耗、广覆盖、大连接的优点,特别适合于智慧农业这种需要大面积部署且网络连接要求稳定的场景。BC26模块是基于NBIoT标准的通信模块,具备良好的网络兼容性和稳定性。

1.4 开发工具的选择

STM32的编程语言选择C语言,C语言执行效率高,大学里主学的C语言,C语言编译出来的可执行文件最接近于机器码,汇编语言执行效率最高,但是汇编的移植性比较差,目前在一些操作系统内核里还有一些低配的单片机使用的较多,平常的单片机编程还是以C语言为主。C语言的执行效率仅次于汇编,语法理解简单、代码通用性强,也支持跨平台,在嵌入式底层、单片机编程里用的非常多,当前的设计就是采用C语言开发。

开发工具选择Keil,keil是一家世界领先的嵌入式微控制器软件开发商,在2015年,keil被ARM公司收购。因为当前芯片选择的是STM32F103系列,STMF103是属于ARM公司的芯片构架、Cortex-M3内核系列的芯片,所以使用Kile来开发STM32是有先天优势的,而keil在各大高校使用的也非常多,很多教科书里都是以keil来教学,开发51单片机、STM32单片机等等。目前作为MCU芯片开发的软件也不只是keil一家独大,IAR在MCU微处理器开发领域里也使用的非常多,IAR扩展性更强,也支持STM32开发,也支持其他芯片,比如:CC2530,51单片机的开发。从软件的使用上来讲,IAR比keil更加简洁,功能相对少一些。如果之前使用过keil,而且使用频率较多,已经习惯再使用IAR是有点不适应界面的。

image-20221210225339928

二、EMQX开源MQTT服务器框架

EMQX是一款开源的、云原生的分布式物联网MQTT消息服务器,设计目标是实现高可靠性,并支持承载海量物联网终端的MQTT连接,以及在海量物联网设备间实现低延时消息路由。基于Erlang/OTP平台开发,充分利用了Erlang/OTP的软实时、低延时和分布式特性。

以下是EMQX服务器框架的详细介绍:

(1)可扩展性:EMQX支持亿级的MQTT服务订阅,单节点能够支持500万MQTT设备连接,集群可扩展至1亿并发MQTT连接。这种强大的扩展能力使其能够适应不同规模的物联网应用。

(2)安全性:EMQX提供了多种安全机制,包括SSL/TLS、密码认证、增强认证和ACL(访问控制列表)等,以保障数据传输和访问的安全性。

(3)规则引擎:EMQX内置了基于SQL的规则引擎,能够实时过滤、转换和处理消息,提供灵活的消息处理机制。这使得应用程序能够根据业务需求对消息进行灵活处理。

(4)数据存储:EMQX企业版还提供了数据存储功能,将客户端上下线状态、订阅关系、离线消息、消息内容以及消息回执等操作记录到各种数据库中。这一功能在服务崩溃或客户端异常离线后,能够保留数据,确保数据的完整性和可靠性。

(5)集群设计:EMQX采用Masterless的大规模分布式集群架构,实现了系统的高可用性和水平扩展。集群设计包括维护订阅表、路由表和主题树等数据结构,以实现消息转发和投递给各节点上的订阅者。

(6)协议支持:EMQX完全支持MQTT 5.0和3.x协议标准,提供了更好的伸缩性、安全性和可靠性。同时,它还提供了对多种其他协议的支持,如WebSocket、TCP、SSL/TLS等。

(7)易用性:EMQX提供了丰富的API和插件管理功能,使得用户可以方便地查看在线客户端信息、踢出客户端、管理插件状态等。它还提供了可视化的管理界面和调试工具,方便用户进行监控和管理。

三、购买ECS云服务器

3.1 登录官网

https://www.huaweicloud.com/

image-20231108104223041

3.2 购买ECS服务器

【1】选择ECS弹性服务器

image-20231108104347511

【2】选择ECS服务器的区域、配置信息、操作系统(我选择的Ubuntu18.04 64位)。

image-20231108104741429

【3】购买弹性公网IP,配置带宽。

image-20231108104901006

【4】配置密码

image-20231108105046704

【5】选择购买时长,我这里选择了1个月时长

image-20231108105311420

【6】确认付费付款

image-20231108105349984

image-20231108105454687

收到邮件提醒,服务器创建成功。 (为了写教程,花费320元,买了一个月服务器)

image-20231108105618267

【7】返回弹性服务器的控制台

image-20231108105525415

【8】点击服务器名字,可以进入到详情页面。

image-20231108110321756

3.3 配置安全组

要确保MQTT服务器常用的几个端口已经开放出出来。

image-20231108110539506

3.4 安装FinalShell

Windows下安装 FinalShell 终端,方便使用SSH协议远程登录到云服务器。 (当然,使用其他方式登录也是一样的)

image-20231108110719420

3.5 远程登录到云服务器终端

【1】新建连接,选择SSH连接。

image-20231108110858411

【2】填入IP地址、用户名、密码

这里的主机就是填服务器的公网IP地址,密码就是创建服务器输入的密码,用户名直接用root。

image-20231108111008259

【3】点击连接服务器

image-20231108111125207

【4】第一次登录会弹出提示框,选择接受并保存

image-20231108111208880

【5】接下来可以看到服务器已经登录成功了。

image-20231108111247385

四、Linux下安装EMQX

本章节将介绍如何在 Ubuntu 系统中下载安装并启动 EMQX。

支持的 Ubuntu 版本:

  • Ubuntu 22.04
  • Ubuntu 20.04
  • Ubuntu 18.04

4.1 官网地址

链接:https://www.emqx.io/docs/zh/v5.2/deploy/install-ubuntu.html

4.2 通过Apt源安装

EMQX 支持通过 Apt 源安装,免除了用户需要手动处理依赖关系和更新软件包等的困扰,具有更加方便、安全和易用等优点。

在命令行终端,复制下面的命令过去,按下回车键。

【1】通过以下命令配置 EMQX Apt 源:

curl -s https://assets.emqx.com/scripts/install-emqx-deb.sh | sudo bash

【2】运行以下命令安装 EMQX:

sudo apt-get install emqx

【3】运行以下命令启动 EMQX:

sudo systemctl start emqx

过程如下:

image-20231108111924723

image-20231108112053540

4.3 EMQX常用的命令

sudo systemctl emqx start    启动
sudo systemctl emqx stop     停止
sudo systemctl emqx restart  重启 

五、配置EMQX服务器

5.1 登录EMQX内置管理控制台

EMQX 提供了一个内置的管理控制台,即 EMQX Dashboard。方便用户通过 Web 页面就能轻松管理和监控 EMQX 集群,并配置和使用所需的各项功能。

在浏览器里输入: http://122.112.225.194:18083 就可以访问EMQX的后台管理页面。可以管理以连接的客户端或检查运行状态。

这里面的IP地址,就是自己ECS云服务器的公网IP地址。

打开浏览器后,输入地址后打开的效果:

image-20231108112216658

默认用户名和密码:

用户名:admin
密码:public

第一次登录会提示你修改新密码,如果不想设置,也可以选择跳过(公网服务器部署,还是要修改密码安全些)。

下面修改新密码:

image-20231108112320173

登录成功的页面显示如下:

image-20231108112412396

5.2 MQTT配置

这里可以配置MQTT的一些参数,根据自己的需求进行配置。

image-20231108112603617

5.3 测试MQTT通信

新建一个客户端,点击连接。

image-20231108112632462

连接之后,然后点击订阅,和发布,如果下面消息能正常的接收。说明MQTT服务器通信是已经正常,没问题了。

并且在这个页面也可以看到主题发布主题订阅的格式。

image-20231108112651564

5.4 MQTT客户端登录服务器测试

接下来就打开我们自己的MQTT客户端登录MQTT服务器进行测试数据的通信。

端口选择: 1883

根据软件参数填入参数,登录,进行主题的发布和订阅。

image-20231108112828605

说明: 目前还没有配置客户端认证,现在只要IP和端口输入正确,MQTT三元组可以随便输入,都可以登录上服务器的,服务器没有对三元组做校验。

EMQ X 默认配置中启用了匿名认证,任何客户端都能接入 EMQX。没有启用认证插件或认证插件没有显式允许/拒绝(ignore)连接请求时,EMQX 将根据匿名认证启用情况决定是否允许客户端连接。

然后打开EMQX的管理后台,可以看到我们的设备已经登录服务器了,名字为test1

image-20231108112900540

在订阅主题的页面也可以看到我们客户端设备订阅的主题。

image-20231108112938585

5.5 客户端认证配置

EMQX 默认配置中启用了匿名认证,任何客户端都能接入 EMQX。没有启用认证插件或认证插件没有显式允许/拒绝(ignore)连接请求时,EMQX 将根据匿名认证启用情况决定是否允许客户端连接。

在正式产品里肯定是要启用认证的,不然任何设备都能接入。

下面就介绍如何配置 客户端认证。

【1】打开客户端认证页面

image-20231107160746380

【2】选择密码认证

image-20231107160844450

【3】选择内置数据库

image-20231107160916912

【4】设置认证方式(都可以默认,不用改),直接点击创建。

image-20231107161002220

【5】创建成功后,点击用户管理

image-20231107161043692

【6】添加用户

image-20231107161154596

image-20231107161254779

【7】添加成功

image-20231107161317252

【8】添加完毕之后,打开MQTT客户端可以进行测试。

登录的时候,MQTT用户名和密码必须输入正确,按照上一步添加的信息进行如实填写,否则是无法登录服务器的。

image-20231108113054185

5.6 客户端授权配置

客户端授权页面可以配置每个客户端(设备)的主题发布,订阅权限。限制它是否可以发布主题,订阅主题。 如果有需要就可以进行配置。

http://127.0.0.1:18083/#/authorization/detail/built_in_database?tab=users

【1】创建数据源

image-20231107153705954

【2】选择内置数据库

image-20231107153725972

【3】完成创建

image-20231107153746654

【4】点击权限管理

image-20231107153810651

【5】选择客户端ID,点击添加

image-20231107153902413

【6】配置权限

image-20231107161803875

5.7 数据转发(集成)

在集成选项里,可以对设备数据处理。 比如:转发到自己的HTTP服务器,转发到自己其他的MQTT服务器,创建规则,某些事件触发某些动作等等。

image-20231107225638965

选择数据桥接。

可以把数据发送端自己的HTTP服务器,或者发送到其他的MQTT服务器。

image-20231107225815739

选择HTTP服务 (如果自己有HTTP服务器,可以将数据转发给自己的HTTP服务器)。

image-20231107225942506

七、MQTT客户端消息互发测试

7.1 添加2个设备

为了方便测试设备间互相订阅主题,数据收发,在客户端认证页面至少添加2个设备。我这里分别添加了test1test2

image-20231107163706657

7.2 设备间测试

设备A订阅设备B的主题,设备B订阅设备A的主题,实现数据互发。

image-20231108113409036

设备A的MQTT信息:

MQTT服务器地址:122.112.225.194
MQTT服务器端口号:1883
MQTT客户端ID:AAA
MQTT用户名:test1
MQTT登录密码:12345678

订阅主题:BBB/#
发布主题:AAA/1
发布的消息:{ "msg": "我是AAA设备" }

设备B的MQTT信息:

MQTT服务器地址:122.112.225.194
MQTT服务器端口号:1883
MQTT客户端ID:BBB
MQTT用户名:test2
MQTT登录密码:12345678

订阅主题:AAA/#
发布主题:BBB/1
发布的消息:{ "msg": "我是BBB设备" }

八、STM32硬件端开发

8.1 BC26模块的AT指令调试过程

BC20/BC26 开启GPS、连接MQTT服务器的AT指令发送流程。

(1)查询模块是否正常

AT

OK

(2)获取卡号,查询卡是否插好

AT+CIMI

460041052911195

OK

(3)激活网络

AT+CGATT=1

OK

(4)获取网络激活状态

AT+CGATT?

+CGATT: 1

OK

(5)查询网络质量

AT+CSQ

+CSQ: 26,0

OK

(6)检查网络状态

AT+CEREG=? //检查网络状态
+CEREG: 0,1 //找网成功
OK

(7)激活GPS

激活GPS,要等一段时间
AT+QGNSSC=1

OK

(8)查询GPS激活状态

查询激活状态,1表示成功激活
AT+QGNSSC?

+QGNSSC: 1

OK

(9)获取一次GPS定位语句

AT+QGNSSRD="NMEA/RMC"
+QGNSSRD: $GNRMC,120715.00,A,3150.78179,N,11711.93433,E,0.000,,310818,,,A,V*19
OK

(10)连接MQTT服务器

AT+QMTOPEN=0,"a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com",1883

OK

+QMTOPEN: 0,0

(11)登录MQTT服务器

命令格式: AT+QMTCONN=<tcpconnectID>,<clientID>,<username>,<password>
AT+QMTCONN=0,"6210e8acde9933029be8facf_dev1_0_0_2022021913","6210e8acde9933029be8facf_dev1","6cea55404b463e666cd7a6060daba745bbaa17fe7078dfef45f8151cdf19673d"

OK

+QMTCONN: 0,0,0

(12)订阅主题

命令格式: AT+QMTSUB=<tcpconnectID>,<msgID>,"<topic1>”,<qos1>[,"<topic2>,<qos2>]

AT+QMTSUB=0,1,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/messages/down",2

OK

+QMTSUB: 0,1,0,2

(13)发布主题

命令格式:AT+QMTPUB=<tcpconnectID>,<msgID>,<qos>,<retain>,"<topic>","<msg>"

先发送指令: 
AT+QMTPUB=0,0,0,0,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/repor"

等待返回 ">" 
接着发送数据.不需要加回车。
"{"services": [{"service_id": "gps","properties":{"longitude":12.345,"latitude":33.345}}]}"
数据发送完毕,再发送结束符。 十六进制的值--0x1a  。某些串口调试助手可以适应ctrl+z 快捷键输入0xA
等待模块返回"OK",到此数据发送完成。    
OK

+QMTPUB: 0,0,0

8.2 BH1750传感器

下面贴出的是BH1750核心读取代码,因为BH1750本身就是标准的IIC协议,下面就不贴出IIC协议了,直接贴出封装读取光敏信息的代码。

#include "bh1750.h"
float Read_BH1750_Data()
{
    unsigned char t0;
    unsigned char t1;
    float t;
    u8 r_s=0;
    IIC_Start(); //发送起始信号
    IIC_WriteOneByteData(0x46);
    r_s=IIC_GetACK();//获取应答
    if(r_s)printf("error:1\r\n");
    IIC_WriteOneByteData(0x01);
    r_s=IIC_GetACK();//获取应答
     if(r_s)printf("error:2\r\n");
    IIC_Stop(); //停止信号 
    
    IIC_Start(); //发送起始信号
    IIC_WriteOneByteData(0x46);
    r_s=IIC_GetACK();//获取应答
    if(r_s)printf("error:3\r\n");
    IIC_WriteOneByteData(0x01);
    r_s=IIC_GetACK();//获取应答
    if(r_s)printf("error:4\r\n");
    IIC_Stop(); //停止信号 
    
    IIC_Start(); //发送起始信号
    IIC_WriteOneByteData(0x46);
    r_s=IIC_GetACK();//获取应答
    if(r_s)printf("error:5\r\n");
    IIC_WriteOneByteData(0x10);
    r_s=IIC_GetACK();//获取应答
    if(r_s)printf("error:6\r\n");
    IIC_Stop(); //停止信号 
    
    DelayMs(100); //等待
    
    IIC_Start(); //发送起始信号
    IIC_WriteOneByteData(0x47);
    r_s=IIC_GetACK();//获取应答
    if(r_s)printf("error:7\r\n");
    
    t0=IIC_ReadOneByteData(); //接收数据
    IIC_SendACK(0); //发送应答信号
    t1=IIC_ReadOneByteData(); //接收数据
    IIC_SendACK(1); //发送非应答信号
    IIC_Stop(); //停止信号
    
     t=(((t0<<8)|t1)/1.2);
     return t;  
}


8.3 DHT11温湿度模块

下面贴出的是 DHT11 温湿度传感器驱动代码。

#include "dht11.h"
#include "delay.h"

//复位DHT11
void DHT11_Rst(void)	   
{                 
	  DHT11_IO_OUT(); 	//SET OUTPUT
    DHT11_DQ_OUT=0; 	//拉低DQ
    DelayMs(20);    	//拉低至少18ms
    DHT11_DQ_OUT=1; 	//DQ=1 
	  DelayUs(30);     	//主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();//SET INPUT	 
  while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
	{
		retry++;
		DelayUs(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
    while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
	{
		retry++;
		DelayUs(1);
	};
	if(retry>=100)return 1;	    
	return 0;
}
//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
	while(DHT11_DQ_IN&&retry<100)//等待变为低电平
	{
		retry++;
		DelayUs(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)//等待变高电平
	{
		retry++;
		DelayUs(1);
	}
	DelayUs(40);//等待40us
	if(DHT11_DQ_IN)return 1;
	else return 0;		   
}

//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)    
{        
    u8 i,dat;
    dat=0;
	for (i=0;i<8;i++) 
	{
   		dat<<=1; 
	    dat|=DHT11_Read_Bit();
    }						    
    return dat;
}


//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
{        
 	u8 buf[5];
	u8 i;
	DHT11_Rst();
	//printf("------------------------\r\n");
	if(DHT11_Check()==0)
	{
		for(i=0;i<5;i++)//读取40位数据
		{
			buf[i]=DHT11_Read_Byte();
		}
		if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
		{
			*humi=buf[0];
			*temp=buf[2];
		}
	}else return 1;
	return 0;	    
}


//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在    	 
u8 DHT11_Init(void)
{
	RCC->APB2ENR|=1<<2;    //使能PORTA口时钟 
	GPIOA->CRL&=0XFF0FFFFF;//PORTA.5 推挽输出
	GPIOA->CRL|=0X00300000;
	GPIOA->ODR|=1<<5;      //输出1				    
	DHT11_Rst();
	return DHT11_Check();
}

8.4 土壤湿度传感器

通过 ADC 模块读取土壤湿度代码:

#include "stm32f1xx_hal.h"
#include "stdio.h"

ADC_HandleTypeDef hadc1;
UART_HandleTypeDef huart1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_ADC1_Init(void);

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_ADC1_Init();

    uint16_t adc_value;

    while (1)
    {
        HAL_ADC_Start(&hadc1); // 启动 ADC 转换
        if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
        {
            adc_value = HAL_ADC_GetValue(&hadc1); // 读取 ADC 值
            char buffer[50];
            sprintf(buffer, "Soil Moisture: %d\r\n", adc_value);
            HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY); // 通过串口打印土壤湿度数据
        }
        HAL_Delay(1000); // 延时1秒
    }
}

void SystemClock_Config(void)
{
    // 略,根据实际情况配置系统时钟
}

static void MX_ADC1_Init(void)
{
    ADC_ChannelConfTypeDef sConfig = {0};

    hadc1.Instance = ADC1;
    hadc1.Init.ScanConvMode = DISABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 1;
    if (HAL_ADC_Init(&hadc1) != HAL_OK)
    {
        Error_Handler();
    }

    sConfig.Channel = ADC_CHANNEL_0; // 修改为实际连接的通道
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_13CYCLES_5; // 根据实际情况调整采样时间
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
}

static void MX_USART1_UART_Init(void)
{
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    if (HAL_UART_Init(&huart1) != HAL_OK)
    {
        Error_Handler();
    }
}

void Error_Handler(void)
{
    while (1)
    {
    }
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif

8.5 硬件连线

硬件连接方式:
1. TFT 1.44 寸彩屏接线
GND   电源地
VCC   接5V或3.3v电源
SCL   接PC8(SCL)
SDA   接PC9(SDA)
RST   接PC10
DC    接PB7
CS    接PB8
BL	  接PB11


2. 板载LED灯接线
LED1---PA8
LED2---PD2 (被串口占用)


3. 板载按键接线
K0---PA0 
K1---PC5 
K2---PA15


4. ESP8266--WiFi模块
ATK-ESP8266串口WIFI模块与STM32的串口2相连接。
PA2(TX)--RXD 模块接收脚
PA3(RX)--TXD 模块发送脚
GND---GND 地
VCC---VCC 电源(3.3V~5.0V)


5. 环境光检测检测:BH1750数字传感器
SDA-----PB7
SCL-----PB6
GND---GND 地
VCC---VCC 电源(3.3V~5.0V)


6. 温湿度传感器: DHT11
VCC--VCC
GND---GND
DAT---PA5 


7. 土壤湿度传感器
VCC--->5V
GND--->GND
DAT--->PA1


8. 继电器控制--水泵抽水
VCC--->5V
GND--->GND
DAT--->PA6

8.6 KEIL工程

image-20230411145037563

8.7 取模软件使用

本地设备的LCD显示屏上会显示各种传感器数据,需要用到中文、数字、字母。

当前使用的取模软件如下:

image-20230215171354548

这是软件的设置页面:

image-20230222103504914

8.8 核心代码

   while(1)
   {
        //按键可以手动控制开启水泵和补光灯
        key=KEY_Scan();
        if(key)printf("key=%d\r\n",key);
       
        //模式切换
        if(key==2)
        {
            mode=!mode; //模式切换
            printf("按键按下: 执行模式切换: %d\r\n",mode);
        }  
       
        //控制水泵开关
        if(key==1)
        {
            MOTOR=!MOTOR;
            printf("控制水泵开关: %d\r\n",MOTOR);
        }      
        //轮询时间到达
        if(time_cnt>20)
        {
            //模式显示
            if(mode)  //自动模式
            {
                LCD_ShowChineseFont(90,16*6+2,16,HZ_FONT_16[28],RED,0);
                LCD_ShowChineseFont(90+16,16*6+2,16,HZ_FONT_16[29],RED,0);
            }
            else    //手动模式
            {
                LCD_ShowChineseFont(90,16*6+2,16,HZ_FONT_16[30],RED,0);
                LCD_ShowChineseFont(90+16,16*6+2,16,HZ_FONT_16[31],RED,0);
            }
            
            //放到水里61,接触空气是135
            //越湿润值越小,越干旱值越大
            //土壤湿度
            SOIL=GetAdcCHxDATA(1);
            SOIL = 4095 - SOIL;  //将干燥度转为湿度
            printf("土壤湿度:%d\r\n",SOIL);
            SOIL = (float)SOIL / (float)4095 * 100.0;
            
            //【土壤湿度显示】        
            sprintf(data_buff,"%5d",SOIL);
            Gui_DrawFont_GBK16(72,16*4+2,RED,0,(u8*)data_buff);

            //自动模式下控制自动浇水
            if(mode)
            {
                //如果 SOIL <=0 表示是非法值,没有放在土壤里
                if(SOIL>1)
                {
                    //根据湿度自动开关水泵浇水
                    //这里的值根据自己实际测试情况修改
                    if(SOIL<MAX_SOIL)
                    {
                        //第一次进来,才需要清除计数变量
                        if(motor_flag==0)
                        {
                            motor_sec_cnt=0;
                        }
                        motor_flag=1;  //浇水
                    }
                    else
                    {
                        motor_flag=0;  //停止浇水
                    } 
                }
                
            }
            
            //【湿度阀值显示】
            sprintf(data_buff,"%4d",MAX_SOIL);
            Gui_DrawFont_GBK16(72,16*5+2,RED,0,(u8*)data_buff);
                
            //【1】光强度
            //读取光强度
            BH1750=Read_BH1750_Data();
            printf("光强度:%d\r\n",BH1750);

            //显示光强度
            sprintf(data_buff,"%4d",BH1750);
            Gui_DrawFont_GBK16(72,16*2+2,RED,0,(u8*)data_buff);
            
            
            //【2】温湿度
            //读取温湿度
            if(DHT11_Read_Data(&DHT11_T,&DHT11_H))
            {
                printf("温度读取失败.\r\n");
            }
              
            printf("环境温度:%d\r\n环境湿度:%d\r\n",DHT11_T,DHT11_H);

            //显示温度
            sprintf(data_buff,"%4d",DHT11_T);
            Gui_DrawFont_GBK16(72,16*0+2,RED,0,(u8*)data_buff);
            
            //显示湿度
            sprintf(data_buff,"%4d",DHT11_H);
            Gui_DrawFont_GBK16(72,16*1+2,RED,0,(u8*)data_buff);
            
             //土壤温度
            DS18B20=DS18B20_Get_Temp();

            //转换土壤温度
            sprintf(data_buff,"%d.%d",DS18B20>>4,DS18B20&0xF);   
            f_ds18b02=atof(data_buff);
            
            //显示土壤温度
            sprintf(data_buff,"%6.2f",f_ds18b02);
            Gui_DrawFont_GBK16(72,16*4+2,RED,0,(u8*)data_buff);
            
            printf("环境温度:%d℃\n",DHT11_T);
            printf("环境湿度:%d\n",DHT11_H);
            printf("土壤湿度:%d\r\n",SOIL);
            printf("土壤温度:%f\r\n",f_ds18b02);
            
            time_cnt=0;
            LED1=!LED1;
            
            //上报数据
            sprintf(data_buff,"{\"services\": [{\"service_id\": \"stm32\",\"properties\":{\"DHT11_T\":%d,\"DHT11_H\":%d,\"BH1750\":%d,\"SOIL\":%d,\"DS18B20\":%f,\"MOTOR\":%d,\"mode\":%d,\"MAX_SOIL\":%d}}]}",
            DHT11_T,DHT11_H,BH1750,SOIL,f_ds18b02,MOTOR,mode,MAX_SOIL);
            
            MQTT_PublishData(POST_TOPIC,data_buff,0);
            printf("更新数据.\r\n");
        }
        
        
        //接收WIFI返回的数据
        if(USART3_RX_FLAG)
        {
            USART3_RX_BUFFER[USART3_RX_CNT]='\0';
            
            printf("WIFI收到数据:\r\n");
            //向串口打印服务器返回的数据
            for(i=0;i<USART3_RX_CNT;i++)
            {
                printf("%c",USART3_RX_BUFFER[i]);
            }
            
            //如果是服务器下发了属性事件  
            if(USART3_RX_CNT>5)
            {
                //开浇水电机
                if(strstr((char*)&USART3_RX_BUFFER[5],"\"MOTOR\":1"))
                {
                    motor_sec_cnt=0;
                    motor_flag=1;
                }
                //关浇水电机
                else if(strstr((char*)&USART3_RX_BUFFER[5],"\"MOTOR\":0"))
                {
                    motor_flag=0;
                }
                //设置湿度阀值
                else if(strstr((char*)&USART3_RX_BUFFER[5],"\"MAX_SOIL\":"))
                {
                    find_p=strstr((char*)&USART3_RX_BUFFER[5],"\"MAX_SOIL\":");
                    MAX_SOIL=atoi(find_p+11);
                    printf("设置湿度阀值:%d\r\n",MAX_SOIL);
                }
                
                //自动模式
                if(strstr((char*)&USART3_RX_BUFFER[5],"\"mode\":1"))
                {
                    mode=1;
                }
                //手动模式
                else if(strstr((char*)&USART3_RX_BUFFER[5],"\"mode\":0"))
                {
                    mode=0;
                }
                
                
                //使用字符串查找函数
                //设置属性,浇水
                //"motor":1
                if(strstr((char*)&USART3_RX_BUFFER[5],"sys/commands/request_id="))
                {
                    char *p=NULL;
                    p=strstr((char*)&USART3_RX_BUFFER[5],"request_id=");
                    if(p)
                    {        
                        //解析数据
                        //$oc/devices/6434f5044f1d6803244e5db4_dev1/sys/commands/request_id=d2d11779-5bf6-42e5-a5b8-6efc91c7166c{"paras":{"MOTOR":1},"service_id":"stm32","command_name":"浇水电机控制"}
                        strncpy(request_id,p,47);      
                    }
                    
                    //上报数据
                    sprintf(mqtt_message,"{\"result_code\":0,\"response_name\":\"COMMAND_RESPONSE\",\"paras\":{\"result\":\"success\"}}");
                    
                    sprintf(data_buff,"$oc/devices/6434f5044f1d6803244e5db4_dev1/sys/commands/response/%s",
                    request_id);
                    
                    MQTT_PublishData(data_buff,mqtt_message,0);
                    
                    printf("发布主题:%s\r\n",data_buff);
                    printf("发布数据:%s\r\n",mqtt_message);
                }      
            }
            
            USART3_RX_CNT=0;
            USART3_RX_FLAG=0;
        }
        
        DelayMs(10);
        time_cnt++;
	 }

8.9 程序正常运行效果

设备运行过程中会通过串口打印调试信息,我们可以通过串口打印了解程序是否正常。比如:ESP8266是否联网成功,是否连接到服务器。

image-20230224011611980

image-20230224013625715

8.10 硬件技术问题

(1)STM32与物联网服务器的交互协议

STM32硬件端与物联网服务器通过WIFI进行通信。每间隔一端时间,STM32就会向物联网服务器上传当前传感器的数据。

传感器上传组合的MQTT主题报文格式如下:

//上报数据
sprintf(data_buff,"{\"services\": [{\"service_id\": \"stm32\",\"properties\":{\"DHT11_T\":%d,\"DHT11_H\":%d,\"BH1750\":%d,\"SOIL\":%d,\"DS18B20\":%f,\"MOTOR\":%d,\"mode\":%d,\"MAX_SOIL\":%d}}]}",
DHT11_T,DHT11_H,BH1750,SOIL,f_ds18b02,MOTOR,mode,MAX_SOIL);

MQTT_PublishData(POST_TOPIC,data_buff,0);

手机APP给STM32下发的消息:

len:162,Data:f$oc/devices/6434f5044f1d6803244e5db4_dev1/sys/commands/request_id=c1dc39a7-97d1-49f4-bee3-c6bc96e002c4{"paras":{"MOTOR":0},"service_id":null,"command_name":null}
len:166,Data:f$oc/devices/6434f5044f1d6803244e5db4_dev1/sys/commands/request_id=b5c6cee2-c3fc-4fc9-a9c5-8528e180ece4{"paras":{"MAX_SOIL":60},"service_id":null,"command_name":null}
len:166,Data:f$oc/devices/6434f5044f1d6803244e5db4_dev1/sys/commands/request_id=11715fde-1f5b-4864-8499-73cbedc24ec9{"paras":{"MAX_SOIL":89},"service_id":null,"command_name":null}
len:162,Data:f$oc/devices/6434f5044f1d6803244e5db4_dev1/sys/commands/request_id=29f10139-e414-4632-aeb1-4d17ac6a3959{"paras":{"MOTOR":1},"service_id":null,"command_name":null}
len:161,Data:f$oc/devices/6434f5044f1d6803244e5db4_dev1/sys/commands/request_id=86722292-d182-485a-a20f-e980643ead0e{"paras":{"mode":0},"service_id":null,"command_name":null}

(2)ESP8266-WIFI功能介绍

当前项目采用ESP8266模块完成与智慧大屏的通讯,ESP8266支持AP和STA模式;当前项目里配置成STA模式,去连接指定的WIFI热点,连接之后配置成TCP客户端模式,连接智慧大屏创建的TCP服务器,然后就可以进行数据传输同通讯。ESP8266和智慧大屏必须在一个局域网里才可以连接,也就是需要连接同一个热点。

下面是ESP8266的特点介绍:

ESP8266是一款Wi-Fi适用的可编程网络单芯片,搭载一个Tensilica L106 32-位处理器,可支持802.11 b/g/n Wi-Fi 功能,还有一个低功耗的10-bit精度的模拟/数字信号处理器和高度集成的低功耗,高安全性的TLS/SSL协议核心,能够支持用户终端上的全功能的低成本IoT应用。

(3)DHT11温湿度传感器

DHT11是一款有已校准数字信号输出的温湿度传感器。 其精度湿度±5%RH, 温度±2℃,量程湿度5~95%RH, 温度-20~+60℃。

DHT11应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性和卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,使其成为该类应用中,在苛刻应用场合的最佳选择。产品为4针单排引脚封装,连接方便。

该传感器包括一个电阻式测湿元件和一个 NTC测温元件,并与一个高性能 8 位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11 与单片机之间能采用简单的单总线进行通信,仅仅需要一个 I/O 口。传感器内部湿度和温度数据 40Bit 的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。DHT11 功耗很低,5V 电源电压下,工作平均最大电流 0.5mA。

DHT11 的技术参数如下:

⚫ 工作电压范围:3.3V-5.5V
⚫ 工作电流 :平均 0.5mA
⚫ 输出:单总线数字信号
⚫ 测量范围:湿度 20~90%RH,温度 0~50℃
⚫ 精度 :湿度±5%,温度±2℃
⚫ 分辨率 :湿度 1%,温度 1

DHT11 数字湿温度传感器采用单总线数据格式。即,单个数据引脚端口完成输入输出双向传输。其数据包由 5Byte(40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。

DHT11 的数据格式为:8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和。其中校验和数据为前四个字节相加。

九、硬件选型

【1】母对母杜邦线

作用: 连接模块与单片机。

链接:https://detail.tmall.com/item.htm?ali_refid=a3_430582_1006:1104520036:N:MsF9mE9KLTC2IibWJh%20K1A==:adaa6d3d7abe6f1f07b87a36416ee4fb&ali_trackid=1_adaa6d3d7abe6f1f07b87a36416ee4fb&id=14466195609&skuId=3936936817454&spm=a230r.1.14.1

image-20230316142240375

【2】洞洞板

链接:https://detail.tmall.com/item.htm?spm=a230r.1.14.34.16b221829wBwAI&id=525489414251&ns=1&abbucket=9&skuId=3929211749440

image-20221031234443667

【3】USB下载线

链接:https://detail.tmall.com/item.htm?areaId=500100&cat_id=2&id=36635861113&is_b=1&rn=b29713f11d07002439272415bce5c7e1&skuId=3762719825529&spm=a220m.1000858.1000725.31.46e677b4qLTl6B&user_id=1695056989

image-20221208211912983

【4】STM32F103RCT6开发板

主控CPU采用STM32F103RCT6,这颗芯片包括48 KB SRAM、256 KB Flash、2个基本定时器、4个通用定时器、2个高级定时器、51个通用IO口、5个串口、2个DMA控制器、3个SPI、2个I2C、1个USB、1个CAN、3个12位ADC、1个12位DAC、1个SDIO接口,芯片属于大容量类型,配置较高,整体符合硬件选型设计。当前选择的这款开发板自带了一个1.4寸的TFT-LCD彩屏,可以显示当前传感器数据以及一些运行状态信息。

链接:https://detail.tmall.com/item.htm?id=540109077095&skuId=4456080806080&spm=a1z0d.6639537.1997196601.4.69157484Ospeps

image-20230316142634303

【5】 DHT11温湿度传感器

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有可靠性与卓越的长期稳定性,成本低、相对湿度和温度测量、快响应、抗干扰能力强、信号传输距离长、数字信号输出、精确校准。传感器包括一个电容式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。可用于暖通空调、除湿器、测试及检测设备、消费品、汽车、自动控制、数据记录器、气象站、家电、湿度调节器、医疗、其他相关湿度检测控制。

特点如下:

1、可以检测周围环境的湿度

2、可以检测周围环境的温度

3、湿度测量范围:20%-95%(0度-50度范围)湿度测量误差:±5%4、温度测量范围:o度-50度温度测量误差:±2度

4、工作电压3.3V-5V

5、输出形式数字输出

https://detail.tmall.com/item.htm?id=15598344236&ali_refid=a3_430582_1006:1104520036:N:TAiHo9GD8eTurMzH21/csQ==:bead289475b4a86864858785eb160a52&ali_trackid=1_bead289475b4a86864858785eb160a52&spm=a230r.1.14.1&skuId=4835521242383

image-20220319211432662

image-20220319211455249

【6】BH1750光敏传感器

链接:https://detail.tmall.com/item.htm?abbucket=3&id=543135220776&ns=1&spm=a230r.1.14.16.d7597d40XRn0Ec

image-20230207211644477

【7】 ESP8266 WIFI

链接:https://detail.tmall.com/item.htm?spm=a230r.1.14.1.322267d12csSBI&id=609757779633&ns=1&abbucket=9&skuId=4447338308660

image-20230211133730875

■模块采用串口(LVTTL) 与MCU (或其他串口设备) 通信,内置TCP/IP协议栈,能够实现串口与WIFI之间的转换
■模块支持LVTTL串口, 兼容3…3V和5V单片机系统
■模块支持串 口转WIFI STA、串口转AP和WIFI STA+WIFI AP的模式,从而快速构建串口-WIFI数据传输方案
■模块小巧(19mm*29mm), 通过6个2.54mm间距排针与外部连接

image.png
image.png
image.png
image.png
image.png
image.png

【8】 继电器

链接:https://detail.tmall.com/item.htm?abbucket=0&id=41268748362&ns=1&spm=a21n57.1.0.0.1f98523c5N83UP

image-20230404101859351

【9】土壤湿度传感器

链接: https://detail.tmall.com/item.htm?spm=a230r.1.14.16.f54e4813fvxQrB&id=37365775741&ns=1&abbucket=19&skuId=4136086852947

image-20221202143001552

十、上位机开发

10.1 说明

一套完整的物联网产品。会分为设备端,服务器,上位机部分。

这个章节,介绍采用Qt开发上位机的过程。利用EMQX提供的API接口与MQTT客户端设备进行通信,完成数据上传,命令下发等功能。

10.2 查看全部的API接口

帮助文档地址: https://www.emqx.io/docs/zh/v5.0/admin/api.html#%E8%AE%A4%E8%AF%81

EMQX 提供了管理监控 REST API,这些 API 遵循 OpenAPI (Swagger) 3.0 规范。
EMQX 在 REST API 上做了版本控制,EMQX 5.0.0 以后的所有 API 调用均以 /api/v5 开头。

EMQX 服务启动后,可以访问 http://localhost:18083/api-docs/index.html (opens new window)来查看 API 的文档。还可以直接在 Swagger UI 上尝试执行一些 API。

比如: 我的EMQX服务器是在华为云ECS服务器上搭建,公网IP是: 122.112.225.194

那我访问API文档的地址就是下面这样的格式: 在浏览器里打开即可。

http://122.112.225.194:18083/api-docs/index.html

访问效果如下:

image-20231109102334735

10.3 创建API密匙

【1】登录EMQX的后台管理页面: http://122.112.225.194:18083/

【2】找到菜单里的 系统设置选项–>API密匙。

image-20231109102603268

【3】创建密匙。

image-20231109102641949

【4】填写密匙名称

image-20231109102751550

【5】创建成功

image-20231109102813593

【6】得到API Key 和 Secret Key

API Key    :  f072a6e9758b8cdf
Secret Key :  LzwPB71Yf7PTED39C7RGboz9C9ANhv83ULUynTANgog4hG    

10.4 测试API: 获取节点信息

上一步已经创建好API的访问密匙,这里就以 获取节点信息为例,调用获取节点信息的API接口,获取节点 信息。

接口在API文档里的介绍:

image-20231109104834087

根据前面的API访问路径规则说明; 那么,获取节点信息的API完整访问路径为:

http://122.112.225.194:18083/api/v5/nodes

接下来就用python写一份代码,测试一下接口是否可以正常访问。 python代码直接放服务器运行(主要是我本地没有安装python环境,云服务器的环境是已经安装OK的,测试方便)。

【1】在云服务器上创建一个python文件,方便测试代码

image-20231109103413275

【2】创建之后FinaShell自动上传到服务器

image-20231109103503799

【3】编辑代码

在这里双击要编辑的文件,就可以打开文件进行编辑。默认采用内置的编辑器,也可以选择自己电脑上的外置编辑器。

image-20231109103639018

【4】代码编辑完成,按下键盘快捷键Ctrl + S 保存文件内容,保存之后文件内容会自动同步到服务器。

image-20231109103835524

保存后提示,自动上传。

image-20231109103905198

写入的代码如下:

import urllib.request
import json
import base64

username = 'f072a6e9758b8cdf'
password = 'LzwPB71Yf7PTED39C7RGboz9C9ANhv83ULUynTANgog4hG'

url = 'http://122.112.225.194:18083/api/v5/nodes'

req = urllib.request.Request(url)
req.add_header('Content-Type', 'application/json')

auth_header = "Basic " + base64.b64encode((username + ":" + password).encode()).decode()
req.add_header('Authorization', auth_header)

with urllib.request.urlopen(req) as response:
    data = json.loads(response.read().decode())

print(data)

【5】执行代码,返回结果

通过返回信息来看,节点信息获取是没有问题的。

root@emqx:~/emqx# python3 http_api_test.sh 
[{'connections': 0, 'edition': 'Opensource', 'live_connections': 0, 'load1': 0.0, 'load15': 0.0, 'load5': 0.0, 'log_path': '/var/log/emqx', 'max_fds': 1048576, 'memory_total': '3.66G', 'memory_used': '612.59M', 'node': 'emqx@127.0.0.1', 'node_status': 'running', 'otp_release': '25.3.2-2/13.2.2', 'process_available': 2097152, 'process_used': 543, 'role': 'core', 'sys_path': '/usr/lib/emqx', 'uptime': 84000040, 'version': '5.3.1-alpha.1'}]
root@emqx:~/emqx# python3 http_api_test.sh 
[{'connections': 0, 'edition': 'Opensource', 'live_connections': 0, 'load1': 0.0, 'load15': 0.0, 'load5': 0.0, 'log_path': '/var/log/emqx', 'max_fds': 1048576, 'memory_total': '3.66G', 'memory_used': '613.23M', 'node': 'emqx@127.0.0.1', 'node_status': 'running', 'otp_release': '25.3.2-2/13.2.2', 'process_available': 2097152, 'process_used': 543, 'role': 'core', 'sys_path': '/usr/lib/emqx', 'uptime': 84008046, 'version': '5.3.1-alpha.1'}]

image-20231109104126534

10.5 在线调试(获取主题列表)

在编写代码之前,可以先测试下API接口的效果,可以直接在Swagger UI界面直接调试API。

地址: http://122.112.225.194:18083/api-docs/index.html#/

例如:以获取以订阅主题列表的API接口为例。

【1】在Swagger UI界面上找到对应的API接口。

image-20231109135332598

【2】点击API说明,展开详情

image-20231109135420842

【3】点击右边的试试看按钮。

image-20231109135445907

【4】点击执行

image-20231109135529687

【5】然后会弹出提示框,让你填入用户名和密码。

这个用户名和密码就是前面创建API密匙生成的API Key(用户名)Secret Key(密码)

API Key    :  f072a6e9758b8cdf
Secret Key :  LzwPB71Yf7PTED39C7RGboz9C9ANhv83ULUynTANgog4hG  

image-20231109135552701

【6】根据提示输入用户名和密码,再点击登录。

image-20231109135742095

【7】再次点击执行,就可以看到接口返回的数据了。

并且在页面上也写出了,请求的信息。使用curl命令行给出详细的请求过程,参考这个就可以自己写代码了。

image-20231109135909136

【8】API调用,curl命令行执行代码如下:

curl -X 'GET' \
  'http://122.112.225.194:18083/api/v5/topics?node=emqx%40127.0.0.1&page=1&limit=50' \
  -H 'accept: application/json'

【9】python代码实现

import requests  
  
url = 'http://122.112.225.194:18083/api/v5/topics?node=emqx%40127.0.0.1&page=1&limit=50'  
headers = {'accept': 'application/json'}  
  
response = requests.get(url, headers=headers)  
  
if response.status_code == 200:  
    data = response.json()  
    # 在这里处理返回的数据  
    print(data)  
else:  
    print("请求失败,状态码:", response.status_code)

10.6 在线调试(发布主题)

API里也支持发布主题,利用HTTP协议发布主题消息,如果设备端订阅了该主题,就可以收到API接口发布的消息。

【1】先找到发布主题的API接口

image-20231109140301178

【2】点击API名字,展开详情

image-20231109140335187

【3】点击右边的Try it out按钮。

image-20231109140418433

【4】参数填写说明

因为这个接口是发送主题的,需要填参数,填自己需要发布什么主题,什么消息。

出来的框框里就是发布信息,根据自己需要修改。

image-20231109140519545

topic就是发布的主题。 payload 就是发布的消息内容。 只要MQTT客户端订阅了这个主题,就可以收到发布的消息。

这个主题的名字可以随便改的。我这里就用默认的名字和内容测试。

image-20231109143824763

{
  "payload_encoding": "plain",
  "topic": "api/example/topic",
  "qos": 0,
  "payload": "hello emqx api",
  "properties": {
    "payload_format_indicator": 0,
    "message_expiry_interval": 0,
    "response_topic": "some_other_topic",
    "correlation_data": "string",
    "user_properties": {
      "foo": "bar"
    },
    "content_type": "text/plain"
  },
  "retain": false
}

【5】MQTT客户端登录。

打开MQTT客户端,登录服务器,订阅api/example/topic主题。

image-20231109144015067

【6】在API调试页面,点击执行

image-20231109144118094

【7】执行之后,在MQTT客户端的就可以收到API下发的消息了。

image-20231109144140647

【8】API接口调用,curl命令行执行的代码如下:

curl -X 'POST' \
  'http://122.112.225.194:18083/api/v5/publish' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "payload_encoding": "plain",
  "topic": "api/example/topic",
  "qos": 0,
  "payload": "hello emqx api",
  "properties": {
    "payload_format_indicator": 0,
    "message_expiry_interval": 0,
    "response_topic": "some_other_topic",
    "correlation_data": "string",
    "user_properties": {
      "foo": "bar"
    },
    "content_type": "text/plain"
  },
  "retain": false
}'

【9】Python代码实现

import requests  
import json  
  
url = 'http://122.112.225.194:18083/api/v5/publish'  
headers = {  
    'accept': 'application/json',  
    'Content-Type': 'application/json'  
}  
data = {  
    "payload_encoding": "plain",  
    "topic": "api/example/topic",  
    "qos": 0,  
    "payload": "hello emqx api",  
    "properties": {  
        "payload_format_indicator": 0,  
        "message_expiry_interval": 0,  
        "response_topic": "some_other_topic",  
        "correlation_data": "string",  
        "user_properties": {  
            "foo": "bar"  
        },  
        "content_type": "text/plain"  
    },  
    "retain": False  
}  
  
response = requests.post(url, headers=headers, data=json.dumps(data))  
  
if response.status_code == 200:  
    print("消息发布成功")  
else:  
    print("消息发布失败,状态码:", response.status_code)

10.7 安装Qt环境

Qt的中文官网: https://www.qt.io/zh-cn/image-20221207160550486

image-20221207160606892

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

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

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

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

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

image-20221203151742653

说明: 我这里只是介绍PC端的环境搭建(这个比较简单)。 Android的开发环境比较麻烦,可以去我的博客里看详细文章。

选择MinGW 32-bit 编译器:

image-20221203151750344

10.8 新建Qt工程

【1】新建工程

image-20231109131248752

【2】设置工程名字

image-20231109131438532

【3】选择QWiget父类

image-20231109131459281

【4】选择编译套件: 如果电脑没有安装VS环境,就选MinGW,这个比较简单没有其他依赖环境问题。 具体看自己编译的程序最终在哪里运行,来选择合适的编译器。

image-20231109131533021

【5】创建完成

image-20231109131624052

【6】点击左下角设置发布类型。默认是Debug,选择Release,运行效率高一些(如果需要调试就选择Debug)。

image-20231109131733058

10.9 设计代码

【1】获取服务器节点信息

QString requestUrl;
QNetworkRequest request;

request.setUrl(QUrl("http://100.112.225.194:18083/api/v5/nodes"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QString username = "f072a6e9758b8cdf";
QString password = "LzwPB71Yf7PTED39C7RGboz9C9ANhv83ULUynTANgog4hG";
QString concatenated = username + ":" + password;
QByteArray data = concatenated.toLocal8Bit().toBase64();
QString headerData = "Basic " + data;
request.setRawHeader("Authorization", headerData.toLocal8Bit());

//发送请求
manager->get(request);

【2】获得订阅的主题列表

QString requestUrl;
QNetworkRequest request;

request.setUrl(QUrl("http://100.112.225.194:18083/api/v5/topics"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QString username = "f072a6e9758b8cdf";
QString password = "LzwPB71Yf7PTED39C7RGboz9C9ANhv83ULUynTANgog4hG";
QString concatenated = username + ":" + password;
QByteArray data = concatenated.toLocal8Bit().toBase64();
QString headerData = "Basic " + data;
request.setRawHeader("Authorization", headerData.toLocal8Bit());

//发送请求
manager->get(request);

【3】发布主题

void Widget::publish_topic_http_post(QString topic,QString message)
{
    QString requestUrl;
    QNetworkRequest request;

    request.setUrl(QUrl(QString("http://%1:18083/api/v5/publish").arg(emqx_ip)));
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    request.setRawHeader("Authorization", "Basic ZjA3MmE2ZTk3NThiOGNkZjpMendQQjcxWWY3UFRFRDM5QzdSR2JvejlDOUFOaHY4M1VMVXluVEFOZ29nNGhH");

    QJsonObject payload;
    payload["payload_encoding"] = "plain";
    payload["topic"] = topic;    // "api/example/topic";
    payload["qos"] = 0;
    payload["payload"] =message; // "hello emqx api";

    QJsonObject properties;
    properties["payload_format_indicator"] = 0;
    properties["message_expiry_interval"] = 0;
    properties["response_topic"] = "some_other_topic";
    properties["correlation_data"] = "string";
    QJsonObject userProperties;
    userProperties["foo"] = "bar";
    properties["user_properties"] = userProperties;
    properties["content_type"] = "text/plain";

    payload["properties"] = properties;
    payload["retain"] = false;

    QJsonDocument jsonDoc(payload);
    QByteArray jsonData = jsonDoc.toJson();

    //发送请求
    manager->post(request, jsonData);
}

调用示例:

//发布主题
publish_topic_http_post("api/cmd/topic","hello mqtt dev");

十一、制作过程

image-20240226015633136

img

十二、总结

本智慧农业管理系统设计与实现项目基于STM32F103RCT6微控制器为核心,通过集成DHT11温湿度传感器、BH1750光照强度传感器和土壤湿度检测传感器等设备,构建了一套全面的农田环境监测系统。当环境参数超出预设阈值时,系统能够实时报警并自动或提醒进行灌溉、施肥等操作,同时利用蜂鸣器发出声音警报。

在远程控制方面,系统采用NBIoT-BC26模块实现了无线通信功能,将采集到的数据传输至云端MQTT服务器,并通过EMQX开源框架搭建的服务器处理数据。用户可通过微信小程序随时随地查看农田环境的各项实时数据,实现对农作物生长环境的远程监控,并能便捷地执行手动灌溉、开启补光灯等远程控制操作。

本项目的成功实施,不仅有效提升了农业生产过程中的智能化水平,降低了人工管理成本,而且为实现精准农业和智慧农业提供了有力的技术支持。未来,随着物联网技术、云计算和人工智能技术的进一步发展,这套智慧农业管理系统将有望在更多领域推广使用,助力我国现代农业朝着更高效、智能、可持续的方向迈进。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1511995.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java代码审计工程师直播第六期

本期直播课程将深入探讨Java代码审计的关键概念和技术。涵盖课题包括安全漏洞分析、代码审查方法、常见漏洞案例分析等。学员将通过实例掌握代码审计实战技能&#xff0c;提升对Java应用程序安全的认知和技能水平。 课程大小&#xff1a;6.1G 课程下载&#xff1a;https://do…

Docker 容器化技术:构建高效、可移植的开发环境和部署流程|Docker 三要素

镜像、容器、镜像仓库是 Docker 中最核心的三个概念&#xff0c;组成了 Docker 的整个生命周期。 &#xff08;镜像、容器、镜像仓库三者运行关系&#xff09; 1、镜像 镜像是 Docker 的核心元素质疑&#xff0c;作为容器运行的基础&#xff0c;Docker Hub 官网提供了庞大的镜…

第十五届蓝桥杯(Web 应用开发)模拟赛 3 期-大学组(被题目描述坑惨了)

目录 1.创意广告牌 2.原子化css 3.神秘咒语 4.朋友圈 5.美食蛋白揭秘 6.营业状态变更 7.小说阅读器 8.冰岛人 9.这是一个”浏览器“ 10.趣味加密解密 总结 1.创意广告牌 这个题目不多说了&#xff0c;只要知道这些css应该都能写出来&#xff0c;不会的平时多查查文…

突破界限的力量:探索Facebook如何打破国界、文化和语言的障碍

在当今全球化的时代&#xff0c;社交媒体已经成为人们生活中不可或缺的一部分&#xff0c;而Facebook作为其中的先驱者&#xff0c;更是在打破国界、文化和语言障碍方面发挥着举足轻重的作用。它不仅成为了人们交流、连接的重要平台&#xff0c;更是为不同国家、不同文化、不同…

固态存储是未来|浅析SSD架构的演进与创新技术-1

常见的SSD架构中&#xff0c;包括了SSD控制器、NAND颗粒、DRAM颗粒三大组件&#xff0c;SSD控制器的固件需要兼顾坏块管理、ECC纠错、垃圾回收GC、磨损均衡WL、NAND die介质管理、缓存交互等等。 随着时代的发展&#xff0c;SSD架构&#xff0c;也不断有新的挑战和需求。基于小…

抓取Instagram数据:Fizzler库带您进入C#爬虫程序的世界

引言 在当今数字化的世界中&#xff0c;数据是无价之宝。社交媒体平台如Instagram成为了用户分享照片、视频和故事的热门场所。作为开发人员&#xff0c;我们可以利用爬虫技术来抓取这些平台上的数据&#xff0c;进行分析、挖掘和应用。本文将介绍如何使用C#编写一个简单的Ins…

一条 sql 语句可能导致的表锁和行锁以及死锁检测

锁 MDL 当对一个表做增删改查操作的时候&#xff0c;加 MDL 读锁&#xff1b;当要对表做结构变更操作的时候&#xff0c;加 MDL 写锁 ALTER TABLE tbl_name NOWAIT add column ... ALTER TABLE tbl_name WAIT N add column ... …

小程序bindtap 和 catchtap 的区别以及如何使用

Hello大家好&#xff01;我是咕噜铁蛋&#xff01;我今天要和大家聊一聊小程序开发中的一个常见问题&#xff1a;bindtap 和 catchtap 的区别以及如何使用。这两个在小程序开发中经常被提及的事件绑定方式&#xff0c;它们之间到底有什么不同呢&#xff1f;让我们一起来深入探讨…

Prometheus监控Mysql数据库在Grafana展示

未安装prometheus和Grafana可以参考这个博客操作 Docker安装Prometheus监控-CSDN博客 Windows版本数据库 一、在数据库服务器安装mysqld-exporter 下载mysqld-exporter文件 Releases prometheus/mysqld_exporter GitHub 二、配置mysqld-exporter文件 1、解压进入mysqld…

微服务架构中的拆分粒度决策

大家好&#xff01;今天我来和大家分享一下微服务架构中的拆分粒度决策问题&#xff0c;希望能帮助大家更好地理解和应用微服务架构&#xff01; 问题背景 在设计和实施微服务架构时&#xff0c;拆分粒度的决策非常重要。拆分得太细&#xff0c;会增加系统间通信和部署的复杂性…

基于Springboot的驾校预约学习系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的驾校预约学习系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

12、设计模式之代理模式(Proxy)

一、什么是代理模式 代理模式属于结构型设计模式。为其他对象提供一种代理以控制对这个对象的访问。 在某些情况下&#xff0c;一个对象不适合或者不能直接引用另一个对象&#xff0c;而代理对象可以在客户端和目标对象之间起到中介的作用。 二、分类 代理模式分为三类&#…

css3实现3D立方体旋转特效源码

源码介绍 CSS3自动旋转正方体3D特效是一款基于css3 keyframes属性制作的图片相册自动旋转立方体特效 效果展示 下载地址 css3实现3D立方体旋转特效代码

Go——下划线

"_"是特殊标识符&#xff0c;用来忽略结果。 1. 下划线在import中 在golang中&#xff0c;import的作用是导入其他package。 import下划线的作用&#xff1a;当导入一个包时&#xff0c;该包下的文件里所有init()函数都会被执行&#xff0c;然而&#xff0c;有些时候…

Spring Boot如何自定义自己的Starter组件?

一、为什么要自定义starter 在我们的日常开发工作中&#xff0c;经常会有一些独立于业务之外的配置模块&#xff0c;我们经常将其放到一个特定的 包下&#xff0c;然后如果另一个工程需要复用这块功能的时候&#xff0c;需要将代码硬拷贝到另一个工程&#xff0c;重新集成一 遍…

SQLiteC/C++接口详细介绍之sqlite3类(三)

上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;二&#xff09; 下一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;四&#xff09; 6.sqlite3_create_module与sqlite3_create_module_v2函数 用于创建自定义SQLite模块。创建自定义模块可以让S…

ChatGPT无法登录,提示我们检测到可疑的登录行为,将阻止进一步的尝试。请与管理员联系

1. 问题描述 之前本来已经连续稳定使用ChatGPT好几个月了&#xff0c;但是今天尝试登录ChatGPT的时候&#xff0c;却提示&#xff1a;我们检测到可疑的登录行为&#xff0c;将阻止进一步的尝试。请与管理员联系。 此外&#xff0c;我还在网上看到了一些相关的消息&#xff0c;…

Kafka的基本介绍以及扩展

文章目录 基本操作新增Topic查询Topic修改Topic删除Topic 生产者和消费者创建生产者创建消费者 Broker扩展Producer扩展Topic、Partition、Message扩展存储策略容错机制 基本操作 新增Topic 指定两个分区&#xff0c;两个副本&#xff0c;replication不能大于集群中的broker数…

HarmonyOS预览功能报错:[webpack-cli] SyntaxError: Unexpected end of JSON input

harmonyos预览功能报错 在使用DevEco Studio写页面&#xff0c;进行预览的时候报错&#xff1a; [Compile Result] [webpack-cli] SyntaxError: Unexpected end of JSON input [Compile Result] at JSON.parse (<anonymous>) [Compile Result] at updateCached…

Fair Data Exchange:区块链实现的原子式公平数据交换

1. 引言 2024年斯坦福大学和a16z crypto research团队 论文 Atomic and Fair Data Exchange via Blockchain 中&#xff0c;概述了一种构建&#xff08;包含过期EIP-4844 blobs的&#xff09;fair data-markets的协议。该论文源自a16z crypto的暑期实习计划&#xff0c;与四名…