MCU OTA 服务

更新时间:2024-04-11 06:52:37下载pdf

概述

OTA(Over-the-Air)即空中下载技术,通过网络远程为设备更新和升级软件程序。

Wi-Fi 标准协议接入支持 MCU OTA 功能。通过 涂鸦 IoT 开发平台,先将需要更新的固件文件上传至涂鸦服务器,然后 Wi-Fi 模组通过涂鸦协议对文件进行分包传输,最后 MCU 接收升级包并写入本地闪存,最终实现固件的升级。详细的平台操作,参考 固件升级选择和管理固件版本

功能流程

MCU OTA 功能需要设备 MCU 资源支持,设备配网成功后,通过平台配置或升级检测,可以启动 OTA 流程。升级流程如下:

模组MCU发送 MCU 升级启动回复升级包分包传输大小发送第 1 包固件数据存储固件数据,并在 5 秒内回复接收正确如果 5 秒内未收到回复,重发 3 次,未收到回复则升级失败发送第 N 包固件数据回复接收正确发送最后一包固件数据校准固件信息,并切换新固件,成功后重启,升级完成模组MCU

Wi-Fi 模组发送完所有的升级包,重新发送查询产品指令 0x01,MCU 需要在一分钟回复产品信息中的 MCU 软件版本号(升级后的版本号),版本号需要和在涂鸦后台配置升级的版本号保持一致。

MCU OTA 升级成功条件是固件包传输完成,设备重启上报新的版本号,两者同时完成才会提示成功。

使用场景

设备不论是在调试阶段还是出厂阶段,都可以使用 MCU OTA 功能进行 MCU 固件的更新迭代,实现设备功能的优化升级。在 MCU 硬件资源支持的情况下,建议增加 OTA 功能。

  • 固件管理:使用 MCU OTA 功能需要先将验证正确的 MCU 固件上传到涂鸦服务器,固件上传及管理操作参考 固件管理

  • 应用说明:根据是否移植涂鸦协议 SDK 分为移植 SDK 和自行对接。这两种方式都需要自行完成 BootLoader 开发。

    • 移植 SDK:使用 MCU SDK 提供的函数,回复固件分包传输大小启动升级过程,并接收模组发送的固件包,接收到的固件包需要 MCU 自行处理。可参考下面 SDK 示例介绍。

    • 自行对接:需要自行实现 OTA 功能相关协议指令,完成固件传输配置及固件包接收处理。

      设备需要先完成协议基础功能对接,MCU RAM 需大于 260B。

升级触发机制

  • 何时升级由客户在涂鸦 IoT 开发平台自己的产品页面配置相关升级选项触发。模组仅作为支持 MCU 升级的数据传输通道,也不对数据内容做任何解析。

  • 目前,涂鸦平台的 MCU 升级支持以下三种升级方式的配置:

    • App 提醒升级:用户每次进入设备控制面板都会收到升级提醒的弹窗,是否确认升级由用户自己在 App 确认。
    • App 强制升级:App 会有升级提醒弹窗,如果用户不确认升级,用户就没法正常使用该产品的控制面板。
    • App 检测升级:App 不会有任何升级提醒的弹窗,必须要用户自己在 App 上单击相关固件版本检测。如果有高版本的固件配置,才会显示升级提示信息。

指令列表

OTA 流程涉及以下协议指令:

命令字 命令说明
0x0a MCU 升级启动(升级包大小通知)
0x0b MCU 升级包传输

MCU 升级启动(升级包大小通知)

启动 OTA 升级后,模组首先会使用0x0a指令告诉 MCU 升级固件包大小,此时 MCU 需回复模组固件分包传输大小。

升级包分包传输大小

回复升级包分包传输值 升级包分包大小
0x00 默认 256 字节(兼容旧固件)
0x01 512 字节
0x02 1024 字节

模组发送

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x00
命令字 1 0x0a
数据长度 2 0x0004/0x0008
数据 4 固件包字节数,unsigned int,大端
数据 1 MCU Type: 10~19
数据 3 预留扩展
校验和 1 从帧头开始,按字节求和,得出的结果对 256 求余

示例55 aa 00 0a 00 04 00 00 68 00 75

表示固件包长度 26624,即 26KB。

