基于STM32+华为云IOT设计的智能垃圾桶

news2024/12/23 19:13:56

一、项目介绍

在商业街、小吃街和景区等人流密集的场所,垃圾桶的及时清理对于提供良好的游客体验至关重要。然而,传统的垃圾桶清理方式通常是定时或定期进行,无法根据实际情况进行及时响应,导致垃圾桶溢满,影响环境卫生,给游客带来不便和不满。

为了解决这一问题,本项目基于STM32F103ZET6主控芯片和华为云物联网平台,设计了一套智能垃圾桶管理系统。该系统通过NBIOT-BC26模块连接到华为云物联网平台,实现了垃圾桶数据的实时采集和上传。

在本地,垃圾桶通过多种传感器进行数据采集。使用DHT11模块实时监测环境温度和湿度,以了解垃圾桶所处环境的状态。采用中科微电子出品的GPS模块,通过串口输出GPS数据,实现垃圾桶的定位功能。垃圾桶口还配备了红外传感器,用于检测垃圾桶是否已满。

通过NBIOT-BC26模块,采集到的数据被实时上传到华为云物联网平台。在保洁人员管理中心,开发了一个数据大屏,采用Qt开发,运行在Windows系统下。数据大屏展示了该区域内垃圾桶的详细情况,包括环境温度、湿度、GPS定位和垃圾桶的满溢状态。

当垃圾桶满了时,上位机会实时发送短信通知保洁人员进行清理,并提供垃圾桶的位置信息,以便保洁人员快速响应并进行清理操作。

通过这套智能垃圾桶管理系统,垃圾桶的清理可以根据实际情况进行及时调度,提高了垃圾桶的使用效率,改善了环境卫生状况,提升了游客的体验感。同时,保洁人员能够更加高效地管理垃圾桶,提升工作效率,减少资源浪费。整个系统的设计在提供一个智能、高效的垃圾桶管理解决方案,为公共场所的环境卫生管理带来便利和改进。

image-20230728140353572

image-20230728140410093

image-20230728141958810

二、设计思路总结

2.1 硬件选型

【1】主控芯片:STM32F103ZET6

  • STM32F103系列是意法半导体(STMicroelectronics)推出的低功耗、高性能的32位ARM Cortex-M3微控制器系列。选择STM32F103ZET6作为主控芯片,是因为它具有较高的计算能力和丰富的外设接口,能够满足项目的需求。

【2】通信模块:NBIOT-BC26

  • NBIOT-BC26是一种窄带物联网(NB-IoT)通信模块,支持低功耗、广覆盖、远距离的物联网通信。它能够将垃圾桶采集到的数据通过NB-IoT网络上传到云平台,实现实时监测和远程管理。

【3】传感器模块:

  • DHT11模块:用于采集环境温度和湿度数据。DHT11是一种低成本、数字式温湿度传感器,具有简单的接口和良好的性能,适用于本项目的温湿度监测需求。
  • GPS模块:用于实现垃圾桶的定位功能。选择中科微电子出品的GPS模块,通过串口输出GPS数据,能够准确获取垃圾桶的位置信息。
  • 红外传感器:用于检测垃圾桶是否已满。红外传感器能够通过红外线的反射来判断垃圾桶内是否有垃圾,从而判断垃圾桶的满溢状态。

【5】数据大屏:采用Qt开发,运行在Windows系统下。数据大屏通过图形界面展示垃圾桶的详细情况,包括环境温度、湿度、GPS定位和垃圾桶的满溢状态。

项目的硬件选型包括主控芯片、通信模块、传感器模块、红外传感器和数据大屏。这些硬件组件相互配合,实现了智能垃圾桶管理系统的功能,包括数据采集、通信传输、定位功能和信息展示。

2.2 硬件设计

【1】主控芯片选择:
作为整个系统的核心,选择了STM32F103ZET6作为主控芯片。该芯片具有较高的计算能力和丰富的外设接口,能够满足项目的需求。主控芯片负责与各个硬件模块进行通信和数据处理,同时控制通信模块的数据传输。

【2】通信模块选择:
为了实现垃圾桶数据的实时采集和上传,选择了NBIOT-BC26通信模块。NBIOT-BC26支持窄带物联网通信,具有低功耗、广覆盖和远距离传输的特点,能够将采集到的数据通过NB-IoT网络上传到华为云物联网平台。

【3】传感器模块选择:

  • DHT11模块用于采集环境温度和湿度数据。DHT11是一种低成本、数字式温湿度传感器,通过数字信号输出温湿度数值,具有简单的接口和良好的性能,适用于本项目的温湿度监测需求。
  • GPS模块用于实现垃圾桶的定位功能。选择中科微电子出品的GPS模块,通过串口输出GPS数据,能够准确获取垃圾桶的位置信息。
  • 红外传感器设计:
    为了检测垃圾桶是否已满,采用红外传感器。红外传感器能够通过红外线的反射来判断垃圾桶内是否有垃圾,从而判断垃圾桶的满溢状态。红外传感器与主控芯片相连,通过数字输入口接收传感器的信号,并进行处理判断。

【4】数据大屏设计:
数据大屏采用Qt开发,运行在Windows系统下。通过图形界面展示垃圾桶的详细情况,包括环境温度、湿度、GPS定位和垃圾桶的满溢状态。主控芯片通过与数据大屏的通信接口实时传输数据,数据大屏根据接收到的数据进行展示。

