Device OTA development

Updated at:

Over-the-Air (OTA) technology lets you remotely update devices. IoT Platform supports OTA firmware updates for devices.

Background

The following figure shows the firmware update process based on the MQTT protocol.

OTA example walkthrough

  • You can use OTA APIs to download firmware to a device. However, you must implement the logic to store and apply the downloaded firmware.

  • Storing the firmware means saving the downloaded firmware to a storage medium, such as flash memory.

  • Applying the firmware, which includes loading the newly downloaded firmware, depends on your specific business requirements, such as requiring a user to click an upgrade button.

Note

For the complete OTA workflow, see OTA Service.

The following examples show how to implement OTA features using the basic API and the advanced API.

OTA example: Basic API

This section uses the src/ota/examples/ota_example_mqtt.c example to demonstrate how to implement OTA features using the basic API.

  1. Set the device trituple and initialize the connection for the OTA service.

    int main(int argc, char *argv[]) {
        ...
        /**< Get device information. */
        HAL_SetProductKey(PRODUCT_KEY);
        HAL_SetDeviceName(DEVICE_NAME);
        HAL_SetDeviceSecret(DEVICE_SECRET);
        /**< end*/
        _ota_mqtt_client()
    }
  2. In the _ota_mqtt_client function, establish a connection and configure the main OTA logic.

        /* Device AUTH */
        if (0 != IOT_SetupConnInfo(g_product_key, g_device_name, g_device_secret, (void **)&pconn_info)) {
            EXAMPLE_TRACE("AUTH request failed!");
            rc = -1;
            goto do_exit;
        }
    
        /* Initialize MQTT parameter */
        memset(&mqtt_params, 0x0, sizeof(mqtt_params));
        mqtt_params.port = pconn_info->port;
        mqtt_params.host = pconn_info->host_name;
        mqtt_params.client_id = pconn_info->client_id;
        mqtt_params.username = pconn_info->username;
        mqtt_params.password = pconn_info->password;
        mqtt_params.pub_key = pconn_info->pub_key;
    
        mqtt_params.request_timeout_ms = 2000;
        mqtt_params.clean_session = 0;
        mqtt_params.keepalive_interval_ms = 60000;
        mqtt_params.read_buf_size = OTA_MQTT_MSGLEN;
        mqtt_params.write_buf_size = OTA_MQTT_MSGLEN;
    
        mqtt_params.handle_event.h_fp = event_handle;
        mqtt_params.handle_event.pcontext = NULL;
    
        /* Construct an MQTT client with the specified parameters. */
        pclient = IOT_MQTT_Construct(&mqtt_params);
        if (NULL == pclient) {
            EXAMPLE_TRACE("MQTT construct failed");
            rc = -1;
            goto do_exit;
        }
  3. In the _ota_mqtt_client function, initialize OTA. This involves subscribing to the device's firmware update notifications.

        h_ota = IOT_OTA_Init(PRODUCT_KEY, DEVICE_NAME, pclient);
        if (NULL == h_ota) {
            rc = -1;
            EXAMPLE_TRACE("initialize OTA failed");
            goto do_exit;
        }
  4. Create a loop to continuously listen for OTA update messages.

        int ota_over = 0;
        do {
            uint32_t firmware_valid;
            EXAMPLE_TRACE("wait ota upgrade command....");
    
            /* Receive MQTT messages. */
            IOT_MQTT_Yield(pclient, 200);
    
            /* Check if the received message is a firmware update notification. */
            if (IOT_OTA_IsFetching(h_ota)) {
             /* Download the OTA content and report the download progress. */
             /* Verify the MD5 hash of the firmware. */
            }
    
            } while (!ota_over);

    The firmware update logic can be executed only when the IOT_OTA_IsFetching function returns 1. This occurs after a firmware update event is pushed from the server. The following steps describe how to push a firmware update event.

    1. Go to the OTA Service page in the IoT Platform console and click Add New Firmware.

    2. Click Create Firmware and then Verify Firmware.

    3. Click Batch Upgrade for the new firmware. Select the product that corresponds to the device trituple in examples/ota/ota_mqtt-example.c.

    4. From the target version drop-down list, select the current version. Set Upgrade Scope to Directional Upgrade. From the Equipment Scope list, select the device that corresponds to your device trituple, and then click OK.

  5. Download the OTA content and report the progress.

        do {
    
            /* Download the OTA firmware. */
            len = IOT_OTA_FetchYield(h_ota, buf_ota, OTA_BUF_LEN, 1);
            if (len > 0) {
                if (1 != fwrite(buf_ota, len, 1, fp)) {
                    EXAMPLE_TRACE("write data to file failed");
                    rc = -1;
                    break;
                }
            } else {
    
                /* Report the download progress. */
                IOT_OTA_ReportProgress(h_ota, IOT_OTAP_FETCH_FAILED, NULL);
                EXAMPLE_TRACE("ota fetch fail");
            }
    
            /* Get OTA information. */
            /* Get the downloaded data size, total file size, MD5 information, and version. */
            IOT_OTA_Ioctl(h_ota, IOT_OTAG_FETCHED_SIZE, &size_downloaded, 4);
            IOT_OTA_Ioctl(h_ota, IOT_OTAG_FILE_SIZE, &size_file, 4);
            IOT_OTA_Ioctl(h_ota, IOT_OTAG_MD5SUM, md5sum, 33);
            IOT_OTA_Ioctl(h_ota, IOT_OTAG_VERSION, version, 128);
    
            last_percent = percent;
            percent = (size_downloaded * 100) / size_file;
            if (percent - last_percent > 0) {
    
                /* Report the download progress. */
                IOT_OTA_ReportProgress(h_ota, percent, NULL);
                IOT_OTA_ReportProgress(h_ota, percent, "hello");
            }        IOT_MQTT_Yield(pclient, 100);
    
            /* Check if the download is complete. */
        } while (!IOT_OTA_IsFetchFinish(h_ota));
  6. Verify the MD5 hash value.

        IOT_OTA_Ioctl(h_ota, IOT_OTAG_CHECK_FIRMWARE, &firmware_valid, 4);
        if (0 == firmware_valid) {
            EXAMPLE_TRACE("The firmware is invalid");
        } else {
            EXAMPLE_TRACE("The firmware is valid");
        }
    
        ota_over = 1;
  7. Call IOT_OTA_Deinit to release all resources.

        if (NULL != h_ota) {
            IOT_OTA_Deinit(h_ota);
        }
    
        if (NULL != pclient) {
            IOT_MQTT_Destroy(&pclient);
        }
    
        if (NULL != msg_buf) {
            HAL_Free(msg_buf);
        }
    
        if (NULL != msg_readbuf) {
            HAL_Free(msg_readbuf);
        }
    
        if (NULL != fp) {
            fclose(fp);
        }
    
        return rc;
  8. Store the firmware.

    In the _ota_mqtt_client function, use the following functions to open, write to, and close a file.

    fp = fopen("ota.bin", "wb+")
    ...
    if (1 != fwrite(buf_ota, len, 1, fp)) {
        EXAMPLE_TRACE("write data to file failed");
        rc = -1;
        break;
    }
    ...
    if (NULL != fp) {
        fclose(fp);
    }

