本文以C Link SDK中的Demo文件./demos/cota_basic_demo.c为例,介绍如何调用Link SDK的API,帮助您实现设备的远程配置功能。

背景信息

  • 远程配置功能的更多信息,请参见概述
  • 远程配置功能基于MQTT接入,开发过程中涉及MQTT接入的代码说明,请参见MQTT接入

步骤一:初始化

  1. 添加头文件。
    ……
    ……
    #include "aiot_ota_api.h"
    ……
  2. 配置底层依赖和日志输出。
        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. 调用aiot_ota_init,创建OTA客户端实例,并初始化默认参数。
        ota_handle = aiot_ota_init();
        if (NULL == ota_handle) {
            printf("aiot_ota_init failed\r\n");
            aiot_mqtt_deinit(&mqtt_handle);
            return -2;
        }

步骤二:配置功能

调用aiot_ota_setopt,配置以下功能。

  1. 关联MQTT连接的句柄。
    注意 在设置远程配置参数前,请确保已配置设备认证信息等相关参数。具体操作,请参见MQTT配置连接参数
    • 示例代码:
          aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
    • 相关参数:
      配置项 示例 说明
      AIOT_OTAOPT_MQTT_HANDLE mqtt_handle 远程配置功能的请求基于MQTT连接,通过该配置项,关联MQTT连接句柄。
  2. 设置远程配置的指令消息的回调。
    • 示例代码:
          aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, demo_ota_recv_handler);
    • 相关参数:
      配置项 示例 说明
      AIOT_OTAOPT_MQTT_HANDLE demo_ota_recv_handler 当设备收到来自物联网平台的远程配置指令时,调用该回调函数。

步骤三:设备主动请求配置消息

如果物联网平台下发远程配置指令时,设备不在线,但设备上线后,可主动请求获取远程配置的指令消息。

说明 如果物联网平台未创建设备任务,调用接口后,返回revice task get detail reply, task_id:[$next],status:[9]
  1. 调用aiot_mqtt_pub,向物联网平台发送请求,以获取远程配置信息。
    • 示例代码:
          {
              char *topic_string = "/sys/a18wP******/LightSwitch/thing/config/get";
              char *payload_string = "{\"id\":\"123\",\"params\":{\"configScope\":\"product\",\"getType\":\"file\"}}";
      
              res = aiot_mqtt_pub(mqtt_handle, topic_string, (uint8_t *)payload_string, strlen(payload_string), 0);
              if (res < STATE_SUCCESS) {
                  printf("aiot_mqtt_pub failed: -0x%04X\r\n", -res);
                  /* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
                  goto exit;
              }
          }
    • 相关参数:
      参数 示例值 说明
      topic_string /sys/a18wP******5/LightSwitch/thing/config/get 请求获取远程配置信息的Topic。

      关于Topic的更多信息,请参见什么是Topic

      示例代码中:

      • a18wP******为设备的ProductKey。
      • LightSwitch为设备的DeviceName。

      更多信息,请参见获取设备认证信息

      payload_string {\"id\":\"123\",\"params\":{\"configScope\":\"product\",\"getType\":\"file\"}} 请求消息的内容。

      请求消息的消息的内容为JSON格式,是Alink格式数据中params的值。详细说明,请参见设备主动请求配置信息

  2. 物联网平台接收请求后,向设备返回配置信息。
    说明 远程配置内容大小的上限是64 KB。
    您需在物联网平台控制台,开启远程配置,编辑配置信息。具体操作,请参见编辑配置文件
  3. 调用aiot_mqtt_recv,接收远程配置指令消息。
    • 如果g_dl_handle指针会为空, 则只从网络上轮询MQTT报文。
    • 如果g_dl_handle指针为非空, 则调用aiot_download_recv函数。
    while (1) {
            aiot_mqtt_process(mqtt_handle);
            res = aiot_mqtt_recv(mqtt_handle);
    
            if (res == STATE_SYS_DEPEND_NWK_CLOSED) {
                sleep(1);
            }
            if (NULL != g_dl_handle) {
                /* 完成远程配置的接收前, 将mqtt的收包超时调整到100毫秒, 以减少两次远程配置的下载间隔*/
                int32_t ret = aiot_download_recv(g_dl_handle);
                timeout_ms = 100;
    
                if (STATE_DOWNLOAD_FINISHED == ret) {
                    aiot_download_deinit(&g_dl_handle);
                    /* 完成远程配置的接收后, 将mqtt的收包超时调整回到默认值5000毫秒 */
                    timeout_ms = 5000;
                }
                aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS, (void *)&timeout_ms);
            }
        }
  4. 设备接收配置指令的消息后,触发回调函数demo_ota_recv_handler
    您可以参考以下内容,编写回调函数的处理逻辑:
    • 物联网平台通过Topic /sys/${ProductKey}/${DeviceName}/thing/config/push,向设备下发远程配置指令。
    • Link SDK解析物联网平台指令,解析后的数据结构类型为aiot_ota_recv_t,Link SDK自动解析收到的配置消息。
    • 远程配置的消息类型为AIOT_OTARECV_COTA
    • 通过回调函数,发起HTTPS协议的请求,下载远程配置文件。回调函数的编写逻辑,请参见步骤五:下载远程配置文件