整个硬件设计思路是将各个硬件模块与主控芯片相连接,通过主控芯片的控制和数据处理,实现数据的采集、通信传输、定位功能和信息展示。通过合理选择硬件组件,并进行适当的连接和接口设计,实现了智能垃圾桶管理系统的功能。

2.3 软件设计

【1】系统架构设计:
软件设计的第一步是确定系统的整体架构。根据项目需求,可以采用分层架构设计,将系统划分为应用层、业务逻辑层和驱动层。应用层负责与用户交互,业务逻辑层处理具体的业务逻辑,驱动层与硬件模块进行通信和控制。

【2】硬件驱动设计:
针对每个硬件模块,需要编写相应的驱动程序。主控芯片与通信模块、传感器模块和红外传感器进行通信,通过串口、I2C、SPI等接口与它们进行数据交互。每个硬件模块的驱动程序应包括初始化、数据采集和控制等功能。

【3】数据处理与逻辑控制:
主控芯片负责接收来自各个硬件模块的数据,并进行处理和逻辑控制。例如,从DHT11传感器读取温湿度数据后,可以进行数据的校验和转换,然后根据设定的阈值判断是否需要进行烘干操作。同时,主控芯片还负责控制红外传感器进行垃圾桶满溢状态的检测。

【4】通信与数据上传:
通过NBIOT-BC26通信模块,将采集到的数据通过NB-IoT网络上传到华为云物联网平台。主控芯片与通信模块进行通信,将需要上传的数据打包成相应的格式,并通过串口等接口发送给通信模块,实现数据的上传。

【5】用户界面设计:
软件还需要设计用户界面,以便用户可以直观地查看垃圾桶的状态和数据信息。可以使用Qt等工具进行界面设计,展示环境温度、湿度、GPS定位和垃圾桶的满溢状态等信息。用户界面与主控芯片进行通信,接收数据并进行展示。

整个软件设计思路是基于系统架构设计,通过硬件驱动、数据处理与逻辑控制、通信与数据上传以及用户界面设计等模块的开发,实现智能垃圾桶管理系统的功能。软件设计需要与硬件设计相结合,保证数据的采集、处理、传输和展示的协调运作。

2.4 系统交互流程

【1】用户打开智能垃圾桶管理系统的应用程序。

【2】系统初始化:

  • 系统进行硬件初始化,包括主控芯片、通信模块、传感器模块和红外传感器的初始化。
  • 确保通信模块连接到NB-IoT网络,并与华为云物联网平台建立通信连接。

【3】环境监测:

  • 系统开始监测环境温度和湿度,并获取垃圾桶的定位信息。主控芯片通过DHT11传感器获取温湿度数据,通过GPS模块获取垃圾桶的位置信息。

【4】数据处理与逻辑控制:

  • 主控芯片对采集到的数据进行处理和逻辑控制。可以根据温度和湿度数据判断垃圾桶是否需要进行清理,并通过红外传感器检测垃圾桶的满溢状态。

【5】数据上传:

  • 主控芯片将处理后的数据通过通信模块上传到华为云物联网平台。数据可以包括环境温度、湿度、GPS定位和垃圾桶的满溢状态等信息。
  • 上传的数据可以以JSON等格式进行打包,通过NB-IoT网络传输到华为云物联网平台。

【6】数据展示:

  • 用户界面接收从主控芯片传输过来的数据,并进行展示。用户可以在界面上查看环境温度、湿度、GPS定位和垃圾桶的满溢状态等信息。
  • 数据展示可以通过图表、文字、图像等形式进行呈现,以便用户直观地了解系统的状态和数据信息。

整个系统的交互流程涉及到硬件模块的数据采集、主控芯片的数据处理与逻辑控制、通信模块的数据上传以及用户界面的数据展示和用户交互。通过这些步骤,用户可以方便地监测和管理智能垃圾桶的状态和数据信息。

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

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

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

image-20221204193824815

3.1 物联网平台介绍

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

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

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

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

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

img

3.2 开通物联网服务

地址: https://www.huaweicloud.com/product/iothub.html

image-20221204194233414

进来默认会提示开通标准版,在2023的1月1号年之后没有基础版了。

image-20230313171311582

image-20230313171325183

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

image-20230321154943337

总结:

端口号:   MQTT (1883)| MQTTS (8883)   
接入地址: 7445c6bcd3.st1.iotda-app.cn-north-4.myhuaweicloud.com

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

Microsoft Windows [版本 10.0.19044.2728]
(c) Microsoft Corporation。保留所有权利。

C:\Users\11266>ping 7445c6bcd3.st1.iotda-device.cn-north-4.myhuaweicloud.com

正在 Ping 7445c6bcd3.st1.iotda-device.cn-north-4.myhuaweicloud.com [117.78.5.125] 具有 32 字节的数据:
来自 117.78.5.125 的回复: 字节=32 时间=42ms TTL=30
来自 117.78.5.125 的回复: 字节=32 时间=35ms TTL=30
来自 117.78.5.125 的回复: 字节=32 时间=36ms TTL=30
来自 117.78.5.125 的回复: 字节=32 时间=36ms TTL=30

117.78.5.125 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 35ms,最长 = 42ms,平均 = 37ms

C:\Users\11266>

image-20230321161044723

MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口比较合适。 接下来的ESP8266就采用1883端口连接华为云物联网平台。

3.3 创建产品

(1)创建产品

点击产品页,再点击左上角创建产品。

image-20230313171503547

(2)填写产品信息

根据自己产品名字填写。

image-20230728135430775

(3)产品创建成功

image-20230728135444283

(4)添加自定义模型

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

先点击自定义模型。