OTA example: Advanced API

This section uses the src/dev_model/examples/linkkit_example_solo.c example to demonstrate how to implement OTA features using the advanced API.

  1. Initialize the master device, register the Firmware-over-the-air (FOTA) callback, and establish a connection to the cloud.

    int res = 0;
    int domain_type = 0, dynamic_register = 0, post_reply_need = 0;
    iotx_linkkit_dev_meta_info_t master_meta_info;
    
    memset(&g_user_example_ctx, 0, sizeof(user_example_ctx_t));
    
    memset(&master_meta_info, 0, sizeof(iotx_linkkit_dev_meta_info_t));
    memcpy(master_meta_info.product_key, PRODUCT_KEY, strlen(PRODUCT_KEY));
    memcpy(master_meta_info.product_secret, PRODUCT_SECRET, strlen(PRODUCT_SECRET));
    memcpy(master_meta_info.device_name, DEVICE_NAME, strlen(DEVICE_NAME));
    memcpy(master_meta_info.device_secret, DEVICE_SECRET, strlen(DEVICE_SECRET));
    
    /* Register Callback */
    ...
    ...
    IOT_RegisterCallback(ITE_FOTA, user_fota_event_handler);
    
    domain_type = IOTX_CLOUD_REGION_SHANGHAI;
    IOT_Ioctl(IOTX_IOCTL_SET_DOMAIN, (void *)&domain_type);
    
    /* Choose Login Method */
    dynamic_register = 0;
    IOT_Ioctl(IOTX_IOCTL_SET_DYNAMIC_REGISTER, (void *)&dynamic_register);
    
    /* post reply doesn't need */
    post_reply_need = 1;IOT_Ioctl(IOTX_IOCTL_RECV_EVENT_REPLY, (void *)&post_reply_need);
    
    /* Create Master Device Resources */
    g_user_example_ctx.master_devid = IOT_Linkkit_Open(IOTX_LINKKIT_DEV_TYPE_MASTER, &master_meta_info);
    if (g_user_example_ctx.master_devid < 0) {
        EXAMPLE_TRACE("IOT_Linkkit_Open Failed\n");
        return -1;}
    
    /* Start Connect Aliyun Server */
    res = IOT_Linkkit_Connect(g_user_example_ctx.master_devid);
    if (res < 0) {
        EXAMPLE_TRACE("IOT_Linkkit_Connect Failed\n");
        return -1;
    }
  2. Implement the user_fota_event_handler callback from the preceding code.

    This callback is triggered in two scenarios:

    • The device receives a new firmware update notification from the cloud.

    • The device initiates a query for a new firmware update, and the cloud responds with a notification.

    After receiving the notification, call IOT_Linkkit_Query to download the firmware.

    int user_fota_event_handler(int type, const char *version){
        char buffer[128] = {0};
        int buffer_length = 128;
    
        /* 0 - new firmware exist, query the new firmware */
        if (type == 0) {
            EXAMPLE_TRACE("New Firmware Version: %s", version);
    
            IOT_Linkkit_Query(EXAMPLE_MASTER_DEVID, ITM_MSG_QUERY_FOTA_DATA, (unsigned char *)buffer, buffer_length);
        }
    
        return 0;
    }
  3. Store the firmware.

    You must implement the following three Hardware Abstraction Layer (HAL) interfaces to store the firmware.

    /* Called by the SDK before the firmware download starts. */
    void HAL_Firmware_Persistence_Start(void);
    
    /* Called by the SDK when it receives firmware data. */
    int HAL_Firmware_Persistence_Write(char *buffer, uint32_t length);
    
    /* Called by the SDK when the firmware download is complete. */
    int HAL_Firmware_Persistence_Stop(void);
  4. Manually query for a new firmware update.

    IOT_Linkkit_Query(user_example_ctx->master_devid, ITM_MSG_REQUEST_FOTA_IMAGE,
                          (unsigned char *)("app-1.0.0-20180101.1001"), 30);

