本文将介绍如何实现设备端通过MQTT通讯协议对物联网平台的设备标签数据进行操作。

API列表

说明 设备标签详情请参见设备标签服务
设备标签

如上图流程所示, 使用C-SDK中的devinfo功能, 可将Key-Value形式组织的设备标签上报到云端, 或者从云端删除某个标签。

以下是完整的devinfo部分API列表及简要说明 (详见components/devinfo/aiot_devinfo_api.h)。

接口名 说明
aiot_devinfo_init 初始化devinfo实例并设置默认参数。
aiot_devinfo_setopt 配置devinfo实例, 详见devinfo选项配置说明
aiot_devinfo_deinit 释放devinfo实例句柄的资源。
aiot_devinfo_send 向MQTT服务器发送devinfo的标签更新请求。

API使用概述

Devinfo模块用于向阿里云物联网平台更新或删除设备的标签, API的使用流程如下:

  1. 首先参考 aiot_mqtt_api.h 的说明, 保证成功建立与物联网平台的MQTT连接。
  2. 调用 aiot_devinfo_init 初始化devinfo会话, 获取会话句柄。
  3. 调用 aiot_devinfo_setopt 配置devinfo会话的参数, 常用配置项见 aiot_devinfo_setopt 的说明。
  4. 调用 aiot_devinfo_send 发送标签变更的请求, 比如更新或删除。
  5. 收到的应答经SDK处理后会调用由 aiot_devinfo_setopt 配置的 AIOT_DEVINFOOPT_RECV_HANDLER 回调函数, 通知用户云端的应答。

例程讲解

现对照demos/devinfo_posix_demo.c例程, 分步骤讲解如何使用API。

设备标签流程

这个例程适用于Linux这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程。

  • 一个线程用于保活长连接。
  • 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调。

接着在MQTT连接上发送设备标签更新或删除请求, 如果云平台的回应报文到达, 从接收线程会调用devinfo消息处理的回调函数, 把上报结果打印出来。