image-20230313171815860

再创建一个服务ID。

image-20230321155733514

接着点击新增属性。

image-20230321155756735

3.4 添加设备

产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。

(1)注册设备

image-20230313173158883

(2)根据自己的设备填写

image-20230728135539727

(3)保存设备信息

创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。

image-20230728135557050

(4) 设备创建完成

image-20230728135620427

3.5 MQTT协议主题订阅与发布

(1)MQTT协议介绍

当前的设备是采用MQTT协议与华为云平台进行通信。

MQTT是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT是专门针对物联网开发的轻量级传输协议。MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。目前MQTT拥有各种平台和设备上的客户端,已经形成了初步的生态系统。

MQTT是一种消息队列协议,使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合,相对于其他协议,开发更简单;MQTT协议是工作在TCP/IP协议上;由TCP/IP协议提供稳定的网络连接;所以,只要具备TCP协议栈的网络设备都可以使用MQTT协议。 本次设备采用的ESP8266就具备TCP协议栈,能够建立TCP连接,所以,配合STM32代码里封装的MQTT协议,就可以与华为云平台完成通信。

华为云的MQTT协议接入帮助文档在这里: https://support.huaweicloud.com/devg-iothub/iot_02_2200.html

img

业务流程:

img

(2)华为云平台MQTT协议使用限制

描述限制
支持的MQTT协议版本3.1.1
与标准MQTT协议的区别支持Qos 0和Qos 1支持Topic自定义不支持QoS2不支持will、retain msg
MQTTS支持的安全等级采用TCP通道基础 + TLS协议(最高TLSv1.3版本)
单帐号每秒最大MQTT连接请求数无限制
单个设备每分钟支持的最大MQTT连接数1
单个MQTT连接每秒的吞吐量,即带宽,包含直连设备和网关3KB/s
MQTT单个发布消息最大长度,超过此大小的发布请求将被直接拒绝1MB
MQTT连接心跳时间建议值心跳时间限定为30至1200秒,推荐设置为120秒
产品是否支持自定义Topic支持
消息发布与订阅设备只能对自己的Topic进行消息发布与订阅
每个订阅请求的最大订阅数无限制

(3)主题订阅格式

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

image-20221207153310037

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

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

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

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

最终的格式:
$oc/devices/6419627e40773741f9fbdac7_dev1/sys/messages/down

(4)主题发布格式

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

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

帮助文档地址:https://support.huaweicloud.com/usermanual-iothub/iot_06_v5_3010.html

image-20221207153637391

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

发布的主题格式:
$oc/devices/{device_id}/sys/properties/report
 
最终的格式:
$oc/devices/6419627e40773741f9fbdac7_dev1/sys/properties/report
发布主题时,需要上传数据,这个数据格式是JSON格式。

上传的JSON数据格式如下:

{
  "services": [
    {
      "service_id": <填服务ID>,
      "properties": {
        "<填属性名称1>": <填属性值>,
        "<填属性名称2>": <填属性值>,
        ..........
      }
    }
  ]
}
根据JSON格式,一次可以上传多个属性字段。 这个JSON格式里的,服务ID,属性字段名称,属性值类型,在前面创建产品的时候就已经介绍了,不记得可以翻到前面去查看。

根据这个格式,组合一次上传的属性数据:
{"services": [{"service_id": "stm32","properties":{"DS18B20":18,"motor_water":1,"motor_oxygen":1,"temp_max":10,"water_hp":130,"motor_food":0,"time_food":0,"oxygen_food":3}}]}

3.6 MQTT三元组

MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。

接下来介绍,华为云平台的MQTT三元组参数如何得到。

(1)MQTT服务器地址

要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。

帮助文档地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home

image-20230302135718882

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

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

华为云的MQTT服务器地址:114.116.232.138
域名:7445c6bcd3.st1.iotda-device.cn-north-4.myhuaweicloud.com
华为云的MQTT端口号:1883

(2)生成MQTT三元组

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

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

下面是打开的页面:

image-20221207154917230

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

直接得到三元组信息。

image-20230321160708924

image-20230321160718302

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

ClientId 6419627e40773741f9fbdac7_dev1_0_0_2023032108
Username 6419627e40773741f9fbdac7_dev1
Password 861ac9e6a579d36888b2aaf97714be7af6c77017b017162884592bd68b086a6e

3.7 模拟设备登录测试

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

(1)填入登录信息

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

image-20230321161132971

(2)打开网页查看

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

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

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

四、上位机开发

为了方便查看设备上传的数据,对设备进行远程控制,接下来利用Qt开发一款Android和windows系统的上位机。

使用华为云平台提供的API接口获取设备上传的数据,也可以给设备下发指令,控制设备。

为了方便查看设备上传的数据,对设备进行远程控制,接下来利用Qt开发一款Android和windows系统的上位机。

使用华为云平台提供的API接口获取设备上传的数据,也可以给设备下发指令,控制设备。

4.1 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

4.2 创建IAM账户

创建一个IAM账户,因为接下来开发上位机,需要使用云平台的API接口,这些接口都需要token进行鉴权。简单来说,就是身份的认证。 调用接口获取Token时,就需要填写IAM账号信息。所以,接下来演示一下过程。

地址: https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users

获取Token时,除了AIM账号外,还需要项目凭证:

faa0973835ab409ab48182e2590f4ad3

image-20230321161348409

image-20230321161414487

鼠标点击自己昵称,点击统一身份认证。

image-20230321161433209

点击左上角创建用户

image-20230321161450557

image-20221207161209880

