zhangxb
zhangxb
  • 发布:2026-07-16 17:00
  • 更新:2026-07-16 17:04
  • 阅读:31

蓝牙DFU升级发现红米手机可以发送成功,但是荣耀手机、华为手机都执行无返回响应

分类:uni-app

蓝牙DFU升级发现红米手机可以发送成功,但是荣耀手机、华为手机都执行无返回响应。请求大佬们解答求求了,这个发送问题卡我好几天了。
源码:testSendInitPacket() {
let _this = this
this.downloadFirmware().then(() => _this.connectToDevice())
.then(() => _this.enableNotify())
.then(() => {
return new Promise((resolve, reject) => {
let _this = this
console.log('setBLEMTU 开始')
// 设置MTU
uni.setBLEMTU({
deviceId: _this.deviceId,
mtu: _this.mtuSize,
success(res) {
console.log('MTU协商: 请求=' + _this.mtuSize)
_this.addDfuLog('MTU协商完成', true)
setTimeout(() => {
console.log('setBLEMTU 结束')
resolve()
}, 1000)
},
fail(err) {
_this.addDfuLog('MTU协商失败: ' + (err.errMsg || JSON.stringify(err)))
console.log('MTU协商失败: ' + (err.errMsg || JSON.stringify(err)))
setTimeout(() => {
reject()
}, 1000)
}
})
})
}).then(() => {
return new Promise((resolve, reject) => {
resolve(_this.testStep1())
})
})
},
// ========== 测试用例:先擦除再发init包(严格按官方流程) ==========
// 参考官方流程:Validate.java 先 performer.enqueue(new Erase(...)) 再 enqueue(new Upload(...))
testStep1() {
let _this = this
_this.addDfuLog('【测试】Step 1: 发送擦除命令...', true)
console.log('【测试】testStep1: 发送擦除命令')

            _this.dfuSequenceNumber++  
            // 官方行为:slot=SECONDARY时payloadMap为null,在buildPacket中转为new HashMap<>(),  
            // CBOR.toBytes(new HashMap<>()) → 0xA0(空CBOR map,1字节)  
            // 注意:必须是0xA0(1字节有效CBOR),不能是0字节的空ArrayBuffer  
            const eraseCbor = new Uint8Array([0xA0]).buffer  
            const eraseFrame = buildSmpFrame(0x01, 0x05, eraseCbor, _this.dfuSequenceNumber)  
            _this.addDfuLog('【测试】擦除命令帧=' + ab2hex(eraseFrame))  

            // 设置擦除响应回调  
            _this.dfuEraseCallback = (response) => {  
                const rc = response.rc !== undefined ? response.rc : 0  
                if (rc === 0) {  
                    _this.addDfuLog('【测试】擦除成功,开始发送init包...', true)  
                    console.log('【测试】testStep1: 擦除成功,发送init包')  
                    _this.dfuEraseCallback = null  

                    // 擦除成功后发送init包  
                    _this.doTestSendInit()  
                } else {  
                    _this.addDfuLog('【测试】擦除失败(rc=' + rc + ')', false, true)  
                    _this.dfuEraseCallback = null  
                }  
            }  

            _this.doTestSend(eraseFrame, 30000, 'dfuEraseCallback')  
        },  

        doTestSendInit() {  
            let _this = this  
            _this.addDfuLog('【测试】Step 2: 构建init包并发送...', true)  
            const firmwareDataView = new Uint8Array(_this.dfuFirmwareBuffer)  
            const firmwareSize = firmwareDataView.length  
            let currentOffset = 0  
            let sentChunks = 0  
            let uploadResolved = false  

            const initResult = buildCborInitUpload(firmwareDataView, _this.dfuFirmwareHash, _this  
                .mtuSize)  
            _this.dfuSequenceNumber++  
            const frame = buildSmpFrame(0x01, 0x01, initResult.cborBuffer, _this.dfuSequenceNumber)  
            const frameHex = ab2hex(frame)  
            const frameSize = frame.byteLength  
            const attLimit = _this.mtuSize - 3  
            _this.addDfuLog('【测试】init包=' + frameSize + '字节, ATT上限=' + attLimit + '字节')  
            console.log('【测试】init包=' + frameSize + '字节, ATT上限=' + attLimit + ', hex=' + frameHex)  

            if (frameSize > attLimit) {  
                _this.addDfuLog('【测试】init包超过ATT上限,无法发送', false, true)  
                return  
            }  

            _this.doTestSend(frame, 120000, 'dfuInitResponseCallback')  
        },  

        doTestSend(buffer, timeoutMs = 10000, callbackType = 'dfuInitResponseCallback') {  
            let _this = this  
            const startTime = Date.now()  
            let responded = false  

            _this.addDfuLog('【测试】正在发送测试包(' + buffer.byteLength + '字节, 回调=' + callbackType + ')...')  

            // 设置临时响应回调(支持擦除回调和init回调)  
            const origInitCallback = _this.dfuInitResponseCallback  
            const origEraseCallback = _this.dfuEraseCallback  
            // 接管指定回调  
            if (callbackType === 'dfuInitResponseCallback') {  
                _this.dfuInitResponseCallback = (rc) => {  
                    responded = true  
                    const elapsed = Date.now() - startTime  
                    _this.addDfuLog('【测试】收到init响应 rc=' + rc + ', 耗时=' + elapsed + 'ms', rc === 0)  
                    console.log('【测试】doTestSend: 收到init响应 rc=' + rc + ', 耗时=' + elapsed + 'ms')  
                    _this.dfuInitResponseCallback = origInitCallback  
                }  
            } else if (callbackType === 'dfuEraseCallback') {  
                _this.dfuEraseCallback = (response) => {  
                    responded = true  
                    const elapsed = Date.now() - startTime  
                    const rc = response.rc !== undefined ? response.rc : 0  
                    _this.addDfuLog('【测试】收到擦除响应 rc=' + rc + ', 耗时=' + elapsed + 'ms', rc === 0)  
                    console.log('【测试】doTestSend: 收到擦除响应 rc=' + rc + ', 耗时=' + elapsed + 'ms')  
                    // 调用原始测试回调(由testStep1设置)  
                    if (origEraseCallback) {  
                        origEraseCallback(response)  
                    }  
                }  
            }  

            // 写类型日志  
            const wType = getWriteType()  
            console.log('【测试】doTestSend: 写类型=' + wType + ', 超时=' + timeoutMs + 'ms')  

            uni.writeBLECharacteristicValue({  
                deviceId: _this.deviceId,  
                serviceId: _this.smpServiceId,  
                characteristicId: _this.smpCharacteristicId,  
                value: buffer,  
                writeType: wType,  
                success: () => {  
                    const writeElapsed = Date.now() - startTime  
                    _this.addDfuLog('【测试】写入成功,等待设备响应...')  
                    console.log('【测试】doTestSend: 写入成功, 写入耗时=' + writeElapsed + 'ms, 等待响应中...')  

                    // 超时处理  
                    setTimeout(() => {  
                        if (!responded) {  
                            _this.addDfuLog('【测试】' + timeoutMs + 'ms超时,设备未响应', false, true)  
                            console.log('【测试】doTestSend: 超时=' + timeoutMs + 'ms, 无响应')  
                            if (callbackType === 'dfuInitResponseCallback') {  
                                _this.dfuInitResponseCallback = origInitCallback  
                            }  
                        }  
                    }, timeoutMs)  
                },  
                fail: (err) => {  
                    _this.addDfuLog('【测试】写入失败: ' + err.errMsg, false, true)  
                    console.log('【测试】doTestSend: 写入失败: ' + err.errMsg)  
                    if (callbackType === 'dfuInitResponseCallback') {  
                        _this.dfuInitResponseCallback = origInitCallback  
                    }  
                }  
            })  
        },  

