Sample code

更新时间:
复制 MD 格式

The ./demo/fota_posix_demo.c sample code demonstrates how to download an over-the-air (OTA) update package over HTTPS. This package contains a single update file used to perform an OTA update on a device.

Background information

  • For more information about the OTA update feature, see OTA update overview.
  • The OTA update feature requires an MQTT connection. For more information about the code for an MQTT connection, see MQTT connection.

Step 1: Initialize the OTA feature

  1. Add the header file.
    ……
    ……
    #include "aiot_ota_api.h"
    ……

  2. Configure underlying dependencies and log output.

        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. Call aiot_ota_init to create an OTA instance.
        ota_handle = aiot_ota_init();
        if (NULL == ota_handle) {
            printf("aiot_ota_init failed\r\n");
            aiot_mqtt_deinit(&mqtt_handle);
            return -2;
        }

Step 2: Configure the OTA feature

Call aiot_ota_setopt to configure the following options.

  1. Associate the handle of the MQTT connection.
    Important Before you configure OTA parameters, you must configure parameters such as device credentials. For more information, see Configure connection parameters for MQTT.
    •     aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
    • Configuration itemExampleDescription
      AIOT_OTAOPT_MQTT_HANDLEmqtt_handleOTA feature requests are based on MQTT connections. This configuration item associates the MQTT connection handle.

  2. Configure the callback for OTA update instruction messages.
    •     aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, demo_ota_recv_handler);
    • Configuration itemExampleDescription
      AIOT_OTAOPT_MQTT_HANDLERdemo_ota_recv_handlerWhen the device receives an OTA update instruction from IoT Platform, this callback function is called.

Step 3: Report the current device version

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

In the following sample code, the version number reported by the device before the OTA update is 1.0.0. In your application, you must obtain the actual version number from the device's configuration area and modify the code accordingly.

Important