image-20221207161308917

image-20221207161327200

创建成功:

image-20221212174359962

image-20221212174412097

image-20230321161557848

4.3 获取影子数据

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

设备影子介绍:

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

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

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

如果对接口不熟悉,可以先进行在线调试:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow

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

image-20230321161636567

image-20230321161701419

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

{
 "device_id": "6419627e40773741f9fbdac7_dev1",
 "shadow": [
  {
   "service_id": "stm32",
   "desired": {
    "properties": null,
    "event_time": null
   },
   "reported": {
    "properties": {
     "DS18B20": 18,
     "motor_water": 1,
     "motor_oxygen": 1,
     "temp_max": 10,
     "water_hp": 130,
     "motor_food": 0,
     "time_food": 0,
     "oxygen_food": 3
    },
    "event_time": "20230321T081126Z"
   },
   "version": 0
  }
 ]
}

4.4 修改设备属性

地址: https://support.huaweicloud.com/api-iothub/iot_06_v5_0034.html

接口说明

设备的产品模型中定义了物联网平台可向设备下发的属性,应用服务器可调用此接口向指定设备下发属性。平台负责将属性以同步方式发送给设备,并将设备执行属性结果同步返回。

修改设备属性的接口,可以让服务器给设备下发指令,如果需要控制设备。

在线调试地址:

https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=UpdateProperties

修改设备属性是属于同步命令,需要设备在线才可以进行调试,先使用MQTT客户端登录服务器,模拟设备上线。

然后进行调试,测试数据远程下发给设备。

【1】利用MQTT客户端先登录设备 (这是同步命令,必须在线才能调试)

image-20230321161923007

【2】点击调试

image-20230321161905033

{"services":{"temp_max":100}}

【4】可以看到,MQTT客户端软件上已经收到了服务器下发的消息

image-20230313175819901

由于是同步命令,服务器必须要收到设备的响应才能顺利完成一个流程,设备响应了服务器才能确定数据下发成功。

image-20230321161941584

MQTT设备端如何响应呢?

设备响应格式说明:https://support.huaweicloud.com/api-iothub/iot_06_v5_3008.html

image-20221203163532648

下面进行实操:

当服务器通过在线调试,发送指令下来之后,客户端将请求ID复制下来,添加到发布主题的格式里,再回复回去,服务器收到了响应,一次属性修改就完美完成了。

image-20230321162053263

就是成功的状态:

image-20230321162026282

**下面是请求的总结: ** (响应服务器的修改设备属性请求)

上报主题的格式:$oc/devices/{device_id}/sys/properties/set/response/request_id=

$oc/devices/6419627e40773741f9fbdac7_dev1/sys/properties/set/response/request_id=

响应的数据:
{"result_code": 0,"result_desc": "success"}

4.5 设计上位机

前面2讲解了需要用的API接口,接下来就使用Qt设计上位机,设计界面,完成整体上位机的逻辑设计。

【1】新建Qt工程

image-20230302144331541

选择工程路径,放在英文路径下。

image-20230321162532573

image-20230313180428670

image-20230313180451177

image-20230313180504518

image-20230321162620141

创建完毕。

新建Android的模板:

image-20230321162657975

image-20230321162731010

image-20230321162741791

image-20230321162812486

【2】界面设计

image-20230728140933190

【4】代码设计:配置参数读取与保存

/*
功能: 保存数据到文件
*/
void Widget::SaveDataToFile(QString text)
{
    /*保存数据到文件,方便下次加载*/
    QString file;
    file=QCoreApplication::applicationDirPath()+"/"+ConfigFile;
    QFile filesrc(file);
    filesrc.open(QIODevice::WriteOnly);
    QDataStream out(&filesrc);
    out << text;  //序列化写字符串
    filesrc.flush();
    filesrc.close();
}


/*
功能: 从文件读取数据
*/
QString Widget::ReadDataFile(void)
{
    //读取配置文件
    QString text,data;
    text=QCoreApplication::applicationDirPath()+"/"+ConfigFile;

    //判断文件是否存在
    if(QFile::exists(text))
    {
        QFile filenew(text);
        filenew.open(QIODevice::ReadOnly);
        QDataStream in(&filenew); // 从文件读取序列化数据
        in >> data; //提取写入的数据
        filenew.close();
    }
    return data; //返回值读取的值
}

【3】代码设计:云端数据解析