跟踪日志:16:44:04.605 parseImageHash: 成功提取哈希: 2b30932216faff7b1e983dc7dc34681045ed8ce966ef3f3f1a383fcd45dfe6fc at pages/ble-debug/utils/smp.js:205
16:44:04.605 从固件尾部 TLV 提取的哈希: 2b30932216faff7b1e983dc7dc34681045ed8ce966ef3f3f1a383fcd45dfe6fc at pages/ble-debug/dfu-upgrade.vue:1030
16:44:07.550 设备 CC:6A:26:AB:FC:31 连接状态变化, 已连接: {"deviceId":"CC:6A:26:AB:FC:31","connected":true} at pages/ble-debug/dfu-upgrade.vue:211
16:44:08.588 setBLEMTU 开始 at pages/ble-debug/dfu-upgrade.vue:784
16:44:08.634 MTU协商: 请求=150 at pages/ble-debug/dfu-upgrade.vue:792
16:44:09.608 setBLEMTU 结束 at pages/ble-debug/dfu-upgrade.vue:795
16:44:10.609 queryFirmwareState 开始 at pages/ble-debug/dfu-upgrade.vue:1043
16:44:10.609 发送镜像状态查询命令... at pages/ble-debug/dfu-upgrade.vue:1099
16:44:10.640 状态查询命令发送成功, writeType:, write at pages/ble-debug/dfu-upgrade.vue:1112
16:44:10.873 ========onBLECharacteristicValueChange进来了========== at pages/ble-debug/dfu-upgrade.vue:484
16:44:10.873 handleSmpResponse2进来了 at pages/ble-debug/dfu-upgrade.vue:540
16:44:10.997 ========onBLECharacteristicValueChange进来了========== at pages/ble-debug/dfu-upgrade.vue:484
16:44:11.030 handleSmpResponse2进来了 at pages/ble-debug/dfu-upgrade.vue:540
16:44:11.030 ========onBLECharacteristicValueChange进来了========== at pages/ble-debug/dfu-upgrade.vue:484
16:44:11.030 handleSmpResponse2进来了 at pages/ble-debug/dfu-upgrade.vue:540
16:44:11.030 ========onBLECharacteristicValueChange进来了========== at pages/ble-debug/dfu-upgrade.vue:484
16:44:11.030 handleSmpResponse2进来了 at pages/ble-debug/dfu-upgrade.vue:540
16:44:11.030 ========onBLECharacteristicValueChange进来了========== at pages/ble-debug/dfu-upgrade.vue:484
16:44:11.030 handleSmpResponse2进来了 at pages/ble-debug/dfu-upgrade.vue:540
16:44:11.030 ========onBLECharacteristicValueChange进来了========== at pages/ble-debug/dfu-upgrade.vue:484
16:44:11.030 handleSmpResponse2进来了 at pages/ble-debug/dfu-upgrade.vue:540
16:44:11.030 ========onBLECharacteristicValueChange进来了========== at pages/ble-debug/dfu-upgrade.vue:484
16:44:11.030 handleSmpResponse2进来了 at pages/ble-debug/dfu-upgrade.vue:540
16:44:11.030 ========onBLECharacteristicValueChange进来了========== at pages/ble-debug/dfu-upgrade.vue:484
16:44:11.030 handleSmpResponse2进来了 at pages/ble-debug/dfu-upgrade.vue:540
16:44:11.030 接收 op=1, groupId=1, cmdId=0, payloadLen=134 at pages/ble-debug/dfu-upgrade.vue:563
16:44:11.030 收到结果:, {"images":[{"slot":0,"version":"1.0.1","hash":"710EA129F48437AADBF51D0B9EEF99A598A04BAE4A3ED50CCC53CBEA340A9B3E","bootable":true,"pending":false,"confirmed":true,"active":true,"permanent":false}],"splitStatus":0} at pages/ble-debug/dfu-upgrade.vue:570
16:44:11.030 接收: 通用捕获器调用 dfuStateCallback, rc=undefined, groupId=1, cmdId=0 at pages/ble-debug/dfu-upgrade.vue:593
16:44:11.030 queryFirmwareState: 设备哈希=710ea129f48437aadbf51d0b9eef99a598a04bae4a3ed50ccc53cbea340a9b3e, BIN哈希=2b30932216faff7b1e983dc7dc34681045ed8ce966ef3f3f1a383fcd45dfe6fc at pages/ble-debug/dfu-upgrade.vue:1059
16:44:11.030 queryFirmwareState 结束 at pages/ble-debug/dfu-upgrade.vue:1073
16:44:15.038 startFirmwareUpload 开始 at pages/ble-debug/dfu-upgrade.vue:822
16:44:15.039 startFirmwareUpload: 当前mtuSize=150, 固件大小=208464字节 at pages/ble-debug/dfu-upgrade.vue:1151
16:44:15.039 buildCborInitUpload: mtuSize=150, maxFrameSize=147, combinedSize=71, maxDataLength=76, maxDataHeaderSize=2, firstChunkSize=74 at pages/ble-debug/utils/cbor.js:203
16:44:15.039 buildCborInitUpload: CBOR载荷长度=134, 总帧大小(含SMP头)=142, ATT MTU限制=150 (ATT负载=147) at pages/ble-debug/utils/cbor.js:218
16:44:15.039 startFirmwareUpload: 帧大小=142, ATT上限=147, 超出=否 at pages/ble-debug/dfu-upgrade.vue:1166
16:44:15.039 命令帧=0a00008600010401a46464617461584a3db8f3960000000000080000b825030000000000010002000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff636c656e1a00032e50636f6666006373686158202b30932216faff7b1e983dc7dc34681045ed8ce966ef3f3f1a383fcd45dfe6fc at pages/ble-debug/dfu-upgrade.vue:1177
16:44:15.064 cmd=1 上传初始化发送成功,等待设备确认... at pages/ble-debug/dfu-upgrade.vue:1183
16:44:15.065 startFirmwareUpload: 设置 dfuInitResponseCallback at pages/ble-debug/dfu-upgrade.vue:1184
16:44:22.683 ========onBLEConnectionStateChange进来了========== at pages/ble-debug/dfu-upgrade.vue:492
16:44:22.683 dfu-upgrade: 设备断开连接, deviceId=CC:6A:26:AB:FC:31 at pages/ble-debug/dfu-upgrade.vue:496

