• [技术干货] 【华为云IOT实践】基于STM32设计的通信机房空调与新风系统联动装置
    一、前言1.1 项目介绍项目设计里用到的全部工具软件和文档源码,都可以在这里下载。https://pan.quark.cn/s/145a9b3f7f53【1】项目开发背景随着信息技术的发展,数据中心和通信机房作为信息处理的核心枢纽,在各行各业中扮演着越来越重要的角色。然而,由于设备密集且持续运行,机房内的温度和湿度控制成为保证设备正常运行的关键因素之一。传统的机房环境管理方式往往依赖于定期的人工巡检和手动调节,这种方式不仅效率低下,而且容易出现管理疏漏,导致设备过热或湿度过高,进而影响设备的稳定性和使用寿命。针对这一现状,本项目提出了一种基于STM32微控制器的通信机房空调与新风系统联动装置的设计方案。通过集成环境温湿度传感器、风扇控制模块以及4G通信模块,实现了对机房环境的实时监测和智能调控。特别是在数据传输方面,借助华为云IOT物联网云平台的强大功能,不仅可以远程监控机房内的环境变化,还能及时调整空调和新风系统的运行策略,确保机房环境始终处于最佳状态。同时,考虑到实际应用中的便捷性,本装置还提供了本地控制功能,用户可以通过直观的OLED显示屏了解当前环境状态,并通过简单的按键操作来调整设置,增强了用户体验。通过这样的设计,本项目不仅解决了传统机房管理中存在的问题,还极大地提高了机房环境管理的智能化水平,为机房管理人员提供了一个高效、可靠、易于操作的解决方案,进一步保障了通信设备的稳定运行,降低了运维成本。【2】设计实现的功能(1)实时采集环境参数:系统配备了DHT11温湿度传感器,能够连续不断地获取机房内的环境温度和湿度数据。(2)风扇通风散热控制:通过继电器模块控制两个5V直流电机小风扇的启停,以达到通风散热的效果,确保机房内部温度维持在安全范围内。(3)数据远程上传:利用合宙Air724UG 4G模块,将环境监测数据上传至华为云IOT物联网云平台,实现数据的云端存储与管理。(4)远程监控与控制:用户可以通过Windows客户端应用程序实时查看机房内的温度、湿度、风扇状态等信息,并能远程设置温度湿度阈值,选择运行模式(自动/手动),以及远程控制风扇的开关。(5)本地控制功能:在没有网络连接的情况下,用户依然可以通过设备上的按键来控制风扇的开关状态,以及切换设备的运行模式。(6)OLED显示屏信息展示:装置上安装了一个0.96寸的OLED显示屏,用于显示两页信息:第一页显示环境温度、湿度、温度和湿度阈值;第二页显示设备运行模式和风扇的开关状态。【3】项目硬件模块组成(1)主控模块:采用STM32F103RCT6微控制器作为核心处理器,负责处理各种传感器数据、控制输出信号以及与外部设备的通信。(2)环境监测模块:使用DHT11温湿度传感器来实时采集机房内的温度和湿度数据,为后续的环境控制提供依据。(3)显示模块:配置了一块0.96寸的OLED显示屏,通过SPI协议接口与主控芯片相连,用于显示当前环境的温度、湿度、设定阈值以及设备的运行状态。(4)风扇控制模块:包括两个5V直流电机的小风扇,通过继电器控制风扇的启动和停止,实现通风散热功能。(5)通信模块:采用合宙Air724UG 4G模块,支持全网通4G网络,用于将采集到的数据上传至华为云IOT物联网云平台,同时也支持接收来自云端的控制指令。(6)人机交互模块:包括若干个按键,允许用户进行本地控制,如设置温度湿度阈值、切换设备运行模式以及控制风扇的开关。(7)电源模块:系统使用一个5V 2A的稳压模块来提供稳定的电力供应,确保各个模块的正常工作。这些硬件模块相互协作,共同构成了一个完整的智能通信机房空调与新风系统联动装置,能够实现环境参数的实时监测、数据的远程传输与控制、以及本地的人机交互等功能。【4】需求总结项目名称: 基于STM32设计的通信机房空调与新风系统联动装置​支持的功能:(1) 能够实时采集环境温度与湿度。(2) 能够控制2个风扇进行通风扇热。(3) 设备端的数据通过4G网络上传到华为云IOT物联网云平台。(4) 能够通过Windows大屏远程查看设备上传的温度、湿度,风扇开关状态,设备运行的模式。(5) 能够通过Windows大屏远程设置温度和湿度阀值,设置运行模式(自动模式和手动模式),以及控制风扇的开启和关闭。(6) 本地设备也可以通过按键控制风扇的开关,以及切换设备运行模式。(7) 本地设备带了一个0.96寸的OLED显示屏,一共显示2个页面。 第一个页面显示: 可以显示环境的温度、环境的湿度、温度阀值、湿度阀值。 第二个页面显示: 设备运行模式、风扇的开关状态。​硬件选型:(1)主控芯片选择STM32F103RCT6(2)OLED显示屏选择SPI协议接口的0.96寸OLED显示屏。(3)4G模块采用合宙的Air724UG 4G模块,支持全网通4G网络。(4)环境温湿度检测采用DHT11模块。(5)风扇采用5V直流电机的小风扇,通过继电器控制风扇的开和关。(6)系统电源采用5V 2A的稳压模块进行供电,提供稳定电源。1.2 设计思路本项目的设计思路主要围绕提高通信机房环境管理的智能化和自动化水平展开。鉴于机房内设备众多且运行负荷大的特点,环境温湿度的实时监测显得尤为重要。选择了DHT11温湿度传感器来持续采集环境数据,确保能够及时反映机房内的温度和湿度变化情况。为了有效控制机房内的温度,设计中引入了风扇控制模块。通过继电器模块来驱动5V直流电机的小风扇,可以根据采集到的温度数据自动或手动控制风扇的开关,以此来调节机房内的温度,防止设备因过热而损坏。考虑到远程监控的需求,选用了合宙Air724UG 4G模块,通过4G网络将环境数据上传至华为云IOT物联网云平台,使得管理者能够在任何地点通过Windows客户端实时查看机房状态,并作出相应的调整。在本地控制方面,为了便于现场工作人员的操作,设计中加入了按键控制功能。通过简单的按键输入,可以实现风扇开关的控制以及设备运行模式的切换。为了使用户能够直观地了解当前机房的状态,项目中还配备了一块0.96寸的OLED显示屏,用以显示环境参数、设置阈值及设备运行模式等重要信息。系统整体采用STM32F103RCT6作为主控芯片,该芯片具备足够的性能来处理传感器数据、执行控制逻辑以及管理与其他模块之间的通信。为了保证系统的稳定运行,采用了5V 2A的稳压电源模块来提供可靠的电力供应。本项目的设计思路是在充分理解通信机房环境管理需求的基础上,结合现代物联网技术和智能控制手段,构建一个集数据采集、分析处理、远程监控与本地控制于一体的智能化管理系统,以提高机房环境管理的效率和可靠性。1.3 系统功能总结功能类别描述环境监测实时采集机房内的温度和湿度数据,使用DHT11模块。风扇控制通过继电器模块控制两个5V直流风扇的启停,实现通风散热。数据传输利用合宙Air724UG 4G模块将环境数据上传至华为云IOT物联网云平台。远程监控通过Windows客户端远程查看温度、湿度、风扇状态及设备运行模式。远程设置允许用户远程设置温度和湿度阈值,切换运行模式(自动/手动),控制风扇开关。本地控制提供按键输入功能,实现风扇开关控制及设备运行模式切换。显示信息OLED显示屏显示环境参数、设置阈值、设备运行模式及风扇状态。电源管理采用5V 2A稳压电源模块,确保系统稳定运行。1.4 开发工具的选择【1】设备端开发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是有点不适应界面的。【2】上位机开发上位机的开发选择Qt框架,编程语言采用C++;Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。Qt能轻松创建具有原生C++性能的连接设备、用户界面(UI)和应用程序。它功能强大且结构紧凑,拥有直观的工具和库。二、部署华为云物联网平台华为云官网: cid:link_9打开官网,搜索物联网,就能快速找到 设备接入IoTDA。2.1 物联网平台介绍华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。2.2 开通物联网服务地址: cid:link_8点击立即创建。正在创建标准版实例,需要等待片刻。创建完成之后,点击实例名称。 可以看到标准版实例的设备接入端口和地址。在上面也能看到 免费单元的限制。开通之后,点击总览,也能查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。总结:端口号: MQTT (1883)| MQTTS (8883) 接入地址:ad635970a1.st1.iotda-device.cn-north-4.myhuaweicloud.com根据域名地址得到IP地址信息: 打开Windows电脑的命令行控制台终端,使用ping 命令。ping一下即可。Microsoft Windows [版本 10.0.19045.4170](c) Microsoft Corporation。保留所有权利。C:\Users\11266>ping ad635970a1.st1.iotda-device.cn-north-4.myhuaweicloud.com正在 Ping ad635970a1.st1.iotda-device.cn-north-4.myhuaweicloud.com [117.78.5.125] 具有 32 字节的数据:来自 117.78.5.125 的回复: 字节=32 时间=35ms TTL=93来自 117.78.5.125 的回复: 字节=32 时间=36ms TTL=93来自 117.78.5.125 的回复: 字节=32 时间=36ms TTL=93来自 117.78.5.125 的回复: 字节=32 时间=39ms TTL=93117.78.5.125 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),往返行程的估计时间(以毫秒为单位): 最短 = 35ms,最长 = 39ms,平均 = 36msC:\Users\11266>MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口比较合适。 接下来的ESP8266就采用1883端口连接华为云物联网平台。2.3 创建产品(1)创建产品(2)填写产品信息根据自己产品名字填写,下面的设备类型选择自定义类型。(3)产品创建成功创建完成之后点击查看详情。(4)添加自定义模型产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。模型简单来说: 就是存放设备上传到云平台的数据。你可以根据自己的产品进行创建。比如:烟雾可以叫 MQ2温度可以叫 Temperature湿度可以叫 humidity火焰可以叫 flame其他的传感器自己用单词简写命名即可。 这就是你的单片机设备端上传到服务器的数据名字。先点击自定义模型。再创建一个服务ID。接着点击新增属性。2.4 添加设备产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。(1)注册设备(2)根据自己的设备填写(3)保存设备信息创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。(4)设备创建完成(5)设备详情2.5 MQTT协议主题订阅与发布(1)MQTT协议介绍当前的设备是采用MQTT协议与华为云平台进行通信。MQTT是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT是专门针对物联网开发的轻量级传输协议。MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。目前MQTT拥有各种平台和设备上的客户端,已经形成了初步的生态系统。MQTT是一种消息队列协议,使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合,相对于其他协议,开发更简单;MQTT协议是工作在TCP/IP协议上;由TCP/IP协议提供稳定的网络连接;所以,只要具备TCP协议栈的网络设备都可以使用MQTT协议。 本次设备采用的ESP8266就具备TCP协议栈,能够建立TCP连接,所以,配合STM32代码里封装的MQTT协议,就可以与华为云平台完成通信。华为云的MQTT协议接入帮助文档在这里: cid:link_6业务流程:(2)华为云平台MQTT协议使用限制描述限制支持的MQTT协议版本3.1.1与标准MQTT协议的区别支持Qos 0和Qos 1支持Topic自定义不支持QoS2不支持will、retain msgMQTTS支持的安全等级采用TCP通道基础 + TLS协议(最高TLSv1.3版本)单帐号每秒最大MQTT连接请求数无限制单个设备每分钟支持的最大MQTT连接数1单个MQTT连接每秒的吞吐量,即带宽,包含直连设备和网关3KB/sMQTT单个发布消息最大长度,超过此大小的发布请求将被直接拒绝1MBMQTT连接心跳时间建议值心跳时间限定为30至1200秒,推荐设置为120秒产品是否支持自定义Topic支持消息发布与订阅设备只能对自己的Topic进行消息发布与订阅每个订阅请求的最大订阅数无限制(3)主题订阅格式帮助文档地址:cid:link_6对于设备而言,一般会订阅平台下发消息给设备 这个主题。设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。以当前设备为例,最终订阅主题的格式如下:$oc/devices/{device_id}/sys/messages/down 最终的格式:$oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down(4)主题发布格式对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。这个操作称为:属性上报。帮助文档地址:cid:link_2根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:发布的主题格式:$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":{"DHT11_T":30,"DHT11_H":10,"BH1750":1,"MQ135":0}}]}2.6 MQTT三元组MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。接下来介绍,华为云平台的MQTT三元组参数如何得到。(1)MQTT服务器地址要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。帮助文档地址:cid:link_1MQTT协议的端口支持1883和8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用1883端口进连接的。根据上面的域名和端口号,得到下面的IP地址和端口号信息: 如果设备支持填写域名可以直接填域名,不支持就直接填写IP地址。 (IP地址就是域名解析得到的)华为云的MQTT服务器地址:117.78.5.125华为云的MQTT端口号:1883如何得到IP地址?如何域名转IP? 打开Windows的命令行输入以下命令。ping ad635970a1.st1.iotda-device.cn-north-4.myhuaweicloud.com(2)生成MQTT三元组华为云提供了一个在线工具,用来生成MQTT鉴权三元组: cid:link_7打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。下面是打开的页面:填入设备的信息: (上面两行就是设备创建完成之后保存得到的)直接得到三元组信息。得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。ClientId 663cb18871d845632a0912e7_dev1_0_0_2024050911Username 663cb18871d845632a0912e7_dev1Password 71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac2372.7 模拟设备登录测试经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。(1)填入登录信息打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。(2)打开网页查看完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。点击详情页面,可以看到上传的数据:到此,云平台的部署已经完成,设备已经可以正常上传数据了。(3)MQTT登录测试参数总结MQTT服务器: 117.78.5.125MQTT端口号: 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":{"DHT11_T":30,"DHT11_H":10,"BH1750":1,"MQ135":0}}]}2.8 创建IAM账户创建一个IAM账户,因为接下来开发上位机,需要使用云平台的API接口,这些接口都需要token进行鉴权。简单来说,就是身份的认证。 调用接口获取Token时,就需要填写IAM账号信息。所以,接下来演示一下过程。地址: cid:link_4【1】获取项目凭证 点击左上角用户名,选择下拉菜单里的我的凭证项目凭证:28add376c01e4a61ac8b621c714bf459【2】创建IAM用户鼠标放在左上角头像上,在下拉菜单里选择统一身份认证。点击左上角创建用户。创建成功:【3】创建完成用户信息如下:主用户名 l19504562721IAM用户 ds_abc密码 DS123456782.9 获取影子数据帮助文档:cid:link_5设备影子介绍:设备影子是一个用于存储和检索设备当前状态信息的JSON文档。每个设备有且只有一个设备影子,由设备ID唯一标识设备影子仅保存最近一次设备的上报数据和预期数据无论该设备是否在线,都可以通过该影子获取和设置设备的属性简单来说:设备影子就是保存,设备最新上传的一次数据。我们设计的软件里,如果想要获取设备的最新状态信息,就采用设备影子接口。如果对接口不熟悉,可以先进行在线调试:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow在线调试接口,可以请求影子接口,了解请求,与返回的数据格式。调试完成看右下角的响应体,就是返回的影子数据。设备影子接口返回的数据如下:{ "device_id": "663cb18871d845632a0912e7_dev1", "shadow": [ { "service_id": "stm32", "desired": { "properties": null, "event_time": null }, "reported": { "properties": { "DHT11_T": 18, "DHT11_H": 90, "BH1750": 38, "MQ135": 70 }, "event_time": "20240509T113448Z" }, "version": 3 } ]}调试成功之后,可以得到访问影子数据的真实链接,接下来的代码开发中,就采用Qt写代码访问此链接,获取影子数据,完成上位机开发。链接如下:https://ad635970a1.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/28add376c01e4a61ac8b621c714bf459/devices/663cb18871d845632a0912e7_dev1/shadow三、上位机开发为了方便查看设备上传的数据,接下来利用Qt开发一款Android手机APP 和 Windows上位机。使用华为云平台提供的API接口获取设备上传的数据,进行可视化显示,以及远程控制设备。3.1 Qt开发环境安装Qt的中文官网: https://www.qt.io/zh-cn/QT5.12.6的下载地址:https://download.qt.io/archive/qt/5.12/5.12.6或者去网盘里下载:https://pan.quark.cn/s/145a9b3f7f53打开下载链接后选择下面的版本进行下载:qt-opensource-windows-x86-5.12.6.exe 13-Nov-2019 07:28 3.7G Details软件安装时断网安装,否则会提示输入账户。安装的时候,第一个复选框里勾选一个mingw 32编译器即可,其他的不管默认就行,直接点击下一步继续安装。选择MinGW 32-bit 编译器: (一定要看清楚了)说明: 我这里只是介绍PC端,也就是Windows系统下的Qt环境搭建。 Android的开发环境比较麻烦,如果想学习Android开发,想编译Android程序的APP,需要自己去搭建Android环境。也可以看下面这篇文章,不过这个文章是在Qt开发专栏里付费的,需要订阅专栏才可以看。 如果不想付费看,也可以自行找其他教程,自己搭建好必须的环境就行了Android环境搭建的博客链接: cid:link_33.2 新建上位机工程前面2讲解了需要用的API接口,接下来就使用Qt设计上位机,设计界面,完成整体上位机的逻辑设计。【1】新建工程【2】设置项目的名称。【3】选择编译系统【4】选择默认继承的类【5】选择编译器【6】点击完成【7】工程创建完成3.3 设计UI界面与工程配置【1】打开UI文件打开默认的界面如下:【2】开始设计界面根据自己需求设计界面。3.5 编译Windows上位机点击软件左下角的绿色三角形按钮进行编译运行。编译之后的效果:3.6 配置Android环境如果想编译Android手机APP,必须要先自己配置好自己的Android环境。(搭建环境的过程可以自行百度搜索学习)然后才可以进行下面的步骤。【1】选择Android编译器【2】创建Android配置文件创建完成。【3】配置Android图标与名称【3】编译Android上位机Qt本身是跨平台的,直接选择Android的编译器,就可以将程序编译到Android平台。然后点击构建。成功之后,在目录下可以看到生成的apk文件,也就是Android手机的安装包,电脑端使用QQ发送给手机QQ,手机登录QQ接收,就能直接安装。生成的apk的目录在哪里呢? 编译完成之后,在控制台会输出APK文件的路径。知道目录在哪里之后,在Windows的文件资源管理器里,找到路径,具体看下图,找到生成的apk文件。D:/linux-share-dir/QT/build-app_Huawei_Eco_tracking-Android_for_arm64_v8a_Clang_Qt_5_12_6_for_Android_ARM64_v8a-Release/android-build//build/outputs/apk/debug/android-build-debug.apk四、STM32代码开发4.1 MQTT协议设计代码字数过多,无法显示...4.2 OLED显示屏驱动代码#include "oled.h"#include "stdlib.h"#include "oledfont.h" #include "delay.h"//OLED模式设置//0: 4线串行模式 (模块的BS1,BS2均接GND)//1: 并行8080模式 (模块的BS1,BS2均接VCC)#define OLED_MODE 1 //---------------------------OLED端口定义-------------------------- #define OLED_CS PDout(6)#define OLED_RST PGout(15) #define OLED_RS PDout(3)#define OLED_WR PGout(14) #define OLED_RD PGout(13) //PC0~7,作为数据线#define DATAOUT(x) GPIOC->ODR=(GPIOC->ODR&0xff00)|(x&0x00FF); //输出 //使用4线串行接口时使用 #define OLED_SCLK PCout(0)#define OLED_SDIN PCout(1) #define OLED_CMD 0 //写命令#define OLED_DATA 1 //写数据//OLED控制用函数void OLED_WR_Byte(u8 dat,u8 cmd); void OLED_Display_On(void);void OLED_Display_Off(void);void OLED_Refresh_Gram(void); void OLED_Init(void);void OLED_Clear(void);void OLED_DrawPoint(u8 x,u8 y,u8 t);void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot);void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode);void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size); //OLED的显存//存放格式如下.//[0]0 1 2 3 ... 127 //[1]0 1 2 3 ... 127 //[2]0 1 2 3 ... 127 //[3]0 1 2 3 ... 127 //[4]0 1 2 3 ... 127 //[5]0 1 2 3 ... 127 //[6]0 1 2 3 ... 127 //[7]0 1 2 3 ... 127 u8 OLED_GRAM[128][8]; //更新显存到LCD void OLED_Refresh_Gram(void){ u8 i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++)OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA); } }#if OLED_MODE==1 //8080并口 //向SSD1306写入一个字节。//dat:要写入的数据/命令//cmd:数据/命令标志 0,表示命令;1,表示数据;void OLED_WR_Byte(u8 dat,u8 cmd){ DATAOUT(dat); OLED_RS=cmd; OLED_CS=0; OLED_WR=0; OLED_WR=1; OLED_CS=1; OLED_RS=1; } #else//向SSD1306写入一个字节。//dat:要写入的数据/命令//cmd:数据/命令标志 0,表示命令;1,表示数据;void OLED_WR_Byte(u8 dat,u8 cmd){ u8 i; OLED_RS=cmd; //写命令 OLED_CS=0; for(i=0;i<8;i++) { OLED_SCLK=0; if(dat&0x80)OLED_SDIN=1; else OLED_SDIN=0; OLED_SCLK=1; dat<<=1; } OLED_CS=1; OLED_RS=1; } #endif //开启OLED显示 void OLED_Display_On(void){ OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON}//关闭OLED显示 void OLED_Display_Off(void){ OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF} //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!! void OLED_Clear(void) { u8 i,n; for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00; OLED_Refresh_Gram();//更新显示}//画点 //x:0~127//y:0~63//t:1 填充 0,清空 void OLED_DrawPoint(u8 x,u8 y,u8 t){ u8 pos,bx,temp=0; if(x>127||y>63)return;//超出范围了. pos=7-y/8; bx=y%8; temp=1<<(7-bx); if(t)OLED_GRAM[x][pos]|=temp; else OLED_GRAM[x][pos]&=~temp; }//x1,y1,x2,y2 填充区域的对角坐标//确保x1<=x2;y1<=y2 0<=x1<=127 0<=y1<=63 //dot:0,清空;1,填充 void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot) { u8 x,y; for(x=x1;x<=x2;x++) { for(y=y1;y<=y2;y++)OLED_DrawPoint(x,y,dot); } OLED_Refresh_Gram();//更新显示}//在指定位置显示一个字符,包括部分字符//x:0~127//y:0~63//mode:0,反白显示;1,正常显示 //size:选择字体 12/16/24void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode){ u8 temp,t,t1; u8 y0=y; u8 csize=(size/8+((size%8)?1:0))*(size/2); //得到字体一个字符对应点阵集所占的字节数 chr=chr-' ';//得到偏移后的值 for(t=0;t { if(size==12)temp=asc2_1206[chr][t]; //调用1206字体 else if(size==16)temp=asc2_1608[chr][t]; //调用1608字体 else if(size==24)temp=asc2_2412[chr][t]; //调用2412字体 else return; //没有的字库 for(t1=0;t1<8;t1++) { if(temp&0x80)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp<<=1; y++; if((y-y0)==size) { y=y0; x++; break; } } } }//m^n函数u32 mypow(u8 m,u8 n){ u32 result=1; while(n--)result*=m; return result;} //显示2个数字//x,y :起点坐标 //len :数字的位数//size:字体大小//mode:模式 0,填充模式;1,叠加模式//num:数值(0~4294967295); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size){ u8 t,temp; u8 enshow=0; for(t=0;t { temp=(num/mypow(10,len-t-1))%10; if(enshow==0&&t<(len-1)) { if(temp==0) { OLED_ShowChar(x+(size/2)*t,y,' ',size,1); continue; }else enshow=1; } OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1); }} //显示字符串//x,y:起点坐标 //size:字体大小 //*p:字符串起始地址 void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size){ while((*p<='~')&&(*p>=' '))//判断是不是非法字符! { if(x>(128-(size/2))){x=0;y+=size;} if(y>(64-size)){y=x=0;OLED_Clear();} OLED_ShowChar(x,y,*p,size,1); x+=size/2; p++; } } //初始化SSD1306 void OLED_Init(void){ RCC->APB2ENR|=1<<4; //使能PORTC时钟 RCC->APB2ENR|=1<<5; //使能PORTD时钟 RCC->APB2ENR|=1<<8; //使能PORTG时钟 GPIOD->CRL&=0XF0FF0FFF;//PD3,6 推挽输出 GPIOD->CRL|=0X03003000; GPIOD->ODR|=1<<3; GPIOD->ODR|=1<<6; #if OLED_MODE==1 //8080并口模式 GPIOC->CRL=0X33333333; //PC0~7 OUT GPIOC->ODR|=0X00FF; GPIOG->CRH&=0X000FFFFF; //PG13,14,15 OUT GPIOG->CRH|=0X33300000; GPIOG->ODR|=7<<13; #else //4线SPI模式 GPIOC->CRL&=0XFFFFFF00; //PC0,1 OUT GPIOC->CRL|=0X00000033; GPIOC->ODR|=3<<0; GPIOG->CRH&=0X0FFFFFFF; //RST GPIOG->CRH|=0X30000000; GPIOG->ODR|=1<<15;#endif OLED_CS=1; OLED_RS=1; OLED_RST=0; delay_ms(100); OLED_RST=1; OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示 OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率 OLED_WR_Byte(80,OLED_CMD); //[3:0],分频因子;[7:4],震荡频率 OLED_WR_Byte(0xA8,OLED_CMD); //设置驱动路数 OLED_WR_Byte(0X3F,OLED_CMD); //默认0X3F(1/64) OLED_WR_Byte(0xD3,OLED_CMD); //设置显示偏移 OLED_WR_Byte(0X00,OLED_CMD); //默认为0 OLED_WR_Byte(0x40,OLED_CMD); //设置显示开始行 [5:0],行数. OLED_WR_Byte(0x8D,OLED_CMD); //电荷泵设置 OLED_WR_Byte(0x14,OLED_CMD); //bit2,开启/关闭 OLED_WR_Byte(0x20,OLED_CMD); //设置内存地址模式 OLED_WR_Byte(0x02,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10; OLED_WR_Byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127; OLED_WR_Byte(0xC0,OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数 OLED_WR_Byte(0xDA,OLED_CMD); //设置COM硬件引脚配置 OLED_WR_Byte(0x12,OLED_CMD); //[5:4]配置 OLED_WR_Byte(0x81,OLED_CMD); //对比度设置 OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮) OLED_WR_Byte(0xD9,OLED_CMD); //设置预充电周期 OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2; OLED_WR_Byte(0xDB,OLED_CMD); //设置VCOMH 电压倍率 OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc; OLED_WR_Byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示 OLED_WR_Byte(0xAF,OLED_CMD); //开启显示 OLED_Clear();} 4.3 DHT11温湿度模块驱动代码#include "dht11.h"#include "delay.h"//IO方向设置#define DHT11_IO_IN() {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=8<<12;}#define DHT11_IO_OUT() {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=3<<12;}////IO操作函数 #define DHT11_DQ_OUT PGout(11) //数据端口 PG11 #define DHT11_DQ_IN PGin(11) //数据端口 PG11u8 DHT11_Init(void); //初始化DHT11u8 DHT11_Read_Data(u8 *temp,u8 *humi);//读取温湿度u8 DHT11_Read_Byte(void); //读出一个字节u8 DHT11_Read_Bit(void); //读出一个位u8 DHT11_Check(void); //检测是否存在DHT11void DHT11_Rst(void); //复位DHT11 //复位DHT11void DHT11_Rst(void) { DHT11_IO_OUT(); //SET OUTPUT DHT11_DQ_OUT=0; //拉低DQ delay_ms(20); //拉低至少18ms DHT11_DQ_OUT=1; //DQ=1 delay_us(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++; delay_us(1); }; if(retry>=100)return 1; else retry=0; while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us { retry++; delay_us(1); }; if(retry>=100)return 1; return 0;}//从DHT11读取一个位//返回值:1/0u8 DHT11_Read_Bit(void) { u8 retry=0; while(DHT11_DQ_IN&&retry<100)//等待变为低电平 { retry++; delay_us(1); } retry=0; while(!DHT11_DQ_IN&&retry<100)//等待变高电平 { retry++; delay_us(1); } delay_us(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(); 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<<8; //使能PORTG口时钟 GPIOG->CRH&=0XFFFF0FFF;//PORTG.11 推挽输出 GPIOG->CRH|=0X00003000; GPIOG->ODR|=1<<11; //输出1 DHT11_Rst(); return DHT11_Check();}五、总结本项目设计一种基于STM32微控制器的智能通信机房空调与新风系统联动装置,以提升通信机房的环境管理效率和自动化水平。该装置能够实时监测机房内的环境温度和湿度,并根据预设阈值自动控制风扇进行散热,从而保持机房内部环境的适宜条件。系统利用合宙Air724UG 4G模块将采集到的数据上传至华为云IOT物联网云平台,支持远程监控及控制功能。通过Windows客户端界面,管理员可以实时查看机房内环境参数,调整设定值,并控制风扇的工作状态。此外,本地操作也得到了考虑,通过简单的按键输入即可完成风扇控制和工作模式的切换。为了方便现场人员查看当前状态,设备还配备了一块0.96寸的OLED显示屏,用于显示环境参数、设置值及运行状态等信息。整个系统由一个5V 2A的稳压电源模块供电,确保了系统的稳定性和可靠性。此项目的实施有助于提高通信机房的智能化管理水平,减少人工干预,降低维护成本。
  • [问题求助] 设备接入Iot云在应用开发中遇到鉴权计算问题求助
    求助下,这个请求头中的 Authorization 和 X-Security-Token 怎么算的,有没有个ArkTs的demo这里指引的查找AK/SK也没说哪个是Authorization啊
  • [问题求助] 华为云与微信小程序连接问题,以及用postman测试问题
    微信小程序获取影子数据显示授权问题,但是我的IAM用户以及授权给IOT设备接入服务用postman测试也显示一样问题求大佬解决,以及想了好几天了 555555555555~
  • [技术干货] 智慧鱼缸接入华为智慧生活生态链
    一. 引言1.1 项目背景随着智能家居技术的发展和人们对高品质生活的追求日益增长,智能鱼缸作为一种结合了科技与自然美的家居装饰品,正逐渐成为智能家居领域的新宠。本项目旨在设计一款基于华为云智慧生活生态链的智能鱼缸,它不仅能够提供一个安全舒适的环境让鱼儿健康生长,还能让用户通过手机应用远程管理和监控鱼缸的各种状态。通过集成多种传感器和执行机构,这款智能鱼缸能够自动监测水质、维持适宜的水温和氧气水平,并且还能实现自动投喂、自动换水、鱼缸壁清洁等功能,极大地减轻了养鱼爱好者的工作负担,提升了养鱼的乐趣和便捷性。考虑到现代人忙碌的生活节奏和对个性化需求的追求,本项目集成了华为云的智慧生活App,这使得用户可以通过手机轻松地设置鱼缸的各项参数,包括但不限于恒温温度、换水阈值、投喂间隔和量等,甚至还能根据用户所选养的鱼种自动优化这些设置。此外,为了进一步提升用户体验,还引入了全景视频监控功能,用户可以在手机端360度无死角地观察鱼缸内部情况;同时,鱼缸还支持氛围灯调节,可通过华为云的小艺助手进行智慧联动控制,营造出不同的观赏效果。整体而言,这款智能鱼缸不仅代表了智能家居技术的最新进展,也为养鱼爱好者提供了一个更加智能化、人性化的养鱼体验。1.2 目的与目标本项目的目标是设计并开发一款基于华为云智慧生活生态链的智能鱼缸,为用户提供一个高度自动化、智能化的养鱼体验。该智能鱼缸集成了多项先进技术,包括水质监测、远程控制、自动维护等功能,解决传统鱼缸在养鱼过程中遇到的一系列问题,如频繁的手动维护、难以精确控制的水质条件等。通过本项目的实施具体目标:水质监测与自动调节:实现鱼缸水体含氧量、TDS值等关键水质指标的实时监测,并能够根据检测结果自动调节含氧量、水质等,以确保鱼儿的健康生长环境。远程控制与智能化管理:通过华为云智慧生活App,用户可以随时随地通过智能手机远程监控和控制鱼缸的状态,包括设置自动投喂的时间间隔、调节鱼缸内的温度等,实现智能化管理。自动维护功能:开发自动换水、鱼缸壁清洁等维护功能,减少用户的日常维护工作,提高养鱼效率和体验。全景视频监控:利用四个内置摄像头,为用户提供360度全景视频监控功能,使用户能够在手机端全方位地观察鱼缸内部情况,增强观赏性和互动性。个性化设置与一键优化:根据用户选择的鱼种类型,自动优化鱼缸的各项参数设置,如恒温温度、换水阀值、投喂间隔等,提供更符合特定鱼类需求的养鱼环境。智能化联动与场景编排:支持与华为云智慧生活App的深度集成,实现与其他智能设备的联动控制,为用户提供更加丰富的智慧化场景体验。稳定的网络连接与安全性保障:采用G模块保证稳定的网络连接,同时确保用户数据的安全性和隐私保护。可靠的电源管理方案:通过内置锂电池和外置电源的双电源供电模式,确保即使在突发断电的情况下,智能鱼缸也能持续运行,保持鱼缸内环境的稳定。1.3 功能需求含氧量检测:集成含氧量传感器,实时监测鱼缸中水的含氧量,并通过华为云平台向用户手机发送数据更新。远程投喂与自动投喂:支持用户通过手机应用设置自动投喂的时间间隔,实现远程或自动投喂鱼食的功能。自动充氧:根据含氧量检测结果自动开启或关闭充氧装置,确保水中的含氧量维持在适宜水平。自动换水:配备自动换水系统,当检测到水质不佳时,自动启动换水过程,保持水质清洁。鱼缸壁自动清洁:设计一套基于舵机和抹布的自动清洁系统,定期清理鱼缸壁上的污垢。自动恒温:具备温度传感器和加热装置,允许用户通过手机应用远程设定恒定的水温。TDS值检测:安装TDS值传感器,监测水中的溶解固体含量,并将数据上传至华为云平台。视频全场景监控:在鱼缸内部安装四个摄像头,通过拼接算法生成全景视频,用户可在手机端进行360度观看。手机APP控制:开发专用的手机应用程序,集成华为云智慧生活App,支持用户根据鱼的种类一键设置鱼缸参数。场景联动与设备管理:支持设备添加、远程控制、状态查看、设备共享等功能,并可与其他华为生态链设备联动使用。氛围灯控制:支持通过手机应用开启或关闭鱼缸内的氛围灯,增加观赏性,并支持华为云“小艺”助手的语音控制。4G联网模块:采用4G模块实现设备与华为云平台之间的稳定通信。主控芯片:不限制。双电源供电方案:支持内置锂电池与外置电源的双重供电机制,确保断电情况下仍能正常运行。华为云物联网服务器:通过华为云的智慧生活服务器实现数据存储、分析与远程访问。1.4 接入智慧生活APP帮助文档:cid:link_0智慧生活App作为华为全场景智慧体验的重要入口,可以实现华为自研设备与生态伙伴设备的统一管理。通过开发H5接入智慧生活App,为消费者带来如下功能体验:设备添加: 用户可以通过智慧生活App快速发现设备并添加设备,简单易用。设备管控: 用户添加设备后,可以通过智慧生活App实现远程控制设备、查看状态、分类管理、共享设备、删除设备等。场景联动: 通过场景编排可以实现不同产品、不同功能的联动,为用户提供智慧化的场景体验。接入原理通过线上或线下方式开发H5,并上传至Device Partner平台。平台会将H5开发包推送至HarmonyOS Connect云。消费者通过智慧生活App调用云端的H5开发包,从而实现对设备的管理和控制。二. 系统概述2.1 系统架构为了更好地说明本项目的系统架构,我们可以将其分为几个主要部分:硬件架构、软件架构以及网络架构。下面是对这些部分的具体描述:(1) 硬件架构主控单元:微控制器 (MCU): 使用STM32L431RCT6作为核心处理器。电源管理模块: 包括电池充电电路、稳压电路等。4G通信模块: Air724UG 4G模块用于与华为云平台进行数据交换。环境监测与控制系统:水质监测传感器:含氧量传感器温度传感器TDS(总溶解固体)传感器自动控制系统:自动充氧泵加热棒自动投喂器水质净化过滤系统鱼缸壁自动清洁系统视觉监控系统:多个高清摄像头用于全方位监控鱼缸内部情况。图像处理单元用于图像拼接和传输。用户交互界面:LED显示屏显示当前状态。按钮用于本地控制和调试。备用电源:内置锂电池提供不间断供电。(2) 软件架构嵌入式系统软件:嵌入式操作系统(如FreeRTOS)用于任务调度。设备驱动程序支持各种传感器和执行器。4G通信协议栈实现数据传输。云平台软件:华为云IoT平台用于设备接入、数据存储、消息推送等。数据分析与处理模块。用户认证与权限管理。移动应用:客户端应用(iOS/Android)用于用户远程监控和控制。用户界面设计与交互逻辑。与华为云平台的接口实现。(3) 网络架构本地网络:4G网络连接主控单元与华为云平台。Wi-Fi连接供用户在本地网络内进行控制。云端网络:华为云IoT平台作为中心节点处理所有数据。数据安全加密传输。远程用户接入:移动应用通过互联网与华为云平台交互。用户数据同步与更新。(4) 系统交互流程数据采集:传感器持续监测水质、含氧量、温度等数据。摄像头捕捉鱼缸内部画面。数据处理与传输:MCU收集数据并进行初步处理。通过4G模块将数据发送至华为云IoT平台。云平台处理:IoT平台接收数据并存储。数据分析后产生控制指令或通知。用户控制:用户通过移动应用获取实时数据。用户通过移动应用发送控制命令。设备响应:IoT平台将控制指令发送给设备。MCU接收指令并控制相关硬件执行动作。2.2 关键组件介绍(1)主控单元微控制器 (MCU): 如STM32系列,负责处理来自各个传感器的数据,并控制执行器的动作。通信模块:4G通信模块: 如Air724UG,用于与外部网络(例如华为云平台)建立连接。Wi-Fi模块: 用于本地网络连接,方便用户通过智能手机或其他设备访问系统。电源管理单元: 包括电池充电电路、稳压电路等,确保系统的稳定运行。(2)环境监测与控制系统水质监测传感器:pH值传感器温度传感器溶解氧(DO)传感器电导率(TDS)传感器自动控制系统:自动充氧泵: 根据DO水平自动调节氧气供应。加热棒: 保持水温在适宜范围内。自动投喂器: 根据设定的时间和分量自动投喂饲料。水质净化过滤系统: 维护水质清洁。鱼缸壁自动清洁系统: 清除鱼缸壁上的藻类和其他污垢。(3)视觉监控系统高清摄像头: 用于捕捉鱼缸内的视频流。图像处理单元: 对摄像头捕获的图像进行处理,识别鱼类的行为或健康状况。(4)用户交互界面LED显示屏: 显示系统状态、监测数据等信息。触摸屏: 允许用户直接与系统互动,设置参数或查看详细数据。按钮: 提供基本的本地控制功能,如紧急停机。(5)远程监控与管理华为云IoT平台:提供数据存储、处理和分析能力。支持设备管理和远程控制功能。保证数据的安全性和可靠性。移动应用程序:用户可以通过智能手机或平板电脑访问系统。实现远程监控、接收警报、调整系统设置等功能。(6)数据安全与加密加密算法: 采用SSL/TLS等加密技术保障数据传输的安全。用户认证: 通过用户名和密码等方式验证用户的合法性。(7)备用电源内置电池: 在停电时为系统提供不间断供电。2.3 工作原理(1)环境监测:含氧量检测:使用溶解氧传感器实时监测鱼缸水体中的含氧量。传感器将数据传送给主控芯片STM32L431RCT6。TDS值检测:通过TDS传感器监测水中的溶解固体含量。数据同样被送至主控芯片进行处理。(2)水质控制与维护:自动充氧:当含氧量低于预设阈值时,STM32L431RCT6控制充氧泵启动。充氧泵工作直到含氧量恢复到理想水平。自动换水:通过TDS值和其它水质参数判断是否需要更换水体。自动换水系统根据需要更换部分水体,以维持良好的水质。鱼缸壁自动清洁:采用舵机和抹布组合,定时清洁鱼缸壁上的污垢。(3)远程监控与控制:视频全场景监控:鱼缸内部安装的四个摄像头捕捉鱼缸内部画面。通过图像处理单元将多路视频流合成全景视频。视频流通过4G模块上传至华为云服务器。手机APP控制:用户通过华为云智慧生活App远程查看鱼缸状态。用户可以设置恒温温度、换水阈值、投喂间隔等参数。通过App接收水质异常报警等信息。(4)用户定制与一键设置:自动参数设置:用户根据鱼的种类选择相应的养鱼模式。App自动调整各项参数以适应所选鱼种的需求。(5)联动与场景编排:设备管理与控制:用户可以通过智慧生活App添加设备、查看状态、远程控制。支持设备共享和删除等功能。场景联动:通过场景编排实现不同功能的联动,例如定时开启氛围灯、自动投喂等。(6)通信与网络:设备联网:4G模块Air724UG用于设备与华为云服务器之间的数据传输。数据传输与处理:传感器数据通过主控芯片处理后上传至华为云IoT平台。平台进行数据分析、存储,并将结果反馈给用户。(7)电源管理:双电源供电:内置锂电池提供不间断供电。外置电源适配器用于日常供电。当外部电源中断时,锂电池自动接管供电。(8)安全保障:数据加密:传输至华为云的数据采用加密技术保护。用户认证:通过智慧生活App进行用户身份验证。三. 硬件设计3.1 主控单元本项目的主控单元是整个智能鱼缸系统的中枢神经,它负责收集来自各个传感器的数据、处理这些数据并据此作出决策来控制执行器的动作。主控单元的核心是STM32L431RCT6微控制器,这是一款高性能且低功耗的微控制器,非常适合应用于需要实时处理能力及长电池寿命的应用场合。主控单元的设计围绕STM32L431RCT6微控制器展开,该微控制器基于Arm® Cortex®-M4内核,具备FPU(浮点运算单元)和DSP指令集,能够在处理复杂的计算任务时提供出色的性能。它还拥有丰富的外设接口,如USART、SPI、I2C、ADC等,这些接口使得主控单元能够轻松地与外部传感器和其他组件进行通信。为了满足智能鱼缸的长时间运行需求,STM32L431RCT6还提供了先进的电源管理模式,可在保证系统正常运行的同时显著降低功耗。在硬件层面,主控单元集成了多种传感器,包括但不限于溶解氧传感器、TDS传感器、温度传感器等,这些传感器持续监测鱼缸内的水质参数和环境条件。一旦这些传感器捕获到相关数据,它们会立即将数据发送给主控单元。主控单元通过内置的ADC转换器将模拟信号转换为数字信号,然后由STM32L431RCT6进行数据处理。基于这些数据,主控单元能够判断水质是否达到预设的标准,如果发现任何参数偏离了理想的范围,它会迅速采取行动,例如启动充氧泵增加水中氧气含量、激活自动换水机制以保持水质清洁,或者控制鱼缸壁自动清洁装置清除附着物等。除了处理传感器数据外,主控单元还需要与执行器进行交互,如控制充氧泵、自动换水系统和清洁装置等。这些执行器的驱动通常通过PWM(脉宽调制)信号来实现,而STM32L431RCT6提供了足够的PWM输出通道来支持这些操作。此外,主控单元还负责与用户界面进行交互,包括接收用户的设置指令并通过Wi-Fi或4G模块与远程服务器进行通信,以实现对智能鱼缸的远程监控和控制。为了确保系统的稳定性和安全性,主控单元还包括电源管理电路、看门狗定时器、实时时钟RTC等组件。电源管理电路确保即使在外部电源不稳定的情况下也能提供稳定的电压供应;看门狗定时器则在发生故障时复位系统,防止死锁情况的发生;实时时钟RTC则用于提供准确的时间基准,这对于定时任务和数据记录至关重要。主控单元不仅是一个数据处理中心,还是一个协调者,它连接了所有的传感器和执行器,并通过高效的算法和精确的控制逻辑确保智能鱼缸能够自动维持一个适宜鱼类生活的环境。通过这种方式,用户可以享受到更加轻松愉快的养鱼体验,同时确保鱼儿能够在一个健康稳定的环境中茁壮成长。3.2 传感器3.2.1 含氧量检测器含氧量检测传感器是智能鱼缸系统中非常重要的组成部分之一,它用于监测鱼缸中水体的溶解氧(Dissolved Oxygen, DO)含量。准确测量水中的溶解氧含量对于维持鱼缸内鱼类的健康至关重要,因为溶解氧水平直接影响到鱼类的呼吸和生存。下面详细介绍含氧量检测传感器的工作原理及其在本项目中的应用。含氧量检测传感器通常采用极谱法或光学法来进行测量。在本项目中,我们使用的是极谱法传感器,因为它在水体监测中广泛使用并且具有较高的准确性和稳定性。工作原理:极谱法含氧量检测传感器的工作原理基于电化学反应。这种传感器通常包含两个电极——参考电极和工作电极,以及电解液。工作电极(通常是铂或金制成的圆盘或线圈)浸没在水中,而参考电极(通常是银/氯化银电极)则与工作电极相连。电解液(通常是氯化钾溶液)用于在两电极之间形成电导通路。当传感器工作时,会在工作电极上施加一个恒定的电压(通常为0.6V到0.8V),这会在电极表面产生一个电流。在这个电压下,溶解在水中的氧气在工作电极上被还原,产生电子和离子。这一过程可以表示为:\[ \text{O}_2 + 2\text{H}_2\text{O} + 4e^- \rightarrow 4\text{OH}^- \]产生的电子流经外部电路到达参考电极,而在参考电极处,这些电子被消耗掉。因此,通过测量流过电路的电流大小,就可以推算出水体中溶解氧的浓度。电流与溶解氧浓度之间存在线性关系,这意味着电流的大小可以直接转化为溶解氧的浓度值。为了确保测量的准确性,含氧量检测传感器通常会配备温度补偿电路。这是因为水体中的溶解氧浓度会受到温度的影响,温度升高会导致溶解氧浓度下降。通过温度补偿,传感器可以校正温度变化对测量结果的影响,从而提供更准确的读数。在本项目中,含氧量检测传感器会持续监测鱼缸中的溶解氧含量,并将数据发送给主控单元STM32L431RCT6。主控单元会对这些数据进行处理,并根据设定的阈值来决定是否需要启动充氧泵来增加水中的溶解氧含量。此外,主控单元还会将这些数据上传至华为云IoT平台,以便用户通过手机应用实时监控鱼缸的含氧量,并在必要时采取措施。通过这种方式,含氧量检测传感器确保了鱼缸中的溶解氧水平始终处于适宜鱼类生存的最佳范围内,从而为鱼缸内的生物提供了一个健康稳定的居住环境。3.2.2 TDS值检测器TDS值检测器是智能鱼缸系统中的一个重要组成部分,用于监测水体中的总溶解固体(Total Dissolved Solids, TDS)含量。TDS值是指水中溶解的矿物质、盐分、金属离子等物质的总量,这些物质的存在会影响水质,进而影响鱼缸中鱼类的健康和生存。下面详细介绍TDS值检测器的工作原理及其在本项目中的应用。工作原理:TDS值检测器的工作原理基于电导率测量。水是一种导体,其导电性取决于其中溶解的离子的数量。一般来说,水中溶解的离子越多,水的电导率就越高。TDS值检测器通过测量水的电导率来间接估算其中的TDS含量。电导率与TDS含量之间存在近似线性的关系,因此可以通过测量电导率来推算出TDS值。具体过程:电导率测量:TDS值检测器通常包含一对电极,这两个电极被放置在待测水样中。当电流通过这两电极时,水中的溶解离子会携带电流从一个电极流向另一个电极。通过测量流过电极的电流强度,可以确定水的电导率。TDS值计算:由于水的电导率与其中的TDS含量成正比关系,TDS值检测器会根据预先标定的系数(即转换因子)将电导率转换为TDS值。转换因子通常是经验确定的,它考虑了水体中可能存在的不同类型的溶解固体。TDS值(mg/L)可以通过以下公式计算得出:\[ \text{TDS} = \text{电导率} \times \text{转换因子} \]转换因子可能因水质的不同而有所变化,因此对于特定的水体类型,最好使用经过标定的转换因子。温度补偿:水的电导率会随温度的变化而变化,温度升高时电导率也会增加。因此,TDS值检测器通常会配备温度传感器,用于测量水温并进行温度补偿。温度补偿有助于确保测量结果的准确性,即使在温度变化的情况下也能得到稳定的TDS值。在本项目中,TDS值检测器会持续监测鱼缸中的水质,并将数据发送给主控单元STM32L431RCT6。主控单元会对这些数据进行处理,并根据设定的阈值来决定是否需要启动自动换水系统来改善水质。此外,主控单元还会将这些数据上传至华为云IoT平台,以便用户通过手机应用实时监控鱼缸的TDS值,并在必要时采取措施。通过这种方式,TDS值检测器确保了鱼缸中的水质始终处于适宜鱼类生存的最佳范围内,从而为鱼缸内的生物提供了一个健康稳定的居住环境。3.2.3 温度传感器在本项目中使用的温度传感器是DS18B20防水探头,这是一种非常流行的数字温度传感器,广泛应用于各种需要精确温度测量的场合,包括智能家居、工业控制等领域。下面详细介绍DS18B20防水探头的工作原理及其在智能鱼缸系统中的应用。DS18B20防水探头的工作原理DS18B20是一种单线数字温度传感器,它通过一根数据线(加上电源和地线)与微控制器进行通信。这种传感器的优点在于它只需要一个数据引脚就能与微控制器通信,大大简化了硬件连接。DS18B20的温度测量范围通常为-55°C 至 +125°C,精度可达±0.5°C,非常适合用于需要精确温度控制的应用。通信协议DS18B20采用了1-Wire(一总线)通信协议,这意味着它可以通过一根数据线与微控制器进行数据交换。1-Wire协议不仅减少了所需的引脚数量,还支持在同一根线上连接多个DS18B20传感器,只需通过每个传感器唯一的序列号进行区分即可。内部结构DS18B20内部包含一个温度敏感元件和一个1-Wire通信接口。当温度发生变化时,温度敏感元件会产生相应的电信号变化,然后通过1-Wire接口将这些变化转换为数字信号发送给微控制器。测量过程初始化: 微控制器首先发送一个复位脉冲来初始化DS18B20,确保传感器处于准备状态。ROM序列号: 接下来,微控制器发送一个ROM命令来识别DS18B20的唯一序列号,这是为了区分同一总线上的多个DS18B20传感器。温度转换: 微控制器发送温度转换命令(如0x44),DS18B20开始进行温度测量。在测量过程中,传感器会将温度转换为数字信号,并将其存储在其内部寄存器中。读取温度: 微控制器发送读取命令(如0xBE),DS18B20将测量结果从内部寄存器中读出并发送给微控制器。数据处理: 微控制器收到的温度数据是以16位二进制格式存储的,通常需要进行简单的数学运算才能转换为摄氏度或华氏度的温度值。特点防水封装: 为了适用于鱼缸这种潮湿环境,DS18B20通常采用防水封装,确保传感器不会受到水分的影响。高精度: DS18B20的温度测量精度较高,可以满足智能鱼缸对温度控制的要求。低功耗: DS18B20在不进行温度测量时进入低功耗模式,这有助于延长电池寿命。在智能鱼缸系统中的应用在智能鱼缸系统中,DS18B20防水探头用于监测鱼缸内的水温。温度数据会被发送给主控单元STM32L431RCT6进行处理。主控单元根据接收到的温度数据,可以控制加热器的开启或关闭,以维持水温在适宜的范围内。此外,温度数据也会通过4G模块Air724UG上传至华为云IoT平台,用户可以通过智慧生活App实时查看鱼缸的温度,并根据需要设置恒温温度。通过这种方式,DS18B20防水探头确保了智能鱼缸系统能够准确地监测并控制水温,为鱼缸内的生物提供一个健康稳定的居住环境。3.4 通信模块Air724UG 4G模块是智能鱼缸系统中用于实现无线数据传输的关键组件之一,它主要用于将鱼缸系统收集到的数据通过4G网络发送到华为云IoT平台,同时也支持从云平台接收指令。下面详细介绍Air724UG 4G模块的工作原理及其在本项目中的应用。Air724UG 4G模块的工作原理Air724UG是一款高性能的4G LTE Cat.4模块,支持全球多个频段,能够在全球范围内提供可靠的4G连接。该模块采用Quectel的芯片组,具有体积小巧、功耗低的特点,适合嵌入式应用。1. 通信协议Air724UG模块支持多种通信协议,包括TCP/IP、UDP、FTP、HTTP等,这些协议使得模块能够与远程服务器进行高效的数据交换。此外,它还支持AT命令集,允许主控单元通过简单的文本命令控制模块的操作。2. 数据传输上行数据: 当鱼缸系统中的传感器收集到数据(如含氧量、TDS值、水温等)时,这些数据会被发送给主控单元STM32L431RCT6。数据处理: 主控单元会对数据进行初步处理,并将数据打包成适合传输的格式。下行数据: 主控单元通过串行接口(如UART)将打包后的数据发送给Air724UG模块。无线传输: Air724UG模块接收到数据后,会通过4G网络将数据发送到指定的服务器地址,即华为云IoT平台。3. 指令接收指令发送: 华为云IoT平台可以向Air724UG模块发送指令,例如要求改变鱼缸的某些设置或执行特定任务。指令处理: Air724UG模块接收到指令后,通过串行接口将指令转发给主控单元。执行指令: 主控单元根据接收到的指令执行相应的操作,比如开启加热器、启动充氧泵等。4. 网络连接Air724UG模块支持多种4G LTE频段,可以根据所在地区的网络条件自动选择最优的频段进行连接。它还支持自动重连功能,确保在网络信号不稳定的情况下也能保持稳定的连接。5. 电源管理Air724UG模块具有较低的功耗特点,适合长期运行的应用场景。模块还支持多种省电模式,可以在不需要传输数据时进入低功耗状态,从而节省电力。在智能鱼缸系统中的应用在本项目中,Air724UG 4G模块的主要作用是作为鱼缸系统与华为云IoT平台之间的通信桥梁。当鱼缸中的传感器检测到数据时,这些数据会被主控单元STM32L431RCT6收集并处理。处理后的数据随后被发送给Air724UG模块,该模块通过4G网络将数据上传至华为云IoT平台。用户可以通过智慧生活App查看这些数据,并根据需要发送指令来控制鱼缸系统的各项功能。此外,Air724UG模块还支持从华为云IoT平台接收指令,例如用户通过智慧生活App更改鱼缸的设置时,这些指令会被发送到模块,再由模块转发给主控单元执行相应的操作。通过这种方式,Air724UG 4G模块确保了智能鱼缸系统能够实现远程监控和控制,为用户提供了一个高度自动化的养鱼体验。
  • [问题求助] 小熊派的NB模块有问题,好像卡检测不到
    AT+CIMI,返回ERRORAT+CSQ。返回 +CSQ:99,99  OK AT +CMEE=1,返回ERRORAT+CFUN? ,返回 +CFUN:0  OK AT+CGATT?,返回 +CGATT:0  OK 
  • [问题求助] 例程中智慧路灯,数据上传不到华为云平台,可以连接上
    数据上传不到华为云平台,可以连接上 linkmain:V1.3.1 AT 20:00:29 ON Jul 22 2024  WELCOME TO IOT_LINK SHELL LiteOS:/>ATRCV:46 Bytes: REBOOT_CAUSE_SECURITY_RESET_PIN Neul  OK  ATSND:5 Bytes:ATE0 ATRCV:6 Bytes: OK  ATSND:7 Bytes:AT+NRB ATRCV:13 Bytes: REBOOTING  ATRCV:42 Bytes: REBOOT_CAUSE_APPLICATION_AT Neul  OK  ATSND:5 Bytes:ATE0 ATRCV:6 Bytes: OK  ATSND:5 Bytes:ATE0 ATRCV:6 Bytes: OK  ATSND:5 Bytes:ATE0 ATRCV:6 Bytes: OK  ATSND:13 Bytes:AT+QREGSWT=1 ATRCV:6 Bytes: OK  ATSND:10 Bytes:AT+CMEE=1 ATRCV:6 Bytes: OK  ATSND:12 Bytes:AT+NCONFIG? ATRCV:1001 Bytes: +NCONFIG:AUTOCONNECT,FALSE +NCONFIG:CR_0354_0338_SCRAMBLING,TRUE +NCONFIG:CR_0859_SI_AVOID,TRUE +NCONFIG:COMBINE_ATTACH,FALSE +NCONFIG:CELL_RESELECTION,TRUE +NCONFIG:ENABLE_BIP,FALSE +NCONFIG:MULTITONE,TRUE +NCONFIG:NAS_SIM_POWER_SAVING_ENABLE,TRUE +NCONFIG:BARRING_RELEASE_DELAY,64 +NCONFIG:RELEASE_VERSION,14 +NCONFIG:RPM,FALSE +NCONFIG:SYNC_TIME_PERIOD,0 +NCONFIG:IPV6_GET_PREFIX_TIME,15 +NCONFIG:NB_CATEGORY,2 +NCONFIG:RAI,TRUE +NCONFIG:HEAD_COMPRESS,FALSE +NCONFIG:RLF_UPDATE,TRUE +NCONFIG:CONNECTION_REESTABLISHMENT,TRUE +NCONFIG:TWO_HARQ,TRUE +NCONFIG:PCO_IE_TYPE,EPCO +NCONFIG:T3324_T3412_EXT_CHANGE_REPORT,FALSE +NCONFIG:NON_IP_NO_SMS_ENABLE,FALSE +NCONFIG:SUPPORT_SMS,FALSE +NSND:29 Bytes:AT+NCONFIG=AUTOCONNEC +NCONFIG:SINR_MEAS_CONTROL,TRUE +NCONFIG:RELAXED_MONITORING,TRUE +NCONFIG:ALL_R14_FEATURES,TRUE +NCONFIG:ALL_R15_FEATURES,FALSE +NCONFIG:OTDOA_ENABLE,TRUE +NCONFIG:PS_DATA_OFF,TRUE +NCONFIG:POWER_OFF_GUARD_TIME,10 +NCONFIG:SUSPEND_UICC,FALSE  OK  ATSND:5 Bytes:ATE0 ATRCV:6 Bytes: OK  ATSND:5 Bytes:ATE0 ATRCV:6 Bytes: OK  ATSND:7 Bytes:AT+NRB ATRCV:6 Bytes: OK  ATRCV:13 Bytes: REBOOTING  ATRCV:42 Bytes: REBOOT_CAUSE_APPLICATION_AT Neul  OK  ATSND:5 Bytes:ATE0 ATRCV:6 Bytes: OK  ATSND:5 Bytes:ATE0 ATRCV:6 Bytes: OK  ATSND:10 Bytes:AT+NBAND? ATRCV:22 Bytes: +NBAND:5,8,3  OK  ATSND:10 Bytes:AT+CFUN=0 ATRCV:6 Bytes: OK  ATSND:16 Bytes:AT+NBAND=5,8,20 ATRCV:9 Bytes: ERROR  ATSND:10 Bytes:AT+CFUN=1 ATRCV:6 Bytes: OK  ATSND:10 Bytes:AT+CFUN=1 ATRCV:6 Bytes: OK  ATSND:10 Bytes:AT+COPS=0 ATRCV:6 Bytes: OK  ATSND:11 Bytes:AT+CGATT=1 ATRCV:9 Bytes: ERROR  ATSND:10 Bytes:AT+NNMI=1 ATRCV:6 Bytes: OK  ATSND:10 Bytes:AT+CGATT? ATRCV:18 Bytes: +CGATT:0  OK  ATRCV:16 Bytes: +QLWEVTIND:0  GET THE LWM2M:ind:0 ATRCV:16 Bytes: +QLWEVTIND:3  GET THE LWM2M:ind:3 ATSND:10 Bytes:AT+CGATT? ATRCV:18 Bytes: +CGATT:1  OK  NB MODULE RXTX READY NOW 
  • [问题求助] 删除挂载设备后重新注册
    【功能模块】小熊派NB-Iot物联网模块【操作步骤&问题现象】删除挂载设备后重新注册,显示标识码已存在
  • [问题求助] 请问AMOP为啥不能连续发数据,消息会进行堆积,接收端接收不到消息
    请问AMOP为啥不能连续发数据,消息会进行堆积,接收端接收不到消息
  • [技术干货] 【华为云IOT】基于香橙派 AIpro 设计的医院人脸红外测温系统(从0开始开发)
    一、前言在公共卫生事件频发的当下,尤其是在全球性疫情爆发后,国家对公共空间的健康监测和管理提出了更高的要求。医院、疾病防控中心和发热门诊作为疫情防控的第一线,需要高效且精准地对进出人员进行健康筛查,以防止病毒传播,保障医护人员及患者的安全。传统的手动体温检测方式不仅效率低下,而且存在交叉感染的风险,开发一种能够自动、快速、准确地进行人体温度监测与身份识别的系统显得非常的重要。当前文章会完整的介绍,如何采用香橙派AIpro设计出一套医院人脸红外测温系统。香橙派AIpro是一款高性价比的边缘计算设备,搭载了昇腾 AI 处理器,可提供 8TOPS INT8 的计算能力,能够运行Ubuntu 22.04操作系统,这为部署复杂的深度学习算法提供了硬件基础。本系统利用OpenCV和SSD算法模型进行人脸检测,通过各方面的模型训练,能确保在复杂光线和遮挡条件下仍能有效识别个体;结合红外测温技术,可以非接触式地测量额头温度,避免了传统接触式测文章温可能带来的卫生问题。考虑到环境因素对测温结果的影响,系统还配备了温湿度传感器,以实时监测并校准测温数据。为了实现数据的实时监控与分析,系统通过MQTT协议将收集到的信息上传至华为云物联网云平台,便于远程监控和数据分析,有助于疫情趋势的预测和资源的合理调配。本项目整体提供了一个智能化、自动化的人脸识别与体温监测解决方案,以提高公共卫生领域的响应速度和防控效率,减少人力资源的投入,同时降低潜在的感染风险,为构建安全健康的医疗环境贡献力量。本项目在完成最终的功能开发前,会先单个完成模块的功能开发,实现了单个模块功能之后,最终在整体合在一起实现最终的项目开发。整体项目会从搭建环境开始, 一步一步实现最终的项目效果。下面的开发出来的最终人脸检测设备最终设计效果:二、主控板介绍香橙派 AIpro开发板是香橙派联合华为精心打造的高性能 AI 开发板,搭载了昇腾 AI 处理器,可提供 8TOPS INT8 的计算能力,内存提供了 8GB 和 16GB两种版本。可以实现图像、视频等多种数据分析与推理计算,可广泛用于教育、机器人、无人机等场景。下面是香橙派 AIpro开发板的配置说明:模块规格昇腾 AI 处理器4 核 64 位 Arm 处理器 + AI 处理器AI 算力半精度(FP16):4 TFLOPS整数精度(INT8):8 TOPS内存类型:LPDDR4X 容量:8GB 或 16GB存储板载 32MB 的 SPI FlashMicro SD 卡插槽eMMC 插座:可外接 eMMC 模块M.2 M-Key 接口:可接 2280 规格的 NVMe SSD 或 SATA SSD以太网支持 10/100/1000Mbps板载 PHY 芯片:RTL8211FWi-Fi+蓝牙支持 2.4G 和 5G 双频 WIFIBT4.2模组:欧智通 62221BUUCUSB2 个 USB3.0 Host 接口1 个 Type-C 接口(只支持 USB3.0,不支持 USB2.0)摄像头2 个 MIPI CSI 2 Lane 接口显示2 个 HDMI 接口 1 个 MIPI DSI 2 Lane 接口音频一个 3.5mm 耳机孔,支持音频输入输出2 个 HDMI 音频输出40 pin 扩展口用于扩展 UART、I2C、SPI、PWM 和 GPIO 等接口按键一个复位键,一个关机键,一个升级按键拨码开关2 个拨码开关:用于控制 SD 卡、eMMC 和 SSD 启动选项电源支持 Type-C 供电,20V PD-65W 适配器LED 灯一个电源指示灯和一个软件可控指示灯风扇接口4pin,0.8mm 间距,用于接 12V 风扇,支持 PWM 控制电池接口2pin,2.54mm 间距,用于接 3 串电池,支持快充调试串口Micro USB 接口的调试串口支持的操作系统Ubuntu 22.04 和 openEuler 22.03外观规格介绍产品尺寸:107*68mm 重量:82g下面是香橙派 AIpro开发板的功能模块介绍:三、搭建开发环境3.1 准备需要的配件(1)准备一张至少32G的TFT卡,用来烧写系统。(2)准备一个读卡器,方便插入TFT卡,好方便插入到电脑上拷贝系统(3)香橙派 AIpro 主板一个(4)一根网线(方便插路由器上与香橙派 AIpro 连接)(5)一根type-C的电源线 + 电源插头(3A电流),这个主板买回来是带了电源的。 也可以用自己Android手机的数据线就行,拿手机充电器供电,因为目前Android手机电源线都是都是type-C 也支持快充的,电流也是满足需求的。(6)一个USB摄像头,用于后续项目开发里获取周围的实时图像,识别人脸。 (项目开发需要使用)(7)一个串口协议的红外测温传感器,用于后续项目开发里测量体温。(项目开发需要使用)(8)一个外放音箱,支持3.5mm的耳机插孔,方便后续项目开发里播放语音提示。(项目开发需要使用)(9)一块显示屏(这个不是必须的,可以直接Windows远程桌面访问系统,对前期开发来说没有任何影响,只要做成最终的产品才需要配屏幕)。3.2 开发板实物图拿回来的香橙派 AIpro 开发板实物是这样的。3.3 下载开发板资料拿到板子之后,第一件事肯定是先去官网下载板子对应的相关的资料。比如:用户手册、系统镜像、原理图、开发工具什么的。官网地址:cid:link_1翻到下面,找到资料下载地址,直接下载就行,下载会跳转到网盘。系统镜像我选择的 ubuntu22.04。资料下载下来之后,可以看到有一份官方的说明文档,指导板子的基本使用。如何烧写系统,如何启动系统等等。3.4 下载系统烧写工具链接:cid:link_0下载下来之后,直接双击正常安装,安装好之后打开的界面如下。 (选择本地制作)然后将 TF卡通过读卡器插到电脑上,准备烧写系统(就算有些电脑自带了TF卡的插槽也建议用USB读卡器,这个电脑自带的TF卡槽烧写系统无法启动)。 TF卡的容量至少要32G,最好是64G。选择要烧写的系统镜像文件(就是刚刚通过网盘下载的ubuntu系统镜像)。然后点击烧录镜像。弹出提示框,选择确认。然后可以看到,系统正在烧写中了,精心等待即可。烧录成功之后,会弹窗提示弹出SD卡。将TF卡从电脑上弹出,拔掉就行了。3.5 设置开发板启动模式香橙派 AIpro开发板支持从 TF 卡、eMMC 和 SSD(支持 NVMe SSD 和 SATA SSD)启动。具体从哪个设备启动是由开发板背面的两个拨码(BOOT1 和 BOOT2)开关来控制的。BOOT1 和 BOOT2 两个拨码开关都支持左右两种设置状态,所以总共有 4 种设置状态,开发板目前只使用了其中的三种。不同的设置状态对应的启动设备如下表所示:将BOOT1 和 BOOT2 两个拨码开关 全部拨到靠右的位置就可以选择从TF卡启动了。3.6 启动系统【1】将烧写好的TF卡插在板子上。【2】插好网线。网线一端接开发板的网口,一端接路由器,自己的笔记本电脑也是接的同一个路由器,让板子与电脑在同一个局域网内,方便接下来远程登录开发板的系统。【3】插好电源板子是没有电源开关的,电源线插好之后,系统就启动了。刚启动的时候风扇的声音会比较大,等待几秒就正常了。按下开发板左上角的PWR_OFF可以关闭系统,点击旁边的RESET可以重启系统。3.7 SSH远程登录系统系统启动之后,会自动请求路由器分配IP地址,我们只需要登录到开发板连接的路由器后台,就可以看到新接入的设备。我的用是小米路由器。直接在浏览器里输入:192.168.31.1 即可进入到路由器的后台终端。从下面图片里可以看到,香橙派 AIpro 已经分配到IP地址了,192.168.31.136。为了方便远程登录到系统终端,可以下载安装一个FinalShell 软件。下载地址:cid:link_4软件安装打开后,建立一个新的SSH链接,具体看下图的操作。这里面的主机IP地址就是从路由器后台看到的,分配给香橙派 AIpro开发板的IP地址。 端口号是固定的22,这是SSH协议的固定端口。用户名是root,密码是:Mind@123 这是烧写的香橙派 AIpro系统固定的用户名和密码,也就是系统内置的。双击刚才建立好的链接,就可以登录到系统终端。 进去终端之后基本上就可以进行正常的开发了。3.8 安装xdrp工具为了方便图形化方式开发,可以使用windows系统通过远程桌面登录香橙派 AIpro,就可以看到界面了,不过需要先安装工具。进入到香橙派 AIpro终端之后,输入安装命令:sudo apt-get install xrdp按下回车之后,会弹出确认窗口。输入 y之后,按下回车,继续安装。下面是完整的命令安装过程: 按顺序执行就行了。#安装xrdpsudo apt-get install xrdp#安装vnc4serversudo apt-get install vnc4server tightvncserver#安装xubuntu-desktopsudo apt-get install xubuntu-desktop#向xsession中写入xfce4-sessionecho “xfce4-session” >~/.xsession#开启xrdp服务sudo service xrdp restart注意,如果之后断电了,远程桌面无法链接上。可以先卸载xrdp,再重新安装即可。sudo apt-get remove --purge xrdp3.9 Window远程登录在windows上打开运行命令的窗口,输入mstsc来打开远程桌面。输入mstsc,点击确定。弹出窗口后,填入IP地址(这就是你的香橙派 AIpro 开发板的IP地址),点击连接。正常登录之后,就可以看到远程桌面的界面了。输入账号和密码。用户名是root,密码是:Mind@123 这是烧写的香橙派 AIpro系统固定的用户名和密码,也就是系统内置的。登录之后的界面:好了。接下来就可以进行项目的正式开发了。3.10 取消自动休眠Ubuntu桌面镜像会自动休眠,输入以下指令禁用休眠。sudo systemctl status sleep.targetsudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target四、安装Qt开发环境因为最终的项目需要使用界面,我的项目准备采用Qt进行开发,这一章节进行安装Qt开发环境。4.1 安装qtcreator在命令行终端分别输入以下命令安装qtcreator:root@orangepiaipro:~# sudo apt-get updateroot@orangepiaipro:~# sudo apt-get install qtcreatorroot@orangepiaipro:~# sudo apt-get install qtmultimedia5-devroot@orangepiaipro:~# sudo apt-get install libqt5serialport5-dev4.2 启动qtcreator安装好之后,就可以看到Qt软件了,点击即可打开。这是启动之后的效果。4.3 配置编译器默认安装Qt之后,编译器是配置错误,无法正常使用。【1】打开设置页面【2】选择编译器套件【3】配置编译器套件。将编译器改为GCC。4.4 新建工程Qt环境【1】新建工程【2】设置工程名称和路径(自己单独建立一个文件夹存放工程)【3】选择继承的基类【4】选择编译器套件【5】创建完成这就是创建好的工程。【6】点击左下角绿色三角形编译运行。下面是正常运行的效果,已经弹窗窗口,说明Qt的环境已经OK了。五、开发板初步测试为了了解下开发板本身的性能,先采用开发板开发一些小项目测试测试效果。现在系统根目录创建一个work目录,方便存放接下来的项目文件。5.1 项目1:开发一个基于HTTP协议的网络摄像头【1】项目介绍本项目主要采用C语言开发,实现了一个网络摄像头项目,在橙派 AIpro开发板上实现了一个HTTP服务器,,处理浏览器的请求,当浏览器访问过来时,就将本地采集到的摄像头画面发送给浏览器,与浏览器建立长连接通信,直接传输JPG图片,实现摄像头画面实时显示效果。支持登录页面,做了一个账号登录界面,访问服务器之后需要输入账号密码才可以正常进入服务器查看共享的画面。如果分享个摄像头画面,那是非常的方便的,想要查看分享的摄像头画面只需要浏览器里输入服务器的IP地址登录进去就可以看画面了。通过本项目的测试,可以了解到USB摄像头的读取效果,网络传输的效果。 为后续的其他项目开发做一个参考。【2】编写项目代码项目开发,代码编写先在Windows下进行,开发完毕,再拷贝到橙派 AIpro开发板上。这是在Windows下开发好的项目代码:【3】上传项目代码打开FinalShell终端,可以直接将开发好的项目源码,整个目录上传到香橙派 AIpro系统。通过FinalShell终端可以很方便的将香橙派 AIpro系统文件下载到本地,也可以将本地的文件很方便的上传上去。在开发项目的阶段是很方便的。【4】插入USB摄像头将USB摄像头插入到开发板的USB口,然后ls /dev/video* 查看摄像头的设备节点,确定摄像头是否识别成功。【4】编译运行项目项目里已经构建好了Makefile文件,直接make就可以编译。编译之后,运行项目。(base) root@orangepiaipro:~/work/http_camera# make(base) root@orangepiaipro:~/work/http_camera# ./http_app ./server <server_port> </dev/videoX>(base) root@orangepiaipro:~/work/http_camera# ./http_app 666 /dev/video0 运行命令的含义:./http_app 666 /dev/video0 666表示服务器的端口号。 /dev/video0是摄像头的端口号。【5】浏览器访问在自己电脑浏览器地址栏里输入:http://192.168.31.136:666就可以看到登录页面。登录成功之后,可以看到摄像头的实时画面。5.2 项目2: 基于华为云设计的智能家居控制系统【1】项目介绍基于香橙派 AIpro开发板设计的智能家居控制系统,通过MQTT协议连接华为云物联网云平台;通过DHT11传感器读取环境温湿度,将数据上传到华为云物联网云平台。在华为云云平台上也可以远程控制硬件端连接的LED灯,控制3种颜色显示。通过本项目的完整开发测试,可以掌握香橙派的GPIO口的基本使用以及网络的测试。为后续的其他项目开发做一个参考测试。【2】安装wiringPi(1)安装 wiringOP 前,请先确保 Linux 系统中存在/etc/orangepi-release 这个配置文件,里面的内容为:BOARD=orangepiaipro。(base) root@orangepiaipro:~/work/# cat /etc/orangepi-releaseBOARD=orangepiaipro(2)如果 Linux 中没有/etc/orangepi-release 这个配置文件,可以使用下面的命令创建一个。(base) root@orangepiaipro:~/work/# echo "BOARD=orangepiaipro" | sudo tee /etc/orangepi-release(3)下载 wiringOP 的代码。(base) root@orangepiaipro:~/work/# sudo apt-get update(base) root@orangepiaipro:~/work/# sudo apt-get install -y git(base) root@orangepiaipro:~/work/# git clone https://github.com/orangepi-xunlong/wiringOP.git -b next(4)然后编译安装 wiringOP。(base) root@orangepiaipro:~/work/# sudo apt-get install -y gcc make build-essential(base) root@orangepiaipro:~/work/# cd wiringOP(base) root@orangepiaipro:~/work/wiringOP# sudo ./build clean(base) root@orangepiaipro:~/work/wiringOP# sudo ./build(5)编译完之后,可以看到生成的文件查看GPIO口信息。(base) root@orangepiaipro:~/work/wiringOP# cd gpio/(base) root@orangepiaipro:~/work/wiringOP/gpio# ./gpio readall +------+-----+----------+--------+---+ AI PRO +---+--------+----------+-----+------+ | GPIO | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | GPIO | +------+-----+----------+--------+---+----++----+---+--------+----------+-----+------+ | | | 3.3V | | | 1 || 2 | | | 5V | | | | 76 | 0 | SDA7 | OFF | 0 | 3 || 4 | | | 5V | | | | 75 | 1 | SCL7 | OFF | 0 | 5 || 6 | | | GND | | | | 226 | 2 | GPIO7_02 | OFF | 0 | 7 || 8 | 0 | OFF | UTXD0 | 3 | 14 | | | | GND | | | 9 || 10 | 0 | OFF | URXD0 | 4 | 15 | | 82 | 5 | GPIO2_18 | OFF | 0 | 11 || 12 | 0 | OFF | GPIO7_03 | 6 | 227 | | 38 | 7 | GPIO1_06 | IN | 1 | 13 || 14 | | | GND | | | | 79 | 8 | GPIO2_15 | IN | 1 | 15 || 16 | 1 | IN | GPIO2_16 | 9 | 80 | | | | 3.3V | | | 17 || 18 | 0 | IN | GPIO0_25 | 10 | 25 | | 91 | 11 | SPI0_SD0 | OFF | 0 | 19 || 20 | | | GND | | | | 92 | 12 | SPI0_SDI | OFF | 0 | 21 || 22 | 1 | IN | GPIO0_02 | 13 | 2 | | 89 | 14 | SPI0_CLK | OFF | 0 | 23 || 24 | 0 | OFF | SPI0_CS | 15 | 90 | | | | GND | | | 25 || 26 | 0 | IN | GPIO2_19 | 16 | 83 | | | | SDA6 | | | 27 || 28 | | | SCL6 | | | | 231 | 17 | URXD7 | OFF | 0 | 29 || 30 | | | GND | | | | 84 | 18 | GPIO2_20 | IN | 0 | 31 || 32 | 0 | IN | GPIO1_01 | 19 | 35 | | 128 | 20 | GPIO4_00 | IN | 1 | 33 || 34 | | | GND | | | | 228 | 21 | GPIO7_04 | OFF | 0 | 35 || 36 | 0 | OFF | GPIO2_17 | 22 | 81 | | 3 | 23 | GPIO0_03 | IN | 1 | 37 || 38 | 0 | IN | GPIO7_06 | 24 | 230 | | | | GND | | | 39 || 40 | 0 | OFF | GPIO7_05 | 25 | 229 | +------+-----+----------+--------+---+----++----+---+--------+----------+-----+------+ | GPIO | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | GPIO | +------+-----+----------+--------+---+ AI PRO +---+--------+----------+-----+------+可以通过命令行测试GPIO口:(base) root@orangepiaipro:~/work# ./gpio mode 2 out 设置 wPi 为2的这个IO口为输出模式(base) root@orangepiaipro:~/work# ./gpio write 2 0 设置 wPi 为2的这个IO口输出0 (低电平)(base) root@orangepiaipro:~/work# ./gpio write 2 1 设置 wPi 为2的这个IO口输出1 (高电平)【3】GPIO口布局【4】控制LED灯编写的测试代码:#include <stdio.h>#include <wiringPi.h>#include <stdlib.h>#include <string.h>/*控制继电器高低电平亮灯*/#define LEDG 0#define LEDB 1#define LEDR 2int main(){ wiringPiSetup(); //置引脚编号方式为wiringPi编码 pinMode(LEDG,OUTPUT); pinMode(LEDB,OUTPUT); pinMode(LEDR,OUTPUT); while(1) { //全部关闭 digitalWrite(LEDG,LOW); digitalWrite(LEDB,LOW); digitalWrite(LEDR,LOW); //亮蓝色 digitalWrite(LEDG,HIGH); sleep(1); //全部关闭 digitalWrite(LEDG,LOW); digitalWrite(LEDB,LOW); digitalWrite(LEDR,LOW); //亮绿色 digitalWrite(LEDB,HIGH); sleep(1); //全部关闭 digitalWrite(LEDG,LOW); digitalWrite(LEDB,LOW); digitalWrite(LEDR,LOW); //亮红色 digitalWrite(LEDR,HIGH); sleep(1); } return 0;}编译代码:(base) root@orangepiaipro:~/work# gcc led.c -lwiringPi运行代码:(base) root@orangepiaipro:~/work# ./a.out 运行效果:【5】DHT11温湿度传感器数据读取代码:#include <wiringPi.h>#include <stdio.h>#include <stdlib.h> //编译:gcc -Wall -o dht11 dht11.c -lwiringPi -o app typedef unsigned char uint8;typedef unsigned int uint16;typedef unsigned long uint32; #define HIGH_TIME 32 int pinNumber = 5;uint32 databuf; uint8 readSensorData(void){ uint8 crc; uint8 i; pinMode(pinNumber, OUTPUT); // set mode to output digitalWrite(pinNumber, 0); // output a high level delay(25); digitalWrite(pinNumber, 1); // output a low level pinMode(pinNumber, INPUT); // set mode to input pullUpDnControl(pinNumber, PUD_UP); delayMicroseconds(27); if (digitalRead(pinNumber) == 0) //SENSOR ANS { while (!digitalRead(pinNumber)) ; //wait to high for (i = 0; i < 32; i++) { while (digitalRead(pinNumber)) ; //data clock start while (!digitalRead(pinNumber)) ; //data start delayMicroseconds(HIGH_TIME); databuf *= 2; if (digitalRead(pinNumber) == 1) //1 { databuf++; } } for (i = 0; i < 8; i++) { while (digitalRead(pinNumber)) ; //data clock start while (!digitalRead(pinNumber)) ; //data start delayMicroseconds(HIGH_TIME); crc *= 2; if (digitalRead(pinNumber) == 1) //1 { crc++; } } return 1; } else { return 0; }} int main(void){ printf("PIN:%d\n", pinNumber); wiringPiSetup(); //置引脚编号方式为wiringPi编码 pinMode(pinNumber, OUTPUT); // set mode to output digitalWrite(pinNumber, 1); // output a high level printf("Starting...\n"); while (1) { pinMode(pinNumber, OUTPUT); // set mode to output digitalWrite(pinNumber, 1); // output a high level delay(3000); if (readSensorData()) { printf("Sensor data read ok!\n"); printf("RH:%d.%d\n", (databuf >> 24) & 0xff, (databuf >> 16) & 0xff); printf("TMP:%d.%d\n", (databuf >> 8) & 0xff, databuf & 0xff); databuf = 0; } else { printf("Sensor dosent ans!\n"); databuf = 0; } } return 0;}编译代码:(base) root@orangepiaipro:~/work# gcc dht11.c -lwiringPi运行代码:(base) root@orangepiaipro:~/work# ./a.out PIN:5Starting...RH:68.4TMP:30.4RH:68.5TMP:30.2RH:68.2TMP:30.4RH:68.1TMP:30.3RH:68.1TMP:30.6RH:68.1TMP:30.4实物图:【6】注册华为云设备华为云物联网平台的整体就不再详细展示了,可以直接看视频。B站的视频链接:cid:link_2(1)注册产品(2)注册设备(3)创建命令(4)得到MQTT三元组IP地址:117.78.5.125端口号:1883ClientId 6693872aa559ef6226685350_dev1_0_0_2024071408Username 6693872aa559ef6226685350_dev1Password 8ce1b26a6fac2c52402d2911a9a951efe6026f71041037a963bebdd4a099190f订阅主题:$oc/devices/6693872aa559ef6226685350_dev1/sys/messages/down发布主题:$oc/devices/6693872aa559ef6226685350_dev1/sys/properties/report发布数据:{"services": [{"service_id": "stm32","properties":{"DHT11_T":23,"DHT11_H":80}}]}【7】编写整体项目接下来就编写代码,连接华为云物联网平台,完成数据上传。 将采集的温湿度数据上传到华为云物联网云平台。 同时支持在华为云物联网平台下发命令远程控制设备端的LED灯。代码是采用纯C语言编写,实现了MQTT协议,完成了与物联网云平台交互。关于MQTT协议的整体编写过程,可以直接看视频:cid:link_3(1)这是写好的项目代码完整的代码:#include <stdio.h>#include <sys/types.h> /* See NOTES */#include <sys/socket.h>#include <netinet/in.h>#include <netinet/ip.h> /* superset of previous */#include <arpa/inet.h>#include <poll.h>#include <unistd.h>#include <stdlib.h>#include <pthread.h>#include <signal.h>#include "mqtt.h"#include "main.h"#include <netdb.h>#include <stdio.h>#include <wiringPi.h>#include <stdlib.h>#include <string.h>/*控制继电器高低电平亮灯*/#define LEDG 0#define LEDB 1#define LEDR 2//服务器IP#define SERVER_IP "117.78.5.125"#define SERVER_PORT 1883 //端口号//MQTT三元组#define ClientID "6693872aa559ef6226685350_dev1_0_0_2024071408"#define Username "6693872aa559ef6226685350_dev1"#define Password "8ce1b26a6fac2c52402d2911a9a951efe6026f71041037a963bebdd4a099190f"//密文 //订阅主题:#define SET_TOPIC "$oc/devices/6693872aa559ef6226685350_dev1/sys/messages/down"//订阅//发布主题:#define POST_TOPIC "$oc/devices/6693872aa559ef6226685350_dev1/sys/properties/report"//发布char mqtt_message[1024*1024];//上报数据缓存区char request_id[100];char mqtt_cmd_message[100];char mqtt_cmd_data[100];int sockfd;/*获取平台下发数据*/void *pth_work_func(void *arg){ char buff[1024]; int size=0; int i=0; while(1) { size=Client_GetData(buff); printf("size=%d\r\n",size); if(size<0)break; for(i=0;i<size;i++) { printf("%c ",buff[i]); } buff[size]='\0'; if(size>5) { printf("%s\r\n",buff+5); if(strstr((char*)&buff[5],"sys/commands/request_id=")) { char *p=NULL; p=strstr((char*)&buff[5],"request_id"); if(p) { //解析数据 //$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/get/request_id=5f359b5c-542f-460e-9f51-85e82150ff4a{"service_id":"gps"} strncpy(request_id,p,47); } //上报数据 sprintf(mqtt_cmd_message,"{\"result_code\":0,\"response_name\":\"COMMAND_RESPONSE\",\"paras\":{\"result\":\"success\"}}"); sprintf(mqtt_cmd_data,"$oc/devices/6693872aa559ef6226685350_dev1/sys/commands/response/%s", request_id); MQTT_PublishData(mqtt_cmd_data,mqtt_cmd_message,0); printf("应答-发布主题:%s\r\n",mqtt_cmd_data); printf("应答-发布数据:%s\r\n",mqtt_cmd_message); } if(strstr((char*)&buff[5],"\"LED_SW\":1")) { //全部关闭 digitalWrite(LEDG,LOW); digitalWrite(LEDB,LOW); digitalWrite(LEDR,LOW); //亮蓝色 digitalWrite(LEDG,HIGH); } if(strstr((char*)&buff[5],"\"LED_SW\":2")) { //全部关闭 digitalWrite(LEDG,LOW); digitalWrite(LEDB,LOW); digitalWrite(LEDR,LOW); //亮绿色 digitalWrite(LEDB,HIGH); } if(strstr((char*)&buff[5],"\"LED_SW\":3")) { //全部关闭 digitalWrite(LEDG,LOW); digitalWrite(LEDB,LOW); digitalWrite(LEDR,LOW); //亮红色 digitalWrite(LEDR,HIGH); } if(strstr((char*)&buff[5],"\"LED_SW\":0")) { //全部关闭 digitalWrite(LEDG,LOW); digitalWrite(LEDB,LOW); digitalWrite(LEDR,LOW); } } printf("\r\n"); }} /*信号处理函数*/ void signal_func(int sig){ //printf("捕获的信号:%d\n",sig); if(sig==SIGALRM) { MQTT_SentHeart();//心跳包 alarm(5); }}typedef unsigned char uint8;typedef unsigned int uint16;typedef unsigned long uint32; #define HIGH_TIME 32 int pinNumber = 5;uint32 databuf; uint8 readSensorData(void){ uint8 crc; uint8 i; pinMode(pinNumber, OUTPUT); // set mode to output digitalWrite(pinNumber, 0); // output a high level delay(25); digitalWrite(pinNumber, 1); // output a low level pinMode(pinNumber, INPUT); // set mode to input pullUpDnControl(pinNumber, PUD_UP); delayMicroseconds(27); if (digitalRead(pinNumber) == 0) //SENSOR ANS { while (!digitalRead(pinNumber)) ; //wait to high for (i = 0; i < 32; i++) { while (digitalRead(pinNumber)) ; //data clock start while (!digitalRead(pinNumber)) ; //data start delayMicroseconds(HIGH_TIME); databuf *= 2; if (digitalRead(pinNumber) == 1) //1 { databuf++; } } for (i = 0; i < 8; i++) { while (digitalRead(pinNumber)) ; //data clock start while (!digitalRead(pinNumber)) ; //data start delayMicroseconds(HIGH_TIME); crc *= 2; if (digitalRead(pinNumber) == 1) //1 { crc++; } } return 1; } else { return 0; }} unsigned int DHT11_T;// 环境温度unsigned int DHT11_H;// 环境湿度int main(){ wiringPiSetup(); //置引脚编号方式为wiringPi编码 pinMode(LEDG,OUTPUT); pinMode(LEDB,OUTPUT); pinMode(LEDR,OUTPUT); //DHT11温湿度初始化 pinMode(pinNumber, OUTPUT); // set mode to output digitalWrite(pinNumber, 1); // output a high level int stat; sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd==-1) { printf("网络套接字打开失败\n"); return 0; } signal(SIGPIPE,SIG_IGN);/*忽略SIGPIPE信号*/ signal(SIGALRM,signal_func);/*闹钟信号*/ /*连接服务器*/ struct sockaddr_in addr; addr.sin_family=AF_INET;//IPV4 addr.sin_port=htons(SERVER_PORT);/*端口号*/ addr.sin_addr.s_addr=inet_addr(SERVER_IP);//inet_addr(ip);//服务器IP if(connect(sockfd, (struct sockaddr *)&addr,sizeof(struct sockaddr_in))==0) { printf("server connect ok\n"); MQTT_Init(); while(1) { /*登录服务器*/ if(MQTT_Connect(ClientID,Username,Password)==0) { break; } sleep(1); printf("server connect ....\n"); } printf("MQTT_Connect OK\r\n"); //订阅物联网平台数据 stat=MQTT_SubscribeTopic(SET_TOPIC,1,1); if(stat) { close(sockfd); printf("MQTT_SubscribeTopic ERROR\r\n"); exit(0); } printf("MQTT_SubscribeTopic ok\r\n"); /*创建线程*/ pthread_t id; pthread_create(&id, NULL,pth_work_func,NULL); pthread_detach(id);//设置分离属性 //发送心跳包 // alarm(5);//闹钟函数,时间到达会产生SIGALRM信号 while(1) { //读取DHT11温湿度数据 pinMode(pinNumber, OUTPUT); // set mode to output digitalWrite(pinNumber, 1); // output a high level delay(3000); if (readSensorData()) { printf("DHT11 Sensor data read ok!\n"); printf("RH:%d.%d\n", (databuf >> 24) & 0xff, (databuf >> 16) & 0xff); printf("TMP:%d.%d\n", (databuf >> 8) & 0xff, databuf & 0xff); //温度整数部分 DHT11_H=((databuf >> 24) & 0xff); printf("DHT11_T:%d\r\n",DHT11_T); //湿度整数部分 DHT11_T=((databuf >> 8) & 0xff); printf("DHT11_H:%d\r\n",DHT11_H); databuf = 0; } else { printf("Sensor dosent ans!\n"); databuf = 0; } //组合传感器状态数据 sprintf(mqtt_message,"{\"services\": [{\"service_id\": \"stm32\",\"properties\":{\"DHT11_T\":%d,\"DHT11_H\":%d}}]}",DHT11_T,DHT11_H);//温度 //上报数据 MQTT_PublishData(POST_TOPIC,mqtt_message,0); printf("MQTT_PublishData....\r\n"); sleep(2); } }}(2)这是编译运行后的效果(3)在华为云物联网平台后台,可以看到设备已经在线了,同时也实时收到设备端上传的数据。(4)下发命令测试。 通过命令下发控制设备端的LED灯。5.3 项目3:OpenCV+卷积神经网络实现人脸识别本项目通过OpenCV加载训练好的SSD模型,实现人脸检测,能够在图像中找到并标记出人脸的位置和置信度。通过本项目,可以验证整个系统的算法运行速度。为后续的项目开发做参考。(1)安装python (烧写的系统本身自带了完整的Python环境,可以不需要安装,如果没有才需要安装)sudo apt updatesudo apt install python3(2)编写代码加载模型识别人脸import cv2import numpy as npimport timeprototxt_path = "./deploy.prototxt.txt"model_path = "./res10_300x300_ssd_iter_140000_fp16.caffemodel"image_path = "6.jpg"# 加载模型model = cv2.dnn.readNetFromCaffe(prototxt_path, model_path)# 读取图像image = cv2.imread(image_path)h, w = image.shape[:2]# 准备模型输入的 blobblob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0), swapRB=False)# 设置 blob 作为模型的输入model.setInput(blob)# 进行推断并获取输出start_time = time.time()output = model.forward()end_time = time.time()# 遍历检测结果font_scale = 1.0for i in range(output.shape[2]): confidence = output[0, 0, i, 2] # 通过置信度阈值过滤弱检测结果 if confidence > 0.5: box = output[0, 0, i, 3:7] * np.array([w, h, w, h]) (start_x, start_y, end_x, end_y) = box.astype("int") # 绘制边界框和置信度 cv2.rectangle(image, (start_x, start_y), (end_x, end_y), (255, 0, 0), 2) text = f"{confidence * 100:.2f}%" cv2.putText(image, text, (start_x, start_y - 10), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 0, 0), 2)# 显示和保存带有检测结果的图像cv2.imwrite("beauty_detected.jpg", image)# 输出识别耗时print(f"识别耗时:{end_time - start_time:.3f} 秒")(3)运行效果(4)将图片下载下来打开5.4 项目4:OpenCV+YOLOv3实现目标检测本项目通过OpenCV加载YOLOV3官方的模型,实现目标。通过本项目,可以验证整个系统的算法运行速度。为后续的项目开发做参考。实现代码:#include <opencv2/opencv.hpp>#include <opencv2/dnn.hpp>#include <fstream>#include <iostream>#include <algorithm>#include <cstdlib>using namespace std;using namespace cv;using namespace cv::dnn;void image_detection();String yolo_cfg = "./yolov3.cfg";String yolo_model = "./yolov3.weights";int main(int argc, char** argv){ image_detection();}void image_detection() { //加载网络模型 Net net = readNetFromDarknet(yolo_cfg, yolo_model); //net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE); net.setPreferableTarget(DNN_TARGET_CPU); std::vector<String> outNames = net.getUnconnectedOutLayersNames(); for (int i = 0; i < outNames.size(); i++) { printf("output layer name : %s\n", outNames[i].c_str()); } vector<string> classNamesVec; ifstream classNamesFile("./coco.names"); if (classNamesFile.is_open()) { string className = ""; while (std::getline(classNamesFile, className)) classNamesVec.push_back(className); } // 加载图像 Mat frame = imread("6.jpg"); Mat inputBlob = blobFromImage(frame, 1 / 255.F, Size(416, 416), Scalar(), true, false); net.setInput(inputBlob); // 检测 std::vector<Mat> outs; net.forward(outs, outNames); vector<double> layersTimings; double freq = getTickFrequency() / 1000; double time = net.getPerfProfile(layersTimings) / freq; ostringstream ss; ss << "detection time: " << time << " ms"; putText(frame, ss.str(), Point(20, 20), 0, 0.5, Scalar(0, 0, 255)); vector<Rect> boxes; vector<int> classIds; vector<float> confidences; for (size_t i = 0; i < outs.size(); ++i) { // Network produces output blob with a shape NxC where N is a number of // detected objects and C is a number of classes + 4 where the first 4 // numbers are [center_x, center_y, width, height] float* data = (float*)outs[i].data; for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols) { Mat scores = outs[i].row(j).colRange(5, outs[i].cols); Point classIdPoint; double confidence; minMaxLoc(scores, 0, &confidence, 0, &classIdPoint); if (confidence > 0.5) { int centerX = (int)(data[0] * frame.cols); int centerY = (int)(data[1] * frame.rows); int width = (int)(data[2] * frame.cols); int height = (int)(data[3] * frame.rows); int left = centerX - width / 2; int top = centerY - height / 2; classIds.push_back(classIdPoint.x); confidences.push_back((float)confidence); boxes.push_back(Rect(left, top, width, height)); } } } vector<int> indices; NMSBoxes(boxes, confidences, 0.5, 0.2, indices); for (size_t i = 0; i < indices.size(); ++i) { int idx = indices[i]; Rect box = boxes[idx]; String className = classNamesVec[classIds[idx]]; putText(frame, className.c_str(), box.tl(), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255, 0, 0), 2, 8); rectangle(frame, box, Scalar(0, 0, 255), 2, 8, 0); } # 保存结果图片 cv2.imwrite('detections.jpg', frame) waitKey(0); return;}将识别的图片结果拷贝下来,查看效果。五、测温项目开发5.1 外设模块选型需要用到的传感器如下:(1)LU90614非接触式红外测温模块(串口协议),用于测量体温。(2)DHT11温湿度传感器,用于测量环境的温湿度。(3)USB摄像头,用于捕获图像,检测人脸。(4)一块香橙派 AIpro主控板。(5)一个三色LED灯,用于显示检测的体温状态。 红、绿、蓝 三种颜色。5.2 整体的项目代码整体项目是采用Qt开发的,因为需要通过显示屏展示界面,在界面上显示人脸的识别效果,温度测量效果等信息。【1】技术实现方式说明(1)这里面的LU90614非接触式红外测温模块 采用USB-TTL模块接入系统的。在/dev目录下的节点是ttyUSB0。没有使用开发板本身的IO口。(2)本项目是先在Windows下开发完成后,再上传到香橙派 AIpro开发板运行,在香橙派 AIpro里安装了Qt的开发环境。(3)体温传感器的串口数据读取,没有采用Qt本身的串口接口,而是采用了标准Linux下的方式读取串口数据。(4)摄像头的采集没有采用Qt的内置接口,而是采用了Linux下V4L2框架完成的图像采集。(5)MQTT协议没有采用第三方库,是自己基于Linux下的socket,从0开始编写的。(6)显示屏采用HDMI接口的7寸显示屏。作为整个项目的界面终端。【2】摄像头图像采集代码下面是采集USB实时画面的代码。#include <stdio.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>#include <unistd.h>#include <errno.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <linux/videodev2.h>#define WIDTH 640#define HEIGHT 480struct buffer { void *start; size_t length;};int pthread_run(){ int fd; struct v4l2_format fmt; struct v4l2_requestbuffers req; struct v4l2_buffer buf; enum v4l2_buf_type type; struct buffer *buffers; unsigned char *rgb888_buffer; // 打开摄像头设备 fd = open("/dev/video0", O_RDWR); if (fd == -1) { perror("打开/dev/video0失败"); return 1; } // 设置格式 memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = WIDTH; fmt.fmt.pix.height = HEIGHT; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // 使用YUYV格式,常见于USB摄像头 fmt.fmt.pix.field = V4L2_FIELD_NONE; if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) { perror("设置格式失败"); close(fd); return 1; } // 请求缓冲区 memset(&req, 0, sizeof(req)); req.count = 1; // 缓冲区数量 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) { perror("请求缓冲区失败"); close(fd); return 1; } // 分配并映射缓冲区 buffers = calloc(req.count, sizeof(*buffers)); if (!buffers) { perror("分配缓冲区内存失败"); close(fd); return 1; } // 查询缓冲区 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) { perror("查询缓冲区失败"); close(fd); return 1; } // 内存映射 buffers[0].length = buf.length; buffers[0].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (buffers[0].start == MAP_FAILED) { perror("内存映射失败"); close(fd); return 1; } // 开始流式传输 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) { perror("开始流式传输失败"); close(fd); return 1; } // 捕获循环(示例:捕获一帧) while (1) { // 入队缓冲区 if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) { perror("入队缓冲区失败"); close(fd); return 1; } // 出队缓冲区 if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) { perror("出队缓冲区失败"); close(fd); return 1; } // 处理帧(转换为RGB888格式) // 示例:将YUYV转换为RGB888 rgb888_buffer = (unsigned char *)malloc(WIDTH * HEIGHT * 3); for (int i = 0, j = 0; i < WIDTH * HEIGHT * 2; i += 4, j += 6) { // YUYV到RGB888的简化转换(实际应用中可能需要更复杂的算法) unsigned char Y0 = ((unsigned char *)buffers[0].start)[i + 0]; unsigned char U = ((unsigned char *)buffers[0].start)[i + 1]; unsigned char Y1 = ((unsigned char *)buffers[0].start)[i + 2]; unsigned char V = ((unsigned char *)buffers[0].start)[i + 3]; rgb888_buffer[j + 0] = Y0 + 1.402 * (V - 128); // 红色分量 rgb888_buffer[j + 1] = Y0 - 0.344 * (U - 128) - 0.714 * (V - 128); // 绿色分量 rgb888_buffer[j + 2] = Y0 + 1.772 * (U - 128); // 蓝色分量 rgb888_buffer[j + 3] = Y1 + 1.402 * (V - 128); // 红色分量 rgb888_buffer[j + 4] = Y1 - 0.344 * (U - 128) - 0.714 * (V - 128); // 绿色分量 rgb888_buffer[j + 5] = Y1 + 1.772 * (U - 128); // 蓝色分量 } // 使用rgb888_buffer进行进一步处理(如保存到文件、显示) // 示例:保存到文件 FILE *fp = fopen("frame.rgb", "wb"); if (fp) { fwrite(rgb888_buffer, 1, WIDTH * HEIGHT * 3, fp); fclose(fp); } free(rgb888_buffer); break; // 示例中仅处理一帧,所以退出循环 } // 停止流式传输 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) { perror("停止流式传输失败"); close(fd); return 1; } // 解除内存映射 munmap(buffers[0].start, buf.length); // 清理和关闭 free(buffers); close(fd); return 0;}【3】体温数据采集(串口)下面是采集体温传感器的代码。#include <stdio.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>#include <unistd.h>#include <errno.h>#include <sys/select.h>#include <poll.h>#define SERIAL_DEVICE "/dev/ttyUSB0"#define BAUDRATE B9600int temp_read_pthread() { int fd; char *sendbuf = "\xFA\xC5\xBF"; // 发送体温模式指令 char recvbuf[8]; struct termios options; struct pollfd pfd; int timeout = 1000; // 超时时间,单位毫秒 // 打开串口设备文件 if ((fd = open(SERIAL_DEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) { perror("open serial port failed"); exit(1); } // 设置串口参数 tcgetattr(fd, &options); cfmakeraw(&options); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_iflag &= ~(IXON | IXOFF | IXANY); options.c_oflag &= ~OPOST; cfsetispeed(&options, BAUDRATE); cfsetospeed(&options, BAUDRATE); tcsetattr(fd, TCSANOW, &options); // 初始化poll结构体 pfd.fd = fd; pfd.events = POLLIN; // 发送命令 write(fd, sendbuf, 3); while (1) { // 使用poll等待数据 if (poll(&pfd, 1, timeout) > 0) { int nread = read(fd, recvbuf, sizeof(recvbuf)); if (nread > 0) { printf("Received data: "); for (int i = 0; i < nread; i++) { printf("%02x ", recvbuf[i]); } printf("\n"); } else { printf("Read error: %s\n", strerror(errno)); } } else { printf("No data received in %d ms\n", timeout); } } close(fd); return 0;}【4】人脸识别图像处理下面是完成人脸识别处理的代码。#include "image_handle.h"#pragma execution_character_set("utf-8")//关闭线程void ImageHandle::close(){ run_flag=0; this->quit(); this->wait();}//线程执行函数void ImageHandle::run(){ QImage use_image; while(run_flag) { //如果没有图像可以处理 if(start_run==0) { //休眠100毫秒 msleep(100); continue; } //表示已经处理过 start_run=0; //表示开始处理图像 Handle_flag=1; //调用图像处理算法 对 image 的图像进行处理 //1. 人脸识别 opencv_face(m_image); //处理完毕之后 //将图像传出去给UI界面显示 emit HandleSend(m_image); //处理完毕 Handle_flag=0; }}//传入待处理的图片数据void ImageHandle::SetImage(QImage &image){ if(Handle_flag==0) { start_run=1; //表示有图像可以处理了 //保存待处理的原图像 m_image=image; }}void printMatInfo(const cv::Mat& mat){ QTextStream out(stdout); out << "Type: " << mat.type() << endl; out << "Channels: " << mat.channels() << endl; out << "Size: " << mat.size().width << "x" << mat.size().height << endl; out << "Depth: " << mat.depth() << endl; out << "Element Size: " << mat.elemSize() << " bytes" << endl; out << "Total Size: " << mat.total() * mat.elemSize() << " bytes" << endl;}bool saveMatToFile(const cv::Mat& mat, const std::string& filename){ // 将cv::Mat保存为图像文件 bool success = cv::imwrite(filename, mat); if (!success) { // 保存失败时输出错误信息 std::cerr << "Failed to save image: " << filename << std::endl; } return success;}// 绘制马赛克void drawMosaic(Mat& image, Rect roi) { // 将人脸区域缩小为一定比例,以增加马赛克效果 Rect smallRoi = roi; smallRoi.x += smallRoi.width * 0.1; smallRoi.y += smallRoi.height * 0.1; smallRoi.width -= smallRoi.width * 0.2; smallRoi.height -= smallRoi.height * 0.2; // 对缩小后的人脸区域进行马赛克处理 Mat mosaic = image(smallRoi); //可以调整数字,调整马赛克的像素大小 resize(mosaic, mosaic, Size(smallRoi.width / 20, smallRoi.height / 20), INTER_NEAREST); resize(mosaic, image(smallRoi), smallRoi.size(), 0, 0, INTER_NEAREST);}#include "widget.h"//人脸检测代码void ImageHandle::opencv_face(QImage qImage){ QTime time; time.start(); //(1)包含必要的头文件和命名空间: //(2)加载人脸检测模型 std::string cnn_file_path= OpenCV_CNN_MODEL_FILE_PATH; //CNN模型文件路径 std::string prototxt_path = cnn_file_path+"/deploy.prototxt.txt"; std::string model_path = cnn_file_path+"/res10_300x300_ssd_iter_140000_fp16.caffemodel"; cv::dnn::Net model = cv::dnn::readNetFromCaffe(prototxt_path, model_path); //(3)加载图片: //Mat frame = imread("D:\\1.png"); // 替换为你的图片路径 Mat frame = QImage_to_cvMat(qImage); if (frame.empty()) { ss_log_text("待识别的图片加载失败...\n"); // 处理图片加载失败的情况 return; } int h = frame.rows; int w = frame.cols; cv::Mat blob = cv::dnn::blobFromImage(frame, 1.0, cv::Size(300, 300), cv::Scalar(104.0, 177.0, 123.0)); //(4)进行人脸检测: model.setInput(blob); cv::Mat output = model.forward(); cv::Mat detectionMat(output.size[2], output.size[3], CV_32F, output.ptr<float>()); //(5)给每个检测到的人脸绘制马赛克: int face_number=0; for (int i = 0; i < detectionMat.rows; ++i) { float confidence = detectionMat.at<float>(i, 2); if (confidence > 0.5) { //记录人脸数量 face_number++; int start_x = static_cast<int>(detectionMat.at<float>(i, 3) * w); int start_y = static_cast<int>(detectionMat.at<float>(i, 4) * h); int end_x = static_cast<int>(detectionMat.at<float>(i, 5) * w); int end_y = static_cast<int>(detectionMat.at<float>(i, 6) * h); // 马赛克处理 cv::Rect roi(start_x, start_y, end_x - start_x, end_y - start_y); cv::Mat face_roi = frame(roi); cv::resize(face_roi, face_roi, cv::Size(), 0.05, 0.05, cv::INTER_LINEAR); cv::resize(face_roi, frame(roi), roi.size(), 0, 0, cv::INTER_NEAREST); // 绘制边框和文字 cv::rectangle(frame, cv::Point(start_x, start_y), cv::Point(end_x, end_y), cv::Scalar(255, 0, 0), 2); std::ostringstream ss; ss << confidence * 100 << "%"; cv::putText(frame, ss.str(), cv::Point(start_x, start_y - 5), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(255, 0, 0), 2); } } //传递出人脸数量 emit ss_face_number(face_number); // 在图像上显示识别消耗的时间 std::ostringstream time_ss; time_ss << "Time: " << time.elapsed() << " ms"; cv::putText(frame, time_ss.str(), cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(255, 0, 0), 2); //转为QImage QImage out_image=Mat_to_QImage(frame); ss_log_text(tr("耗时:%1 ms\n").arg(time.elapsed())); //qDebug()<<"子线程:"<<QThread::currentThread(); //保存结果 m_image=out_image.copy();}QImage convertToRGB888(const QImage& image){ if (image.format() == QImage::Format_RGB888) { return image; // Already in RGB888 format } QImage convertedImage = image.convertToFormat(QImage::Format_RGB888); return convertedImage;}//可以用。 OpenCV4.0已测试。//Mat ImageHandle::QImage_to_cvMat(QImage image){ cv::Mat mat; //qDebug() << image.format(); switch(image.format()) { case QImage::Format_ARGB32: case QImage::Format_RGB32: case QImage::Format_ARGB32_Premultiplied: mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine()); break; case QImage::Format_RGB888: mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine()); //opencv 3.x以及一下,颜色用 CV_BGR2RGB cv::cvtColor(mat, mat, COLOR_BGR2RGB); break; case QImage::Format_Indexed8: mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine()); break; } return mat;}QImage ImageHandle::Mat_to_QImage(Mat mat){#if 0 QImage image; // 检查矩阵是否有效 if (!mat.empty()) { // 创建QImage对象,并分配内存 image = QImage(mat.cols, mat.rows, QImage::Format_ARGB32); // 根据Mat的类型和通道数来设置Qt图像格式 switch (mat.type()) { case CV_8UC4: image = QImage(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_ARGB32); break; case CV_8UC3: image = QImage(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_RGB888); break; case CV_8UC1: image = QImage(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_Indexed8); break; } // 对象回收 if (image.format() != QImage::Format_RGB32) { image = image.convertToFormat(QImage::Format_RGB32); } }#else // Check if the image is valid if (mat.empty()) return QImage(); // Convert the image color space cv::Mat rgbMat; cv::cvtColor(mat, rgbMat, cv::COLOR_BGR2RGB); // Create the QImage QImage image(rgbMat.data, rgbMat.cols, rgbMat.rows, static_cast<int>(rgbMat.step), QImage::Format_RGB888);#endif return image.copy();}​六、总结本项目利用香橙派 AIpro开发了一个创新的健康监测解决方案,能够提升医院、疾病防控中心和发热门诊等关键场所的公共卫生管理水平。系统利用香橙派AIpro的强大计算能力,搭载Ubuntu 22.04操作系统,实现了高效的人脸识别与非接触式体温测量功能,显著增强了疾病早期预警和控制的能力。通过整体项目开发完成后,这块基于香橙派 AIpro的性能是完全满足了要求;运行了10几个小时, 整个板子不发烫,只是启动的时候风扇有明显噪声,正常进入系统之后,风扇的声音就正常,基本处于静音状态。 板子构造小巧,很容易集成,进行项目开发。
  • [问题求助] 华为云大屏读取流转的cvs文件,还是不能实时更新
    数据数据好像只能读取第一个,不能更新,预览后也没用
  • [问题求助] 华为云大屏读取csv文件为啥数值不动
    华为云大屏读取obs存储的csv文件为啥数值不动,好像只能读取第一行,请问有啥解决方法?
  • [问题求助] 应用使用AMQPS接收消息,为什么好多信息都接收不全
    应用使用AMQPS接收消息,为什么好多信息都接收不全,还有AMQP消息队列详情中订阅列表什么意思,我只用一个应用侧,为啥这么多客户端IDimport threadingimport timeimport signalimport sysimport socketimport jsonfrom proton import SSLDomainfrom proton.handlers import MessagingHandlerfrom proton.reactor import Container# 重连次数reconnectTimes = 0# 用于控制主线程的事件循环running = True# TCP/IP 服务器配置tcp_host = '10.16.47.108'tcp_port = 8080def current_time_millis():return str(int(round(time.time() * 1000)))class AmqpClient(MessagingHandler):def __init__(self, host, port, accessKey, accessCode, queueName, instanceId):super(AmqpClient, self).__init__()self.host = hostself.port = portself.accessKey = accessKeyself.accessCode = accessCodeself.queueName = queueNameself.instanceId = instanceIddef on_start(self, event):# 接入域名,请参见AMQP客户端接入说明文档。url = "amqps://%s:%s" % (self.host, self.port)timestamp = current_time_millis()userName = "accessKey=" + self.accessKey + "|timestamp=" + timestamp + "|instanceId=" + self.instanceIdpassWord = self.accessCode# 默认不校验服务端证书sslDomain = SSLDomain(SSLDomain.MODE_CLIENT)sslDomain.set_peer_authentication(SSLDomain.ANONYMOUS_PEER)self.conn = event.container.connect(url, user=userName, password=passWord, heartbeat=60, ssl_domain=sslDomain,reconnect=False)event.container.create_receiver(self.conn, source=self.queueName)def on_connection_opened(self, event):global reconnectTimesreconnectTimes = 0print("Connection established, remoteUrl: %s" % event.connection.hostname)def on_connection_closed(self, event):print("Connection closed: %s" % self)ReconnectThread("reconnectThread").start()def on_connection_error(self, event):print("Connection error: %s" % self)ReconnectThread("reconnectThread").start()def on_transport_error(self, event):if event.transport.condition:if event.transport.condition.info:print("%s: %s: %s" % (event.transport.condition.name, event.transport.condition.description,event.transport.condition.info))else:print("%s: %s" % (event.transport.condition.name, event.transport.condition.description))else:print("Unspecified transport error")ReconnectThread("reconnectThread").start()def on_message(self, event):message = event.messagecontent = message.bodyprint("Received message: content=%s" % content)# 解析消息内容try:data = json.loads(content)services = data.get('notify_data', {}).get('body', {}).get('services', [])for service in services:if service['service_id'] == 'GoodType':good_name = service['properties'].get('GoodName')good_id = service['properties'].get('GoodId')if good_name and good_id:send_tcp_message(good_name, good_id)except json.JSONDecodeError:print("Failed to decode JSON from message content")def send_tcp_message(good_name, good_id):message = {"ReportType": "Send","ReportCategory": "Good","GoodName": good_name,"GoodId": good_id}message_json = json.dumps(message)print(f"Prepared JSON message to send: {message_json}")try:with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:s.connect((tcp_host, tcp_port))s.sendall(message_json.encode('utf-8'))print(f"Sent TCP JSON message: {message_json}")except Exception as e:print(f"Failed to send TCP JSON message: {e}")class ReconnectThread(threading.Thread):def __init__(self, name):threading.Thread.__init__(self)self.name = namedef run(self):global reconnectTimesreconnectTimes += 1time.sleep(15 if reconnectTimes > 15 else reconnectTimes)Container(AmqpClient(amqpHost, amqpPort, amqpAccessKey, amqpAccessCode, amqpQueueName, instanceId)).run()def signal_handler(sig, frame):global runningprint('Exiting...')running = Falsesys.exit(0)# 以下参数配置请参考连接配置说明# AMQP接入域名amqpHost = "95e74068ba.st1.iotda-app.cn-north-4.myhuaweicloud.com"# AMQP接入端口amqpPort = 5671# 接入凭证键值amqpAccessKey = 'ZyjyH9Ax'# 接入凭证密钥amqpAccessCode = 'ghc4ScCaA7vqzgIZqErEezDsvPO5FCaC'# 订阅队列名称amqpQueueName = '12345678'# 实例Id,同一Region购买多个标准版实例时需要填设置该参数。instanceId = '5b6a80f0-75d5-4091-b28a-7809838c524c'# 捕捉SIGINT信号(通常是CTRL+C)signal.signal(signal.SIGINT, signal_handler)# 创建并运行容器container = Container(AmqpClient(amqpHost, amqpPort, amqpAccessKey, amqpAccessCode, amqpQueueName, instanceId))# 启动一个线程来运行AMQP客户端thread = threading.Thread(target=container.run)thread.start()# 保持主线程存活,等待接收消息while running:time.sleep(1)
  • [问题求助] 有没有WiFi模组AT命令用MQTT上华为云的教程或者文档
    有没有WiFi模组AT命令用MQTT上华为云的AT指令文档,小熊派带的WIFI模块能使用MQTT上华为云吗,一般是使用什么方式上云的
  • [问题求助] 在DevEco studio里通过npm下载了node.js的SDK无法使用
    新手学习,根据huaweicloud-sdk-nodejs-v3/README_CN.md at master · huaweicloud/huaweicloud-sdk-nodejs-v3 · GitHub 里提供的文档,出现如上问题,求解,
  • [问题求助] MQTT客户端模拟硬件设备连接登录失败,在用at指令中的域名接入指令一直报错
    之前创建好设备后模拟MQTT客户端登陆能够正确登陆上,显示在线状态,现在不行了,查了异常运行日志显示这个问题已经尝试过换成域名解析IP还是失败
总条数:797 到第
上滑加载中