//解析反馈结果
void Widget::replyFinished(QNetworkReply *reply)
{
    QString displayInfo;

    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

    //读取所有数据
    QByteArray replyData = reply->readAll();

    qDebug()<<"状态码:"<<statusCode;
    qDebug()<<"反馈的数据:"<<QString(replyData);

    //更新token
    if(function_select==3)
    {
        displayInfo="token 更新失败.";
        //读取HTTP响应头的数据
        QList<QNetworkReply::RawHeaderPair> RawHeader=reply->rawHeaderPairs();
        qDebug()<<"HTTP响应头数量:"<<RawHeader.size();
        for(int i=0;i<RawHeader.size();i++)
        {
            QString first=RawHeader.at(i).first;
            QString second=RawHeader.at(i).second;
            if(first=="X-Subject-Token")
            {
                Token=second.toUtf8();
                displayInfo="token 更新成功.";

                //保存到文件
                SaveDataToFile(Token);
                break;
            }
        }
        QMessageBox::information(this,"提示",displayInfo,QMessageBox::Ok,QMessageBox::Ok);
        return;
    }

    //判断状态码
    if(200 != statusCode)
    {
        //解析数据
        QJsonParseError json_error;
        QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
        if(json_error.error == QJsonParseError::NoError)
        {
            //判断是否是对象,然后开始解析数据
            if(document.isObject())
            {
                QString error_str="";
                QJsonObject obj = document.object();
                QString error_code;
                //解析错误代码
                if(obj.contains("error_code"))
                {
                    error_code=obj.take("error_code").toString();
                    error_str+="错误代码:";
                    error_str+=error_code;
                    error_str+="\n";
                }
                if(obj.contains("error_msg"))
                {
                    error_str+="错误消息:";
                    error_str+=obj.take("error_msg").toString();
                    error_str+="\n";
                }

                //显示错误代码
                QMessageBox::information(this,"提示",error_str,QMessageBox::Ok,QMessageBox::Ok);
            }
         }
        return;
    }

    //设置属性
    if(function_select==12 || function_select==13)
    {
        //解析数据
        QJsonParseError json_error;
        QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
        if(json_error.error == QJsonParseError::NoError)
        {
            //判断是否是对象,然后开始解析数据
            if(document.isObject())
            {
                QJsonObject obj = document.object();
                if(obj.contains("response"))
                {
                    QJsonObject obj1=obj.take("response").toObject();
                    int val=0;
                    QString success;
                    if(obj1.contains("result_code"))
                    {
                         val=obj1.take("result_code").toInt();
                    }
                    if(obj1.contains("result_desc"))
                    {
                         success=obj1.take("result_desc").toString();
                    }

                    if(val==0 && success =="success")
                    {
                        //显示状态
                        QMessageBox::information(this,"提示","远程命令操作完成.",QMessageBox::Ok,QMessageBox::Ok);
                        return;
                    }
                    else
                    {
                        //显示状态
                        QMessageBox::information(this,"提示","设备未正确回应.请检查设备网络.",QMessageBox::Ok,QMessageBox::Ok);
                        return;
                    }
                }
            }
         }
    }

    //查询设备属性
    if(function_select==0)
    {
        //解析数据
        QJsonParseError json_error;
        QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
        if(json_error.error == QJsonParseError::NoError)
        {
            //判断是否是对象,然后开始解析数据
            if(document.isObject())
            {
                QJsonObject obj = document.object();
                if(obj.contains("shadow"))
                {
                    QJsonArray array=obj.take("shadow").toArray();
                    for(int i=0;i<array.size();i++)
                    {
                        QJsonObject obj2=array.at(i).toObject();
                        if(obj2.contains("reported"))
                        {
                            QJsonObject obj3=obj2.take("reported").toObject();


                            if(obj3.contains("properties"))
                            {
                                QJsonObject properties=obj3.take("properties").toObject();

                                qDebug()<<"开始解析数据....";
                            }
                        }
                    }
                }
            }
         }
        return;
    }
}

五、代码实现

5.1 BC26连接云平台实现代码

下面是使用STM32F103ZET6和BC26连接华为云物联网平台实现MQTT设备登录、主题订阅和主题发布的实现代码:

#include "stdio.h"
#include "string.h"
#include "stdlib.h"

// 定义华为云物联网平台的服务器地址、端口号、设备ID和设备密码
#define MQTT_SERVER "mqtt://xxxxxx.iotplatform.com"  // 请替换为实际的服务器地址
#define MQTT_PORT 1883  // 请根据实际情况修改端口号
#define DEVICE_ID "your_device_id"  // 请替换为实际的设备ID
#define DEVICE_PASSWORD "your_device_password"  // 请替换为实际的设备密码

// 定义MQTT相关的参数
#define MQTT_CLIENT_ID "your_client_id"  // 请替换为实际的客户端ID
#define MQTT_TOPIC "your_topic"  // 请替换为实际的主题

// 定义MQTT消息接收回调函数
void mqtt_message_received(char *topic, char *payload) {
    printf("Received message on topic: %s\n", topic);
    printf("Payload: %s\n", payload);
}

// 建立MQTT连接
void mqtt_connect() {
    // 连接到华为云物联网平台的MQTT服务器
    // 这里使用的是MQTT的QoS 1级别
    // 请根据实际情况修改QoS级别和其他参数
    char command[256];
    sprintf(command, "AT+QMTCFG=\"aliauth\",0,%d,\"%s\",\"%s\"", MQTT_PORT, DEVICE_ID, DEVICE_PASSWORD);
    printf("Sending command: %s\n", command);
    // 发送AT指令连接到MQTT服务器
    // ...

    // 订阅主题
    sprintf(command, "AT+QMTSUB=0,1,\"%s\",1", MQTT_TOPIC);
    printf("Sending command: %s\n", command);
    // 发送AT指令订阅主题
    // ...
}

// 发布MQTT消息
void mqtt_publish(char *payload) {
    // 发布消息到指定的主题
    char command[256];
    sprintf(command, "AT+QMTPUB=0,0,0,0,\"%s\"", MQTT_TOPIC);
    printf("Sending command: %s\n", command);
    // 发送AT指令设置发布的主题
    // ...

    sprintf(command, "AT+QMTPUB=0,1,0,0,%d", strlen(payload));
    printf("Sending command: %s\n", command);
    // 发送AT指令设置消息的长度
    // ...

    printf("Sending payload: %s\n", payload);
    // 发送消息的内容
    // ...

    // 等待MQTT服务器返回发布结果
    // ...
}

