uni-app 【报Bug】uni.onBLEConnectionStateChange CALLBACK.value是否自动分包 每个包20大小 为何出现不触发或丢包现象

发布于 1周前 作者 yuanlaile 来自 Uni-App

uni-app 【报Bug】uni.onBLEConnectionStateChange CALLBACK.value是否自动分包 每个包20大小 为何出现不触发或丢包现象

产品分类

uniapp/App

PC开发环境

  • 操作系统:Windows
  • 操作系统版本号:23H2
  • HBuilderX类型:正式
  • HBuilderX版本号:3.99

移动端开发环境

  • 手机系统:Android
  • 手机系统版本号:Android 14
  • 手机厂商:华为
  • 手机机型:荣耀v30 pro

项目信息

  • 页面类型:vue
  • vue版本:vue3
  • 打包方式:离线
  • 项目创建方式:HBuilderX

示例代码

import {  
    ab2hex,  
    hexCharCodeToStr,  
    hexToArrayBuffer  
} from './utils.js'  
const BlModule = (function() {  

    let events = {}  
    let BLDeviceList = []  
    // 设备id  
    let deviceId = null  
    // 读取id  
    let read = {  
        serviceId: null,  
        uuid: null  
    }  
    // 写入id  
    let write = {  
        serviceId: null,  
        uuid: null  
    }  

    function _blModule() {  
        const _this = this  
        init()  
        search()  

        this.on = function(eventName, listener) {  
            if (!events[eventName]) {  
                events[eventName] = [];  
            }  
            events[eventName].push(listener);  
        }  
        // 连接设备  
        this.connect = connect  

        //发送消息  
        this.sendWrite = sendWrite  
        this.sendRead = sendRead  
        this.sendHeart = heart  
        // 获取 设备id  
        this.getDeviceId = function() {  
            return deviceId  
        }  

        // 获取所有特征编码  
        this.getCharacteristicIds = function() {  
            return {  
                read: read,  
                write: write,  
                notify: notify,  
                indicate: indicate  
            }  
        }  
    }  

    /**  
     * 开启蓝牙  
     */  
    function init() {  
        uni.openBluetoothAdapter({  
            success() {  
                uni.getBluetoothAdapterState({  
                    fail(error) {  
                        uni.showToast({  
                            title: '查看手机蓝牙是否打开',  
                            icon: 'none'  
                        });  
                    }  
                })  
            },  
            fail(err) {  
                uni.showToast({  
                    title: '查看手机蓝牙是否打开',  
                    icon: 'none'  
                });  
            }  
        })  
    }  

    /**  
     * 搜索设备  
     */  
    function search() {  
        uni.startBluetoothDevicesDiscovery({  
            success() {  
                console.log('开始搜索');  
                // 开启监听回调  
                uni.onBluetoothDeviceFound(found)  
            }  
        })  
    }  

    /**  
     * 发现新设备添加至DlDeviceList中  
     * @param {Object} res  
     */  
    function found(res) {  
        const oldArr = JSON.parse(JSON.stringify(BLDeviceList))  
        BLDeviceList = oldArr.concat(res.devices).filter(item => item.name != "")  
        // 对搜索到的蓝牙设备去重  
        for (let i = 0; i < BLDeviceList.length; i++) {  
            for (let j = i + 1; j < BLDeviceList.length; j++) {  
                if (BLDeviceList[i].name == BLDeviceList[j].name) {  
                    BLDeviceList.splice(j, 1)  
                }  
            }  
        }  
        emit('updateBlDevice', BLDeviceList)  
    }  

    /**  
     * 连接设备  
     * @param {object} data 设备信息  
     * @param {string} data.deviceId 设备信息  
     */  
    function connect(data) {  
        deviceId = data.deviceId // 将获取到的设备ID存起来  
        uni.createBLEConnection({  
            deviceId: deviceId,  
            success(res) {  
                // 停止搜索  
                stopDiscovery()  
                setMaxMTU()  
                uni.showLoading({  
                    title: '连接中...'  
                })  
                setTimeout(async () => {  
                    await getServices()  
                    uni.hideLoading()  
                    emit('ConnectSuccess')  
                }, 1000)  
            },  
            fail(err) {  
                console.error(err)  
                uni.showToast({  
                    title: '连接失败,请稍后重试',  
                    icon: 'error'  
                })  
            }  
        })  
    }  

    /**  
     * 设置最大值  
     */  
    function setMaxMTU() {  
        uni.setBLEMTU({  
            deviceId: deviceId,  
            mtu: 512,  
            success(res) {  
                console.log("设置最大值成功")  
            }  
        })  
    }  

    /**   
     * 关闭搜索  
     */  
    function stopDiscovery() {  
        uni.stopBluetoothDevicesDiscovery({  
            success(res) {  
                console.log('停止成功')  
            },  
            fail(err) {  
                console.log('停止失败', err)  
            }  
        })  
    }  

    /**  
     * 获取服务  
     */  
    function getServices() {  
        return new Promise((resolve, reject) => {  
            let characteristics = []  
            uni.getBLEDeviceServices({  
                deviceId: deviceId,  
                success: async (res) => {  
                    const asyncRes = await Promise.all(res.services.map(async (item) => {  
                        const c = await getCharacteristics(item.uuid)  
                        return c  
                    }));  
                    asyncRes.map(item => {  
                        characteristics = characteristics.concat(item)  
                    })  
                    characteristics.map((c, j) => {  
                        if (c.properties.read && !c.properties.write) {  
                            read = {  
                                serviceId: c.serviceId,  
                                uuid: c.uuid  
                            }  
                        }  
                        if (c.properties.write && !c.properties.read) {  
                            write = {  
                                serviceId: c.serviceId,  
                                uuid: c.uuid  
                            }  
                        }  
                        if (c.properties.notify) {  
                            const st = setTimeout(()=>{  
                                notifyEvn({  
                                    serviceId: c.serviceId,  
                                    uuid: c.uuid  
                                })  
                                clearTimeout(st)  
                            }, 1000,)  
                            resolve()  
                        }  
                    })  
                },  
                fail(err) {  
                    console.log('连接失败');  
                    reject(err)  
                }  
            })  
        })  
        // 如果是自动链接的话,uni.getBLEDeviceServices方法建议使用setTimeout延迟1秒后再执行  

    }  
    /**  
     * 获取特征值  
     */  
    function getCharacteristics(uuid) {  
        // 如果是自动链接的话,uni.getBLEDeviceCharacteristics方法建议使用setTimeout延迟1秒后再执行  
        return new Promise((resolve, reject) => {  
            uni.getBLEDeviceCharacteristics({  
                deviceId: deviceId,  
                serviceId: uuid,  
                success(res) {  
                    res.characteristics.map(item => {  
                        item.serviceId = uuid  
                    })  
                    const properties = res.characteristics  
                    resolve(properties) // 可以在此判断特征值是否支持读写等操作,特征值其实也需要提前向硬件佬索取的  
                },  
                fail(err) {  
                    reject(err)  
                }  
            })  
        })  
    }  

    /**  
     * 开启消息监听  
     */  
    function notifyEvn(notify) {  
        uni.notifyBLECharacteristicValueChange({  
            state: true, // 启用 notify 功能  
            deviceId: deviceId, // 设备id  
            serviceId: notify.serviceId, // 监听指定的服务  
            characteristicId: notify.uuid, // 监听对应的特征值  
            success(res) {  
                console.log('开启设备监听');  
                listenValueChange()  
            },  
            fail(err) {  
                console.error('消息监听失败', err)  
            }  
        })  
    }  

    /**  
     * 监听消息变化  
     */  
    function listenValueChange() {  
        uni.onBLECharacteristicValueChange(res => {  
            let resHex = ab2hex(res.value)  
            // emit('newMsg', resHex)  
        })  
    }  

    /**  
     * 发送写入命令  
     * 接受一个16进制字符串  
     */  
    function sendWrite(val) {  
        // 向蓝牙设备发送一个0x00的16进制数据  
        const buffer = hexToArrayBuffer(val);  
        uni.writeBLECharacteristicValue({  
            deviceId: deviceId,  
            serviceId: write.serviceId,  
            characteristicId: write.uuid,  
            value: buffer,  
            writeType: 'write',  
            success(res) {  
                emit('SendSuccess')  
            },  
            fail(err) {  
                console.error(err)  
                uni.showToast({  
                    title: '指令发送失败',  
                    icon: 'none'  
                })  
            }  
        })  
    }  

    /**  
     * 发送读取指令  
     * @param {string} val  
     */  
    function sendRead(val) {  
        // 向蓝牙设备发送一个0x00的16进制数据  
        const buffer = hexToArrayBuffer(val);  
        uni.readBLECharacteristicValue({  
            deviceId: deviceId,  
            serviceId: read.serviceId,  
            characteristicId: read.uuid,  
            success(res) {  
                emit('SendSuccess')  
            },  
            fail(err) {  
                console.error(err)  
                uni.showToast({  
                    title: '指令发送失败',  
                    icon: 'none'  
                })  
            }  
        })  
    }  

    /**  
     * 发送心跳数据  
     * 接受一个16进制字符串  
     */  
    function heart(val) {  
        // 向蓝牙设备发送一个0x00的16进制数据  
        const buffer = hexToArrayBuffer(val);  
        uni.writeBLECharacteristicValue({  
            deviceId: deviceId,  
            serviceId: write.serviceId,  
            characteristicId: write.uuid,  
            value: buffer,  
            writeType: 'write',  
            success(res) {},  
            fail(err) {  
                console.error(err)  
                uni.showToast({  
                    title: '指令发送失败',  
                    icon: 'none'  
                })  
            }  
        })  
    }  
    /**  
     * 事件监听器  
     */  
    function emit(eventName, data) {  
        if (!events[eventName]) {  
            return;  
        }  
        events[eventName].forEach(listener => listener(data));  
    }  

    return _blModule  
})()  

