Developing OTA Capabilities on Devices


The OTA capability largely depends on the physical configuration and function of the device. EnOS provides a device SDK that includes OTA capabilities for hardware developers, which supports the following capabilities.

  • Reporting the firmware version: The device reports its version when it gets connected to EnOS. EnOS stores the device’s version number.
  • Receiving the upgrade request pushed by EnOS: The device receives the upgrade request pushed by EnOS and downloads the firmware file.
  • Sending upgrade requests: The device can send upgrade requests to EnOS and search whether there are any new versions available.
  • Reporting upgrade progress and the reasons for upgrade failure: The device can report its upgrade progress or the reasons for upgrade failure during its upgrade process to EnOS for upgrade management.

Note

The upgrade depends on whether the flash on the device side has enough physical space. EnOS only provides message channels and file download services for OTA upgrade, and an SDK for developers to call the interfaces. The developers will need to switch over to the new firmware, reboot, and restart after the file download.

Firmware Upgrade Topics

EnOS provides message topics for firmware upgrade, and the upgrade request supports MQTT-based message transmission and subscription.

  1. The device reports its firmware version to EnOS. The device reports its current firmware version after it connects to EnOS through this topic:

    Request topic: /sys/${productkey}/${devicekey}/ota/device/inform

    Response topic: /sys/${productkey}/${devicekey}/ota/device/inform_reply

  2. The device receives the OTA request from EnOS. The device receives the OTA upgrade notifications pushed from EnOS, including the firmware name, version, MD5, and download URL through this topic:

    Request topic: /sys/${productkey}/${devicekey}/ota/device/upgrade

    Response topic: /sys/${productkey}/${devicekey}/ota/device/upgrade_reply

  3. The device reports its firmware upgrade progress. The device requests the firmware download through HTTPS, and reports the firmware upgrade event, error code, and download progress (%) through this topic:

    Request topic: /sys/${productkey}/${devicekey}/ota/device/progress

    Response topic: /sys/${productkey}/${devicekey}/ota/device/progress_reply

  4. The device requests for new firmware versions. EnOS creates the OTA upgrade policy as “Upon device request”. The device sends the upgrade request through this topic and receives the available versions returned from EnOS:

    Request topic: /sys/${productkey}/${devicekey}/ota/device/getversion

    Response topic: /sys/${productkey}/${devicekey}/ota/device/getversion_reply

Before You Start

Step 1: Installing Device SDK

Download enos-device-sdk-java. If Maven is used, its dependencies are given as follows:

<dependency>
  <groupId>com.envisioniot</groupId>
  <artifactId>enos-mqtt</artifactId>
  <version>2.1.2</version>
</dependency>

Step 2: Downloading Device Firmware

Download the file through cloud service SDK

Sample Code

The sample code for developing the OTA capabilities by using the Java SDK is shown below.

import com.envisioniot.enos.iot_mqtt_sdk.core.IConnectCallback;
import com.envisioniot.enos.iot_mqtt_sdk.core.MqttClient;
import com.envisioniot.enos.iot_mqtt_sdk.core.exception.EnvisionException;
import com.envisioniot.enos.iot_mqtt_sdk.core.msg.IMessageHandler;
import com.envisioniot.enos.iot_mqtt_sdk.core.msg.IMqttDeliveryMessage;
import com.envisioniot.enos.iot_mqtt_sdk.core.profile.DefaultProfile;
import com.envisioniot.enos.iot_mqtt_sdk.message.downstream.ota.OtaUpgradeCommand;
import com.envisioniot.enos.iot_mqtt_sdk.message.upstream.ota.*;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * device firmware over-the-air
 */
public class OtaSample {

    //Please use your own productKey, deviceKey, and deviceSecret
    static String productKey = "testPK";
    static String deviceKey = "testDK";
    static String deviceSecret = "testDS";

    //Use the actual mqtt-broker url
    static String brokerUrl = "tcp://{mqtt-broker-url}";

    static MqttClient client;

    public static void main(String[] args) throws Exception {
        client = new MqttClient(new DefaultProfile(brokerUrl, productKey, deviceKey, deviceSecret));
        initWithCallback(client);

        //Report firmware version first
        reportVersion("initVersion");

        //        upgradeFirmwareByCloudPush();


        //        upgradeFirmwareByDeviceReq();
    }

