OTA固件升级文件为.bin类型的升级解说:
这边主要分享升级的一个大致实现思路, 细节方面还需和嵌入式工程师沟通具体的协议内容, 下文涉及的协议仅供参考。欢迎交流哈!!!指令发送和回复解析对照看,基本都是一发一收。
1. 解析.bin升级文件
class BinFileParseUtils {
companion object {
fun parseBinFile(inputStream: InputStream?): ArrayList<ByteArray> {
val mBinBytes = ArrayList<ByteArray>()
var bufferedInputStream: BufferedInputStream? = null
var byteArrayOutputStream: ByteArrayOutputStream? = null
try {
bufferedInputStream = BufferedInputStream(inputStream)
byteArrayOutputStream = ByteArrayOutputStream()
// 操作(分段读取)
val flush = ByteArray(1024) // 缓冲容器
var len: Int // 接收长度
while (bufferedInputStream.read(flush).also { len = it } != -1) {
byteArrayOutputStream.write(flush, 0, len)
}
byteArrayOutputStream.flush()
// 获取数据
val binArray: ByteArray = byteArrayOutputStream.toByteArray()
if (binArray != null && binArray.isEmpty()) {
return mBinBytes
}
val totalLength = ceil(binArray.size / 128.0).toInt()
var blockNumber = 1
for (i in 0 until totalLength) {
val arr = ByteArray(136)
if (i == totalLength - 1) {
val temp: Int = binArray.size % 128
if (temp != 0) {
Arrays.fill(arr, 0xff.toByte())
// 128 个字节位数补齐
System.arraycopy(binArray, 128 * i, arr, 6, temp)
} else {
System.arraycopy(binArray, 128 * i, arr, 6, 128)
}
} else {
System.arraycopy(binArray, 128 * i, arr, 6, 128)
}
arr[0] = 0xaa.toByte()
arr[1] = 0xab.toByte()
arr[2] = 131.toByte()
arr[3] = 0x86.toByte()
arr[4] = (blockNumber shr 8 and 0xff).toByte() // 高位
arr[5] = (blockNumber and 0xff).toByte() // 低位
blockNumber++
val crcBytes = ByteArray(131)
System.arraycopy(arr, 3, crcBytes, 0, 131)
val int2Bytes: ByteArray = makeCrc(crcBytes)
arr[134] = int2Bytes[0]
arr[135] = int2Bytes[1]
mBinBytes.add(arr)
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
inputStream?.close()
bufferedInputStream?.close()
byteArrayOutputStream?.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
return mBinBytes
}
}
}
2. 定义发送指令集
class BinUpdateOrderManager {
private var mCommandTool: BaseCommand? = null
private var mCurrentSendOrder: ByteArray? = null
private val mHandler: Handler by lazy {
Handler()
}
private var mShouldResend = false//是否需要重发当前包
private val mCheckRunnable: Runnable = Runnable {
if (mShouldResend) {
mCommandTool?.write(mCurrentSendOrder)
MdBleLog.d(HexTypeUpdateUtil.TAG, "重发指令 ${HexUtil.formatHexString(mCurrentSendOrder)}")
checkReply()
} else {
updateFail("指令重发无响应")
}
}
constructor(mCommandTool: BaseCommand) {
this.mCommandTool = mCommandTool
}
/**
* 升级握手指令
*/
fun sendHandshake() {
sendOrder(0xaa, 0xa7, 0x05, 0x81, 0x20, 0x13, 0x01, 0x16)
MdBleLog.d(BinTypeUpdateUtil.TAG, "升级握手指令 ${mCurrentSendOrder?.bytes2HexString()}")
}
/**
* APP跳转BOOT层指令
*/
fun sendJump2Boot() {
sendOrder(0xaa, 0xa7, 0x05, 0x82, 0x20, 0x13, 0x01, 0x16)
MdBleLog.d(BinTypeUpdateUtil.TAG, "发送APP层跳转Boot层指令 ${mCurrentSendOrder?.bytes2HexString()}")
}
/**
* APP向设备索要种子数据指令
*/
fun sendGetSeedData() {
sendOrder(0xaa, 0xa7, 0x05, 0x83, 0x20, 0x13, 0x01, 0x16)
MdBleLog.d(
BinTypeUpdateUtil.TAG,
"发送APP向设备索要种子数据指令 ${mCurrentSendOrder?.bytes2HexString()}"
)
}
/**
* APP向设备发送秘钥数据指令
*/
fun sendDecodeKey(dataArray: ByteArray) {
if (dataArray.isEmpty() || dataArray.size < 4) {
updateFail("秘钥数据异常")
return
}
val bytes = BinSecretKeyUtils.secretKey(dataArray)
sendOrder(
0xaa,
0xa7,
0x05,
0x84,
bytes[0].toInt(),
bytes[1].byte2Int(),
bytes[2].byte2Int(),
bytes[3].byte2Int()
)
MdBleLog.d(BinTypeUpdateUtil.TAG, "APP向设备发送秘钥数据指令 ${mCurrentSendOrder?.bytes2HexString()}")
}
/**
* 擦除FLASH指令
*/
fun sendClearFlash() {
sendOrder(0xaa, 0xa7, 0x05, 0x85, 0x20, 0x13, 0x01, 0x16)
MdBleLog.d(BinTypeUpdateUtil.TAG, "发送擦除FLASH指令 ${mCurrentSendOrder?.bytes2HexString()}")
}
/**
* 发送数据完成指令
*/
fun sendBinDataComplete() {
sendOrder(0xaa, 0xa7, 0x05, 0x87, 0x20, 0x13, 0x01, 0x16)
MdBleLog.d(BinTypeUpdateUtil.TAG, "发送数据完成指令 ${mCurrentSendOrder?.bytes2HexString()}")
}
/**
* 跳转APP指令
*/
fun sendJump2App() {
sendOrder(0xaa, 0xa7, 0x05, 0x88, 0x20, 0x13, 0x01, 0x16)
MdBleLog.d(BinTypeUpdateUtil.TAG, "发送跳转APP指令 ${mCurrentSendOrder?.bytes2HexString()}")
}
private fun sendOrder(vararg arr: Int) {
mCommandTool?.let {
checkReply(true)
mCurrentSendOrder = it.connectParams(*arr)
it.write(mCurrentSendOrder)
}
}
fun sendBin(byteArray: ByteArray) {
mCommandTool?.let {
checkReply(true)
mCurrentSendOrder = byteArray
it.write(mCurrentSendOrder)
}
}
private fun checkReply(shouldResend: Boolean = false) {
mShouldResend = shouldResend
removeDelayCheck()
mHandler.postDelayed(mCheckRunnable, 6000)
}
fun removeDelayCheck() {
mHandler.removeCallbacks(mCheckRunnable)
}
private fun updateFail(desc: String) {
MdBleLog.d(BinTypeUpdateUtil.TAG, "固件升级失败---UpdateOrderManager: $desc")
MdBleDataHandler.instance.dispatchEvent(DisPatchEventType.TYPE_UPDATE_FAILURE, desc)
}
}
3. 解析下位机应答指令
class BinTypeUpdateUtil private constructor() : OnDataHandlerListener {
private lateinit var mOrderManager: BinUpdateOrderManager
private var mMaxProgress = 100
private var mReceiveBackCount = 0
private var mSendBlockNumber = 0
private val mDecodeKeys = ByteArray(4)
private var mBinBytes = ArrayList<ByteArray>() // 升级包byte数组
companion object {
val instance = SingletonHolder.holder
const val TAG = "BinTypeUpdateUtil"
}
private object SingletonHolder {
val holder = BinTypeUpdateUtil()
}
fun addDeviceAndStartUpdate(baseBleDevice: BaseBleDevice?, path: String?) {
if (baseBleDevice?.mCommandTool == null
|| path.isNullOrEmpty()
|| !File(path).exists()
) {
updateFail("addDevice null")
return
}
baseBleDevice.mIsUpDataDevice = true
MdBleDataHandler.instance.addOnDataHandlerListener(this)
mOrderManager = BinUpdateOrderManager(baseBleDevice.mCommandTool!!)
mBinBytes = BinFileParseUtils.parseBinFile(FileInputStream(File(path)))
mReceiveBackCount = 0
mSendBlockNumber = 0
if (mBinBytes.isNotEmpty()) {
mMaxProgress = 8 + mBinBytes.size
mOrderManager.sendHandshake()
} else {
updateFail("bin文件解析异常")
}
}
override fun onUpdateOtaDataChange(byteArray: ByteArray?) {
mReceiveBackCount++
val percent = ceil(mReceiveBackCount * 1.0 / mMaxProgress * 100).toInt()
MdBleLog.d(TAG, "升级进度 ${percent}%")
MdBleDataHandler.instance.dispatchEvent(
DisPatchEventType.TYPE_UPDATE_PROGRESS_PERCENT,
percent
)
byteArray?.let {
val hexFormatString = it.bytes2HexString()
when (it[3]) {
0x81.toByte() -> {
MdBleLog.d(TAG, "收到握手指令应答 $hexFormatString")
// 01固件工作在APP层;02固件工作在BOOT层;
when {
byteArray[4] == 0x01.toByte() -> mOrderManager.sendJump2Boot() // 跳转到BOOT层
byteArray[4] == 0x02.toByte() -> mOrderManager.sendGetSeedData() // APP向设备索要种子数据指令
else -> updateFail("握手失败")
}
}
0x82.toByte() -> {
MdBleLog.d(TAG, "收到跳转到BOOT层应答 $hexFormatString")
// 跳转到BOOT层成功,APP向设备索要种子数据指令
if (byteArray[4] == 0x01.toByte()) {
mOrderManager.sendGetSeedData()
} else {
updateFail("跳转到BOOT层失败")
}
}
0x83.toByte() -> {
MdBleLog.d(TAG, "收到设备回复种子数据 $hexFormatString")
mDecodeKeys[0] = it[4]
mDecodeKeys[1] = it[5]
mDecodeKeys[2] = it[6]
mDecodeKeys[3] = it[7]
mOrderManager.sendDecodeKey(mDecodeKeys)
}
0x84.toByte() -> {
MdBleLog.d(TAG, "收到解密应答 $hexFormatString")
when (byteArray[4]) {
0x01.toByte() -> mOrderManager.sendClearFlash() // 解密成功
0x02.toByte() -> mOrderManager.sendDecodeKey(mDecodeKeys) // 再次申请解密
else -> updateFail("解密失败")
}
}
0x85.toByte() -> {
MdBleLog.d(TAG, "收到flash擦除应答 $hexFormatString")
sendBin() // 开始发送升级包
}
0x86.toByte() -> {
val receiveBlockNumber: Int =
(byteArray[4] and 0xff.toByte()).byte2Int() * 256 + (byteArray[5] and 0xff.toByte()).byte2Int()
MdBleLog.d(TAG, "收到发送升级包应答 $receiveBlockNumber -- $hexFormatString")
if (receiveBlockNumber > mSendBlockNumber) {
mSendBlockNumber = receiveBlockNumber
if (mSendBlockNumber < mBinBytes.size) {
// 继续发送升级包
sendBin()
} else {
// 发送数据完成指令
mOrderManager.sendBinDataComplete()
}
} else {
sendBin()
}
}
0x87.toByte() -> {
MdBleLog.d(TAG, "收到数据完成指令应答 $hexFormatString")
if (byteArray[4] == 0x01.toByte()) {
//跳转APP指令
mOrderManager.sendJump2App()
} else {
updateFail("接收bin数据错误")
}
}
0x88.toByte() -> {
MdBleLog.d(TAG, "收到跳转APP指令应答 $hexFormatString")
if (byteArray[4] == 0x01.toByte()) {
//跳转APP指令应答
updateSuccess()
} else {
updateFail("跳转APP失败")
}
}
0x08.toByte() -> {
MdBleLog.d(TAG, "收到升级串口指令错误 $hexFormatString")
updateFail("升级串口指令错误")
}
}
}
}
private fun sendBin() {
MdBleLog.d(TAG, "发送bin数据 ${mBinBytes[mSendBlockNumber].bytes2HexString()}")
mOrderManager.sendBin(mBinBytes[mSendBlockNumber])
}
private fun updateSuccess() {
MdBleLog.d(TAG, "updateSuccess")
mOrderManager.removeDelayCheck()
MdBleDataHandler.instance.dispatchEvent(DisPatchEventType.TYPE_UPDATE_SUCCESS)
MdBleDataHandler.instance.removeDataHandlerListener(this)
}
private fun updateFail(exceptionDesc: String) {
MdBleLog.d(TAG, exceptionDesc)
MdBleDataHandler.instance.dispatchEvent(DisPatchEventType.TYPE_UPDATE_FAILURE, exceptionDesc)
MdBleDataHandler.instance.removeDataHandlerListener(this)
}
}
升级核心步骤如上