MCU 返回

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x03
命令字 1 0x0a
数据长度 2 0x0001
数据 1 升级包分包传输大小:
  • 0x00:默认 256 字节(兼容旧固件)
  • 0x01:512 字节
  • 0x02:1024 字节
校验和 1 从帧头开始,按字节求和,得出的结果对 256 求余

示例55 aa 03 0a 00 01 00 0d

MCU 升级包传输

模组通过该指令按包传输固件包给到 MCU,MCU 接收到传输分包进行接收处理。若 MCU 5s 内未回复模组,模组将进行重发,若 3 次重发后仍未收到 MCU 回复,则认为升级失败。

  • 升级包传输数据格式:包偏移 + 包数据。
  • MCU 若收到该帧数据长度为 4 字节,并且包偏移 ≥ 固件大小,则包传输结束。

模组发送

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x00
命令字 1 0x0b
数据长度 2 0x0004+N
数据 4+N
  • data[0]–data[3]:固定为包偏移
  • data[4]–data[n]:数据包内容
校验和 1 从帧头开始,按字节求和,得出的结果对 256 求余

示例

若要升级的文件大小 530 字节,(最后一包数据可不回复)

  • 第一包数据,包偏移为 0x00000000,数据包长度为 256 字节
    55 aa 00 0b 01 04 00000000 xx…xx XX
  • 第二包数据,包偏移为 0x00000100,数据包长度为 256 字节
    55 aa 00 0b 01 04 00000100 xx…xx XX
  • 倒数第 2 包数据,包偏移为 0x00000200,数据包长度为 18 字节
    55 aa 00 0b 00 16 00000200 xx…xx XX
  • 最后一包,包偏移为 0x00000212,数据包长度为 0 字节
    55 aa 00 0b 00 04 00000212 xx...xx XX

MCU 返回

字段 字节数 说明
帧头 2 0x55aa
版本 1 0x03
命令字 1 0x0b
数据长度 2 0x0000
数据 0
校验和 1 从帧头开始,按字节求和,得出的结果对 256 求余

示例55 aa 03 0b 00 00 0d

使用示例

开启 MCU OTA 功能

打开 SUPPORT_MCU_FIRM_UPDATE 宏定义,开启 MCU 固件 OTA 功能。

#define         SUPPORT_MCU_FIRM_UPDATE                 //开启 MCU 固件升级功能,默认关闭

升级包分包传输大小配置

/* Firmware package size selection */
#ifdef SUPPORT_MCU_FIRM_UPDATE
#define PACKAGE_SIZE                   0        //包大小为 256 字节
//#define PACKAGE_SIZE                   1        //包大小为 512 字节
//#define PACKAGE_SIZE                   2        //包大小为 1024 字节
#endif
  1. 在串口接收数据处理函数 data_handle() 中,收到模组发来的 MCU 升级启动命令。

    /**
    * @brief 数据帧处理
    * @param[in] {offset} 数据起始位
    * @return Null
    */
    void data_handle(unsigned short offset)
    {
    ......
            case UPDATE_START_CMD:                                  //升级开始
                //获取升级包大小全局变量
                firm_flag = PACKAGE_SIZE;
                if(firm_flag == 0) {
                    firm_size = 256;
                }else if(firm_flag == 1) {
                    firm_size = 512;
                }else if(firm_flag == 2) {
                    firm_size = 1024;
                }
    
                firm_length = wifi_data_process_buf[offset + DATA_START];
                firm_length <<= 8;
                firm_length |= wifi_data_process_buf[offset + DATA_START + 1];
                firm_length <<= 8;
                firm_length |= wifi_data_process_buf[offset + DATA_START + 2];
                firm_length <<= 8;
                firm_length |= wifi_data_process_buf[offset + DATA_START + 3];
    
                upgrade_package_choose(PACKAGE_SIZE);
                firm_update_flag = UPDATE_START_CMD;
            break;
        ......
    }
    
  2. MCU SDK 调用 upgrade_package_choose() 回复分包大小,该函数定义在 protocol.c 文件中。开发者需通过以上宏,实现升级包分包传输大小配置

    /**
    * @brief 升级包大小选择
    * @param[in] {package_sz} 升级包大小
    * @ref           0x00:256 字节 (默认)
    * @ref           0x01:512 字节
    * @ref           0x02:1024 字节
    * @return Null
    * @note   MCU 需要自行实现该功能
    */
    void upgrade_package_choose(unsigned char package_sz)
    {
        #error "请自行实现升级包大小选择代码,完成后请删除该行"
        unsigned short send_len = 0;
        send_len = set_wifi_uart_byte(send_len, package_sz);
        wifi_uart_write_frame(UPDATE_START_CMD, MCU_TX_VER, send_len);
    }
    
  3. 调试助手示意图

    MCU OTA 服务

