小熊派Nano接入华为云

news2024/11/22 4:23:24

一、华为云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 NULL
   char *event_time;                         ///< eventtime, which could be NULL means use the platform time
   oc_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 key
    char                 *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.coloradmin.cn/o/2245092.html

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

相关文章

如何在 Ubuntu 上安装 Jellyfin 媒体服务器

Jellyfin 是一个开源的媒体服务器软件&#xff0c;让你可以整理、管理和流式传输你的个人媒体收藏&#xff0c;比如电影、音乐、电视节目和照片&#xff0c;而且完全免费&#xff0c;没有订阅费用或数据收集的担忧。 简介 媒体管理&#xff1a;Jellyfin 整理媒体库&#xff0…

Android集成FCM(Firebace Cloud Messaging )

集成FCM官方文档 Firebace主页面 将 Firebase 添加到您的 Android 应用 1、进入Firebace页面&#xff0c;创建自己的项目 2、点击自己创建好的项目&#xff0c;在右侧选择Cloud Messaging 3、点击Android去创建 google-services.json 4、将下载的 google-services.json 文件…

实时直播平台如何实现美颜功能?第三方美颜API与美颜SDK的技术

通过实时美颜技术&#xff0c;主播可以轻松实现肤色优化、五官调整以及滤镜效果&#xff0c;极大提升观众的观看体验。本篇文章&#xff0c;小编将深入讲解实时直播平台如何通过第三方美颜API与美颜SDK实现美颜功能&#xff0c;以及其中的技术实现与关键要点。 一、实时美颜的…

使用GDB或Delve对已经运行起来的Go程序进行远程调试

同步发布在我的博客&#xff0c;欢迎来点赞。 使用 GDB 或 Delve 对已经运行起来的 Go 程序进行远程调试 使用 GDB 或 Delve 对已经运行起来的 Go 程序进行远程调试 背景 Java 程序可以很方便地通过 jdwp 参数指定一个对外端口进行远程调试&#xff0c;如 java \ -agentlib…

Ubuntu问题 -- 设置ubuntu的IP为静态IP (图形化界面设置) 小白友好

目的 为了将ubuntu服务器IP固定, 方便ssh连接人在服务器前使用图形化界面设置 设置 找到自己的网卡名称, 我的是 eno1, 并进入设置界面 查看当前的IP, 网关, 掩码和DNS (注意对应eno1) nmcli dev show掩码可以通过以下命令查看完整的 (注意对应eno1) , 我这里是255.255.255.…

实现一个string的indexof方法,给出时空复杂度估计

文心快码(BaiduComate)是基于百度文心大模型&#xff0c;在研发全流程全场景下为开发者提供辅助建议的智能代码助手。结合百度积累多年的编程现场大数据、外部优秀开源数据&#xff0c;可为开发者生成更符合实际研发场景的优秀代码&#xff0c;提升编码效率&#xff0c;释放“十…

ESP8266 STA模式TCP客户端 电脑手机网络调试助手

1.STA模式TCP客户端和电脑网络调试助手 2.STA模式TCP客户端和手机网络调试助手

【lamafactory BLEU ROUGLE L评测】

1、BLEU/ROUGLE评测界面 2、这个是用BLEU 和ROUGL来评测 目录&#xff1a;saves/Qwen2-7B-Chat/lora/eval_2024-11-14-16-28-19/ 在saves文件夹 生成的文件如下 all_results.json文件 说明模型在这个测试集上是不好的 3、可以查看预测结果的文件 predict_result.json

Ros Noetic 20.04 跑通mpc_ros包保姆级教程

前言: 本文将简述mpc_ros包在noetic20.04中的安装,mpc是 一种跟踪、MPC_ROS 是一个基于ROS(Robot Operating System)的模型预测控制(Model Predictive Control,MPC)库。该项目旨在为机器人控制提供一个灵活且高效的MPC实现,使得开发者能够在ROS环境中轻松集成和使用MPC…

游戏+AI的发展历程,AI技术在游戏行业的应用有哪些?

