This article describes how to call the API operations of Link SDK for C to download an over-the-air (OTA) update package that contains a single file over MQTT and perform an update on a device. In this example, the ./demo/mota_multi_file_demo.c sample code file is used.

Background information

  • For more information about the OTA update feature, see Overview.
  • You must establish an MQTT connection. For more information, see Overview.

Step 1: Initialize a client

  1. Add header files.
    ……
    ……
    #include "aiot_ota_api.h"
    #include "aiot_mqtt_download_api.h"
    ……
  2. Add the underlying dependency and configure the log output feature.
    /* The collection of system-related functions that are stored in the portfiles/aiot_port folder. */
    extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
    
    /* TODO: If you need to disable logging, do not implement the following function. If you need to reduce log entries, you can specify the printing logic based on the code parameter.
     * Example: [1578463098.611][LK-0309] pub: /ota/device/upgrade/a13FN******/ota_demo
     * The code in the preceding log entry is 0309 (hexadecimal). For more information about the values of the code parameter, see core/aiot_state_api.h.
     */
    
    /* The callback to print logs of the SDK. */
    int32_t demo_state_logcb(int32_t code, char *message)
    {
        printf("%s", message);
        return 0;
    }
  3. Call the aiot_ota_init operation to create an OTA client instance and initialize the default parameters.
        ota_handle = aiot_ota_init();
        if (NULL == ota_handle) {
            goto exit;
        }

Step 2: Configure required features

Call the aiot_ota_setopt operation to configure the following items.

  1. Associate with an MQTT connection handle.
    Notice Before you set OTA update-specific parameters, make sure that device authentication information is specified. For more information, see Example.
    • Sample code
          aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
    • Parameters:
      Parameter Example Description
      AIOT_OTAOPT_MQTT_HANDLE mqtt_handle You must establish an MQTT connection. This parameter is used to associate with the MQTT connection handle.
  2. Configure a callback to process OTA update commands.
    • Sample code
          aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, user_ota_recv_handler);
    • Parameters:
      Parameter Example Description
      AIOT_OTAOPT_MQTT_HANDLER user_ota_recv_handler This callback function is called when the device receives an OTA update command from IoT Platform.

Step 3: Submit the version number of the device

After the device establishes an MQTT connection with IoT Platform, call the aiot_ota_report_version operation to submit the version number of the device. IoT Platform determines whether an update is required based on the version number.

In this example, the submitted version number is 1.0.0. In actual business scenarios, you must obtain the version number from the device settings and specify the logic to submit the version number.

Notice

Before you perform an OTA update, you must submit the version number at least once.

    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);
    }

Step 4: Receive update commands

  1. After you add an update package to IoT Platform and initiate an update task, IoT Platform sends an update command to the device.
    For more information, see Add an update package.
  2. The device calls the aiot_mqtt_recv operation to receive the message. After the device recognizes the message as an OTA update command, the demo_ota_recv_handler callback is called.
  3. Specify the processing logic of the callback.
    When you specify the processing logic of the callback, take note of the following items:
    • IoT Platform uses the /ota/device/upgrade/${ProductKey}/${DeviceName} topic to send the OTA update command to the device.

      For more information about ${ProductKey} and ${DeviceName}, see Obtain device authentication information.

    • AIOT_OTARECV_FOTA indicates the message type.
      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;
              }
           ……
           ……
      
      }
    • For more information about the Alink data format of OTA update commands, see Push OTA update package information to a device.
    • aiot_ota_recv_t indicates the data format. Link SDK automatically parses the received message.
    • You can specify the processing logic of the callback based on the sample code. For more information, see Step 1 and Step 2 in the Step 5: Download the update package and perform an OTA update section.

Step 5: Download the update package and perform an OTA update

Notice After the device receives the update command pushed by IoT Platform, the device does not automatically download the update package. You must call the API operation of Link SDK to download the package.

