子设备 OTA 升级

更新时间:2023-09-20 09:26:54下载pdf

设备固件 OTA(Over-the-air)空中升级是物联网中迭代的重要功能之一,云端提供提供了固件 OTA 升级与管理服务,本文将详细介绍 OTA 流程与使用方法。

功能描述

App云端SDK设备应用子设备MQTT: 升级命令MQTT: 升级命令dev_upgrade 回调tuya_iot_upgrade_devHTTPS: 下载文件HTTPS: 回应GET_FILE_DATA_CB向文件写入图片数据MQTT: 上报下载进度条MQTT: 上报下载进度条loop发送固件数据ACKtuya_iot_dev_upgd_progress_rep-tMQTT: 更新下载进度条MQTT: 更新下载进度条loop上报新版本号tuya_iot_gw_subdevice_updateHTTPS: 更新子设备版本号MQTT: 上报 OTA 结果App云端SDK设备应用子设备子设备固件 OTA 升级的交互流程
  1. SDK 通知应用有新固件。
  2. 应用调用 SDK 的固件下载接口。
  3. SDK 分块下载固件文件,把下载的数据给应用处理。
  4. 固件下载完成,SDK 通知应用下载结果,应用根据下载结果做决策。
    • 若下载成功,应用把固件发送给子设备,并调用 SDK 的更新进度条接口。
    • 若下载失败,应用调用 SDK 的升级状态上报接口,上报升级失败。

前提条件

创建固件 KEY

涂鸦 IoT 开发平台从固件 KEY 的维度来管理固件,一个固件 KEY 对应 N 个版本固件。所以在上传固件之前,您需要先创建固件 KEY。创建固件 KEY 的步骤如下:

  1. 产品列表 找到您已经创建的产品。

  2. 单击 继续开发。后续以一个三路开关产品为例,介绍固件 KEY 的创建流程。

    子设备 OTA 升级

  3. 进入产品开发页面,单击 硬件开发 选项卡,在 硬件开发 页面选择 新增自定义固件

    子设备 OTA 升级

  4. 在新增固件页面填写信息,完成后单击 生成固件 KEY,完成固件 KEY 创建。

    关键字段说明:

    • 固件类型:协议类型 < 10 是 SDK 预留,您要使用 >= 10,所以此处选择 扩展固件

    • 升级通道号:与绑定子设备接口的协议类型入参一致。

    • 固件升级超时时间:在超时时间内未上报新的版本号,App 提示升级超时。

      子设备 OTA 升级

新建固件版本

上一小节已成功创建固件 KEY,本小节介绍如何上传固件。

  1. 单击 新增固件版本

    子设备 OTA 升级

  2. 填写 固件版本,并上传 生产固件升级固件,单击 保存并上架

    此处仅为了演示,上传的固件内容为 “Hello World” 字符串。

    子设备 OTA 升级
  3. 上架确认,单击 确认上架

    子设备 OTA 升级

升级验证

上一小节已新建一个固件版本,并将固件上传到涂鸦 IoT 开发平台,本小节介绍如何添加白名单验证。

  1. 展开 更多,选择 OTA 升级

    子设备 OTA 升级

  2. 选择您新创建的固件 KEY,并单击 新建固件升级

    子设备 OTA 升级
  3. 选择刚才新建的固件版本作为 固件版本,选择 App 提醒升级 作为 升级方式,并输入版本描述后,单击 确定

    子设备 OTA 升级
  4. 单击 验证,需要输入设备虚拟 ID 添加到白名单,打开 App 进入子设备的面板主页,会提示有新版可升级。

    设备虚拟 ID 可以在 App 上查看。进入子设备的面板后,轻按右上角的 “…” 打开高级页面,再轻按 设备信息 就能看到。

    子设备 OTA 升级

使用方法

  • 调用 tuya_subdev_user_sigle_type_reg 接口注册设备管理回调,并实现 dev_upgrade 的回调接口。
  • 实现 网关接收固件 以及 通知子设备升级 回调。
  • 实现与子设备的升级交互。

数据结构

/**
 * @brief sub-device management callback
 */