人工智能&#xff08;AI&#xff09;与游戏的结合&#xff0c;不仅是技术进步的体现&#xff0c;更是人类智慧的延伸。从最初的简单规则到如今的复杂决策系统&#xff0c;AI在游戏领域的发展历史可谓波澜壮阔。 早在2001年&#xff0c;就有研究指出游戏人工智能领域&#xff0…

HarmonyOs DevEco Studio小技巧31--卡片的生命周期与卡片的开发

Form Kit简介 Form Kit&#xff08;卡片开发服务&#xff09;提供一种界面展示形式&#xff0c;可以将应用的重要信息或操作前置到服务卡片&#xff08;以下简称“卡片”&#xff09;&#xff0c;以达到服务直达、减少跳转层级的体验效果。卡片常用于嵌入到其他应用&#xff0…

Redis的过期删除策略和内存淘汰机制以及如何保证双写的一致性

Redis的过期删除策略和内存淘汰机制以及如何保证双写的一致性 过期删除策略内存淘汰机制怎么保证redis双写的一致性?更新策略先删除缓存后更新数据库先更新数据库后删除缓存如何选择&#xff1f;如何保证先更新数据库后删除缓存的线程安全问题&#xff1f; 过期删除策略 为了…

单元测试框架gtest学习(三)—— 事件机制

前言 上节我们学习了gtest的各种宏断言 单元测试框架gtest学习&#xff08;二&#xff09;—— 认识断言-CSDN博客 本节我们介绍gtets的事件机制 虽然 Google Test 的核心是用来编写单元测试和断言的&#xff0c;但它也允许在测试执行过程中进行事件的钩取和自定义&#xf…

Unity Inspector窗口可编辑的脚本变量

Inspector可编辑的脚本变量 默认会显示的变量 在Unity中&#xff0c;为了方便我们进行一些属性的设置及调试&#xff0c;我们所写的公有基础数据类型会显示在Inspector之中&#xff0c;我们可以对他进行设置来更改它的取值。 显示私有变量 在有些情况下&#xff0c;设计代码…

力扣 LeetCode 110. 平衡二叉树(Day8:二叉树)

解题思路&#xff1a; 等于 -1 时&#xff0c;直接 return -1 class Solution {public boolean isBalanced(TreeNode root) {return getHeight(root) ! -1;}public int getHeight(TreeNode root) {if (root null) return 0;int leftDepth getHeight(root.left);if (leftDep…

unity3d————基础篇小项目(设置界面)

代码示例&#xff1a; 设置界面 using System.Collections; using System.Collections.Generic; using UnityEngine;public class SettingPanel : BasePanel<SettingPanel> {public UIButton btnClose;public UISlider sliderMusic;public UISlider sliderSound;public…

商用密码产品认证名录说明

《商用密码产品认证目录》是为贯彻落实《中华人民共和国密码法》&#xff0c;进一步健全完善商用密码产品认证体系&#xff0c;更好满足商用密码产业发展需要&#xff0c;根据《国家密码管理局 市场监管总局关于调整商用密码产品管理方式的公告》《市场监管总局 国家密码管理局…

从零做一个遥控玩具,需要学什么

遥控玩具编程是一个充满乐趣和挑战的领域&#xff0c;它完美地结合了硬件控制和软件编程。记得小时候拿着遥控器操控玩具汽车&#xff0c;总觉得神奇。如今站在程序员的视角&#xff0c;终于明白这背后是怎样的技术在支撑。 这些有趣的遥控玩具&#xff0c;其核心都是基于单片…

java的web项目如何连接数据库

mysql-connector.jar 是一个Java库文件&#xff0c;它提供了Java应用程序与MySQL数据库进行交互的接口 首先准备好这个文件&#xff0c;没有的话在网上搜索 将它放在这个位置&#xff0c;MySQL连接配置就好了&#xff0c; 如果拖不进去&#xff0c;右击项目名称&#xff0c;…

视频修复技术和实时在线处理

什么是视频修复&#xff1f; 视频修复技术的目标是填补视频中的缺失部分&#xff0c;使视频内容连贯合理。这项技术在对象移除、视频修复和视频补全等领域有着广泛的应用。传统方法通常需要处理整个视频&#xff0c;导致处理速度慢&#xff0c;难以满足实时处理的需求。 技术发…