2026-07-16 17:00 负责人:无 分享
已邀请:
Ask小助手

Ask小助手 - Ask社区AI助手

欢迎到专业群( uni-app 官方技术交流群 1 ) 咨询,群中有相关专业的管理员和群友。

根据你提供的代码和日志,结合知识库中的相关信息,这个问题很可能与 setBLEMTU 在不同安卓机型上的兼容性差异有关。以下是分析和建议:

问题分析

从日志看,状态查询命令(小数据包)可以正常发送和接收,但 init 包(142字节)发送后无响应。关键点在于:setBLEMTU 在荣耀/华为手机上可能没有真正生效,导致实际 MTU 仍为默认值 23(ATT 负载仅 20 字节),而你代码中按请求值 150 计算的 ATT 上限为 147,未对 142 字节的 init 包做分包处理,直接发送了超出实际承载能力的数据帧。

可能原因

  1. setBLEMTU 调用时机问题:根据社区反馈,部分安卓机型(包括华为)在 createBLEConnection 成功后立即调用 setBLEMTU 会不生效或无响应。参考 ask社区问答ask社区问答

  2. MTU 实际协商值与请求值不一致:你代码中使用 this.mtuSize(请求值 150)计算 ATT 上限,但实际协商的 MTU 可能并非此值。根据 插件市场文档,Android 端蓝牙碎片化严重,不同机型表现不一致,部分机型 setBLEMTU 后实际 MTU 仍为默认 23。

  3. 未监听 onBLEMTUChange:根据插件市场文档建议,连接设备前应先注册 onBLEMTUChange,因为连接成功后可能直接就协商好 MTU 了,未提前监听会错失通知。