typedef struct __ty_gw_subdev_mgr_cbs_s {
    GW_PERMIT_ADD_DEV_CB                dev_add;            // permit joining callback, see GW_PERMIT_ADD_DEV_CB
    GW_DEV_DEL_CB                       dev_del;            // remove callback, see GW_DEV_DEL_CB
    DEV_RESET_IFM_CB                    dev_reset;          // reset callback, see DEV_RESET_IFM_CB
    GW_BIND_DEV_INFORM_CB               dev_bind;           // bind result callback, see GW_BIND_DEV_INFORM_CB

    DEV_OBJ_DP_CMD_CB                   dp_cmd_obj;         // obj DP command, see DEV_OBJ_DP_CMD_CB
    DEV_RAW_DP_CMD_CB                   dp_cmd_raw;         // raw DP command, see DEV_RAW_DP_CMD_CB
    DEV_DP_QUERY_CB                     dp_query;           // DP query, see DEV_DP_QUERY_CB

    DEV_HEARTBEAT_SEND_CB               dev_hb;             // heartbeat query callback, see DEV_HEARTBEAT_SEND_CB
    DEV_UG_INFORM_CB                    dev_upgrade;        // upgrade callback, see DEV_UG_INFORM_CB
    GW_DEV_WAKEUP_CB                    dev_wakeup;         // low power device wakeup callback, see GW_DEV_WAKEUP_CB
    GW_DEV_GRP_INFM_CB                  dev_grp_info;       // group control callback, see GW_DEV_GRP_INFM_CB
    GW_DEV_SCENE_INFM_CB                dev_sce_info;       // scene control callback, see GW_DEV_SCENE_INFM_CB

    GW_DEV_SIGMESH_TOPO_UPDAET_CB       bt_topo_update;     // Bluetooth LE device added callback, see GW_DEV_SIGMESH_TOPO_UPDAET_CB
    GW_DEV_SIGMESH_DEV_CACHE_DEL_CB     bt_cache_del;       // Bluetooth LE device removed callback, see GW_DEV_SIGMESH_DEV_CACHE_DEL_CB
    GW_DEV_SIGMESH_CONN_CB              bt_conn;            // Bluetooth LE device event callback, see GW_DEV_SIGMESH_CONN_CB

    DEV_ONLINE_STAT_SEND_CB             dev_online;         // online state changed callback, see DEV_ONLINE_STAT_SEND_CB
}TY_GW_SUBDEV_MGR_CBS_S;
/**
 * @brief tuya sdk ota firmware info
 */
typedef struct {
    /** firmware type */
    DEV_TYPE_T tp;
    /** upgrade type, see UPGRADE_TYPE_T */
    UPGRADE_TYPE_T type;
    /** firmware download URL */
    CHAR_T fw_url[FW_URL_LEN + 1];
    /** firmware version */
    CHAR_T sw_ver[SW_VER_LEN + 1];
    /** firmware size in BYTE */
    UINT_T file_size;
    /** firmware HMAC */
    CHAR_T fw_hmac[FW_HMAC_LEN + 1];
    /** firmware MD5 */
    CHAR_T fw_md5[FW_MD5_LEN + 1];
    /** is difference OTA or not */
    BOOL_T diff_ota;
} FW_UG_S;

API 说明

设备升级进度上报

#define tuya_iot_dev_upgd_progress_rept(percent, devid, tp) \
tuya_iot_dev_upgd_progress_with_remain_time(percent, devid, tp, 0)

应用调用该接口,可向云端上报当前升级进度。

设备升级结果上报

#define TUS_RD 1                     // ready
#define TUS_UPGRDING 2               // upgrading
#define TUS_UPGRD_FINI 3             // finish
#define TUS_UPGRD_EXEC 4             // error
OPERATE_RET tuya_iot_dev_upgd_result_report(IN CONST CHAR_T *dev_id, IN CONST DEV_TYPE_T type, IN CONST INT_T result);

应用调用该接口,可向云端上报子设备升级错误结果。默认情况下,版本号升级变化即成功

设备升级

OPERATE_RET tuya_iot_upgrade_dev_notify(IN CONST CHAR_T *devid,
                                      IN CONST FW_UG_S *fw, \
                                      IN CONST GET_FILE_DATA_CB get_file_cb, \
                                      IN CONST UPGRADE_NOTIFY_CB upgrd_nofity_cb, \
                                      IN CONST PVOID_T pri_data, \
                                      BOOL_T notify, UINT_T download_buf_size);