export default BlModule

操作步骤

根据以上代码连接蓝牙后,发送指令触发消息监听,

预期结果

连接蓝牙后发送指令后,如有消息应当触发消息,并且大于20字节分包后应当返回所有分包

实际结果

连接蓝牙后发送指令,有的时候不触发监听,消息大于20字节后存在丢包现象

bug描述

uni.onBLEConnectionStateChange CALLBACK.value是会自动分包吗? 每个包为20个字节大小?会出现不触发或者大于20个丢包的现象。


1 回复

在使用 uni.onBLEConnectionStateChange 进行蓝牙连接状态监听时,如果出现不触发或丢包现象,可能是由于以下几个原因导致的:

1. 蓝牙设备通信限制

蓝牙设备在通信时,通常会有每包数据大小的限制。对于 BLE(低功耗蓝牙)设备,每个数据包的大小通常为 20 字节。如果发送的数据超过这个大小,设备或系统可能会自动进行分包处理。如果分包处理不当,可能会导致丢包或数据不完整。

2. 设备或系统问题

某些蓝牙设备或手机系统可能在处理蓝牙数据时存在 bug 或性能瓶颈,导致数据包丢失或 uni.onBLEConnectionStateChange 回调不触发。可以尝试在不同的设备或系统版本上进行测试,确认是否是设备或系统的问题。