解决建议

  1. 调整 setBLEMTU 调用时机:连接成功后延迟 1-2 秒再调用 setBLEMTU,或先发送一个小命令成功后再调用。参考 ask社区问答 中的方案:
// 连接成功后延迟调用 setBLEMTU  
setTimeout(() => {  
    uni.setBLEMTU({  
        deviceId: this.deviceId,  
        mtu: 247, // 建议不超过 247,部分机型不支持更大值  
        success(res) {  
            console.log('MTU协商成功', res)  
        },  
        fail(err) {  
            console.log('MTU协商失败', err)  
        }  
    })  
}, 2000)

本助手使用国产模型,生成代码可能不准确。专业的代码生成需要全球顶尖的AI Coding模型。推荐使用uni-agent

  1. 监听 onBLEMTUChange 获取实际 MTU:不要假设 setBLEMTU 成功后 MTU 就是请求值,应通过回调获取实际值:
// 连接前注册监听  
uni.onBLEMTUChange((res) => {  
    console.log('实际MTU:', res.mtu)  
    this.actualMtu = res.mtu  
})

本助手使用国产模型,生成代码可能不准确。专业的代码生成需要全球顶尖的AI Coding模型。推荐使用uni-agent

  1. 根据实际 MTU 做分包处理:使用实际协商的 MTU 值计算 ATT 上限(实际 MTU - 3),对超过上限的数据帧进行分包发送:
const attLimit = (this.actualMtu || 23) - 3  
if (frameSize > attLimit) {  
    // 需要分包发送  
    this.sendInChunks(frame, attLimit)  
} else {  
    this.doTestSend(frame, 120000, 'dfuInitResponseCallback')  
}

本助手使用国产模型,生成代码可能不准确。专业的代码生成需要全球顶尖的AI Coding模型。推荐使用uni-agent

  1. 确认 writeType 设置:日志显示 writeType: 为空,请确认 getWriteType() 返回的值是否正确。对于 DFU 升级场景,通常需要使用 writeWithResponse(值为 1)确保数据可靠传输。

  2. MTU 值建议不超过 247:根据社区反馈,部分机型最大只支持 247,设置 512 等更大值会报错。参考 ask社区问答

参考链接

如果以上方案仍无法解决跨机型兼容性问题,可以考虑使用插件市场的蓝牙 DFU 原生插件(如 dodo-uniplugin-bleDFU蓝牙升级),原生插件在底层处理了各机型的蓝牙差异。

内容为 AI 生成,仅供参考

要回复问题请先登录注册