文档目标
蓝牙设备通常需要空中升级(OTA)的能力进行固件更新,本规范定义了空中升级(OTA)的基本流程和指令集。本规范基于《蓝牙设备GATT规范》实现。
交互流程
为了保证OTA的的安全性,在进行设备OTA之前,必须完成安全认证流程。若认证失败则不允许进行OTA。安全认证详情参考《蓝牙设备GATT规范》。
基础规范使用
广播规范
空中升级(OTA)是可选功能,如果蓝牙设备实现了此功能,需要在广播规范的FMSK字段中标示。
服务规范
传输过程使用基础规范已定义的Service和Characteristics,采用指令类型区分。手机App发送固件时,采用WriteWithNoRsp
Characteristics(0xFED7),用于加快传输速度。同时蓝牙设备收到数据包后,采用Notify Characteristics(0xFED8)对App应答。接收固件过程中,发现数据序号错误的时候,蓝牙设备上报最后一次正确的序号。
数据传输规范
数据传输完全使用基础规范定义的数据格式和规则。
版本格式
固件类型为一个1字节数字,用来区分同一个设备不同类型固件。版本格式为一个4字节数字,如:0x00010302,表示固件版本号为:“1.3.2”,版本号只允许递增,最大版本号为:“99.99.99”。版本格式规则如下:
字节序 | 说明 | 取值范围 | 示例 |
---|---|---|---|
0 | 修订号 | 0x00~0x63 | “1.3.2”,修订号为0x02 |
1 | 次版本号 | 0x00~0x63 | “1.3.2”,次版本号为0x03 |
2 | 主版本号 | 0x00~0x63 | “1.3.2”,主版本号为0x01 |
3 | 保留 | 无 | 无 |
升级规则
1、升级用固件推送到服务端,并填写固件版本信息和升级原因。
2、当服务端确认推送的固件版本比设备端上报的固件版本新时,服务端推送升级通知到手机App。
3、手机App转发升级请求及固件版本信息(仅包含应用版本信息)和固件大小,CRC。
4、蓝牙设备检查固件的应用版本信息,当App下发的应用版本信息比设备运行的固件的应用版本新时,蓝牙设备进入升级模式,否则退出升级模式。
5、协议设计支持固件断点续传能力,参考“交互流程”第12步,蓝牙设备可以回复上次传输固件的中断的位置,手机App会从指定位置续传。断点续传非强制要求能力,可根据情况实现。
6、协议设计支持快速传输模式,即手机App会连续传输TotalFrame(0
指令集
1、0x20:手机查询设备固件版本
Header(1Byte) | CmdType(1 Byte) | FrameDesc(2 Byte) | PayLoad(1 Byte) |
---|---|---|---|
0x00 | 0x20 | 0x00 0x01 | 0x00 |
固件类型为1字节,默认填0x00。设备端根据固件类型从 0x21 返回对应类型固件的版本号信息。
2、0x21:设备上报设备固件版本,版本为0x00000002
Header(1Byte) | CmdType(1 Byte) | FrameDesc(2 Byte) | PayLoad(5 Byte) |
---|---|---|---|
0x00 | 0x21 | 0x00 0x05 | 0x00 0x00000002 |
固件类型为1字节,默认填0x00。遇到不支持的固件类型,固件类型上报 0xFF。固件版本号为4字节。
3、0x22:手机下发升级请求,升级请求包含五个字段,分别为:固件类型、固件版本号、固件大小、CRC16、升级标示符。
Header(1Byte) | CmdType(1 Byte) | FrameDesc(2 Byte) | PayLoad(12 Byte) |
---|---|---|---|
0x00 | 0x22 | 0x00 0x0C | 0x00 0x00000001 0x00123456 0x7890 0x00 |
固件类型、固件版本号、固件大小、CRC16、升级标示符按照顺序拼接下发。固件类型为1个字节,固件版本号为4个字节,固件大小为4个字节,CRC16为2个字节,升级标示符为1个字节。CRC16算法采用CRC16_CCITT(参考资料 1)。升级标示符含义,0:全量升级 1:增量升级,默认设为0。
4、0x23:设备应答升级请求,1:允许升级 0:不可以升级。
Header(1Byte) | CmdType(1 Byte) | FrameDesc(2 Byte) | PayLoad(5 Byte) |
---|---|---|---|
0x00 | 0x23 | 0x00 0x06 | 0x01 0x00000000 0x0F |
是否允许升级(1字节)+ 上次传输字节数(4字节)+ 允许一次循环可传输的包个数(1字节,0x00~0x0f,表示1~16包)
5、0x24:设备上报当前已接收的帧序号和接收的数据长度
Header(1Byte) | CmdType(1 Byte) | FrameDesc(2 Byte) | PayLoad(5 Byte) |
---|---|---|---|
0x00 | 0x24 | 0x00 0x05 | 0x20 0x00000200 |
注:PayLoad 由接收的帧数和接收数据长度组成,帧数1字节(totalFrame 4bit + frameSeq 4bit),数据长度4字节。当设备发现有丢帧时,设备端上报最后一次收到正确的帧序和数据长度。手机端重新从丢失的帧开始发送,并且发送完本次循环的剩余帧。
6、0x25:手机通知设备固件下发结束,可以做固件检查
Header(1Byte) | CmdType(1 Byte) | FrameDesc(2 Byte) | PayLoad(1 Byte) |
---|---|---|---|
0x00 | 0x25 | 0x00 0x01 | 0x01 |
7、0x26:设备收完包后,上报固件检查结果; 1:固件检查成功;0:固件检查失败
Header(1Byte) | CmdType(1 Byte) | FrameDesc(2 Byte) | PayLoad(1 Byte) |
---|---|---|---|
0x00 | 0x26 | 0x00 0x00 | 0x01 |
9、0x2F:手机下发固件的分包数据
Header(1Byte) | CmdType(1Byte) | FrameDesc(2 Byte) | FwData(N Byte) |
---|---|---|---|
0x00 | 0x2F | 0xF0 0x10 | 0x1234… |
注:固件按规范的数据长度分包,发送16包作为一次循环,Header中帧序号按照0~15循环。
异常处理
重传周期
在一个重传周期内如果连续出现同一错误,则只需发送一次0x24指令。重传周期的时间计算方式为500msTotalFrame;例如每次循环最大允许发送的数据帧数为10,则重传周期为500ms10=5秒。
设备端收包异常处理逻辑
- 当设备发现期望接收的数据包seq与当前收到的数据包seq不相同时,设备回复0x24指令,上报最后一次接收到的连续的数据包的seq和已接收的数据总长度;手机APP或天猫精灵在接收到0x24指令后,使用0x2F指令重新下发丢失的数据包,内容与之前的一致(包括Header和Payload);设备在接收到剩余的数据包之后需要回复一次0x24指令;然后手机APP或天猫精灵继续传输剩余的固件信息。
- 设备端如果发现接收数据错误,在重传周期内只允许上报一次0x24指令;如果正确接收到期望的数据则不受此规则限制。
- 当设备回复0x24指令之后,如果在重传周期内没有收到期望的数据包,则再次回复0x24指令。
- 当设备端重复发送同一0x24指令超过5次时,则断开连接。
当传输的固件末尾最后剩余的数据小于TotalFrame*MTU时,根据实际的拆包个数确定本轮传输的TotalFrame。当设备端发现已经接收到的数据等于固件总长度时,需要直接回复0x24指令。
异常处理小结
设备端在以下情况下需要回复0x24指令:
当设备收齐单次循环发送的所有数据包时,需要回复0x24指令
- 当设备收到与期望的seq不同的数据包时,需要回复0x24指令,当数据错误时每个重传周期内只允许发送1次0x24指令
- 当设备收齐重发的单次循环发送的所有数据包时,需要回复0x24指令
- 当设备在一个重传周期内未接收到期望的seq的数据包,需要回复0x24指令
- 当设备收到完整固件后,需要回复0x24指令
参考资料
- CRC16算法参数模型:CRC-16/CCITT-FALSE、宽度 16、多项式POLY 0x1021、初始值INIT 0xFFFF、结果异或值XOROUT 0x0000、输入数据反转 REFIN false、输出数据反转 REFOUT false。