步骤四:设备接收物联网平台推送的远程配置信息

如果设备在线,物联网平台下发远程配置指令后,设备随即接收远程配置指令,触发回调函数。

具体操作,请参见编写回调函数的处理逻辑

步骤五:下载远程配置文件

注意 接收配置指令后,设备不会自动下载配置文件,您需调用Link SDK提供的API启动下载。
  1. 初始化下载器。
    调用aiot_download_init,创建download客户端实例,并初始化默认参数。
                uint32_t res = 0;
                uint16_t port = 443;
                uint32_t max_buffer_len = 2048;
                aiot_sysdep_network_cred_t cred;
                void *dl_handle = aiot_download_init();
    
                if (NULL == dl_handle || NULL == ota_msg->task_desc) {
                    return;
                }
    
                printf("configId: %s, configSize: %u Bytes\r\n", ota_msg->task_desc->version,
                       ota_msg->task_desc->size_total);
    
                memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
                cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA;
                cred.max_tls_fragment = 16384;
                cred.x509_server_cert = ali_ca_cert;
                cred.x509_server_cert_len = strlen(ali_ca_cert);
  2. 配置下载参数。
    调用aiot_download_setopt,配置下载任务的相关参数。
                /* 设置下载时为TLS下载 */
                if ((STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *)(&cred))) ||
                    /* 设置下载时访问的服务器端口号 */
                    (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *)(&port))) ||
                    /* 设置下载的任务信息, 通过输入参数 ota_msg 中的 task_desc 成员得到, 内含下载地址, 远程配置的大小和版本号*/
                    (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *)(ota_msg->task_desc))) ||
                    /* 设置下载内容到达时, SDK将调用的回调函数 */
                    (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *)(demo_download_recv_handler))) ||
                    /* 设置单次下载最大的buffer长度, 每当这个长度的内存读满了后会通知用户 */
                    (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *)(&max_buffer_len))) ||
                    /* 发送http的GET请求给http服务器 */
                    (STATE_SUCCESS != aiot_download_send_request(dl_handle))) {
                    if (res != STATE_SUCCESS) {
                        aiot_download_deinit(&dl_handle);
                        break;
                    }
                }
  3. 发起下载请求后,接收应答报文,触发回调函数demo_download_recv_handler,执行下载处理。
    您可以参考以下内容,编写回调函数的处理逻辑:
    • 下载消息的数据结构类型为aiot_download_recv_t,是回到函数的入参。
    • 下载消息的packet->type类型AIOT_DLRECV_HTTPBODY
    • 示例代码仅做打印处理,您需根据业务需要,将配置文件下载至指定位置。
    void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata)
    {
        /* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */
        if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) {
            return;
        }
        int32_t percent = packet->data.percent;
        uint8_t *src_buffer = packet->data.buffer;
        uint32_t src_buffer_len = packet->data.len;
    
        /* 如果 percent 为负数, 说明发生了收包异常或者digest校验错误 */
        if (percent < 0) {
            /* 收包异常或者digest校验错误 */
            printf("exception happend, percent is %d\r\n", percent);
            return;
        }
    ……
    ……
    }
  4. 调用aiot_download_report_progress,上报下载进度。
    void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata)
    {
        /* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */
        if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) {
            return;
    ……
    ……
        aiot_download_report_progress(handle, percent);
        printf("config len is %d, config content is %.*s\r\n", src_buffer_len, src_buffer_len, (char *)src_buffer);
    }
  5. 下载结束后,通过aiot_download_deinit,把句柄置g_dl_handle空。
    while (1) {
            aiot_mqtt_process(mqtt_handle);
            res = aiot_mqtt_recv(mqtt_handle);
    
            if (res == STATE_SYS_DEPEND_NWK_CLOSED) {
                sleep(1);
            }
            if (NULL != g_dl_handle) {
                /* 完成远程配置的接收前, 将mqtt的收包超时调整到100ms, 以减少两次远程配置的下载间隔*/
                int32_t ret = aiot_download_recv(g_dl_handle);
                timeout_ms = 100;
    
                if (STATE_DOWNLOAD_FINISHED == ret) {
                    aiot_download_deinit(&g_dl_handle);
                    /* 完成远程配置的接收后, 将mqtt的收包超时调整回到默认值5000ms */
                    timeout_ms = 5000;
                }
                aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS, (void *)&timeout_ms);
            }
        }

步骤六:退出程序

调用aiot_ota_deinit,销毁OTA客户端实例,释放资源。

    aiot_ota_deinit(&ota_handle);

后续步骤

  • 例程文件配置完成后,需进行编译,生成可执行文件./output//cota-basic-demo

    更多信息,请参见编译与运行

  • 关于运行结果的详细说明,请参见运行日志