本文以C Link SDK中的Demo文件./demos/mota_basic_demo.c为例,介绍如何调用Link SDK的API,帮助设备端使用MQTT协议,下载仅含单个升级文件的OTA升级包,实现设备的OTA升级。

背景信息

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

步骤一:初始化OTA功能

  1. 添加头文件。
    ……
    ……
    #include "aiot_ota_api.h"
    #include "aiot_mqtt_download_api.h"
    ……
  2. 配置底层依赖和日志输出。
    /* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
    extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
    
    /* TODO: 如果要关闭日志, 则将该函数实现为空, 如果要减少日志, 可根据code选择不打印
     * 例如: [1578463098.611][LK-0309] pub: /ota/device/upgrade/a13FN******/ota_demo
     * 上述该条日志的code为0309(十六进制), code值的定义见core/aiot_state_api.h
     */
    
    /* 日志回调函数, SDK的日志从此处输出 */
    int32_t demo_state_logcb(int32_t code, char *message)
    {
        printf("%s", message);
        return 0;
    }
  3. 调用aiot_ota_init,创建OTA客户端实例,并初始化默认参数。
        ota_handle = aiot_ota_init();
        if (NULL == ota_handle) {
            goto exit;
        }

步骤二:配置OTA功能

调用aiot_ota_setopt,配置以下功能。

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

步骤三:上报设备当前版本号

设备建立MQTT连接后,调用aiot_ota_report_version,上报当前设备的版本号。物联网平台根据版本号,判断是否需要升级。

以下示例代码中,OTA升级前设备上报的版本号为1.0.0,在实际业务中,您需从设备的配置区获取实际的版本号,并执行编写代码。

注意

设备进行OTA升级前,需至少上报一次版本号。

    cur_version = "1.0.0";
    res = aiot_ota_report_version(ota_handle, cur_version);
    if (res < STATE_SUCCESS) {
        printf("aiot_ota_report_version failed: -0x%04X\r\n", -res);
    }