#define tuya_iot_upgrade_dev(devid, fw, get_file_cb, upgrd_nofity_cb, pri_data) \
  tuya_iot_upgrade_dev_notify(devid, fw, get_file_cb, upgrd_nofity_cb, pri_data, TRUE, 0)

应用收到 SDK 的 dev_upgrade 回调,可调用该接口获取子设备固件。其中:

  • get_file_cb 为获取子设备固件回调
  • upgrd_nofity_cb 为获取固件后通知子设备升级回调

使用示例

STATIC CHAR_T upg_dev[DEV_ID_LEN] = {0};

/**
 * @brief device OTA callback
 * @param[in]     fw          : OTA firmware information
 * @param[in]     total_len   : total length of OTA image
 * @param[in]     offset      : OTA image offset
 * @param[in]     data        : payload of OTA image
 * @param[in]     len         : length of data
 * @param[out]    remain_len  : tell SDK remain length
 * @param[in]     pri_data    : pri_data is passed as the argument of tuya_iot_upgrade_dev()
 */
STATIC OPERATE_RET __dev_ota_data(IN CONST FW_UG_S *fw,
                                  IN CONST UINT_T total_len,
                                  IN CONST UINT_T offset,
                                  IN CONST BYTE_T *data,
                                  IN CONST UINT_T len,
                                  OUT UINT_T *remain_len,
                                  IN PVOID_T pri_data)
{

    /**
     * TODO:
     *      Receiving OTA image data callback. Maybe should write to file
     */

     /**
      * Here we have an example showing what should do in this callback
      *     a) print OTA image data (maybe you should write to file )
      *     b) report to the cloud.
      */

    for (INT_T i = 0; i < len; i++) {
        PR_DEBUG_RAW("%02x ", data[i]);
    }
    PR_DEBUG_RAW("\n");

    UINT_T percent = 0;
    percent = ((offset * 100) / (total_len+1));
    if (percent >= 99) {
        percent = 98;
    }
    tuya_iot_dev_upgd_progress_rept(percent, upg_dev, fw->tp);

    return OPRT_OK;
}

/**
 * @brief device OTA notify callback
 * @param[in]     fw                : OTA firmware information
 * @param[in]     download_result   : result of download OTA image
 * @param[in]     pri_data          : pri_data is passed as the argument of tuya_iot_upgrade_dev()
*/
STATIC VOID __dev_ota_notify(IN CONST FW_UG_S *fw,
                             IN CONST INT_T download_result,
                             IN PVOID_T pri_data)
{

    /**
     * TODO:
     * download_result == OPRT_OK , download success , send notify to sub-device and transform it
     * download_result != OPRT_OK , download failed, report result
     */

    /**
     * Here we have an example :
     *      a) if download succeeded, report new software version
     *      b) if download failed, report result:
     *                  #define TUS_RD 1            // ready
     *                  #define TUS_UPGRDING 2      // upgrading
     *                  #define TUS_UPGRD_FINI 3    // finish
     *                  #define TUS_UPGRD_EXEC 4    // error
     */
    if (download_result == OPRT_OK) {
        tuya_iot_gw_subdevice_update(upg_dev, fw->sw_ver);
    } else {
        tuya_iot_dev_upgd_result_report(upg_dev, fw->tp, 4);
    }
}

/**
 * @brief SDK receives upgrade cmd callback
 * @param[in]     dev_id            : device's ID
 * @param[in]     fw                : OTA firmware information
*/
STATIC VOID __dev_upgrade_cb(CONST CHAR_T *dev_id, CONST FW_UG_S *fw)
{

    /**
      * TODO:
      * call tuya_iot_upgrade_dev() to download sub-device's new image and do something
      */

    strncpy(upg_dev, dev_id, SIZEOF(upg_dev));
    tuya_iot_upgrade_dev(dev_id, fw, __dev_ota_data, __dev_ota_notify, NULL);
}

TY_GW_SUBDEV_MGR_CBS_S dev_mgr_cbs = {
    .dev_upgrade   = __dev_upgrade_cb,
};

tuya_subdev_user_sigle_type_reg( &dev_mgr_cbs , DEV_ATTACH_MOD_1 );