OTA support for multiple modules

In addition to device firmware updates, OTA can also download and update software modules on a device. You must create modules in the IoT Platform console.

In the Add New Firmware dialog box, find the Firmware Module field, select Add New Module, and enter a module name, such as mcu.

In your device-side code, you must set the module parameter to match the module name you configured in the console:

char* module = "mcu";
IOT_Ioctl(IOTX_IOCTL_SET_MODULE, (void *)module);

The rest of the process is the same as a standard OTA test. After you deploy the update task from the IoT Platform console, the device generates logs similar to the following. Pay attention to the module field:

[dbg] otamqtt_UpgrageCb(111): topic=/ota/device/upgrade/a1******PjW/foDzDj*******3PDJ9d
[dbg] otamqtt_UpgrageCb(112): len=431, topic_msg={"code":"1000","data":{"size":143360,"module":"mcu","sign":"867f1536fb********a2205436252","version":"111","url":"https://iotx-******ily.oss-cn-shanghai.aliyuncs.com/ota/338ac9db05545dcab9*********52/ck75mi***********5xbgz61vl.tar?Expires=1582951757&OSSAccessKeyId=aS4***********j6Gy&Signature=urw%2F9WAlizQui*************0A%3D","signMethod":"Md5","md5":"867f1536fb*********436252"},"id":1582865357795,"message":"success"}
[dbg] otamqtt_UpgrageCb(129): receive device upgrade
[inf] ofc_Init(47): protocol: https
received state: -0x092C(msg queue size: 0, max size: 50)
received state: -0x092C(msg enqueue w/ message type: 43)
received state: -0x092C(msg dequeue)
received state: -0x0938(alink event type: 43)
received state: -0x0938(new fota information received, 111)
user_fota_module_event_handler.219: New Firmware Version: 111, module: mcu