步骤四:接收升级指令

  1. 在物联网平台添加升级包,并发起升级任务后,物联网平台向设备端下发升级指令。
    具体操作,请参见添加升级包
  2. 设备端调用aiot_mqtt_recv接收消息,当消息被识别为OTA升级指令后,调用回调函数user_ota_recv_handler,执行对应的处理逻辑。
  3. 编写回调函数的处理逻辑。
    您可以参考以下内容,编写回调函数的处理逻辑:
    • 物联网平台通过Topic /ota/device/upgrade/${ProductKey}/${DeviceName},向设备下发OTA升级包指令。

      ${ProductKey}${DeviceName}的详细说明,请参见获取设备认证信息

    • OTA升级指令的类型为AIOT_OTARECV_FOTA
      void user_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
      {
          uint32_t request_size = 10 * 1024;
          switch (ota_msg->type) {
          case AIOT_OTARECV_FOTA: {
              if (NULL == ota_msg->task_desc || ota_msg->task_desc->protocol_type != AIOT_OTA_PROTOCOL_MQTT) {
                  break;
              }
           ……
           ……
      
      }
    • OTA升级指令消息的Alink数据格式说明,请参见物联网平台推送OTA升级包信息
    • OTA升级指令消息的数据结构类型为aiot_ota_recv_t,Link SDK自动解析收到的升级指令消息。
    • 您可以参考示例代码,编写回调函数的处理逻辑,请参见步骤五的步骤1至2。

步骤五:下载升级包,进行OTA升级

注意 设备端收到物联网平台推送的升级包消息后,不会自动下载升级包,您需要调用Link SDK提供的API启动下载。

触发函数user_ota_recv_handler后,基于MQTT协议,下载器发起下载请求,然后接收物联网平台返回的升级包,进行OTA升级。

  1. 初始化下载器。
    调用aiot_mqtt_download_init,创建download客户端实例,并初始化默认参数。
    void user_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
    {
         ……
         ……
    
            printf("OTA target firmware version: %s, size: %u Bytes\r\n", ota_msg->task_desc->version,
                   ota_msg->task_desc->size_total);
            void *md_handler = aiot_mqtt_download_init();
    
         ……
         ……
    
    }
  2. 配置下载参数。
    调用aiot_mqtt_download_setopt,配置下载任务的相关参数。
    • 示例代码:
              aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_TASK_DESC, ota_msg->task_desc);
              /* 设置下载一包的大小,对于资源受限设备可以调整该值大小 */
              aiot_mqtt_download_setopt(md_handler, AIOT_DLOPT_DATA_REQUEST_SIZE, &request_size);
      
              /* 分段下载升级包,或仅下载升级包的片段时,每个分段的起始和终止位置的字节序号。
               * 若设置范围区间下载,单包报文的数据有CRC校验,但SDK将不进行完整文件MD5校验,
               * 默认下载全部文件,单包报文的数据有CRC校验,且SDK将进行完整文件MD5校验 
               * 若取消下列代码注释,表示仅从文件的第10字节开始下载,下载至第50千字节的第10字节为止。/
              // uint32_t range_start = 10, range_end = 50 * 1024 + 10;
              // aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RANGE_START, &range_start);
              // aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RANGE_END, &range_end);
      
              aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RECV_HANDLE, user_download_recv_handler);
              g_dl_handle = md_handler;
    • 相关参数:
      配置项 示例 说明
      AIOT_MDOPT_TASK_DESC ota_msg->task_desc 设置下载任务。
      AIOT_DLOPT_DATA_REQUEST_SIZE request_size 每次向物联网平台请求的单包数据大小。
      AIOT_MDOPT_RANGE_START range_start 分段下载升级包,或仅下载升级包的片段时,每个分段的起始和终止位置的字节序号。

      如果不设置,表示一次下载全部文件。

      例如,一个1024字节的升级包分两次下载,两次的参数可分别设置为:
      • 第一次:AIOT_DLOPT_RANGE_START=0AIOT_DLOPT_RANGE_END=511
      • 第二次:AIOT_DLOPT_RANGE_START=512AIOT_DLOPT_RANGE_END=1023
      AIOT_MDOPT_RANGE_END range_end
      AIOT_MDOPT_RECV_HANDLE user_download_recv_handler 配置OTA升级包内容的回调。

      设备发起下载请求后,物联网平台返回升级包内容的消息时,触发该回调函数。

  3. 调用aiot_mqtt_download_process,向物联网平台发送下载请求。
        while (1) {
            aiot_mqtt_process(mqtt_handle);
            aiot_mqtt_recv(mqtt_handle);
            if(g_dl_handle != NULL) {
                int32_t res = aiot_mqtt_download_process(g_dl_handle);
         ……
         ……
    
            }
        }
                            
  4. 物联网平台接收下载请求后,向设备返回升级包信息,设备接收消息后,触发回调函数user_download_recv_handler
    您需编写回调函数的处理逻辑,将下载的升级包数据固化至本地。
    void user_download_recv_handler(void *handle, const aiot_mqtt_download_recv_t *packet, void *userdata)
    {
        uint32_t data_buffer_len = 0;
    
        /* 目前只支持packet->type为AIOT_MDRECV_DATA_RESP的情况 */
        if (!packet || AIOT_MDRECV_DATA_RESP != packet->type) {
            return;
        }
    
        /* 应在此实现文件本地固化的操作 */
        FILE *file = fopen("mota_demo.bin", "ab");
        fwrite(packet->data.data_resp.data, packet->data.data_resp.data_size, sizeof(int8_t), file);
        fclose(file);
    
        data_buffer_len = packet->data.data_resp.data_size;
    
        printf("download %03d%% done, +%d bytes\r\n", packet->data.data_resp.percent, data_buffer_len);
    }
  5. 升级包文件下载完成后,调用aiot_mqtt_download_deinit退出程序。
        while (1) {
            aiot_mqtt_process(mqtt_handle);
            aiot_mqtt_recv(mqtt_handle);
            if(g_dl_handle != NULL) {
                int32_t res = aiot_mqtt_download_process(g_dl_handle);
    
                if(STATE_MQTT_DOWNLOAD_SUCCESS == res) {
                    /* 升级成功,可在此处重启并且上报新的版本号 */
                    printf("mqtt download ota success \r\n");
                    aiot_mqtt_download_deinit(&g_dl_handle);
                    break;
                } else if(STATE_MQTT_DOWNLOAD_FAILED_RECVERROR == res
                          || STATE_MQTT_DOWNLOAD_FAILED_TIMEOUT == res
                          || STATE_MQTT_DOWNLOAD_FAILED_MISMATCH == res) {
                    printf("mqtt download ota failed \r\n");
                    aiot_mqtt_download_deinit(&g_dl_handle);
                    break;
                }
            }
        }

步骤六:上报升级后版本号

上报版本号的示例代码,请参见步骤三:上报设备当前版本号

说明
  • 设备完成OTA升级后,需上报最新版本号,否则物联网平台视该OTA升级任务为失败。

  • 如果设备升级后需重启,则设备重启后需上报最新的版本号。

  • 示例代码中不包含升级完成后重新上报版本号的过程,您需自行实现。

步骤七:断开连接

调用aiot_mqtt_disconnect,向物联网平台发送断开连接的报文,然后断开网络连接。

    res = aiot_mqtt_disconnect(mqtt_handle);
    if (res < STATE_SUCCESS) {
        aiot_mqtt_deinit(&mqtt_handle);
        printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
        return -1;
    }

步骤八:退出OTA程序

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

    aiot_ota_deinit(&ota_handle);

后续步骤

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

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

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