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

升级核心步骤如上

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
小程序蓝牙bin文件固件升级的实现代码主要包括以下几个步骤: 1. 获取设备的服务和特征值UUID,以及bin文件的地址。 ```javascript wx.getBLEDeviceServices({ deviceId: deviceId, success: function(res) { // 获取设备的服务列表 var services = res.services; for (var i = 0; i < services.length; i++) { if (services[i].uuid == serviceUUID) { // 获取设备的特征值列表 wx.getBLEDeviceCharacteristics({ deviceId: deviceId, serviceId: services[i].uuid, success: function(res) { var characteristics = res.characteristics; for (var i = 0; i < characteristics.length; i++) { if (characteristics[i].uuid == charaUUID) { // 获取bin文件的地址 wx.chooseMessageFile({ count: 1, type: 'file', success: function(res) { var filePath = res.tempFiles[0].path; // 调用固件升级函数 firmwareUpdate(deviceId, filePath, characteristics[i].uuid); } }); } } } }); } } } }); ``` 2. 将bin文件读取为arraybuffer,并根据设备的特征值大小将数据分段发送给设备。 ```javascript function firmwareUpdate(deviceId, filePath, charaUUID) { wx.getFileSystemManager().readFile({ filePath: filePath, success: function(res) { // 将bin文件读取为arraybuffer var arrayBuffer = res.data; var arrayLength = arrayBuffer.byteLength; var chunkSize = 20; var offset = 0; var chunkIndex = 0; var sendChunk = function() { if (offset >= arrayLength) { console.log('固件升级完成'); return; } var chunk = arrayBuffer.slice(offset, offset + chunkSize); // 发送数据到设备 wx.writeBLECharacteristicValue({ deviceId: deviceId, serviceId: serviceUUID, characteristicId: charaUUID, value: chunk, success: function(res) { console.log('发送数据包' + chunkIndex); offset += chunkSize; chunkIndex++; sendChunk(); }, fail: function(res) { console.log('发送数据包失败'); } }); }; sendChunk(); } }); } ``` 3. 设备接收到数据后进行校验,并将校验结果返回给小程序。 ```c // 接收数据并进行校验 void on_receive_data(uint8_t* data, uint16_t length) { if (length < 3) { return; } uint8_t checksum = 0; for (uint16_t i = 0; i < length - 1; i++) { checksum ^= data[i]; } if (checksum != data[length - 1]) { return; } if (data[0] == 0x01 && data[1] == 0x01) { // 校验成功,返回结果给小程序 uint8_t result[3] = {0x01, 0x02, 0x00}; ble_send_data(result, 3); } } // 发送数据到小程序 void ble_send_data(uint8_t* data, uint16_t length) { uint8_t buffer[20]; uint16_t offset = 0; while (offset < length) { uint16_t chunk_size = MIN(length - offset, 20); memcpy(buffer, data + offset, chunk_size); buffer[chunk_size] = 0x00; ble_send_notify(buffer, chunk_size + 1); offset += chunk_size; } } // 发送通知给小程序 void ble_send_notify(uint8_t* data, uint16_t length) { uint8_t buffer[20]; memset(buffer, 0, sizeof(buffer)); memcpy(buffer, data, length); ble_gatts_hvx_params_t hvx_params; hvx_params.handle = m_conn_handle; hvx_params.type = BLE_GATT_HVX_NOTIFICATION; hvx_params.offset = 0; hvx_params.p_data = buffer; hvx_params.p_len = &length; sd_ble_gatts_hvx(m_conn_handle, &hvx_params); } ``` 以上就是小程序蓝牙bin文件固件升级的实现代码。需要注意的是,在固件升级过程中,需要根据设备的特征值大小将数据分段发送给设备,同时设备也需要接收数据并进行校验,并将校验结果返回给小程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值