Android BLE蓝牙智能设备固件升级之Bin文件升级方式

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)
    }
}

升级核心步骤如上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值