小熊派Nano接入华为云

一、华为云IoTDA创建产品

创建如下服务,并添加对应的属性和命令。

二、小熊派接入

根据小熊派官方示例代码D6完成了小熊派接入华为云并实现属性上传命令下发。源码:小熊派开源社区/BearPi-HM_Nano

1. MQTT连接代码分析

这部分代码在oc_mqtt.c和oc_mqtt.h中

/*该结构体在oc_mqtt.h中*用来存储与MQTT设备相关的认证和标识信息。
*/
struct bp_oc_info
{char client_id[OC_CLIENT_ID_LEN];char username[OC_USERNAME_LEN];char password[OC_PASSWORD_LEN];char user_device_id_flg;
};
typedef struct bp_oc_info *bp_oc_info_t;/*该函数在oc_mqtt.c中*在调用mqtt连接前,通过该函数对连接参数进行赋值
*/
void device_info_init(char *client_id, char * username, char *password)
{oc_info.user_device_id_flg = 1;strncpy(oc_info.client_id,  client_id, strlen(client_id));strncpy(oc_info.username,     username, strlen(username));strncpy(oc_info.password,  password, strlen(password));
}/*该函数在oc_mqtt.c中*调用该函数可以完成mqtt的连接*其中oc_mqtt_entry函数将根据云端地址进行连接,然后连接mqtt
*/
int oc_mqtt_init(void)
{int result = 0;if (init_ok){//LOG_D("oc mqtt already init!");return 0;}if (oc_mqtt_entry() < 0){result = -2;goto __exit;}__exit:if (!result){//LOG_I("oc package(V%s) initialize success.", oc_SW_VERSION);init_ok = 1;//官网这里为0,根据逻辑这里应该为连接成功,连接成功后应该置1避免重复连接。}else{//LOG_E("oc package(V%s) initialize failed(%d).", oc_SW_VERSION, result);}return result;
}

2. 属性上报

华为云IoTDA中,属性上报格式如下:

{"services": [{"service_id": "xxxxx",//服务ID为产品创建后添加的服务"properties": {"temp": 23//属性和对应的值}}]
}

在小熊派源码中通过结构体封装了属性上报的函数,调用方便代码分析如下

typedef struct
{void *nxt;char *service_id;                         ///< the service id in the profile, which could not be NULLchar *event_time;                         ///< eventtime, which could be NULL means use the platform timeoc_mqtt_profile_kv_t *service_property;   ///< the property in the profile, which could not be NULL
}oc_mqtt_profile_service_t;

该结构体位于oc_mqtt.h中,用于表示接入云端的一个服务内容,具体分析如下:

  • void *nxt;是一个指向下一个oc_mqtt_profile_service_t结构体的指针,用于实现服务的链表结构。通过这个字段,可以将多个服务链接在一起。
  • char *service_id;:是一个指向字符的指针,表示服务的ID。在配置文件中,服务的ID是必需的,不能为空(NULL)。
  • char *event_time;:是一个指向字符的指针,表示事件的时间。这个字段可以是NULL,表示使用平台的时间。
  • oc_mqtt_profile_kv_t *service_property;:是一个指向oc_mqtt_profile_kv_t结构体的指针,表示服务的属性。需要上报的属性。
typedef struct
{void                 *nxt;   ///< ponit to the next keychar                 *key;en_oc_profile_data_t  type;void                 *value;
}oc_mqtt_profile_kv_t;typedef enum
{EN_OC_MQTT_PROFILE_VALUE_INT = 0,EN_OC_MQTT_PROFILE_VALUE_LONG,EN_OC_MQTT_PROFILE_VALUE_FLOAT,EN_OC_MQTT_PROFILE_VALUE_STRING,           ///< must be ended with '\0'EN_OC_MQTT_PROFILE_VALUE_LAST,
}en_oc_profile_data_t;

该结构体位于oc_mqtt.h中,用于存储服务的属性它包含以下字段:

  • void *nxt;:是一个指向下一个oc_mqtt_profile_kv_t结构体的指针,用于实现键值对的链表结构。通过这个字段,可以将多个键值对链接在一起。
  • char *key;:这是一个指向字符的指针,表示键的名称。
  • en_oc_profile_data_t type;:这是一个枚举类型,表示值的类型。枚举en_oc_profile_data_t定义了多种数据类型,用于指定与键相关联的值的类型。
  • void *value;:这是一个指向任意类型数据的指针,表示与键相关联的值。由于value的类型是void*,它可以是任何类型的数据,具体类型由type字段指定。

通过这两个结构体构建上报属性的消息更加方便,能够动态添加属性。属性上报代码如下:

/*该函数位于iot_cloud_oc_sample.c中,将需要上报的属性进行初始化*/
static void deal_report_msg(report_t *report)
{oc_mqtt_profile_service_t service;oc_mqtt_profile_kv_t fish_temp;oc_mqtt_profile_kv_t fish_light;oc_mqtt_profile_kv_t fish_pump;oc_mqtt_profile_kv_t fish_heat;service.event_time = NULL;service.service_id = "HomeBox";service.service_property = &fish_temp;service.nxt = NULL;fish_temp.key = "FishTemp";fish_temp.value = &report->temp;fish_temp.type = EN_OC_MQTT_PROFILE_VALUE_INT;fish_temp.nxt = &fish_light;fish_light.key = "FishLight";fish_light.value = g_app_cb.light? "ON" : "OFF";fish_light.type = EN_OC_MQTT_PROFILE_VALUE_STRING;fish_light.nxt = &fish_pump;fish_pump.key = "FishPump";fish_pump.value = g_app_cb.pump ? "ON" : "OFF";fish_pump.type = EN_OC_MQTT_PROFILE_VALUE_STRING;fish_pump.nxt = &fish_heat;fish_heat.key = "FishHeat";fish_heat.value = g_app_cb.heat ? "ON" : "OFF";fish_heat.type = EN_OC_MQTT_PROFILE_VALUE_STRING;fish_heat.nxt = NULL;oc_mqtt_profile_propertyreport(USERNAME, &service);return;
}

其中oc_mqtt_profile_propertyreport函数位于oc_mqtt.h中,该函数是一个用于构建和发布 MQTT 消息,上报设备服务属性。该函数中间接调用了oc_mqtt_profile_package.c文件中的oc_mqtt_profile_propertyrepormake_servicesmake_servicemake_kvsprofile_fmtvalue函数,这些函数协同工作,以 JSON 格式创建服务属性,并通过 MQTT 发布。

int oc_mqtt_profile_propertyreport(char *deviceid,oc_mqtt_profile_service_t *payload)
{int ret = (int)en_oc_mqtt_err_parafmt;char *topic;char *msg;if(NULL == deviceid){if(NULL == s_oc_mqtt_profile_cb.device_id){return ret;}else{deviceid = s_oc_mqtt_profile_cb.device_id;}}if((NULL== payload) || (NULL== payload->service_id) || (NULL == payload->service_property)){return ret;}topic = topic_make(CN_OC_MQTT_PROFILE_PROPERTYREPORT_TOPICFMT, deviceid,NULL);msg = oc_mqtt_profile_package_propertyreport(payload);printf("msg:%s \r\n",msg);if((NULL != topic) && (NULL != msg)){ret = oc_mqtt_publish(topic,(uint8_t *)msg,strlen(msg),(int)en_mqtt_al_qos_1);}else{ret = (int)en_oc_mqtt_err_sysmem;}free(topic);free(msg);return ret;
}

oc_mqtt_profile_propertyreport -->oc_mqtt_profile_package_propertyreport(创建上报信息的json对象)–>make_services(创建json对象数组)–>make_service(创建属性json对象)–>make_kvs(创建属性json数组)–>profile_fmtvalue(创建各个属性内容的json对象)

  • oc_mqtt_profile_propertyreport函数
    • 这个函数负责构建并发布一个 MQTT 消息,该消息包含设备的服务属性报告。
    • 它首先检查deviceidpayload是否为NULL。如果是,则根据全局回调函数中的设备 ID 或返回错误。
    • 使用 topic_make 函数构建 MQTT 主题。
    • 使用oc_mqtt_profile_package_propertyreport 函数打包服务属性报告为消息。
    • 使用oc_mqtt_publish函数(同样未在代码中定义)发布 MQTT 消息。
    • 最后,释放分配的内存并返回结果。
  • make_services函数
    • 这个函数创建一个 JSON 数组,该数组包含多个服务对象的 JSON 表示。
    • 它遍历传入的service_info链表,为每个服务调用make_service函数,并将结果添加到 JSON 数组中。
    • 如果在内存分配过程中发生错误,它会跳转到EXIT_MEM标签,释放已分配的资源,并返回NULL
  • make_service函数
    • 这个函数创建一个 JSON 对象,该对象表示单个服务。
    • 它添加service_id、properties(使用make_kvs函数生成)和可选的event_time到 JSON 对象中。
    • 如果在内存分配过程中发生错误,它会跳转到EXIT_MEM`标签,释放已分配的资源,并返回NULL。
  • make_kvs函数:
    • 这个函数创建一个 JSON 对象,该对象包含键值对的列表,这些键值对表示服务的属性。
    • 它遍历传入的kvlst链表,为每个键值对调用profile_fmtvalue函数,并将结果添加到 JSON 对象中。
    • 如果在内存分配过程中发生错误,它会跳转到EXIT_MEM标签,释放已分配的资源,并返回NULL。
  • profile_fmtvalue函数:
    • 这个函数根据键值对的类型(整数、长整数、浮点数或字符串)创建一个相应的 JSON 值。
    • 它返回创建的 JSON 值,该值可以是数字或字符串。

3. 消息接收

在mqtt连接后,oc_mqtt.c文件中oc_mqtt_entry函数中设置了mqtt的回调函数mq_client.defaultMessageHandler = mqtt_callback;,在函数mqtt_callback中将接收到的值存入结构体oc_mqtt.cmd_rsp_cb中后续进行处理。

/*主函数*/
oc_set_cmd_rsp_cb(oc_cmd_rsp_cb);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;
}/*oc_mqtt.c*/
void oc_set_cmd_rsp_cb(void (*cmd_rsp_cb)(uint8_t *recv_data, uint32_t recv_size, uint8_t **resp_data, uint32_t *resp_size))
{oc_mqtt.cmd_rsp_cb = cmd_rsp_cb;
}
  • 函数 oc_set_cmd_rsp_cb</font>

这个函数用于设置命令响应的回调函数。它接收一个参数:

  • void (*cmd_rsp_cb)(uint8_t *recv_data, uint32_t recv_size, uint8_t **resp_data, uint32_t *resp_size):这是一个函数指针,指向命令响应的回调函数。

函数内部逻辑如下:
- 将传入的回调函数 cmd_rsp_cb赋值给 oc_mqtt.cmd_rsp_cboc_mqtt 是一个结构体,用于存储MQTT相关的配置和状态,其中 cmd_rsp_cb成员用于存储命令响应的回调函数。

  • 回调函数oc_cmd_rsp_cb
    这个函数是命令响应的回调函数,当接收到命令时,这个函数会被调用。它接收四个参数:
    • uint8_t *recv_data:指向接收到的数据的指针。
    • size_t recv_size:接收到的数据的大小。
    • uint8_t **resp_data:指向响应数据的指针的地址,用于返回响应数据。
    • size_t *resp_size>:指向响应数据大小的指针,用于返回响应数据的大小。

接收到的消息存入消息队列进行处理

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

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

相关文章

形态学图像处理(Morphological Image Processing)

形态学图像处理(Morphological Image Processing) 前言 ‍ 本博客为个人总结数字图像处理一课所写&#xff0c;并给出适当的扩展和相应的demo。 写博客跟做 checkpoint​ 很像&#xff0c;毕竟个人还不能达到那种信手拈来的境界&#xff0c;忘了就是从零开始训练&#xff0…

[webgis 0基础到找工作]------JavaScript--DOM事件进阶 day10

1.事件流 事件流是对事件执行过程的描述&#xff0c;了解事件的执行过程有助于加深对事件的理解&#xff0c;提升开发实践中对事件运用的灵活度。 简言之&#xff0c;捕获阶段是【从父到子】的传导过程&#xff0c;冒泡阶段是【从子向父】的传导过程。 1.1捕获和冒泡 如果事件…

MATLAB图注意力网络GAT多标签图分类预测可视化

全文链接&#xff1a;https://tecdat.cn/?p38321 本示例展示了如何使用图注意力网络&#xff08;GATs&#xff09;对具有多个独立标签的图进行分类。当数据中的观测值具有带有多个独立标签的图结构时&#xff0c;可以使用GAT来预测未知标签观测值的标签&#xff08;点击文末“…

CSS优化file控件样式

<div class"file-box"><input type"button" class"btn" value"选择文件" /><inputtype"file"class"file"id"upimg"change"previewFiles"multiple/></div><!-- Vu…

CTF--php伪协议结合Base64绕过

Base64绕过 在ctf中&#xff0c;base64是比较常见的编码方式&#xff0c;在做题的时候发现自己对于base64的编码和解码规则不是很了解&#xff0c;并且恰好碰到了类似的题目&#xff0c;在翻阅了大佬的文章后记录一下&#xff0c;对于base64编码的学习和一个工具 base64编码是…

虚拟网卡驱动和DM9000C移植

网卡驱动程序框架 网卡驱动程序“收发功能”&#xff1a; 只要把上层的数据发给网卡&#xff0c;从网卡来的数据构造成包给上层即可。网卡只需要 “socket”编程&#xff0c;不需要打开某设备。 驱动程序都是以面向对象的思想写的&#xff0c;都有相关的结构体。 编程步骤 …

image compare pyqt 实现

默认显示image1, 鼠标滑动滚动条切换图片显示 可视化效果: image_compare.py import sys from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QSlider, QVBoxLayout, QHBoxLayout from PyQt5.QtCore import Qt, QSize from PyQt5.QtGui import QPixmap, QPainte…

在自动驾驶进行大数据量因果推理实验时,如何减少无用功,提高实验效率?

在对实验结果做反事实推理时&#xff0c;通常需要对数据进行多次循环&#xff0c;然后对多次循环的结果进行处理&#xff0c;如果只在最后结果结束时&#xff0c;再进行处理&#xff0c;可能会由于反事实过程中某个参数设置错误&#xff0c;导致整个反事实实验出现错误&#xf…

浅谈软件开发中的yield关键字:从餐厅服务理解异步编程之美

在现代软件开发中&#xff0c;处理大量数据流时经常会遇到性能和内存消耗的问题。传统的编程方式往往是一次性获取所有数据&#xff0c;这就像餐厅厨师要把所有菜品做完才上菜一样&#xff0c;既不高效也不够灵活。而yield关键字的出现&#xff0c;为我们提供了一种优雅的解决方…

绕过CDN寻找真实IP

在新型涉网案件中&#xff0c;我们在搜集到目标主站之后常常需要获取对方网站的真实IP去进一步的信息搜集&#xff0c;但是现在网站大多都部署了CDN&#xff0c;将资源部署分发到边缘服务器&#xff0c;实现均衡负载&#xff0c;降低网络堵塞&#xff0c;让用户能够更快地访问自…

前后端请求响应

引入 在之前的例子中&#xff0c;我们编写了一个简单的web类&#xff0c;我们运行启动类&#xff0c;启动内嵌的tomcat后就可以在浏览器通过特定的路径访问tomcat中的应用程序。 但之前编写的程序仅仅是个简单的java类&#xff0c;其并未实现某个接口或继承某个类&…

ThreeJS入门(142):THREE.WebGLRenderTarget 知识详解,示例代码

作者&#xff1a; 还是大剑师兰特 &#xff0c;曾为美国某知名大学计算机专业研究生&#xff0c;现为国内GIS领域高级前端工程师&#xff0c;CSDN知名博主&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;webgl&#xff0c;ThreeJS&#xff0c;canvas&#xf…

Streamlit + AI大模型API实现视频字幕提取

简介 在本文中&#xff0c;我将带你探讨如何使用Streamlit和AI大模型API来实现视频字幕提取的技术。Streamlit是一个开源的Python库&#xff0c;用于快速构建数据应用的Web界面&#xff0c;而AI大模型API&#xff0c;如OpenAI&#xff0c;提供了强大的语言处理能力&#xff0c…

SpringBoot - spring.profiles.active最佳实践

文章目录 Pre概述为什么需要多环境配置多环境配置实现步骤1. 配置文件准备2. 激活特定环境方法1&#xff1a;命令行参数方法2&#xff1a;环境变量方法3&#xff1a;IDE 配置方法4&#xff1a;全局配置文件默认设置 3. 配置文件加载顺序配置生效的原理 4. 常见问题多个配置文件…

网安瞭望台第2期:零日漏洞密集爆发、2024年常见网络安全漏洞类型及分析

国内外要闻 Ubuntu 服务器 Needrestart 软件包惊现严重安全漏洞 近日&#xff0c;Ubuntu 服务器&#xff08;自 21.04 版本起默认安装&#xff09;的 Needrestart 软件包被曝存在多个可追溯至数十年前的安全漏洞。这些漏洞允许本地攻击者在无需用户交互的情况下获取根…

Java前端基础——CSS

一、CSS介绍 1.1 什么是CSS CSS(Cascading Style Sheet)&#xff0c;层叠样式表,用于控制页面的样式. CSS 能够对网页中元素位置的排版进行像素级精确控制, 实现美化页面的效果. 能够做到页面的样式和结构分离. 1.2 基本语法规范 选择器 {⼀条/N条声明} • 选择器决定针…

浅议Flink中的通讯工具: Akka

在Flink中&#xff0c;各个组件之间需要频繁交换数据和控制信息。Flink选择了基于Actor模型的Akka框架作为通信基础。 Akka是什么 Actor模型 Actor模型是用于单个进程中并发的场景。 在Actor模型中&#xff1a; ActorSystem负责管理actor生命周期 将每个实体视为独立的 Ac…

Java-05 深入浅出 MyBatis - 配置深入 动态 SQL 参数、循环、片段

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

Vue.js 自定义指令:从零开始创建自己的指令

vue使用directive 前言vue2使用vue3使用 前言 关于使用自定义指令在官网中是这样描述的 vue2:对普通 DOM 元素进行底层操作&#xff0c;这时候就会用到自定义指令。 vue3:自定义指令主要是为了重用涉及普通元素的底层 DOM 访问的逻辑。 在 Vue.js 中使用自定义指令&#xf…

uni-app快速入门(十一)--常用JS API(上)

在前面学习了uni-app的布局、组件、路由等知识点以后&#xff0c;还要掌握uni-app的JS API ,也可以理解为基于uni-app的java script。本节介绍uni-app的request请求、文件上传、数据缓存、获取位置、获取系统信息、获取手机的网络状态、拨打电话API。 一、request请求 使用uni…