The device must report its version number at least once before an OTA update.

    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 the update instruction

  1. After you add an update package and start an update task in the IoT Platform console, IoT Platform sends an update instruction to the device.
    For more information, see Add an update package.
  2. The device calls aiot_mqtt_recv to receive messages. When a message is identified as an OTA update instruction, the demo_ota_recv_handler callback function is called.
  3. Write the processing logic for the callback function.
    Write the processing logic for the callback function based on the following information:
    • IoT Platform sends OTA update package instructions to the device over the topic /ota/device/upgrade/${ProductKey}/${DeviceName}.

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

    • The type of the OTA update instruction is AIOT_OTARECV_FOTA.
      void demo_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
      {
          switch (ota_msg->type) {
              case AIOT_OTARECV_FOTA: {
                  uint32_t res = 0;
                  uint16_t port = 443;
                  uint32_t max_buffer_len = (8 * 1024);
                  aiot_sysdep_network_cred_t cred;
                  void *dl_handle = NULL;
                  void *last_percent = NULL;
      
                  if (NULL == ota_msg->task_desc) {
                      break;
                  }
           ……
           ……
      
      }
    • For information about the Alink data format of OTA update instruction messages, see IoT Platform pushes OTA update package information.
    • The data structure type for OTA update instruction messages is aiot_ota_recv_t. Link SDK automatically parses the received update instruction messages.
    • For sample code that shows how to write the processing logic for the callback function, see Step 5: Download the update package and perform the OTA update.

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

Important The device does not automatically download the update package after it receives the update package message from IoT Platform. You must call a Link SDK API to start the download.

When the demo_ota_recv_handler function is triggered, the downloader initiates a download request over HTTPS to download the update package and perform the OTA update.

  1. Initialize the downloader.
    Call aiot_download_init to create a download
                dl_handle = aiot_download_init();
                if (NULL == dl_handle) {
                    break;
                }
                printf("OTA target firmware version: %s, size: %u Bytes \r\n", ota_msg->task_desc->version,
                       ota_msg->task_desc->size_total);
    
                if (NULL != ota_msg->task_desc->extra_data) {
                    printf("extra data: %s\r\n", ota_msg->task_desc->extra_data);
                }
    
                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. Configure download parameters.
    Call aiot_download_setopt to configure the parameters for the download task.
                /* Set the download to use TLS. */
                aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *)(&cred));
                /* Set the server port number for the download. */
                aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *)(&port));
                /* Set the download task information. This is obtained from the task_desc member of the ota_msg input parameter. It includes the download URL, firmware size, and firmware signature. */
                aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *)(ota_msg->task_desc));
                /* Set the callback function that the SDK calls when the downloaded content arrives. */
                aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *)(demo_download_recv_handler));
                /* Set the maximum cache length for a single download. The user is notified each time this amount of memory is full. */
                aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *)(&max_buffer_len));
                /* Set the data to be shared between different calls of AIOT_DLOPT_RECV_HANDLER. For example, the demo stores the progress here. */
                last_percent = malloc(sizeof(uint32_t));
                if (NULL == last_percent) {
                    aiot_download_deinit(&dl_handle);
                    break;
                }
                memset(last_percent, 0, sizeof(uint32_t));
                aiot_download_setopt(dl_handle, AIOT_DLOPT_USERDATA, (void *)last_percent);
  3. Initiate a download request.
    1. Start the download thread demo_ota_download_thread.
                  res = pthread_create(&g_download_thread, NULL, demo_ota_download_thread, dl_handle);
                  if (res != 0) {
                      printf("pthread_create demo_ota_download_thread failed: %d\r\n", res);
                      aiot_download_deinit(&dl_handle);
                      free(last_percent);
                  } else {
                      /* Set the download thread to the detach type, so it can exit on its own after the firmware content is retrieved. */
                      pthread_detach(g_download_thread);
                  }
    2. In the demo_ota_download_thread download thread, call aiot_download_send_request to send an HTTPS GET request to the specified storage server to download the update package.
      • void *demo_ota_download_thread(void *dl_handle)
        {
            int32_t     ret = 0;
        
            printf("starting download thread in 2 seconds ......\n");
            sleep(2);
        
            /* Request to download from the update package storage server. */
            /*
             * TODO: The following sample code uses one request to get the entire update package.
             *       For devices with limited resources or poor network connectivity, you can also download in segments. This requires a combination of:
             *
             *       aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_START, ...);
             *       aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_END, ...);
             *       aiot_download_send_request(dl_handle);
             *
             *       In this case, you need to place the above combination of statements in a loop to call send_request and recv multiple times.
             *
             */
        
             ……
             ……
        
        }
      • You can download the update package in segments using AIOT_DLOPT_RANGE_START and AIOT_DLOPT_RANGE_END.

        For example, to download a 1024-byte update package in two parts, you can set the parameters as follows:
        • First part: AIOT_DLOPT_RANGE_START=0, AIOT_DLOPT_RANGE_END=511
        • Second part: AIOT_DLOPT_RANGE_START=512, AIOT_DLOPT_RANGE_END=1023
  4. Receive the update package.
    1. After you send the download request, call aiot_download_recv in the demo_ota_download_thread download thread to receive the update package. The received data triggers the demo_download_recv_handler callback function. In this function, you can save the downloaded update package to the device's local storage or file system.
      void *demo_ota_download_thread(void *dl_handle)
      {
           ……
           ……
      
           while (1) {
              /* Receive the update package content from the server over the network. */
              ret = aiot_download_recv(dl_handle);
      
              /* When the entire update package is downloaded, the return value of aiot_download_recv() will be STATE_DOWNLOAD_FINISHED. Otherwise, it is the number of bytes obtained in the current fetch. */
              if (STATE_DOWNLOAD_FINISHED == ret) {
                  printf("download completed\n");
                  break;
              }
          }
           ……
           ……
      }
    2. Define the callback function demo_download_recv_handler and write the logic for storing and upgrading the downloaded package.
      Note The sample code only prints information. It does not include the storage and burning logic for the update package. You must save the update package based on your device's environment and then run the package to complete the OTA update.
      void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata)
      {
          uint32_t data_buffer_len = 0;
          uint32_t last_percent = 0;
          int32_t  percent = 0;
      
          /* Currently, only the case where packet->type is AIOT_DLRECV_HTTPBODY is supported. */
          if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) {
              return;
          }
          percent = packet->data.percent;
      
          /* userdata can store data that needs to be shared between different entries into demo_download_recv_handler(). */
          /* Here, it is used to store the firmware download progress percentage from the last time this callback function was entered. */
          if (userdata) {
              last_percent = *((uint32_t *)(userdata));
          }
      
          data_buffer_len = packet->data.len;
      
          /* If percent is a negative number, it indicates a packet reception exception or a digest verification error. */
          if (percent < 0) {
              printf("exception: percent = %d\r\n", percent);
              if (userdata) {
                  free(userdata);
              }
              return;
          }
           ……
           ……
      
      }
  5. Report the download progress.

    In the demo_download_recv_handler callback function, call aiot_download_report_progress to report the download progress and any exceptions that occur during the update process, such as a burning failure or network disconnection, to IoT Platform.

    • View reported progress:

      The progress is displayed in the IoT Platform console. For more information, see View update status.

    • Reporting progress for normal and abnormal downloads:
      • If the download is normal, the download progress is reported to IoT Platform as an integer percentage. Link SDK automatically calculates the value of the percent parameter and reports it to IoT Platform.
      • If the download is abnormal or a firmware burning exception occurs after the download, you must report the exception to IoT Platform. For a list of protocol error codes between the device and IoT Platform, see aiot_ota_protocol_errcode_t.
    void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata)
    {
         ……
         ……
        /*
         * TODO: After a segment of the update package is successfully downloaded, the user should save the memory
         *       starting at packet->data.buffer with a length of packet->data.len to the device's local storage or file system.
         *
         *       If the burning fails, you should also call aiot_download_report_progress(handle, -4) to report the failure to IoT Platform.
         */
        /* When the value of the percent input parameter is 100, it indicates that the update package has been fully downloaded. */
        if (percent == 100) {
            /*
             * TODO: At this point, you should complete all firmware burning, save the current work, restart the device, and switch to the new firmware to start.
                     Additionally, the new firmware must report the new version number (for example, if upgrading from 1.0.0 to 1.1.0, the new_version value is 1.1.0)
                     to IoT Platform using
    
                     aiot_ota_report_version(ota_handle, new_version);
    
                     IoT Platform will only consider the upgrade successful after receiving the new version number report.
                     Otherwise, it will consider the upgrade to have failed.
             */
        }
    
        /* Simplified output. The progress is printed and reported to the cloud only when the download progress has increased by 5% or more since the last report. */
        if (percent - last_percent >= 5 || percent == 100) {
            printf("download %03d%% done, +%d bytes\n", percent, data_buffer_len);
            aiot_download_report_progress(handle, percent);
            if (userdata) {
                *((uint32_t *)(userdata)) = percent;
            }
        }
    }
  6. Exit the downloader.

    After the update package content is downloaded, call aiot_download_deinit to destroy the download session. The download thread then exits.

        aiot_download_deinit(&dl_handle);
        printf("download thread exit\n");

Step 6: Report the version number after the update

For more information, see Step 3: Report the current device version.

Note
  • After the device completes the OTA update, it must report the latest version number. Otherwise, IoT Platform considers the OTA update task to have failed.

  • If the device needs to be restarted after the update, it must report the latest version number after the restart.

  • The sample code does not include the logic for reporting the version number after the update is complete. You must add this logic to your code.

Step 7: Close the connection

Note

MQTT is typically used for devices that require a persistent connection. Therefore, the program usually does not reach this point.

In the sample program, the main thread is responsible for configuring parameters and establishing a connection. After the connection is established, the main thread can enter hibernation.

You can call aiot_mqtt_disconnect to send a disconnection message to IoT Platform and disconnect from the network.

    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 OTA program

Call aiot_ota_deinit to destroy the OTA instance.

    aiot_ota_deinit(&ota_handle);

What to do next