After the user_ota_recv_handler callback is called, the downloader initiates an MQTT request to receive the OTA update package.

  1. Initialize the downloader.
    Call the aiot_mqtt_download_init operation to create a download client instance and initialize the default parameters.
    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. Set the parameters.
    Call the aiot_mqtt_download_setopt operation to set the parameters that are related to the download task.
    • Sample code
              aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_TASK_DESC, ota_msg->task_desc);
              /* The maximum size of each data packet that can be downloaded. You can specify the value for resource-limited devices. */
              aiot_mqtt_download_setopt(md_handler, AIOT_DLOPT_DATA_REQUEST_SIZE, &request_size);
      
              /* The byte sequence numbers of the start and end positions for each segment. You must specify these parameters if you download an update package by segment or if you need to download a segment of the update package. 
               * If you download an update package within a specified segment, a cyclic redundancy check (CRC) is implemented for the data packet. The SDK does not implement MD5 verification for the downloaded content. 
               * By default, the complete file is downloaded. In this case, a CRC is implemented for the data packet and the SDK implements MD5 verification for the downloaded content. 
               * If you use the following sample code, the download starts from the 10th byte of the file and ends at the 10th byte of the 50th kilobyte. /
              // 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;
    • Parameters:
      Parameter Example Description
      AIOT_MDOPT_TASK_DESC ota_msg->task_desc The basic parameters that are related to the download task.
      AIOT_DLOPT_DATA_REQUEST_SIZE request_size The maximum size of each data packet that can be downloaded from IoT Platform.
      AIOT_MDOPT_RANGE_START range_start The byte sequence numbers of the start and end positions for each segment. You must specify these parameters if you download an update package by segment or if you need to download a segment of the update package.

      If you do not specify these parameters, the complete file is downloaded.

      For example, if you want to download a 1,024-byte update package by using two segments, specify the following values for the parameters:
      • First segment: AIOT_DLOPT_RANGE_START=0, AIOT_DLOPT_RANGE_END=511
      • Second segment: AIOT_DLOPT_RANGE_START=512, AIOT_DLOPT_RANGE_END=1023
      AIOT_MDOPT_RANGE_END range_end
      AIOT_MDOPT_RECV_HANDLE user_download_recv_handler The callback to manage the OTA update package.

      This callback is called when IoT Platform returns the update package after the device initiates a download request.

  3. Call the aiot_mqtt_download_process operation to send a download request to IoT Platform.
        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. IoT Platform receives the request and returns the update package to the device. After the device receives the message, the user_download_recv_handler callback is called.
    You must specify the logic of the callback to persist the downloaded update package on the device.
    void user_download_recv_handler(void *handle, const aiot_mqtt_download_recv_t *packet, void *userdata)
    {
        uint32_t data_buffer_len = 0;
    
        /* You can set packet->type only to AIOT_MDRECV_DATA_RESP. */
        if (!packet || AIOT_MDRECV_DATA_RESP != packet->type) {
            return;
        }
    
        /* Persist the downloaded file on the device. */
        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. After the update package file is downloaded, call the aiot_mqtt_download_deinit operation to exit the program.
        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) {
                    /* The update is successful. You can restart the device and submit the new version number. */
                    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;
                }
            }
        }

Step 6: Submit the version number after the update

For more information about the sample code to submit the version number, see Step 3: Submit the version number of the device.

Note
  • After the device is updated, you must submit the latest version number. Otherwise, IoT Platform determines that the OTA update task fails.

  • The device may need to be restarted after the update. In this case, submit the latest version number after the device is restarted.

  • In this example, the logic to submit the latest version number after the update is not specified. You must specify the logic based on your business requirements.

Step 7: End the connection

Call the aiot_mqtt_disconnect operation to disconnect the device from IoT Platform.

    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;
    }

Step 8: Exit the program

Call the aiot_ota_deinit operation to destroy the OTA client instance and release resources.

    aiot_ota_deinit(&ota_handle);

What to do next

  • After you configure the sample code file, compile the file to generate an executable file. In this example, the ./demos/mota-basic-demo executable file is generated.

    For more information, see Compilation and running.

  • For more information about running results, see View logs.