3. 代码逻辑问题

检查代码逻辑,确保在连接状态变化时正确地处理了回调函数。确保没有遗漏或错误地处理了蓝牙连接状态的变化。

4. 数据缓冲区溢出

如果数据发送频率过高,可能会导致数据缓冲区溢出,从而导致丢包。可以尝试降低数据发送频率,或在接收到数据后及时处理数据,避免缓冲区溢出。

5. 信号干扰

蓝牙通信受到信号干扰的影响较大,如果周围有其他无线设备或干扰源,可能会导致通信不稳定,从而出现丢包现象。

解决方案

  1. 分包处理:手动处理数据分包,确保每包数据不超过 20 字节,并在接收端进行数据重组。
  2. 降低发送频率:降低数据发送频率,避免缓冲区溢出。
  3. 错误处理:在代码中添加错误处理逻辑,确保在出现异常时能够及时处理。
  4. 设备兼容性测试:在不同的设备和系统版本上进行测试,确认问题的根源。
  5. 优化环境:尽量减少蓝牙通信环境中的干扰源,确保通信稳定。

示例代码

以下是一个简单的示例代码,展示如何处理分包数据:

let receivedData = [];

uni.onBLECharacteristicValueChange((res) => {
  const value = res.value;
  receivedData.push(...value);

  // 假设每包数据大小为 20 字节,且最后一包数据不足 20 字节
  if (value.length < 20) {
    const completeData = new Uint8Array(receivedData);
    console.log('Received complete data:', completeData);
    receivedData = []; // 清空缓冲区
  }
});
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!