• [技术干货] BearPi-HM_Nano(Hi3861)对接华为云IOT过程介绍
    一、前言BearPi-HM Nano开发板是一款功能齐全、易于使用、专门为HarmonyOS设计的开发板,适合用于开发基于HarmonyOS的应用程序。板载NFC电路及标准的E53接口,标准的E53接口可扩展智能加湿器、智能台灯、智能安防、智能烟感等案例。 主控MCU是Hi3861,Hi3861芯片是一款高度集成的2.4GHz WLAN SoC芯片,专门为物联网应用而设计。集成了高性能的CPU、2.4GHz无线基带和射频前端以及多种外设接口,如UART、SPI、I2C、GPIO等,可以满足各种物联网设备的需求。这篇文章演示在BearPi-HM_Nano开发板上使用MQTT协议连接华为IoT平台,使用的是E53_IA1 智慧农业扩展板与 BearPi-HM_Nano 开发板实现。下面是BearPi-HM Nano开发板的功能图介绍:二、开通华为云物联网平台在进行开发板环境搭建之前,先开通物联网平台,创建一个产品方便设备上云测试。华为云官网: cid:link_14打开官网,搜索物联网,就能快速找到 设备接入IoTDA。2.1 物联网平台介绍华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。2.2 开通物联网服务地址: cid:link_12进来默认会提示开通标准版,在2023的1月1号年之后没有基础版了。开通之后,点击总览,查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。总结:端口号: 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>MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口比较合适。 接下来的ESP8266就采用1883端口连接华为云物联网平台。2.3 创建产品(1)创建产品点击产品页,再点击左上角创建产品。(2)填写产品信息根据自己产品名字填写。(3)产品创建成功(4)添加自定义模型产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。先点击自定义模型。再创建一个服务ID。接着点击新增属性。2.4 添加设备产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。(1)注册设备(2)根据自己的设备填写(3)保存设备信息创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。(4) 设备创建完成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_10业务流程:(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_10对于设备而言,一般会订阅平台下发消息给设备 这个主题。设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。以当前设备为例,最终订阅主题的格式如下:$oc/devices/{device_id}/sys/messages/down​最终的格式:$oc/devices/6419627e40773741f9fbdac7_dev1/sys/messages/down(4)主题发布格式对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。这个操作称为:属性上报。帮助文档地址:cid:link_8根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:发布的主题格式:$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}}]}2.6 MQTT三元组MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。接下来介绍,华为云平台的MQTT三元组参数如何得到。(1)MQTT服务器地址要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。帮助文档地址:cid:link_7MQTT协议的端口支持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鉴权三元组: cid:link_11打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。下面是打开的页面:填入设备的信息: (上面两行就是设备创建完成之后保存得到的)直接得到三元组信息。得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。ClientId 6419627e40773741f9fbdac7_dev1_0_0_2023032108Username 6419627e40773741f9fbdac7_dev1Password 861ac9e6a579d36888b2aaf97714be7af6c77017b017162884592bd68b086a6e3.7 模拟设备登录测试经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。(1)填入登录信息打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。(2)打开网页查看完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。点击详情页面,可以看到上传的数据。到此,云平台的部署已经完成,设备已经可以正常上传数据了。三、BearPi-HM_Nano开发板环境搭建3.1 环境介绍在嵌入式开发中,很多开发者习惯于使用Windows进行代码编辑、编译、调试下载。比如,使用keil、IAR、或者Visual Studio Code等IDE。但当前阶段,OpenHarmony源码还不支持在Windows环境下进行编译,如Hi3861、Hi3516系列开发板。需要在Linux系统下进行,建议使用Ubuntu的编译环境对源码进行编译。搭建Ubuntu系统的开发环境也相对比较麻烦的,需要安装很多的库,可能遇到网络问题或者Ubuntu系统本身仓库源的问题导致下载不下来,等等的各种问题。对Linux不熟悉的初学者可能就有点头大。在官方的教程里提供了两种方式快速搭建环境。(1)自己安装虚拟机、安装Ubuntu系统、然后根据教程进行安装各种库,完成编译环境的搭建。(2)自己安装虚拟机,下载官方的镜像,解压直接打开运行。--这种就是最简单的,相当于把别人搭建好环境的电脑直接搬过来用,不用管环境搭建问题了,是最方便的。教程地址:cid:link_03.2 下载所需镜像下载官方提供镜像下载地址(百度云):https://pan.baidu.com/s/1T0Tcl3y48C1p5L6y-6HJNg 提取码:eusr下载HiBurn下载地址(百度云):https://pan.baidu.com/s/1bp2ypAfH2HaNPTY2KwEhEA 提取码:1234下载并安装虚拟机VMware Workstation下载地址:https://www.vmware.com/products/workstation-player/workstation-player-evaluation.html下载并安装MobaXterm工具下载地址:https://en.softonic.com/download/moba/windows/post-download下载并安装RaiDrive工具下载地址:https://forspeed.rbread05.cn/down/newdown/5/28/RaiDrive.rar下载并安装开发板USB驱动下载地址:http://www.wch.cn/search?q=ch340g&t=downloads下载并安装VS Code下载地址:https://code.visualstudio.com3.3 运行Ubuntu系统(1)要先安装虚拟机VMware Workstation,安装最新版即可。(2)解压百度云下载的HarmonyOSUbuntu18.4镜像OVF.zip文件到某个目录。(3)打开VMware Workstation工具(4)选择第2步解压的Ubuntu18.4镜像OVF文件夹中,点击打开(5)导入镜像到本地磁盘(选择一个磁盘空间大小≥ 10G的盘),点击导入。(6)点击开启此虚拟机,来开启虚拟机电源(7)此时虚拟机进入登录界面,点击HarmonyOS(8)输入密码:bearpi,然后点击登录(9)进入桌面后,点击桌面空白处右键,点击打开终端(E)在终端中输入ifconfig,然后点击回车,除lo外,另外一个就是你的网卡信息,记录你获取到的IP地址。(11)最小化VMware Workstation,回到Windows桌面上。(12)附加:如果连不上网络 在VMware Workstation中,点击虚拟机>> 设置然后在网络适配器中,改成NAT 模式,点击确定然后再恢复步骤9。3.4 在Windows上远程连接服务器(1)打开MobaXterm工具,并依次点击:Session,SSH 按钮。(2)输入连接信息,远程地址,并点击OK(3)输入账号:bearpi,点击回车(4)输入密码:bearpi,注意,输入密码的时候屏幕不会显示,输完之后点击回车(5)在弹出的界面上,点击Yes保存账号信息,以免下次输入3.5 把ubuntu文件远程映射到Windows上(1)安装RaiDrive软件根据提示,点击下一步安装,全部选项默认。(2)切换为中文语言(3)添加链接信息取消勾选只读SFTP://__ 输入前面获取到的IP地址账户:账号和密码皆为 bearpi其他默认点击确定(4)查看本地映射的ubuntu文件路径3.6 在ubuntu获取源码(1)鼠标焦点移到MobaXterm(2)在MobaXterm中输入:cd /home/bearpi然后回车(3)在MobaXterm中输入:mkdir project && cd project然后回车(4)在MobaXterm中输入:hpm init -t default然后回车(5)在MobaXterm中输入:hpm i @bearpi/bearpi_hm_nano然后回车,等待1-3分钟(根据不同网速)(6)当屏幕中出现Installed.意味着代码获取完成源码也可以直接在Gitee上直接下载。通过git clone单个代码仓库,进入代码仓库主页:cid:link_13,选择需要克隆的代码仓库,执行命令,如:git clone cid:link_13.git -b master执行以下命令编译源码python build.py BearPi-HM_Nano3.7 编译代码(1)在MobaXterm中输入:hpm dist然后回车,等待直到屏幕出现:BUILD SUCCESS字样,说明编译成功。(2)查看编译出的固件位置当编译完后,在Windows中可以直接查看到最终编译的固件,具体路径在:3.8 连接开发板(1)通过TypeC数据线,把电脑与BearPi-HM Nano连接。(2)安装CH340驱动。下载地址:http://www.wch.cn/search?q=ch340g&t=downloads(3)如果虚拟机把USB给抢占进去了,可以先挂起虚拟机,重新拔插USB。(4)查看开发板的串口3.9 下载程序(1)在Windows打开Hiburn工具,并点击Refresh,在COM中选择第八-4看到的COM号然后点击Setting,并选择 Com settings,(2)在Com settings中设置Baud为:921600,点击确定(3)点击 Hiburn工具中的Select file按钮,选中:Hi3861_wifiiot_app_allinone.bin 文件。(4)点击Auto burn复选框,然后点击Connect此时Connect按钮变成Disconnect,等待下载(5)复位开发板RESET按键,开始下载程序(6)直到出现Execution Successful字样,程序下载完成。(7)下载完后,点击Disconnect按钮,便于后面调测使用。3.10 MobaXterm 查看串口打印日志(1)打开MobaXterm,(2)点击:Session、Serial按钮(3)设置Seral port为 Hiburn 同一个串口(4)设置Speed为 115200(5)点击OK(6)复位开发板,此时COM打印出对应日志信息四、连接华为云IOT平台4.1 MQTT相关函数功能介绍(1)设置设备信息void device_info_init(char *client_id, char * username, char *password);设置设备信息,在调用oc_mqtt_init()前要先设置设备信息(2)华为IoT平台 初始化int oc_mqtt_init(void);华为IoT平台初始化函数,需要在使用 华为IoT平台 功能前调用。(3)设置命令响应函数void oc_set_cmd_rsp_cb(void(*cmd_rsp_cb)(uint8_t *recv_data, size_t recv_size, uint8_t **resp_data, size_t *resp_size));设置命令响应回调函数。(4)设备消息上报int oc_mqtt_profile_msgup(char *deviceid,oc_mqtt_profile_msgup_t *payload);是指设备无法按照产品模型中定义的属性格式进行数据上报时,可调用此接口将设备的自定义数据上报给平台,平台将设备上报的消息转发给应用服务器或华为云其他云服务上进行存储和处理。(5)设备上报属性数据int oc_mqtt_profile_propertyreport(char *deviceid,oc_mqtt_profile_service_t *payload);用于设备按产品模型中定义的格式将属性数据上报给平台。(6)网关批量上报属性数据int oc_mqtt_profile_gwpropertyreport(char *deviceid,oc_mqtt_profile_device_t *payload);用于批量设备上报属性数据给平台。网关设备可以用此接口同时上报多个子设备的属性数据。(7)属性设置的响应结果int oc_mqtt_profile_propertysetresp(char *deviceid,oc_mqtt_profile_propertysetresp_t *payload);(8)属性查询响应结果int oc_mqtt_profile_propertygetresp(char *deviceid,oc_mqtt_profile_propertygetresp_t *payload);(9)将命令的执行结果返回给平台int oc_mqtt_profile_cmdresp(char *deviceid,oc_mqtt_profile_cmdresp_t *payload); 平台下发命令后,需要设备及时将命令的执行结果返回给平台,如果设备没回响应,平台会认为命令执行超时。4.2 连接平台在连接平台需要在device_info_init函数里填入CLIENT_ID、USERNAME、PASSWORD。这个信息前面第2章节已经介绍了。WifiConnect("da_666","12345678");device_info_init(CLIENT_ID,USERNAME,PASSWORD);oc_mqtt_init();oc_set_cmd_rsp_cb(oc_cmd_rsp_cb);修改iot_cloud_oc_sample.c文件。4.3 上传数据当需要上传数据时,需要先拼装数据,然后通过oc_mqtt_profile_propertyreport上报数据。代码示例如下:static void deal_report_msg(report_t *report){ oc_mqtt_profile_service_t service; oc_mqtt_profile_kv_t temperature; oc_mqtt_profile_kv_t humidity; oc_mqtt_profile_kv_t luminance; oc_mqtt_profile_kv_t led; oc_mqtt_profile_kv_t motor; service.event_time = NULL; service.service_id = "Agriculture"; service.service_property = &temperature; service.nxt = NULL; temperature.key = "Temperature"; temperature.value = &report->temp; temperature.type = EN_OC_MQTT_PROFILE_VALUE_INT; temperature.nxt = &humidity; humidity.key = "Humidity"; humidity.value = &report->hum; humidity.type = EN_OC_MQTT_PROFILE_VALUE_INT; humidity.nxt = &luminance; luminance.key = "Luminance"; luminance.value = &report->lum; luminance.type = EN_OC_MQTT_PROFILE_VALUE_INT; luminance.nxt = &led; led.key = "LightStatus"; led.value = g_app_cb.led?"ON":"OFF"; led.type = EN_OC_MQTT_PROFILE_VALUE_STRING; led.nxt = &motor; motor.key = "MotorStatus"; motor.value = g_app_cb.motor?"ON":"OFF"; motor.type = EN_OC_MQTT_PROFILE_VALUE_STRING; motor.nxt = NULL; oc_mqtt_profile_propertyreport(USERNAME,&service); return;}4.4 云平台下发的命令接收华为IoT平台支持下发命令,命令是用户自定义的。接收到命令后会将命令数据发送到队列中,task_main_entry函数中读取队列数据并调用deal_cmd_msg函数进行处理,代码示例如下:void oc_cmd_rsp_cb(uint8_t *recv_data, size_t recv_size, uint8_t **resp_data, size_t *resp_size){ app_msg_t *app_msg; int ret = 0; app_msg = malloc(sizeof(app_msg_t)); app_msg->msg_type = en_msg_cmd; app_msg->msg.cmd.payload = (char *)recv_data; printf("recv data is %.*s\n", recv_size, recv_data); ret = osMessageQueuePut(mid_MsgQueue,&app_msg,0U, 0U); if(ret != 0){ free(recv_data); } *resp_data = NULL; *resp_size = 0;}static int task_main_entry( void ){ app_msg_t *app_msg; WifiConnect("TP-LINK_65A8","0987654321"); device_info_init(CLIENT_ID,USERNAME,PASSWORD); oc_mqtt_init(); oc_set_cmd_rsp_cb(oc_cmd_rsp_cb); while(1){ app_msg = NULL; (void)osMessageQueueGet(mid_MsgQueue,(void **)&app_msg,NULL, 0U); if(NULL != app_msg){ switch(app_msg->msg_type){ case en_msg_cmd: deal_cmd_msg(&app_msg->msg.cmd); break; case en_msg_report: deal_report_msg(&app_msg->msg.report); break; default: break; } free(app_msg); } } return 0;}static void deal_cmd_msg(cmd_t *cmd){ cJSON *obj_root; cJSON *obj_cmdname; cJSON *obj_paras; cJSON *obj_para; int cmdret = 1; oc_mqtt_profile_cmdresp_t cmdresp; obj_root = cJSON_Parse(cmd->payload); if(NULL == obj_root){ goto EXIT_JSONPARSE; } obj_cmdname = cJSON_GetObjectItem(obj_root,"command_name"); if(NULL == obj_cmdname){ goto EXIT_CMDOBJ; } if(0 == strcmp(cJSON_GetStringValue(obj_cmdname),"Agriculture_Control_light")){ obj_paras = cJSON_GetObjectItem(obj_root,"paras"); if(NULL == obj_paras){ goto EXIT_OBJPARAS; } obj_para = cJSON_GetObjectItem(obj_paras,"Light"); if(NULL == obj_para){ goto EXIT_OBJPARA; } ///< operate the LED here if(0 == strcmp(cJSON_GetStringValue(obj_para),"ON")){ g_app_cb.led = 1; Light_StatusSet(ON); printf("Light On!"); } else{ g_app_cb.led = 0; Light_StatusSet(OFF); printf("Light Off!"); } cmdret = 0; } else if(0 == strcmp(cJSON_GetStringValue(obj_cmdname),"Agriculture_Control_Motor")){ obj_paras = cJSON_GetObjectItem(obj_root,"Paras"); if(NULL == obj_paras){ goto EXIT_OBJPARAS; } obj_para = cJSON_GetObjectItem(obj_paras,"Motor"); if(NULL == obj_para){ goto EXIT_OBJPARA; } ///< operate the Motor here if(0 == strcmp(cJSON_GetStringValue(obj_para),"ON")){ g_app_cb.motor = 1; Motor_StatusSet(ON); printf("Motor On!"); } else{ g_app_cb.motor = 0; Motor_StatusSet(OFF); printf("Motor Off!"); } cmdret = 0; }EXIT_OBJPARA:EXIT_OBJPARAS:EXIT_CMDOBJ: cJSON_Delete(obj_root);EXIT_JSONPARSE: ///< do the response cmdresp.paras = NULL; cmdresp.request_id = cmd->request_id; cmdresp.ret_code = cmdret; cmdresp.ret_name = NULL; (void)oc_mqtt_profile_cmdresp(NULL,&cmdresp); return;}4.6 平台地址修改据自己使用的平台版本修改oc_mqtt.h中第47行附近的平台对接地址。这个根据自己的产品确定,2023年1月1日新开通的都是标准版了。#ifndef LITEOS_LAB_IOT_LINK_OC_OC_MQTT_OC_MQTT_PROFILE_OC_MQTT_PROFILE_H_#define LITEOS_LAB_IOT_LINK_OC_OC_MQTT_OC_MQTT_PROFILE_OC_MQTT_PROFILE_H_#include <stdint.h>#define OC_SERVER_URL "tcp://183.230.40.39:6002"// #define OC_SERVER_IP "121.36.42.100" //基础版平台对接地址#define OC_SERVER_IP "117.78.5.125" //标准版平台对接地址#define OC_SERVER_PORT 1883#define OC_CLIENT_ID_LEN 128#define OC_USERNAME_LEN 128#define OC_PASSWORD_LEN 1284.7 编译、烧写代码代码修改之后编译烧录,按下开发板的RESET按键,通过串口助手查看日志,会打印温湿度及光照强度信息,打开云平台的后台可以看到设备已经在线。
  • [问题求助] 华为云IOT平台,如何主动检测设备的安全威胁?
    华为云IOT平台,如何主动检测设备的安全威胁?比如设备被植入了木马,信息被串改,华为云应对如何应对?
  • [问题求助] 设备的物理模型和产品模型,这两者有什么区别?
    设备的物理模型和产品模型,这两者有什么区别?
  • [问题求助] 对设备分组,一般是用来做什么?
    对设备分组,一般是用来做什么?
  • [问题求助] 接入华为云Iot平台的设备支持哪几种鉴权协议?
    接入华为云Iot平台的设备支持哪几种鉴权协议?
  • [问题求助] 设备接入华为云Iot平台的SDK在哪里下载?
    设备接入华为云Iot平台的SDK在哪里下载?
  • [问题求助] 物联网平台既然能支持终端设备直接接入,为什么还要多一层网关?网关有什么用?
    物联网平台既然能支持终端设备直接接入,为什么还要多一层网关?网关有什么用?
  • [问题求助] 使用esp01s发送数据到华为云,数据上不去,串口也打印错误
    可以用at指令通过串口上传上数据,一用代码就传不上去
  • [问题求助] AT指令正确但是云端无数据显示
    使用ESP01S的AT指令上传数据,执行OK但是云端没有物模型数据
  • [技术干货] 基于STM32+华为云IOT设计的智能车库管理系统
    一、项目介绍随着城市化进程和汽车拥有率的不断提高,停车难的问题也日益凸显。在城市中,停车场是一个非常重要的基础设施,但是传统的停车场管理方式存在很多问题,比如车位难以管理、停车费用不透明等。为了解决这些问题,智能车库管理系统应运而生。本项目基于STM32+华为云IOT设计的智能车库管理系统,通过红外感应传感器检测停车位的占用情况,将数据上传到华为云物联网平台,并通过微信小程序展示车库的实时停车位情况,包括总停车数量、当前剩余空位数量和车位的编号,并在二维立体图中标注出空闲停车位,方便用户快速找到空闲停车位进行停车。本系统还包括车牌识别自动计费部分,通过车牌识别技术识别车辆进出停车场的时间,并自动计算停车费用,提高了停车场的管理效率和用户体验。本项目的实现将大大提高停车场的管理效率和客户体验,为城市交通管理和用户出行提供了更加便捷和高效的解决方案。二、硬件选型在本项目中,需要选择合适的硬件来实现智能车库管理系统。根据项目需求,需要选取能够完成以下功能的硬件:【1】检测停车位是否有车辆存在的传感器模块【2】能够将传感器数据上传到云平台的通信模块【3】控制整个系统运行的主控芯片【4】提供电源支持的电源模块基于以上需求,最终采用以下硬件:【1】传感器模块:红外传感器模块,例如红外障碍传感器模块,能够检测车辆是否在停车位上。【2】通信模块:EC20-4G模块,支持 4G LTE 网络,能够将传感器数据上传到华为云物联网平台。【3】主控芯片:STM32F103ZET6,具有较高的性能和稳定性,能够满足系统的实时性需求。【4】电源模块:直流稳压电源模块,能够提供稳定的电源支持,保证系统的正常运行。三、系统设计思路【1】硬件设计:使用STM32F103ZET6作为主控芯片,连接红外感应传感器进行车辆检测。配置EC20-4G模块与STM32进行通信,通过AT指令或相关协议将检测到的数据上传到华为云物联网平台。【2】云平台配置:在华为云物联网平台创建设备实例,将EC20-4G模块作为设备接入。配置数据流转规则,使得上传的数据能够正确地传输到云平台并且存储。【3】微信小程序开发:使用微信小程序开发框架,通过调用华为云物联网平台提供的API接口,获取上传的数据。对返回的数据进行解析和处理,计算当前车库总停车数量和剩余空位数量。根据数据绘制二维立体图,并标注出空闲停车位的位置和编号。实现用户界面显示,展示当前车库状态和方便用户找到空闲停车位。整体流程如下: 红外感应传感器通过STM32进行车辆检测,检测结果实时上传到华为云物联网平台。微信小程序通过API接口获取上传的数据,并进行分析和处理,计算出车库总停车数量和剩余空位数量。 然后,在界面上绘制二维立体图并标注空闲停车位。用户可以通过微信小程序查看当前车库的状态,从而快速找到空闲停车位进行停车操作。四、华为云物联网平台部署华为云官网: cid:link_9打开官网,搜索物联网,就能快速找到 设备接入IoTDA。4.1 物联网平台介绍华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。4.2 开通物联网服务地址: cid:link_8点击总览,查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。总结:端口号: MQTT (1883)| MQTTS (8883) 接入地址: a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com根据域名地址得到IP地址信息:Microsoft Windows [版本 10.0.19045.2965](c) Microsoft Corporation。保留所有权利。​C:\Users\11266>ping a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com​正在 Ping a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com [121.36.42.100] 具有 32 字节的数据:来自 121.36.42.100 的回复: 字节=32 时间=38ms TTL=94来自 121.36.42.100 的回复: 字节=32 时间=37ms TTL=94来自 121.36.42.100 的回复: 字节=32 时间=38ms TTL=94来自 121.36.42.100 的回复: 字节=32 时间=36ms TTL=94​121.36.42.100 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),往返行程的估计时间(以毫秒为单位): 最短 = 36ms,最长 = 38ms,平均 = 37ms​C:\Users\11266>​MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口比较合适。 接下来的ESP8266就采用1883端口连接华为云物联网平台。#4.3 创建产品(1)创建产品点击产品页,再点击左上角创建产品。(2)填写产品信息根据自己产品名字填写。(3)产品创建成功(4)添加自定义模型产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。这个模型就是定义自己设备接下来需要向服务器上传那些数据类型。根据自己的数据类型进行编写。先点击自定义模型。再创建一个服务ID。接着点击新增属性。4.4 添加设备产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。(1)注册设备(2)根据自己的设备填写(3)保存设备信息创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。(4)设备创建完成可以点击设备进入到设备详情页面。4.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/6419627e40773741f9fbdac7_dev1/sys/messages/down(4)主题发布格式对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。这个操作称为:属性上报。帮助文档地址:cid:link_3根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:发布的主题格式:$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}}]}4.6 MQTT三元组MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。接下来介绍,华为云平台的MQTT三元组参数如何得到。(1)MQTT服务器地址要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。帮助文档地址:cid:link_2MQTT协议的端口支持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鉴权三元组: cid:link_7打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。下面是打开的页面:填入设备的信息: (上面两行就是设备创建完成之后保存得到的)得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。ClientId 6419627e40773741f9fbdac7_dev1_0_0_2023032108Username 6419627e40773741f9fbdac7_dev1Password 861ac9e6a579d36888b2aaf97714be7af6c77017b017162884592bd68b086a6e4.7 模拟设备登录测试经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。(1)填入登录信息打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。(2)打开网页查看完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。点击详情页面,可以看到上传的数据。到此,云平台的部署已经完成,设备已经可以正常上传数据了。五、代码设计5.1 EC20-4G模块AT指令介绍EC20模块是一种常用的无线通信模块,支持MQTT协议。下面MQTT相关的AT指令:【1】AT+QMTCONN:用于建立与MQTT服务器的连接。功能:通过指定MQTT服务器的地址、端口、客户端ID、用户名和密码等参数,建立与MQTT服务器的连接。【2】AT+QMTDISC:用于断开与MQTT服务器的连接。功能:断开与MQTT服务器的连接。【3】AT+QMTPUB:用于发布MQTT消息。功能:指定MQTT主题和消息内容,将消息发布到MQTT服务器。【4】AT+QMTSUB:用于订阅MQTT主题。功能:订阅指定的MQTT主题,接收该主题下的消息。【5】AT+QMTUNS:用于取消订阅MQTT主题。功能:取消对指定MQTT主题的订阅。【6】AT+QMTRECV:用于接收MQTT消息。功能:接收从MQTT服务器发送的消息。这些是EC20模块MQTT协议相关的AT指令。使用这些指令,可以在EC20模块上实现MQTT设备的登录、主题订阅、主题发布以及消息接收等功能。5.2 EC20连接IOT平台以下是使用STM32和EC20通过MQTT协议连接物联网平台并实现主题订阅和发布的实现代码:#include "stm32f10x.h"#include "stdio.h"#include "string.h"​// 定义串口波特率#define BAUD_RATE 115200​// 定义UART接收缓冲区大小#define UART_RX_BUFFER_SIZE 256​// 定义MQTT服务器地址和端口号#define MQTT_SERVER_ADDRESS "mqtt.example.com"#define MQTT_SERVER_PORT 1883​// 定义MQTT客户端ID#define MQTT_CLIENT_ID "example_client"​// 定义MQTT订阅的主题#define MQTT_SUB_TOPIC "example_topic"​// 定义MQTT发布的主题#define MQTT_PUB_TOPIC "example_topic"​// 定义UART接收缓冲区和索引char uartRxBuffer[UART_RX_BUFFER_SIZE];volatile uint16_t uartRxBufferIndex = 0;​// UART中断处理函数void USART1_IRQHandler(void){ if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { // 读取接收到的数据 char data = USART_ReceiveData(USART1);​ // 将数据存入接收缓冲区 uartRxBuffer[uartRxBufferIndex] = data; uartRxBufferIndex++;​ // 处理接收到的数据 // 这里可以根据需要进行相关操作,例如解析MQTT消息等​ // 清除接收中断标志位 USART_ClearITPendingBit(USART1, USART_IT_RXNE); }}​// 初始化UART1配置void UART1_Config(void){ USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;​ // 使能USART1和GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);​ // 配置USART1引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // TX GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure);​ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // RX GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure);​ // 配置USART1 USART_InitStructure.USART_BaudRate = BAUD_RATE; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure);​ // 配置USART1中断 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);​ // 使能USART1接收中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);​ // 使能USART1 USART_Cmd(USART1, ENABLE);}​// 向UART1发送数据void UART1_SendData(char *data){ while (*data) { // 发送数据 USART_SendData(USART1, *data++); // 等待发送完成 while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); }}​// 连接MQTT服务器void MQTT_Connect(void){ // 构造MQTT CONNECT报文 char connectPacket[256]; sprintf(connectPacket, "\x10\x12\x00\x04MQTT\x04\x02\x00\x3C\x00%s", MQTT_CLIENT_ID);​ // 发送MQTT CONNECT报文 UART1_SendData(connectPacket);}​// 订阅MQTT主题void MQTT_Subscribe(void){ // 构造MQTT SUBSCRIBE报文 char subscribePacket[256]; sprintf(subscribePacket, "\x82\x0F\x00\x01\x00\x0C"MQTT_SUB_TOPIC"\x00\x00");​ // 发送MQTT SUBSCRIBE报文 UART1_SendData(subscribePacket);}​// 发布MQTT消息void MQTT_Publish(char *message){ // 构造MQTT PUBLISH报文 char publishPacket[256]; sprintf(publishPacket, "\x30\x10\x00\x0D"MQTT_PUB_TOPIC"%s", message);​ // 发送MQTT PUBLISH报文 UART1_SendData(publishPacket);}​int main(void){ // 初始化UART1配置 UART1_Config(); // 连接MQTT服务器 MQTT_Connect(); // 订阅MQTT主题 MQTT_Subscribe(); while (1) { // 处理其他任务 // 发布MQTT消息 MQTT_Publish("Hello, world!"); // 等待一段时间 delay_ms(1000); }}在代码中,通过UART1与EC20进行通信,并实现了MQTT的连接、订阅和发布功能。UART1_SendData函数用于向UART1发送数据,MQTT_Connect函数用于连接MQTT服务器,MQTT_Subscribe函数用于订阅MQTT主题,MQTT_Publish函数用于发布MQTT消息。5.3 获取设备影子数据(API接口)帮助文档:cid:link_4设备影子介绍:设备影子是一个用于存储和检索设备当前状态信息的JSON文档。每个设备有且只有一个设备影子,由设备ID唯一标识设备影子仅保存最近一次设备的上报数据和预期数据无论该设备是否在线,都可以通过该影子获取和设置设备的属性简单来说:设备影子就是保存,设备最新上传的一次数据。我们设计的软件里,如果想要获取设备的最新状态信息,就采用设备影子接口。如果对接口不熟悉,可以先进行在线调试:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow在线调试接口,可以请求影子接口,了解请求,与返回的数据格式。设备影子接口返回的数据如下:{ "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 } ]}5.4 修改设备属性(API接口)地址: cid:link_5接口说明设备的产品模型中定义了物联网平台可向设备下发的属性,应用服务器可调用此接口向指定设备下发属性。平台负责将属性以同步方式发送给设备,并将设备执行属性结果同步返回。修改设备属性的接口,可以让服务器给设备下发指令,如果需要控制设备。在线调试地址:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=UpdateProperties修改设备属性是属于同步命令,需要设备在线才可以进行调试,先使用MQTT客户端登录服务器,模拟设备上线。然后进行调试,测试数据远程下发给设备。【1】利用MQTT客户端先登录设备 (这是同步命令,必须在线才能调试)【2】点击调试{"services":{"temp_max":100}}【4】可以看到,MQTT客户端软件上已经收到了服务器下发的消息由于是同步命令,服务器必须要收到设备的响应才能顺利完成一个流程,设备响应了服务器才能确定数据下发成功。六、总结基于STM32和华为云IOT的智能车库管理系统可以实现停车位智能展示功能,方便用户快速找到空闲停车位。系统的核心部分是主控芯片STM32F103ZET6和红外传感器,通过红外传感器检测停车位是否有车辆存在,并实时将检测数据上传到华为云物联网平台。在物联网平台上,可以创建一个设备模型,包括车库总停车数量、当前剩余空位数量和车位的编号等属性。每个停车位对应一个设备,通过红外传感器的检测结果更新对应停车位的状态。EC20-4G模块负责将这些数据发送到华为云物联网平台,实现与云端的通信。微信小程序作为用户界面,通过调用华为云物联网平台提供的接口,获取设备上传的数据并进行处理分析,在界面上显示当前车库总停车数量、当前剩余空位数量和车位的编号。可以使用二维立体图的形式,将停车位的状态进行展示,标注出空闲的停车位,方便用户快速找到空位进行停车。通过红外传感器和EC20模块实时上传数据到华为云物联网平台,微信小程序通过调用接口获取数据并展示在界面上,实现停车位智能展示功能。这种系统可以提高停车场的利用率,提供用户友好的体验,同时也为停车场管理者提供了实时监控和数据分析的便利。
  • [问题求助] IOT平台与传统的SCADA系统有何区别
    IOT平台与传统的SCADA系统有何区别
  • [问题求助] 企业已经自建设备监控系统,是否还需要使用物联网平台IOT,物联网平台的优势在哪,与自建系统又有何区别?
    企业已经自建设备监控系统,是否还需要使用物联网平台IOT,物联网平台的优势在哪,与自建系统又有何区别?
  • [问题求助] iotda中的设备与子设备的关系是怎样的,为什么需要子设备,设备为什么不能直接通过类型进行区分
    iotda中的设备与子设备的关系是怎样的,为什么需要子设备,设备为什么不能直接通过类型进行区分
  • [问题求助] iot平台如何接入监控摄像头,并显示图像,是否有指导案例?
    iot平台如何接入监控摄像头,并显示图像,是否有指导案例?
  • [技术干货] 基于单片机设计的太阳能跟踪器
    一、前言随着对可再生能源的需求不断增长,太阳能作为一种清洁、可持续的能源形式,受到越来越多的关注和应用。太阳能光板通常固定在一个固定的角度上,这限制了它们对太阳光的接收效率。为了充分利用太阳能资源,提高太阳能光板的收集效率,需要设计一个能够自动跟踪太阳光的系统。本项目采用基于单片机的设计方案,主控芯片选择STC89C52。在太阳能光板的四个角上,安装了四个光敏电阻,它们用于检测四个方向太阳光的最强位置。每个光敏电阻通过PCF8591模块与主控芯片相连,利用模数转换器(ADC)采集各个通道的数据值。通过对四个光敏传感器采集到的数据进行处理和比较,主控芯片能够确定太阳光的最强位置所在。然后,通过控制两个28BYJ-48-5V步进电机的运动,太阳能光板可以实现左右和上下方向的旋转。通过调整太阳能光板的倾斜角度,使其与太阳光保持垂直,以获得最大的太阳能收集效率。该太阳能跟踪器的设计旨在实现自动化的太阳光追踪,以提高太阳能光板的能源收集效率。通过使用光敏电阻、ADC转换和步进电机控制等技术手段,系统能够准确地确定太阳光的位置,并自动调整太阳能光板的朝向。这将大大提高太阳能系统的能源输出,并为可再生能源的利用做出贡献。二、系统设计思路2.1 硬件选型【1】主控芯片:STC89C52 STC89C52是一款高性价比的单片机,具有丰富的外设和强大的计算能力。采用基于MCS-51内核的8位单片机架构,拥有存储容量大(8KB Flash和256B RAM)和丰富的IO口(32个),适合控制太阳能跟踪器系统的各种功能。【2】光敏电阻:选择具有高灵敏度和较小尺寸的光敏电阻,并根据光照条件进行选择。通过与PCF8591模块连接,可以将光敏电阻的电阻值变化转换为相应的模拟电压信号。【3】ADC模块:PCF8591 PCF8591是一款常用的4通道12位ADC模块,适用于将模拟信号转换为数字信号。通过连接4个光敏电阻到PCF8591的4个输入通道上,可以实现数据的采集和转换。【4】步进电机:28BYJ-48-5V 28BYJ-48-5V步进电机是一个小型、低功耗的步进电机,适用于低速应用。使用两个步进电机可以控制太阳能光板在水平和垂直方向上的旋转,为太阳能跟踪器提供多个方向的调整。2.2 设计思路【1】硬件连接:根据项目需求,将STC89C52主控芯片与PCF8591模块、ULN2003驱动模块、28BYJ-48-5V步进电机、光敏电阻等进行正确的引脚连接。【2】初始化设置:在主函数开始部分,进行必要的初始化设置,例如设置I/O口方向、定义引脚连接、初始化I2C总线等。【3】光敏电阻采集:通过PCF8591模块采集4个光敏电阻的数据。使用I2C通信协议,向PCF8591模块发送控制字节,选择光敏电阻通道,并通过ADC转换获取光敏电阻的数值。将采集到的数据存储在名为lightSensor的数组中,每个元素对应一个光敏电阻通道。【4】确定最强光位置:根据采集到的光敏电阻数据,通过比较找到最强光的位置。遍历lightSensor数组,记录最大值的索引,表示最强光所在的方向。【5】步进电机控制:根据最强光的位置控制步进电机的旋转,使太阳能光板朝向最大光的方向。根据最大光位置的索引,使用条件语句判断旋转方向,然后调用StepperMotor_Rotate函数控制步进电机旋转。根据需求,可以设置旋转步数和旋转方向,以实现精确的转动控制。【6】延时等待:在步进电机旋转完成后,可以添加适当的延时,以等待太阳能光板调整到新的位置。可以根据实际情况调整延时时间,确保光板稳定后进行下一次采集和控制。【7】循环执行:将上述步骤放置在一个无限循环中,以实现持续的太阳能跟踪。程序将不断采集光敏电阻数据、确定最强光位置,并通过步进电机控制太阳能光板旋转,以获得最大的太阳能收集效率。三、项目代码3.1 PCF8591采集代码以下是利用PCF8591的光敏电阻采集并通过串口打印的实现代码。#include <reg52.h>#include <intrins.h>​// 定义PCF8591模块地址#define PCF8591_ADDR 0x90​// 定义光敏电阻通道#define LDR_CHANNEL_1 0x00#define LDR_CHANNEL_2 0x01#define LDR_CHANNEL_3 0x02#define LDR_CHANNEL_4 0x03​// 定义波特率#define BAUDRATE 9600​// 函数声明void delay(unsigned int time);void uartInit();void uartSendByte(unsigned char dat);void uartSendString(unsigned char *str);void pcf8591Init();unsigned char pcf8591ReadChannel(unsigned char channel);​void main() { unsigned char ldr1, ldr2, ldr3, ldr4; unsigned char str[20]; uartInit(); // 初始化串口 pcf8591Init(); // 初始化PCF8591模块 while(1) { // 读取光敏电阻数据 ldr1 = pcf8591ReadChannel(LDR_CHANNEL_1); ldr2 = pcf8591ReadChannel(LDR_CHANNEL_2); ldr3 = pcf8591ReadChannel(LDR_CHANNEL_3); ldr4 = pcf8591ReadChannel(LDR_CHANNEL_4); // 打印光敏电阻数据到串口 sprintf(str, "LDR1: %d, LDR2: %d, LDR3: %d, LDR4: %d\r\n", ldr1, ldr2, ldr3, ldr4); uartSendString(str); delay(1000); // 延时一段时间后再进行下一次采集和打印 }}​// 延时函数void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) { for(j = 0; j < 125; j++); }}​// 初始化串口void uartInit() { TMOD = 0x20; // 设置定时器1为模式2 SCON = 0x50; // 设置串口工作方式1,允许接收 TH1 = 256 - _cror(_cror(FOSC/12, 4), 4) / BAUDRATE; // 设置波特率 TR1 = 1; // 启动定时器1}​// 串口发送单个字节void uartSendByte(unsigned char dat) { SBUF = dat; while (!TI); // 等待发送完成 TI = 0; // 清除发送完成标志位}​// 串口发送字符串void uartSendString(unsigned char *str) { while (*str) { uartSendByte(*str); str++; }}​// 初始化PCF8591模块void pcf8591Init() { // 发送启动转换命令 I2C_Start(); I2C_Send_Byte(PCF8591_ADDR); // 发送设备地址 I2C_Wait_Ack(); I2C_Send_Byte(0x40); // 发送转换命令,选择通道0 I2C_Wait_Ack(); I2C_Stop();}​// 读取PCF8591模块的指定通道的数据值unsigned char pcf8591ReadChannel(unsigned char channel) { unsigned char value; I2C_Start(); I2C_Send_Byte(PCF8591_ADDR); // 发送设备地址 I2C_Wait_Ack(); I2C_Send_Byte(channel); // 发送通道号 I2C_Wait_Ack(); I2C_Start(); // 重新启动 I2C_Send_Byte(PCF8591_ADDR + 1); // 发送读取命令 I2C_Wait_Ack(); value = I2C_Read_Byte(); // 读取数据 I2C_Send_NAck(); I2C_Stop(); return value;}​3.2 主项目框架代码#include <reg52.h>​// 定义PCF8591模块的引脚连接#define PCF8591_ADDRESS 0x90 // PCF8591模块的I2C地址#define PCF8591_CONTROL 0x00 // PCF8591模块的控制寄存器地址​// 定义步进电机的引脚连接sbit IN1 = P1^0; // 步进电机引脚1sbit IN2 = P1^1; // 步进电机引脚2sbit IN3 = P1^2; // 步进电机引脚3sbit IN4 = P1^3; // 步进电机引脚4​// 定义步进电机旋转方向#define CW 0 // 顺时针#define CCW 1 // 逆时针​// 定义光敏电阻通道#define CHANNEL_0 0 // 光敏电阻通道0#define CHANNEL_1 1 // 光敏电阻通道1#define CHANNEL_2 2 // 光敏电阻通道2#define CHANNEL_3 3 // 光敏电阻通道3​// 延时函数void delay(unsigned int ms) { unsigned int i, j; for (i = ms; i > 0; i--) for (j = 110; j > 0; j--);}​// I2C总线启动void I2C_Start() { SDA = 1; SCL = 1; delay(1); SDA = 0; delay(1); SCL = 0; delay(1);}​// I2C总线停止void I2C_Stop() { SDA = 0; SCL = 1; delay(1); SDA = 1; delay(1);}​// I2C发送一个字节的数据void I2C_SendByte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { SDA = (dat & 0x80) >> 7; dat <<= 1; delay(1); SCL = 1; delay(1); SCL = 0; delay(1); } SDA = 1; delay(1); SCL = 1; delay(1); while (SDA) continue; SCL = 0;}​// 从PCF8591读取一个字节的数据unsigned char PCF8591_ReadByte() { unsigned char i, dat = 0; SDA = 1; for (i = 0; i < 8; i++) { dat <<= 1; SCL = 0; delay(1); SCL = 1; delay(1); if (SDA) dat |= 0x01; } SCL = 0; return dat;}​// 设置PCF8591的控制字节void PCF8591_SetControl(unsigned char ctrl) { I2C_Start(); I2C_SendByte(PCF8591_ADDRESS); I2C_SendByte(PCF8591_CONTROL); I2C_SendByte(ctrl); I2C_Stop();}​// 读取光敏电阻的数据unsigned int ReadLightSensor(unsigned char channel) { unsigned int value; PCF8591_SetControl(0x40 | channel); // 选择光敏电阻通道 delay(10); // 延时等待转换完成 I2C_Start(); I2C_SendByte(PCF8591_ADDRESS | 0x01); // 续上一段​ value = PCF8591_ReadByte(); // 读取高字节 value = (value << 8) + PCF8591_ReadByte(); // 读取低字节 I2C_Stop(); return value;}​// 控制步进电机旋转void StepperMotor_Rotate(unsigned char direction, unsigned int steps) { unsigned int i; for (i = 0; i < steps; i++) { // 顺时针旋转 if (direction == CW) { IN1 = 1; IN2 = 0; IN3 = 0; IN4 = 0; delay(10); IN1 = 0; IN2 = 1; IN3 = 0; IN4 = 0; delay(10); IN1 = 0; IN2 = 0; IN3 = 1; IN4 = 0; delay(10); IN1 = 0; IN2 = 0; IN3 = 0; IN4 = 1; delay(10); } // 逆时针旋转 else if (direction == CCW) { IN1 = 0; IN2 = 0; IN3 = 0; IN4 = 1; delay(10); IN1 = 0; IN2 = 0; IN3 = 1; IN4 = 0; delay(10); IN1 = 0; IN2 = 1; IN3 = 0; IN4 = 0; delay(10); IN1 = 1; IN2 = 0; IN3 = 0; IN4 = 0; delay(10); } }}​// 主函数void main() { unsigned int lightSensor[4]; unsigned char maxIndex; while (1) { // 采集光敏电阻数据 lightSensor[0] = ReadLightSensor(CHANNEL_0); lightSensor[1] = ReadLightSensor(CHANNEL_1); lightSensor[2] = ReadLightSensor(CHANNEL_2); lightSensor[3] = ReadLightSensor(CHANNEL_3); // 确定最强光位置 maxIndex = 0; if (lightSensor[1] > lightSensor[maxIndex]) maxIndex = 1; if (lightSensor[2] > lightSensor[maxIndex]) maxIndex = 2; if (lightSensor[3] > lightSensor[maxIndex]) maxIndex = 3; // 控制步进电机旋转 if (maxIndex == 0) { StepperMotor_Rotate(CW, 100); // 右转 } else if (maxIndex == 1) { StepperMotor_Rotate(CCW, 100); // 左转 } else if (maxIndex == 2) { StepperMotor_Rotate(CW, 100); // 右转 StepperMotor_Rotate(CW, 100); // 右转 } else if (maxIndex == 3) { StepperMotor_Rotate(CCW, 100); // 左转 StepperMotor_Rotate(CCW, 100); // 左转 } delay(1000); // 延时一段时间 }}
总条数:797 到第
上滑加载中