    public static void upgradeFirmwareByCloudPush() {
        client.setArrivedMsgHandler(OtaUpgradeCommand.class, new IMessageHandler<OtaUpgradeCommand, IMqttDeliveryMessage>() {
            @Override
            public IMqttDeliveryMessage onMessage(OtaUpgradeCommand otaUpgradeCommand, List<String> list) throws Exception {
                System.out.println("receive command: " + otaUpgradeCommand);

                Firmware firmware = otaUpgradeCommand.getFirmwareInfo();

                //TODO: download firmware from firmware.fileUrl

                //Mock reporting progress
                reportUpgradeProgress("20", "20");
                TimeUnit.SECONDS.sleep(2);

                reportUpgradeProgress("25", "25");
                TimeUnit.SECONDS.sleep(20);

                reportUpgradeProgress("80", "80");
                TimeUnit.SECONDS.sleep(20);

                //Firmware upgrade success, report new version
                reportVersion(otaUpgradeCommand.getFirmwareInfo().version);

                return null;
            }
        });
    }

    public static void upgradeFirmwareByDeviceReq() throws Exception {
        List<Firmware> firmwareList = getFirmwaresFromCloud();
        String version = null;
        for (Firmware firmware : firmwareList) {
            version = firmware.version;
            StringBuffer sb = new StringBuffer();
            sb.append("Firmware=>[");
            sb.append("version=" + firmware.version);
            sb.append("signMethod=" + firmware.signMethod);
            sb.append("sign=" + firmware.sign);
            sb.append("fileUrl=" + firmware.fileUrl);
            sb.append("fileSize=" + firmware.fileSize);
            sb.append("]");
            System.out.println(sb.toString());
        }
        if (version != null) {
            reportUpgradeProgress("20", "20");
            TimeUnit.SECONDS.sleep(10);
            reportUpgradeProgress("80", "80");
            TimeUnit.SECONDS.sleep(20);
            reportVersion(version);
        }
    }

    public static void reportVersion(String version) throws Exception {
        OtaVersionReportRequest.Builder builder = new OtaVersionReportRequest.Builder();
        builder.setProductKey(productKey).setDeviceKey(deviceKey).setVersion(version);
        OtaVersionReportRequest request = builder.build();
        System.out.println("send =>" + request.toString());
        client.fastPublish(builder.build());
    }

    private static void reportUpgradeProgress(String progress, String desc) throws Exception {
        OtaProgressReportRequest.Builder builder = new OtaProgressReportRequest.Builder();
        builder.setStep(progress).setDesc(desc);
        client.fastPublish(builder.build());
    }

    private static List<Firmware> getFirmwaresFromCloud() throws Exception {
        OtaGetVersionRequest.Builder builder = new OtaGetVersionRequest.Builder();
        builder.setProductKey(productKey).setDeviceKey(deviceKey);
        OtaGetVersionRequest request = builder.build();
        OtaGetVersionResponse response = client.publish(request);
        System.out.println("send getversion request =>" + request.toString());
        System.out.println("receive getversion response =>" + response.toString());
        return response.getFirmwareList();
    }

    private static void initWithCallback(MqttClient client) {
        System.out.println("start connect with callback ... ");
        try {
            client.connect(new IConnectCallback() {
                @Override
                public void onConnectSuccess() {
                    System.out.println("connect success");
                }

                @Override
                public void onConnectLost() {
                    System.out.println("onConnectLost");
                }

                @Override
                public void onConnectFailed(int reasonCode) {
                    System.out.println("onConnectFailed : " + reasonCode);
                }

            });
        } catch (EnvisionException e) {
            e.printStackTrace();
        }
    }
}

Firmware Upgrade Protocol

Reporting the Firmware Version

The device can send its current firmware version to EnOS. You can create an upgrade task for the device that has reported its firmware version.


Upstream TOPIC

  • Request topic: /sys/${productkey}/${devicekey}/ota/device/inform
  • Response topic: /sys/${productkey}/${devicekey}/ota/device/inform_reply


Request data format:

{
    "id":"123",
    "version":"1.0",
    "method":"ota.device.inform",
    "params": {
        "version":"xxxxxxxx"
    }
}

Response data format:

{
    "id": "123",
    "code": 200,
    "data": {}
}
Parameter Description
Parameters Type Mandatory/Optional Description
id Long Optional The message ID. It is a reserved parameter that is reserved for future use.
version String Mandatory The version of the protocol. The current version is 1.0.
method String Mandatory The request method, which is “ota.device.inform”.
params Object Mandatory The parameters used for reporting the version number.
version String Mandatory The reported version number.
code Integer Mandatory The return code. “200” indicates that the requested operation is executed successfully.

Receiving Upgrade Request From EnOS

After the firmware upgrade task is created in the console, qualified online devices will receive the firmware information pushed from EnOS. Offline devices will receive the upgrade request when they next come back online.


Downstream TOPIC:

  • Request topic: /sys/${productkey}/${devicekey}/ota/device/upgrade
  • Response: /sys/${productkey}/${devicekey}/ota/device/upgrade_reply