需要用户关注或修改的部分, 已经用 TODO 在注释中标明。

  1. 设置设备证书

    例程使用的设备证书是公用的, 所以应用于实际业务时, 请替换如下的TODO部分, 传入用户自己真实的设备证书。

    /* TODO: 替换为自己设备的设备证书 */
        char *product_key       = "a13FNXXXXXX";
        char *device_name       = "devinfo_basic_demo";
        char *device_secret     = "r8gHQiRkFnVUjJEWm0LvOCS7lEXXXXXX";
  2. 进入程序入口, 给SDK配置全局的底层依赖和日志回调

    底层依赖描述了硬件平台的资源使用方式, 比如怎样获取时钟, 分配内存等, 日志回调是用户的函数, SDK有log输出的时候会进入这个函数。

    int main(int argc, char *argv[])
    {
        ...
        ...
        /* 配置SDK的底层依赖 */
        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        /* 配置SDK的日志输出 */
        aiot_state_set_logcb(demo_state_logcb);
  3. 给MQTT会话配置参数并建立连接

    这些连接参数包括如何建立TLS连接, 服务器地址在哪, 连接后的数据及事件回调函数是什么等等, 然后用 aiot_mqtt_connect() 建立连接。

    int32_t demo_mqtt_start(void **handle)
    {
        ...
        ...
        /* 创建1个MQTT客户端实例并内部初始化默认参数 */
        mqtt_handle = aiot_mqtt_init();
        ...
        /* 与服务器建立MQTT连接 */
        res = aiot_mqtt_connect(mqtt_handle);

    以下代码创建1个子线程, 专门用于保活设备与云平台之间的长连接。

    /* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
        g_mqtt_process_thread_running = 1;
        res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);

    以下代码创建1个子线程, 专门用于接收从云平台推送下来的MQTT消息。

    /* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
        g_mqtt_recv_thread_running = 1;
        res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
  4. 创建devinfo会话实例并设置回应消息处理回调

    devinfo服务的请求和应答是在MQTT长连接上进行的, 以下代码设置了请求发送后, 如果设备收到了云平台的devinfo回应消息进入哪个用户回调函数。

    /* 配置devinfo会话, 把它和MQTT会话的句柄关联起来 */
        res = aiot_devinfo_setopt(devinfo_handle, AIOT_DEVINFOOPT_MQTT_HANDLE, mqtt_handle);
        ...
        /* TODO: DEVINFO消息回应从云端到达设备时, 会进入此处设置的回调函数 */
        res = aiot_devinfo_setopt(devinfo_handle, AIOT_DEVINFOOPT_RECV_HANDLER, (void *)demo_devinfo_recv_handler);
        ...
        res = aiot_devinfo_setopt(devinfo_handle, AIOT_DEVINFOOPT_EVENT_HANDLER, (void *)demo_devinfo_event_handler);
  5. 向服务器发送标签更新请求

    使用aiot_devinfo_send()接口, 以上面设置的参数, 向服务器发起标签更新请求。

    /* 示例: 发送update请求给云平台 */
        /* TODO: 替换示例JSON中的 testKey 部分, 效果就会变成更新其他Key标识的设备标签 */
        /* TODO: 替换示例JSON中的 testValue 部分, 效果就会变成更新其他Value到设备标签 */
        /* {
            aiot_devinfo_msg_t devinfo_update;
            char *update = "[{\"attrKey\":\"testKey\",\"attrValue\":\"testValue\"}]";
            memset(&devinfo_update, 0, sizeof(aiot_devinfo_msg_t));
            devinfo_update.product_key = product_key;
            devinfo_update.device_name = device_name;
            devinfo_update.type = AIOT_DEVINFO_MSG_UPDATE;
            devinfo_update.data.update.params = update;
            res = aiot_devinfo_send(devinfo_handle, &devinfo_update);
            if (res < STATE_SUCCESS) {
                aiot_devinfo_deinit(&devinfo_handle);
                demo_mqtt_stop(&mqtt_handle);
                return -1;
            }
            printf("aiot_devinfo_send update msg id: %d\n", res);
        } */

    如果打开注释, 重新编译, 以下日志就对应运行例程时被发送的标签更新请求。

    [1583155953.211][LK-0309] pub: /sys/a13FNXXXXXX/devinfo_basic_demo/thing/deviceinfo/update
    [LK-030A] > 7B 22 69 64 22 3A 22 30  22 2C 22 76 65 72 73 69 | {"id":"0","versi
    [LK-030A] > 6F 6E 22 3A 22 31 2E 30  22 2C 22 70 61 72 61 6D | on":"1.0","param
    [LK-030A] > 73 22 3A 5B 7B 22 61 74  74 72 4B 65 79 22 3A 22 | s":[{"attrKey":"
    [LK-030A] > 74 65 73 74 4B 65 79 22  2C 22 61 74 74 72 56 61 | testKey","attrVa
    [LK-030A] > 6C 75 65 22 3A 22 74 65  73 74 56 61 6C 75 65 22 | lue":"testValue"
    [LK-030A] > 7D 5D 7D                                         | }]}
  6. 从接收线程得到服务应答

    demo_mqtt_start() 中, 已开启了1个专门的接收消息线程 demo_mqtt_recv_thread, 它会永无休止的调用 aiot_mqtt_recv() 来收取消息。

    如果得到devinfo的服务应答, 那么会根据上面 AIOT_DEVINFOOPT_RECV_HANDLER 选项的设置, 进入用户侧的回调函数(以下只是把云端应答打印出来)。

    /* TODO: 数据处理回调, 当SDK从网络上收到devinfo消息时被调用 */
    void demo_devinfo_recv_handler(void *handle, const aiot_devinfo_recv_t *packet, void *userdata)
    {
        switch (packet->type) {
            /* 这是云端对devinfo消息的应答报文 */
            case AIOT_DEVINFORECV_GENERIC_REPLY: {
                printf("pk: %s, dn: %s, code: %d, msg id: %d\n", packet->product_key, packet->device_name,
                       packet->data.generic_reply.code, packet->data.generic_reply.msg_id);
        ...
        ...

    以下日志对应的是被成功接收到的服务应答和例程的解析打印。

    [1583155953.266][LK-0309] pub: /sys/a13FNXXXXXX/devinfo_basic_demo/thing/deviceinfo/update_reply
    [LK-030A] < 7B 22 63 6F 64 65 22 3A  32 30 30 2C 22 64 61 74 | {"code":200,"dat
    [LK-030A] < 61 22 3A 7B 7D 2C 22 69  64 22 3A 22 30 22 2C 22 | a":{},"id":"0","
    [LK-030A] < 6D 65 73 73 61 67 65 22  3A 22 73 75 63 63 65 73 | message":"succes
    [LK-030A] < 73 22 2C 22 6D 65 74 68  6F 64 22 3A 22 74 68 69 | s","method":"thi
    [LK-030A] < 6E 67 2E 64 65 76 69 63  65 69 6E 66 6F 2E 75 70 | ng.deviceinfo.up
    [LK-030A] < 64 61 74 65 22 2C 22 76  65 72 73 69 6F 6E 22 3A | date","version":
    [LK-030A] < 22 31 2E 30 22 7D                                | "1.0"}          
    pk: a13FNXXXXXX, dn: devinfo_basic_demo, code: 200, msg id: 0

    code的值是200, 代表云端已经接受了刚才发送的标签更新请求, 此时登录控制台刷新网页可看到标签的值已经发生变化。