3***@qq.com
3***@qq.com
  • 发布:2024-02-03 16:57
  • 更新:2024-02-03 16:57
  • 阅读:213

【报Bug】uni.onBLEConnectionStateChange CALLBACK.value是会自动分包吗?每个包为20个大小?为什么会出现不触发或者丢包的现象

分类:uni-app

产品分类: uniapp/App

PC开发环境操作系统: Windows

PC开发环境操作系统版本号: 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
<template>  
    <scroll-view scroll-y class="box">  
        <view class="item" v-for="(item, index) in BLDeviceList" :key="index" @click="connectBlue(item)">  
            <view>  
                <text>id: {{ item.deviceId }}</text>  
            </view>  
            <view>  
                <text>name: {{ item.name }}</text>  
            </view>  
        </view>  
    </scroll-view>  
    <button @click="SearchBT">搜索蓝牙设备</button>  
    <input  placeholder="输入发送指令" v-model="order"/>  
    <button @click="sendWriteOrder">发 送</button>  
    <input  placeholder="输入读取指令" v-model="read"/>  
    <button @click="sendReadOrder">发 送</button>  
</template>  
<script setup>  
    import BlModule from '@/components/BlModule/index.js'  
    import { ref } from 'vue';  
    let order = ref('CD3613051500015AB76ECD3E')  
    let read = ref('CD3613051500015AC1CD3E')  
    let BLDeviceList =  ref([])  
    let blTools = null  
    // 搜索蓝牙设备  
    function SearchBT() {  
        blTools = new BlModule()  
        // 监听获取设备列表  
        blTools.on('updateBlDevice',(e) => {  
            BLDeviceList.value = e  
        })  
    }  
    // 连接蓝牙设备  
    function connectBlue(data) {  
        blTools.connect(data)  
        // 监听连接成功状态  
        blTools.on('ConnectSuccess', () => {  
            uni.showToast({  
                title: '连接成功',  
                icon: 'none'  
            });  
        })  
    }  

    // 发送写入指令  
    function sendWriteOrder() {  
        blTools.sendWrite('0X'+order.value)  
        // 监听发送成功状态  
        blTools.on('SendSuccess', () => {  
            uni.showToast({  
                title: '发送成功',  
                icon: 'none'  
            });  
        })  
    }  

    // 发送读取命令  
    function sendReadOrder() {  
        blTools.sendWrite('0X'+read.value)  
        // 监听发送成功状态  
        blTools.on('SendSuccess', () => {  
            uni.showToast({  
                title: '发送成功',  
                icon: 'none'  
            });  
        })  
    }  
</script>  

<style>  
    .box {  
        width: 98%;  
        height: 400rpx;  
        box-sizing: border-box;  
        margin: 0 auto 20rpx;  
        border: 2px solid dodgerblue;  
    }  

    .item {  
        box-sizing: border-box;  
        padding: 10rpx;  
        border-bottom: 1px solid #ccc;  
    }  

    button {  
        margin-bottom: 20rpx;  
    }  

    ​ .msg_x {  
        border: 2px solid seagreen;  
        width: 98%;  
        margin: 10rpx auto;  
        box-sizing: border-box;  
        padding: 20rpx;  
    }  

    ​ .msg_x .msg_txt {  
        margin-bottom: 20rpx;  
    }  
</style>

操作步骤:

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

预期结果:

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

实际结果:

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

bug描述:

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

2024-02-03 16:57 负责人:无 分享
已邀请:

要回复问题请先登录注册