4.3.OTA升级方案
随着时代的发展,人与人,人与物,物与物连接的需求越来越旺盛.传统设备在向智能设备转变,不在是孤立与一成不变.对于物联网时代的智能设备,OTA升级技术是一个必不可少的重要技术.
对于物联网领域,OTA在以下方面显得尤为重要:
1.实现快速上线与bug修复.与互联网敏捷开发类似,在OTA的支持下,设备可以快速上线,根据市场需求实现功能快速迭代与bug修复.
2.不同应用场景的功能定制.在物联网领域,同样的产品在不同的需求方,不同的场景下可能有不同的功能需求.通过OTA升级,可以方便快捷的解决.
OTA的升级分类,按不同纬度可以分成不同类别.
按模块可以分为 固件升级包和软件升级包;
按升级包的完整性的话,可以分为全量升级和差分升级;
按升级的时机又可分为预约升级,立即升级等.
按程序运行区域分为原地升级和AB升级
以上是笔者根据一些经验总结出的分类.
大致的升级流程如下:
1.用户将升级包上传到平台后,选择设备进行升级.
2.设备通过topic收到升级包url,通过http请求进行下载操作.
3.设备将升级包下载,并触发设备内的一系列内部的升级逻辑,如校验升级包,合成升级包,解压资源,拷贝到指定路径,失败回滚等
4.向平台汇报最新版本
设备与平台交互流程
设备下载到最新升级包后流程参考 6.设备升级,7.网关子设备升级
采用阿里的协议,去除部分实现
说明 | topic | 格式 |
上报版本 | /ota/deivce/inform/${productKey}/${deviceName} |
Plaintext |
参数 | 类型 | 说明 |
id | String | 消息ID号。String类型的数字,取值范围0~4294967295,且每个消息ID在当前设备中具有唯一性。 |
version | String | OTA模块版本。 |
module | String | OTA模块名。说明上报默认(default)模块的版本号时,可以不上报module参数。设备的默认(default)模块的版本号代表整个设备的固件版本号。 |
当用户在控制台上触发升级操作后,设备会通过升级主题的Topic收到升级包的URL.
topic | 格式 | |
/ota/deivce/upgrade/${productKey}/${deviceName} |
升级包下载协议为HTTPS:
Plaintext |
参数说明
参数 | 类型 | 说明 |
id | Long | 消息ID号。每个消息ID在当前设备中具有唯一性。 |
message | String | 结果信息。 |
code | String | 状态码。 |
version | String | 设备升级包的版本信息。 |
size | Long | 升级包文件大小,单位:字节。OTA升级包中仅有一个升级包文件时,包含该参数。 |
url | String | 升级包在对象存储(OSS)上的存储地址。OTA升级包中仅有一个升级包文件,且下载协议为HTTPS时,包含该参数。 |
dProtocol | String | 升级包下载协议。仅升级包下载协议为MQTT时,包含该参数。 |
streamId | Long | 通过MQTT协议下载OTA升级包时的唯一标识。仅升级包下载协议为MQTT时,包含该参数。 |
streamFileId | Integer | 单个升级包文件的唯一标识。仅升级包下载协议为MQTT时,包含该参数。 |
isDiff | Long | 仅当升级包类型为差分时,消息包含此参数。取值为1,表示仅包含新版本升级包与之前版本的差异部分,需要设备进行差分还原。 |
digestsign | String | OTA升级包文件安全升级后的签名。仅当OTA升级包开启安全升级功能,才有此参数。开启OTA升级包安全升级功能的方法,请参见安全升级。 |
sign | String | OTA升级包文件的签名。OTA升级包中仅有一个升级包文件时,包含该参数。 |
signMethod | String | 签名方法。取值:SHA256``MD5对于Android差分升级包类型,仅支持MD5签名方法。 |
md5 | String | 当签名方法为MD5时,除了会给sign赋值外还会给md5赋值。OTA升级包中仅有一个升级包文件时,包含该参数。 |
module | String | 升级包所属的模块名。说明 模块名为default时,物联网平台不下发module参数。 |
extData | Object | 升级批次标签列表和推送给设备的自定义信息。_package_udi表示自定义信息的字段。单个标签格式:"key":"value"。 |
files | Array | 多个升级包文件的信息列表。OTA升级包中有多个文件时,包含该参数。每个升级包文件信息如下:fileSize:升级包文件大小。fileName:升级包文件的名称。fileUrl、fileMd5、fileSign:含义与url、md5、sign相同。 |
升级包下载协议为MQTT:
JSON |
升级过程中,设备端向服务端推送升级进度.
topic | ||
主题 | /ota/device/progress/${productKey}/${deviceName} |
消息格式:
Plaintext |
参数 | 类型 | 说明 |
id | String | 消息ID号。String类型的数字,取值范围0~4294967295,且每个消息ID在当前设备中具有唯一性。 |
step | String | OTA升级进度。取值范围:1~100的整数:升级进度百分比。-1:升级失败。-2:下载失败。-3:校验失败。-4:烧写失败。设备上报的进度值及其描述信息,可根据设备实际升级场景在设备端配置。 |
desc | String | 当前步骤的描述信息,长度不超过128个字符。如果发生异常,此字段可承载错误信息。 |
module | String | 升级包所属的模块名。说明 上报默认(default)模块的OTA升级进度时,可以不上报module参数。 |
设备端完成OTA升级后,推送新版本信息到Topic:/ota/device/inform/${productKey}/${deviceName}。如果上报的版本与OTA服务要求的版本一致就认为升级成功,反之失败。
说明 | topic | |
升级结果 |
设备升级完成时,建议立即重启设备,设备上线后,立即上报新的版本号。
当设备拿到升级包后,升级流程大致如下:
根据下载的是否是差分包,设备需要判断是否需要合成完整的升级包.
优势 | 劣势 | |
差分升级 | 1.包体积小,传输时间 | 1.实现相对复杂 |
全量升级 | 1.实现简单 | 1.包体积相对更大,升级时间更长 |
按升级程序的运行位置,可以将升级方式分为:A/B升级,原地升级
优势 | 劣势 | |
A/B升级 | 1.容错率高,更能避免固件升级出错 | 1.至少需要两份存储程序的空间 |
原地升级 | 1.直接将程序写入指定位置,节省空间 | 1.容错率低,升级过程中断电,则设备无法运行 |
对于原地升级的问题,可以通过重新发起升级解决.如子设备发现程序损坏,重新向网关请求升级包内容;用户重新升级
7.OTA升级-网关子设备与网关(协议设计仅供参考,未验证)
子设备不直接连接物联网平台,而是通过网关与物联网平台建立连接,复用网关与物联网平台的通信通道。网关设备连接成功后,可使用通道复用能力代理子设备OTA升级,使用的Topic必须是子设备Topic,包含子设备productKey和deviceName的信息。
网关将升级包下载成功后,然后通知子设备发起升级.
网关子设备和网关之间接口各异,有通过can总线,RS232,RS485等接口连接的,有基于可靠传输协议或不可靠传输协议来传输升级包的.需要定义一个统一的升级协议来实现网关子设备的升级.
协议消息中使用的数据类型见表 1-1:
数据类型 | 描述及要求 |
BYTE | 无符号单字节整型(字节,8位) |
WORD | 无符号双字节整型(字,16位) |
DWORD | 无符号四字节整型(双字,32位) |
BYTE[n] | n字节 |
STRING | UTF-8编码,若无数据,置空 |
协议采用大端模式(big-endian)的网络字节序来传递字和双字.
约定如下:字节(BYTE)的传输约定:按照字节流的方式传输;字(WORD)的传输约定:先传递高八位,再传递低八位;
双字节(DWORD)的传输约定:先传递高 24位,然后传递高 16位,在传递高8位,最后传递低8位。
标识位(头) | ||
消息头 | ||
消息体 | ||
校验码 | ||
标识位(尾) |
起始字节 | 字段 | 数据类型 | 说明 |
0 | 命令 | WORD | |
2 | 长度 | WORD | |
4 | 消息流水号 | BYTE | 按发送顺序从 0开始循环累加 |
5 | 消息体属性 | BYTE | 是否分包,加密方式 |
6 | (非必须)消息包封装 | WORD | 如果消息体属性中相关标识位确定消息分包处理,则该项有内容,否则无该项 |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
保留 | 保留 | 分包 | 数据加密方式 | 是否需要响应 | 消息体编解码方式 | 保留 | 保留 |
字段 | 说明 | |
分包 | 1 分包, 0 不分包 | |
数据加密方式 | 0: 无 | |
是否需要响应 | 0: 无需 1:需要 | |
消息体编解码方式 | 0:默认 1:json 2:probuffer |
当消息体属性中第 5 位为 1 时表示消息体为长消息,进行分包发送处理,具体分包信息由消息包封装项决定;若第 13位为 0,则消息头中无消息包封装项字段。
起始字节 | 字段 | 数据类型 | 描述及要求 |
0 | 消息总包数 | WORD | 该消息分包后的总包数 |
2 | 包序号 | WORD | 从 1开始 |
头:0x7e
尾巴:0x7e
校验码指从消息头开始,同后一字节异或,直到校验码前一个字节。校验码占用 1个字节。
消息体格式
命令:0x80
消息头的消息流水号为请求的消息流水号
起始字节 | 字段 | 数据类型 | 描述及要求 |
0 | 结果 | BYTE | 0:成功/确认;1:失败;2:消息有误; 3:不支持 |
网关端发起,子设备响应
命令:0x1
起始字节 | 字段 | 数据类型 | 举例 |
0 | 版本号长度 | BYTE | n |
1 | 版本号 | BYTE数组 | |
1+n | 模块名称长度 | BYTE | m |
1+n+m | 模块名称 | BYTE数组 | |
通用响应
0x80
子设备发起,网关响应
起始字节 | 字段 | 数据类型 | 举例 | 必填 |
0 | 当前版本号长度 | BYTE | n | |
1 | 单前版本号 | BYTE数组 | ||
1+n | 当前模块名称长度 | BYTE | m | |
1+n+m | 当前模块名称 | String | 否 | |
起始字节 | 字段 | 数据类型 | 举例 | 必填 |
0 | 版本号长度 | BYTE | n | |
1 | 版本号 | String | ||
1+n | 模块名称长度 | Byte | m | |
1+n+m | 模块名称 | String | 否 | |
1+n+m+1 | 包大小 | WORD | ||
包md5码 |
网关端发起,子设备响应
升级包一般情况下比较大,需要分成多次发送,此时需要将分包,将分包标志位置为1
当消息体属性中第 5 位为 1 时表示消息体为长消息,进行分包发送处理,具体分包信息由消息包封装项决定;若第 13位为 0,则消息头中无消息包封装项字段。
消息包封装项
起始字节 | 字段 | 数据类型 | 描述及要求 |
0 | 消息总包数 | WORD | 该消息分包后的总包数 |
2 | 包序号 | WORD | 从 1开始 |
起始字节 | 字段 | 数据类型 | 举例 |
0 | 升级包内容 | BYTE | n |
子设备发起,网关响应
起始字节 | 字段 | 数据类型 | 举例 | 必填 |
0 | 版本号长度 | BYTE | n | |
1 | 版本号 | String | ||
1+n | 模块名称长度 | BYTE | m | |
1+n+m | 模块名称 | String | 否 |