int main() {
    // 初始化串口和其他硬件模块
    // ...

    // 连接到华为云物联网平台的MQTT服务器
    mqtt_connect();

    // 进入主循环
    while (1) {
        // 处理其他任务
        // ...

        // 检查是否有需要发布的消息
        // 如果有,调用mqtt_publish函数发布消息
        // ...

        // 检查是否有接收到的MQTT消息
        // 如果有,调用mqtt_message_received函数处理消息
        // ...
    }

    return 0;
}

5.2 BC26模块的MQTT协议指令

BC26模块是一款支持NB-IoT通信技术的物联网模块,可以通过AT指令与外部设备进行通信和控制。

下面是BC26模块与MQTT协议相关的一些常用AT指令及其功能:

【1】AT+QMTOPEN:打开MQTT客户端连接。

  • 功能:通过该指令连接到MQTT服务器。
  • 参数:服务器地址、端口号、用户名和密码等。
  • 示例:AT+QMTOPEN=0,“mqtt://xxxxxx.iotplatform.com”,1883

【2】AT+QMTCLOSE:关闭MQTT客户端连接。

  • 功能:通过该指令关闭与MQTT服务器的连接。
  • 参数:无。
  • 示例:AT+QMTCLOSE=0

【3】AT+QMTCONN:建立MQTT连接。

  • 功能:通过该指令建立与MQTT服务器的连接。
  • 参数:客户端ID、用户名、密码等。
  • 示例:AT+QMTCONN=0,“your_client_id”,“your_username”,“your_password”

【4】AT+QMTDISC:断开MQTT连接。

  • 功能:通过该指令断开与MQTT服务器的连接。
  • 参数:无。
  • 示例:AT+QMTDISC=0

【5】AT+QMTSUB:订阅MQTT主题。

  • 功能:通过该指令订阅指定的MQTT主题。
  • 参数:主题、QoS级别等。
  • 示例:AT+QMTSUB=0,1,“your_topic”,1

【6】AT+QMTUNS:取消订阅MQTT主题。

  • 功能:通过该指令取消订阅指定的MQTT主题。
  • 参数:主题。
  • 示例:AT+QMTUNS=0,1,“your_topic”

【7】AT+QMTPUB:发布MQTT消息。

  • 功能:通过该指令发布消息到指定的MQTT主题。
  • 参数:主题、消息内容、QoS级别等。
  • 示例:AT+QMTPUB=0,0,0,0,“your_topic”

【8】AT+QMTRECV:接收MQTT消息。

  • 功能:通过该指令接收从MQTT服务器接收到的消息。
  • 参数:无。
  • 示例:AT+QMTRECV=0

这些是BC26模块中与MQTT协议相关的一些常用AT指令。

5.3 读取DHT11传感器的温湿度数据

以下是使用STM32F103ZET6读取DHT11传感器的温湿度数据的实现代码:

#include "stm32f10x.h"
#include "dht11.h"

int main(void)
{
    // 初始化DHT11传感器
    DHT11_Init();

    while (1)
    {
        // 读取DHT11传感器的温湿度数据
        DHT11_Result result = DHT11_Read();

        if (result.status == DHT11_OK)
        {
            // 温度数据
            uint8_t temperature = result.temperature;
            // 湿度数据
            uint8_t humidity = result.humidity;

            // 在这里进行温湿度数据的处理和使用
            // ...

            // 延时一段时间后再次读取
            DelayMs(2000);
        }
        else
        {
            // 读取失败,可以进行相应的错误处理
            // ...
        }
    }
}

在主函数中,通过循环不断读取DHT11传感器的温湿度数据。如果读取成功,可以从result结构体中获取温度和湿度数据,并进行相应的处理。如果读取失败,可以根据需要进行错误处理。

5.4 DHT11.c和DHT11.h代码

dht11.h:

#ifndef DHT11_H
#define DHT11_H

#include "stm32f10x.h"

typedef struct
{
    uint8_t status;      // 读取状态,0表示成功,其他表示失败
    uint8_t humidity;    // 湿度值
    uint8_t temperature; // 温度值
} DHT11_Result;

void DHT11_Init(void);
DHT11_Result DHT11_Read(void);

#endif

dht11.c:

#include "dht11.h"

#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_Pin_0

static void DHT11_Delay(uint32_t us)
{
    uint32_t count = us * 8;
    while (count--)
    {
        __NOP();
    }
}

static void DHT11_SetOutput(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}

static void DHT11_SetInput(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}

static uint8_t DHT11_ReadByte(void)
{
    uint8_t byte = 0;
    for (uint8_t i = 0; i < 8; i++)
    {
        while (!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            // 等待低电平结束
        }
        DHT11_Delay(30);
        if (GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            byte |= (1 << (7 - i));
        }
        while (GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            // 等待高电平结束
        }
    }
    return byte;
}

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

    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);

    GPIO_SetBits(DHT11_PORT, DHT11_PIN);
}