升级包存储

  1. 启动 OTA 过程后,模组按照约定的分包大小给 MCU 发送数据包,在串口接收数据处理函数 data_handle() 中,收到模组发来的数据包。

    /**
    * @brief 数据帧处理
    * @param[in] {offset} 数据起始位
    * @return Null
    */
    void data_handle(unsigned short offset)
    {
    ......
            case UPDATE_TRANS_CMD:                                  //升级传输
                if(firm_update_flag == UPDATE_START_CMD) {
                    //停止一切数据上报
                    stop_update_flag = ENABLE;
    
                    total_len = (wifi_data_process_buf[offset + LENGTH_HIGH] << 8) | wifi_data_process_buf[offset + LENGTH_LOW];
    
                    dp_len = wifi_data_process_buf[offset + DATA_START];
                    dp_len <<= 8;
                    dp_len |= wifi_data_process_buf[offset + DATA_START + 1];
                    dp_len <<= 8;
                    dp_len |= wifi_data_process_buf[offset + DATA_START + 2];
                    dp_len <<= 8;
                    dp_len |= wifi_data_process_buf[offset + DATA_START + 3];
    
                    firmware_addr = (unsigned char *)wifi_data_process_buf;
                    firmware_addr += (offset + DATA_START + 4);
    
                    if((total_len == 4) && (dp_len == firm_length)) {
                        //最后一包
                        ret = mcu_firm_update_handle(firmware_addr,dp_len,0);
                        firm_update_flag = 0;
                    }else if((total_len - 4) <= firm_size) {
                        ret = mcu_firm_update_handle(firmware_addr,dp_len,total_len - 4);
                    }else {
                        firm_update_flag = 0;
                        ret = ERROR;
                    }
    
                    if(ret == SUCCESS) {
                        wifi_uart_write_frame(UPDATE_TRANS_CMD, MCU_TX_VER, 0);
                    }
                    //恢复一切数据上报
                    stop_update_flag = DISABLE;
                }
            break;
    ......
    }
    
  2. MCU SDK 调用 mcu_firm_update_handle() 将数据、数据包位置数据和数据包长度传递给 MCU,MCU 根据相关参数做固件数据处理。开发者需实现该函数

    /**
    * @brief MCU 进入固件升级模式
    * @param[in] {value} 固件缓冲区
    * @param[in] {position} 当前数据包在于固件位置
    * @param[in] {length} 当前固件包长度,固件包长度为 0 时表示固件包发送完成
    * @return Null
    * @note   MCU 需要自行实现该功能
    */
    unsigned char mcu_firm_update_handle(const unsigned char value[],unsigned long position,unsigned short length)
    {
        #error "请自行完成 MCU 固件升级代码,完成后请删除该行"
        if(length == 0) {
            //固件数据发送完成
    
        }else {
            //固件数据处理
    
        }
    
        return SUCCESS;
    }
    
  3. 调试助手示意图

    MCU OTA 服务

升级完成

mcu_firm_update_handle() 函数接收到固件包长度为 0 时,表示固件包发送完成,MCU 在此时可以做 MCU 固件升级处理。开发者需实现该函数

/**
* @brief MCU 进入固件升级模式
* @param[in] {value} 固件缓冲区
* @param[in] {position} 当前数据包在于固件位置
* @param[in] {length} 当前固件包长度(固件包长度为 0 时,表示固件包发送完成)
* @return Null
* @note   MCU 需要自行实现该功能
*/
unsigned char mcu_firm_update_handle(const unsigned char value[],unsigned long position,unsigned short length)
{
    #error "请自行完成 MCU 固件升级代码,完成后请删除该行"
    if(length == 0) {
        //固件数据发送完成
    }else {
        //固件数据处理
    }
    return SUCCESS;
}