Request data format:

{
    "id":"123",
    "version":"1.0",
    "method":"ota.device.upgrade",
    "params": {
        "version":"v1.0",
        "sign":"xxxxxxx",
        "signMethod":"md5",
        "fileUrl":"/1b30e0ea83002000/ota/kadak13",
        "fileSize":1024
    }
}

Response data format:

{
    "id": "123",
    "code": 200,
    "data": {}
}
Parameter Description
Parameters Type Mandatory/Optional Description
id Long Optional The message ID. It is a reserved parameter that is reserved for future use.
version String Mandatory The version of the protocol. The current version is 1.0.
method String Mandatory The request method, which is “ota.device.getversion”.
params Struct Optional The parameters of the firmware details.
version String Mandatory The firmware version.
sign String Mandatory The firmware signature.
signMethod String Mandatory The firmware signature algorithm.
fileUrl String Mandatory The firmware file url.
fileSize Long Mandatory The firmware file size in bytes.
code Integer Mandatory The return code. “200” indicates that the requested operation is executed successfully.
data Struct Optional The detailed returned information in JSON format.

Reporting Upgrade Progress

After the device starts to download the firmware, it reports its upgrade progress or reasons of the upgrade failure through this topic. You can view the upgrade progress in the console.


Upstream TOPIC:

  • Request topic: /sys/${productkey}/${devicekey}/ota/device/progress
  • Response topic: /sys/${productkey}/${devicekey}/ota/device/progress_reply


Request data format:

{
    "id":"123",
    "version":"1.0",
    "method":"ota.device.progress",
    "params": {
        "step":"1",
        "desc":"desc"
    }
}

Response data format:

{
    "id": "123",
    "code": 200,
    "data": {}
}
Parameter Description
Parameters Type Mandatory/Optional Description
id Long Optional The message ID. It is a reserved parameter that is reserved for future use.
version String Mandatory The version of the protocol. The current version is 1.0.
method String Mandatory The request method, which is “ota.device.process”.
params Struct Mandatory The parameters of the upgrade progress.
step String Mandatory The upgrade progress. Its normal range is [0, 100], where -1 indicates failure, -2 indicates download failure, -3 indicates verification failure, and -4 indicates burning failure.
desc String Optional The description of the current step, which may contain error information when exceptions occur.
code Integer Mandatory The return code. “200” indicates that the requested operation is executed successfully.
Return Code Error Message Explanation
764 Device task not found The OTA task of this device is not found.
765 Device task not start The device has not started OTA upgrade yet.

Requesting Upgrade

For upgrades that are not pushed by the server, the upgrade task for for device to request for an upgrade may be created in the console. At this point, the cloud will not actively push any upgrade request but wait until the device requests for an upgrade to obtain the upgradeable version information. If there are multiple versions available in the cloud, a list of the upgradeable versions will be returned and the device or its owner may select a suitable one from the list to upgrade.


Upstream TOPIC

  • Request topic: /sys/${productkey}/${devicekey}/ota/device/getversion
  • Response topic: /sys/${productkey}/${devicekey}/ota/device/getversion_reply


Request data format

{
    "id":"123",
    "version":"1.0",
    "method":"ota.device.getversion",
    "params": {
    }
}

Response data format:

{
    "id":"123",
    "code":200,
    "data": [
         {
            "version":"v1.0",
            "sign":"xxxxxxx",
            "signMethod":"md5",
            "fileUrl":"/1b30e0ea83002000/ota/kadak13",
            "fileSize":1024
        }
    ]
}
Parameter Description
Parameters Type Mandatory/Optional Description
id Long Optional The message ID. It is a reserved parameter that is reserved for future use.
version String Mandatory The version of the protocol. The current version is 1.0.
method String Mandatory The request method, which is “ota.device.getversion”.
params Struct Optional None
code Integer Mandatory The return code. “200” indicates that the requested operation is executed successfully.
data Array Optional The detailed returned information in JSON format. If there are multiple available versions for this device, the information for multiple versions will be returned.
version String Mandatory The firmware version.
sign String Mandatory The firmware signature.
signMethod String Mandatory The firmware signature algorithm.
fileUrl String Mandatory The firmware file URL.
fileSize Long Mandatory The firmware file size in bytes.

Exception Handling

The exceptions that occur during a firmware upgrade is indicated by step and desc when the device reports the firmware upgrade progress. A step less than 0 indicates an exception. desc contains the description of this exception.

Upgrade Exceptions
Step Value Description
-1 Upgrade failure.
-2 Download failure.
-3 Verification failure.
-4 Burning failure.

For other custom exceptions, set step less than -4 and desc as the description to that exception.