DHT11_Result DHT11_Read(void)
{
    DHT11_Result result;
    result.status = 1;

    DHT11_SetOutput();
    GPIO_ResetBits(DHT11_PORT, DHT11_PIN);
    DHT11_Delay(18000);
    GPIO_SetBits(DHT11_PORT, DHT11_PIN);
    DHT11_Delay(20);
    DHT11_SetInput();

    if (!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
    {
        while (!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            // 等待低电平结束
        }
        while (GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            // 等待高电平结束
        }

        uint8_t data[5];
        for (uint8_t i = 0; i < 5; i++)
        {
            data[i] = DHT11_ReadByte();
        }

        uint8_t sum = data[0] + data[1] + data[2] + data[3];
        if (sum == data[4])
        {
            result.status = 0;
            result.humidity = data[0];
            result.temperature = data[2];
        }
    }

    return result;
}

dht11.h文件定义了DHT11传感器的初始化函数DHT11_Init()和读取函数DHT11_Read(),以及DHT11_Result结构体用于存储读取结果。

dht11.c文件实现了DHT11传感器的初始化和读取函数。在初始化函数中,配置了DHT11引脚的GPIO模式和速度。在读取函数中,通过发送开始信号和接收数据的方式读取DHT11传感器的温湿度数据,并进行校验。

5.5 GPS数据解析

在STM32F103ZET6上通过串口2读取GPS模块返回的定位数据并解析经纬度和定位状态。

#include "stm32f10x.h"
#include <stdio.h>
#include <string.h>

// 定义串口2接收缓冲区大小
#define RX_BUFFER_SIZE 256

// 定义GPS数据解析状态
typedef enum {
    GPS_STATE_IDLE,        // 空闲状态
    GPS_STATE_RECEIVING,   // 接收中状态
    GPS_STATE_COMPLETE     // 接收完成状态
} GPS_State;

// 定义接收缓冲区和接收状态变量
char rxBuffer[RX_BUFFER_SIZE];
volatile uint16_t rxIndex = 0;
volatile GPS_State gpsState = GPS_STATE_IDLE;

// 处理接收到的GPS数据
void processGPSData() {
    // 在这里进行GPS数据解析和处理
    // 解析经纬度和定位状态等信息
    // 根据需要进行相应的操作或显示
    // 例如,打印经纬度和定位状态
    printf("Latitude: %s\n", latitude);
    printf("Longitude: %s\n", longitude);
    printf("Position Fix Status: %s\n", positionStatus);
}

// 串口2接收中断处理函数
void USART2_IRQHandler(void) {
    if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
        char data = USART_ReceiveData(USART2);
        
        // 接收到回车换行符表示一条完整的GPS数据
        if (data == '\n') {
            rxBuffer[rxIndex] = '\0';
            rxIndex = 0;
            gpsState = GPS_STATE_COMPLETE;
        } else {
            // 将接收到的数据存储到缓冲区中
            rxBuffer[rxIndex] = data;
            rxIndex++;
            
            // 接收缓冲区溢出时进行处理
            if (rxIndex >= RX_BUFFER_SIZE) {
                rxIndex = 0;
                gpsState = GPS_STATE_IDLE;
            }
        }
    }
}

int main(void) {
    // 初始化串口2和GPIO引脚
    // 设置串口2的波特率、数据位、停止位等参数
    
    // 使能串口2接收中断
    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
    NVIC_EnableIRQ(USART2_IRQn);
    
    while (1) {
        // 如果接收到完整的GPS数据
        if (gpsState == GPS_STATE_COMPLETE) {
            // 处理接收到的GPS数据
            processGPSData();
            
            // 处理完成后,重置接收状态为IDLE
            gpsState = GPS_STATE_IDLE;
        }
    }
}

代码中的串口初始化和中断处理部分是基于标准库的使用方式。

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

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

相关文章

钉钉stream机器人-实操详细教程

支持事件订阅、机器人收消息、卡片回调等功能 优点&#xff1a; 配置简单&#xff0c;不依赖也不需要暴露公网IP&#xff0c;无需向公网开放端口 github官方链接&#xff1a;GitHub - open-dingtalk/dingtalk-stream-sdk-python: Python SDK for DingTalk Stream Mode API, Co…

低噪声 256 细分微步进电机驱动MS35774/MS35774A(汽车应用级别)

MS35774/MS35774A 是一款高精度、低噪声的两相步进 电机驱动芯片&#xff0c;芯片内置功率 MOSFET&#xff0c;长时间工作的平均电 流可以达到 1.4A&#xff0c;峰值电流 2A。芯片集成了过温保护、欠压 保护、过流保护、短地保护、短电源保护功能。 主要特点 ◼ 2 相步进电机…

excel subtotal 函数(分类汇总)

函数说明 返回列表中的分类汇总。 语法 SUBTOTAL(function_num,ref1,[ref2],...) SUBTOTAL 函数语法具有以下参数&#xff1a; Function_num 必需。 数字 1-11 或 101-111&#xff0c;用于指定要为分类汇总使用的函数。 如果使用 1-11&#xff0c;将包括手动隐藏的行&…

glTF模型骨骼动画

推荐&#xff1a;使用 NSDT场景编辑器快速搭建3D应用场景 本文详细演示了风车动画的制作过程&#xff1a; 当然&#xff0c;这非常容易硬编码&#xff08;有两个对象&#xff0c;一个静态的&#xff0c;一个旋转的&#xff09;。但是&#xff0c;我计划稍后添加更多动画&#…

GIF动图怎么变成jpg动图?一键分解GIF动画

GIF格式图片怎么转换成jpg格式图片&#xff1f;在日常生活中jpg、png转GIF格式非常的常见&#xff0c;那么gif转换成jpg格式应该怎么操作呢&#xff1f;很简单&#xff0c;给大家分享一款gif动态图片制作&#xff08;https://www.gif.cn/giffenjie&#xff09;工具&#xff0c;…

51单片机项目(12)——基于51单片机的智能台灯设计

本次设计的功能如下&#xff1a; 首先使用PCF8591芯片&#xff0c;实现了AD DA转换&#xff0c;AD采集的是光敏电阻的信息&#xff0c;光照强度越强&#xff0c;电压越小&#xff0c;AD采集到的数值越小。同时将AD采集的数字量作为DA输出时的输入量&#xff0c;模拟输出端接了…

技术对比:Flutter vs. 传统桌面应用开发框架

在移动应用开发领域&#xff0c;Flutter已经赢得了广泛的认可和采用&#xff0c;成为了跨平台移动应用开发的瑞士军刀。然而&#xff0c;Flutter的魅力并不仅限于移动平台&#xff0c;它还可以用于开发桌面应用程序&#xff0c;为开发人员提供了一种全新的选择。本文将深入探讨…

react 路由的使用

react-router-dom 专注于web网页开发 下载依赖&#xff0c;这里使用的版本是5 npm install react-router-dom5 1.路由的基本使用,点击左侧菜单进行高亮&#xff08;进行高亮要使用NavLink&#xff0c;使用了NavLink,会根据 activeClassName"active"找到active的cl…

Vue基础语法的进阶,事件处理器,自定义组件及组件通信

目录 一、事件处理器 1. 概述 2. 实例 二、语法整合 1. 作用 2. 实例 三、自定义组件 1. 概述 2. 实例 四、组件通信 ( 1 ) 讲述 ( 2 ) 父传子 ( 3 ) 子传父 学习后的收获 一、事件处理器 1. 概述 在Vue中&#xff0c;事件处理器是用来处理DOM事件的方法。它…

ubuntu20.04 安装 pyconcorde

这个包似乎对网络环境要求挺高的&#xff0c;我们直接弄个 射线A型号 的飞机 直接使用 pip install pyconcorde 安装&#xff0c;发现在使用里面的包时会报奇怪的错误&#xff0c;于是决定寻找 github 上的 pyconcorde 源码&#xff0c;看文档进行安装 github 地址&#xff1…

msvcp140.dll重新安装的解决方法是什么?(最新方法)

msvcp140.dll 是 Microsoft Visual C Redistributable 的一个动态链接库文件&#xff0c;它包含了 C 运行时库的一些函数和类&#xff0c;对于许多应用程序和游戏来说都是必需的。如果您的系统中缺失了这个文件&#xff0c;可能会导致程序无法正常运行。下面我们将分享修复 msv…

【C++笔记】C++ list类模拟实现

【C笔记】C list类模拟实现 一、初始化和各种构造1.1、准备工作1.2、各种构造和析构 二、插入和删除2.1、插入2.2、删除 三、迭代器3.1、正向迭代器3.2、反向迭代器3.3、提供迭代器位置 四、其他一些接口4.1、链表的长度和判空4.2、返回链表的头尾结点 一、初始化和各种构造 C…

面试题 17.08. 马戏团人塔

题目链接 面试题 17.08. 马戏团人塔 mid 题目描述 有个马戏团正在设计叠罗汉的表演节目&#xff0c;一个人要站在另一人的肩膀上。出于实际和美观的考虑&#xff0c;在上面的人要比下面的人矮一点且轻一点。已知马戏团每个人的身高和体重&#xff0c;请编写代码计算叠罗汉最多…

Microsoft 网络监控

随着网络的发展和变得越来越复杂&#xff0c;公司比以往任何时候都更需要监控其网络基础设施&#xff0c;因为即使是轻微的系统中断也可能导致重大损失。网络监控工具提供实时数据和网络状态的图形概述。这使您能够准确地了解正在发生的事情&#xff0c;以便您知道需要更改的位…

进程间的通信方式

文章目录 1.简单介绍2.管道2.1管道的基础概念**管道读写规则**:**管道特点** 2.2匿名管道匿名管道父子进程间通信的经典案例&#xff1a; 2.3命名管道基本概念:命名管道的创建&#xff1a;命名管道的打开规则&#xff1a;匿名管道与普通管道的区别**例子&#xff1a;用命名管道…

基于SpringBoot+Vue的宠物领养饲养交流管理平台设计与实现

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

HTML实现移动端布局与页面自适应

我们所说的布局方式&#xff0c;这里我们通常指的是width和height在不同页面情况下面的改变。 常见页面的布局方式有 静态布局 &#xff08;px布局&#xff0c;就是固定其高宽&#xff0c;不论页面怎样放大缩小&#xff0c;其占领的依旧是&#xff0c;使用px固定了的高宽&…

这种方法可以解决开发中的重复“造轮子”

一、前言 开发中&#xff0c;一直听到有人讨论是否需要重复造轮子&#xff0c;我觉得有能力的人&#xff0c;轮子得造。但是往往开发周期短&#xff0c;用轮子所节省的时间去更好的理解业务&#xff0c;应用到业务中&#xff0c;也能清晰发现轮子的利弊&#xff0c;一定意义上解…

PyTorch深度学习(三)【Logistic Regression、处理多维特征的输入】

Logistic Regression 这个名字叫做回归&#xff0c;做的是分类。 线性和logistic的模型&#xff1a; 使用的损失函数&#xff1a;二分类交叉熵 &#xff08;这个也叫做BCELoss&#xff09; logistic要做的事&#xff1a; 代码&#xff1a; import torch# import torch.nn.fun…

Java基于SpringBoot的校园疫情防控系统

文章目录 第一章2.主要技术第三章第四章 系统设计4.1功能结构4.2 数据库设计4.2.1 数据库E/R图4.2.2 数据库表 第五章 系统功能实现5.1系统功能模块5.2后台功能模块5.2.1管理员功能 源码咨询 第一章 springboot校园疫情防控系统演示录像2022 一个好的系统能将校园疫情防控的管理…