uni 您所在的位置:网站首页 蓝牙无法进行通信 uni

uni

2024-07-09 19:00| 来源: 网络整理| 查看: 265

吐槽:

先说一下心路历程,因为个人开发的一个APP,需要连接蓝牙模块进行设备控制和双向的数据通信,所以尝试用uni-app开发一个手机程序对购买的蓝牙模块进行连接,emm.......怎么说呢,理论上过程都是通的,但坑还是太多了。今天过程跑通了,特来总结一下。说明下,代码太长了,所以我准备分段说明展示,完整代码到时候我上传到github上,地址最后我写在评论里哈。进入正题...........

1.蓝牙通信整体流程

 上图一共九个步骤就是创建uni-app/微信小程序连接蓝牙设备并进行通信的基本步骤。具体每个模块是怎么回事,请继续阅读。也可以直接转向官网查看。

微信小程序:

wx.readBLECharacteristicValue(Object object) | 微信开放文档 (qq.com)

uni-app:

https://uniapp.dcloud.io/api/system/ble?id=getbledeviceservices

说明:uni和小程序在API接口上基本是一毛一样的,所以在开发的时候可以互相参考着看下。因为微信小程序开发工具扫码真机测试速度快。当然uni自家的HbuilderX自带的真机运行基座也不错。就我本人来讲,我是两个一起参考的。需要注意的就是语法的细微差别。因为uni是基于Vue开发的,用的是Vue的写法,而小程序并不是(其实都差不多)

2.打开蓝牙适配器状态openBluetoothAdapter //开启蓝牙适配器初始化蓝牙模块 openBluetoothAdapter() { //刷新蓝牙设备 this.devices=[] uni.openBluetoothAdapter({ success: (res) => { console.log("开启蓝牙适配器成功(openBluetoothAdapter success)", res); this.startBluetoothDevicesDiscovery(); uni.showToast({ title: "开始扫描设备", icon: "success", }); }, fail: (res) => { uni.showToast({ title: "请开启蓝牙", icon: "none", }); if (res.errCode === 10001) { uni.onBluetoothAdapterStateChange(function (res) { //监听蓝牙适配器是否打开,若打开则自动搜索蓝牙设备(onBluetoothAdapterStateChange) if (res.available) { this.startBluetoothDevicesDiscovery(); } }); } }, }); },

当用户打开了蓝牙的时候就会进入下一步查找蓝牙设备。如果用户没有打开蓝牙,可以通过onBluetoothAdapterStateChange进行判断提示。打开后进入设备搜索。

这里说明下,不论是微信小程序还是uni-app,调用方式都是这种(uni.某某/wx.某某),其都包含有三个回调函数,success,fail,complete。所以都可以直接使用就好了

3.开始搜寻附近的蓝牙外围设备startBluetoothDevicesDiscovery

此操作比较耗费系统资源,请在搜索并连接到设备后调用 stopBluetoothDevicesDiscovery 方法停止搜索,后期熟练了也可以直接写上对应的设备id,直接连他就ok了。

//开启蓝牙设备搜索 startBluetoothDevicesDiscovery() { // 关闭蓝牙适配器的时候将其打开 if (this._discoveryStarted) { return; } this._discoveryStarted = true; uni.startBluetoothDevicesDiscovery({ allowDuplicatesKey: true, success: (res) => { console.log( "开始搜寻蓝牙设备成功(startBluetoothDevicesDiscovery success)", res); uni.showLoading({ title: "正在搜索设备", }); this.onBluetoothDeviceFound(); }, }); }, 4. 监听寻找到新设备的事件onBluetoothDeviceFound //监听寻找到新设备的事件 onBluetoothDeviceFound() { uni.onBluetoothDeviceFound((res)=>{ if(res){ uni.hideLoading(); } res.devices.forEach(device=>{ //过滤掉没有名字的设备 if (!device.name && !device.localName) { return }; //这么操作是为了去除重复 const foundDevices = this.devices//将数据中的数组复制一份,利用动态绑定方式,不断复制最新的数组 const idx = this.inArray(foundDevices, 'deviceId', device.deviceId) if (idx === -1) { this.devices.push(device);//数组里没有的的就向里面添加数据,保证没有重复[uni写法] } }) console.log(this.devices); }); },

 这里要注意的是要对搜索到的设备进行去重复操作,因为蓝牙搜索貌似是这样的,你只要没关闭它,他就一直搜索他,会出现大量的重复设备。这些设备你只需要向你的设备数组中放入一个就行了。

5.连接低功耗蓝牙设备createBLEConnection

官方目前只有低功耗蓝牙设备的接口,就是BLE蓝牙,和传统手机蓝牙有区别。所以你搜索的时候是找不到开着蓝牙的其他手机的。目前我了解的是智能家居设备,小米手环,华为手表等这类的是用的低功耗蓝牙设备。也可以购买BLE低功耗蓝牙模块进行测试,总之低功耗蓝牙设备将来的使用会更广,通过蓝牙模块也可以集成进其他硬件设备中进行控制。

我这个代码是做在了button按钮上,当用户点击某个设备想要连接时,这个设备的信息就传入参数e中,界面图最后展示。

createBLEConnection(e) { const ds = e.currentTarget.dataset this.deviceId = ds.deviceId //将设备名称也传递给全局变量 this.deviceName = ds.name uni.createBLEConnection({ deviceId:this.deviceId, success: (res) => { this.connected=true, console.log('连接时获取设备id',this.deviceId); setTimeout(() => { this.getBLEDeviceServices(this.deviceId); }, 1000); }, fail:(err)=>{ uni.showToast({ title:'建立连接失败', icon:'none' }) return } }) this.stopBluetoothDevicesDiscovery()//此时停止蓝牙搜索 },

这里面有一个巨坑:在uni-app中调用getBLEDeviceServices的时候,不要直接调,直接调是没有任何服务的!!!!!!!!!!!!!!!!一定要设置时间间隔,延迟调用。这个问题在微信小程序中没有,在uni中一定要延迟调用,延迟调用,延迟调用,延迟调用,延迟调用,延迟调用,延迟调用,延迟调用,延迟调用。

6.获取蓝牙设备所有服务getBLEDeviceServices

这里需要说明下,刚开始我也是对各种id云里雾里,其实就三个。简单的说:每个设备都有一个唯一的设备id(deviceId),每个设备中又有不同的服务,他们通过服务id区分(serviceId),有的是只读的,有的是只写的,有的是可读可写,有的可以监听,有的不可以。。。。。所以你要搞清楚那个服务是你要的,要不然后面所有操作都对了就是不行。每个服务id中又有不同的特征值id(characteristicId)也就是uuid,uuid(universally unique Identifier)通用唯一识别码。用来标识蓝牙设备所提供的服务,比如(音频传输、串口通信、打印服务、传真服务、网络服务、文件传输服务、信息同步服务等)。下面就是我当时做的时候的一个截图。

可以看到,我这个服务中所有的特征值id/uuid只有2和3支持读写,但是也不能用,因为他们的notify与indicate都是false,这意味着到时候传递数据时候,你写的程序无法获取数据的变化情况,说白了就是没法通信。所以你得从新换一个服务id,之后再看他里面的特征值id情况。

这个是我候选的一个服务id中的特征值id,发现他的notify是true的,证明可以用这个进行通信。notify和indicate只要有一个为true就行,不用都为true。

getBLEDeviceServices(deviceId) { uni.getBLEDeviceServices({ deviceId:deviceId, success: (res) => { //serviceId固定死了 this.getBLEDeviceCharacteristics(deviceId, this.serviceId) }, fail:(err)=>{ uni.showToast({ title:'获取服务失败', icon:'none' }) return } }) },

这里我的服务id我写死了 所以就不在上res中找了,如果不想写死,可以上res中找,找到后存起来供下面使用。

7.获取蓝牙设备某个服务中所有特征值(characteristic)getBLEDeviceCharacteristics

这里面特征值id我也是之前console出来找到后直接写死了,就不要每次获取了。当监听到notify或者indicate为true是就要启动notifyBLECharacteristicValueChange。

启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。注意:必须设备的特征值支持 notify 或者 indicate 才可以成功调用。

另外,必须先启用 notifyBLECharacteristicValueChange 才能监听到设备 characteristicValueChange 事件

uni.getBLEDeviceCharacteristics({ //设备id与服务id必须要给,特征值id先不用获取,直接写死 deviceId, serviceId, success: (res) => { if(res.characteristics[0].properties.read) { console.log('该特征值可读'); uni.readBLECharacteristicValue({ deviceId, serviceId, characteristicId:this.characteristicId, }); } if(res.characteristics[0].properties.write) { console.log('该特征值可写'); this.canWrite=true; //调用写 this.writeBLECharacteristicValue() } //确保对应服务id下的特征值id具备监听数据变化的特性 if (res.characteristics[0].properties.notify || res.characteristics[2].properties.indicate) { uni.notifyBLECharacteristicValueChange({ deviceId, serviceId, characteristicId: this.characteristicId, state: true,//是否启用notify通知 success:(res)=>{ console.log('通知启用(notifyBLECharacteristicValueChange)',res); } }) } }, fail(res) { console.error('获取蓝牙设备特征值失败(getBLEDeviceCharacteristics)', res) } })

这里面我在判断出设备可读的时候就调用了readBLECharacteristicValue。

读取低功耗蓝牙设备的特征值的二进制数据值。注意:必须设备的特征值支持 read 才可以成功调用

8.监听低功耗蓝牙设备的特征值变化事件onBLECharacteristicValueChange

监听低功耗蓝牙设备的特征值变化事件。必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification。

这样设备发送数据给手机,手机才会获得。传递的值就是value(硬件设备向手机传递数据)。

uni.onBLECharacteristicValueChange((characteristic) => { //记录手机接受的数据 this.myDataMeasure.push(this.ab2hex(characteristic.value)); //记录目前通信的对象(和谁通信,特征值是多少,初始值00) const idx = this.inArray(this.chs, 'uuid', characteristic.characteristicId) const data = {} if (idx === -1) { this.chs.push({ uuid: characteristic.characteristicId, value: this.ab2hex(characteristic.value) }) } else { this.chs[idx] = { uuid: characteristic.characteristicId, value: this.ab2hex(characteristic.value) } } }) 9.写入蓝牙特征值writeBLECharacteristicValue

这是手机向硬件设备进行写入写入二进制数据。注意:必须设备的特征值支持 write 才可以成功调用

writeBLECharacteristicValue() { //向蓝牙设备发送一个0x00的16进制数据 let buffer = new ArrayBuffer(1) //可以自定义复合格式的视图 let dataView = new DataView(buffer) dataView.setUint8(0, this.sendData) uni.writeBLECharacteristicValue({ deviceId: this.deviceId, serviceId: this.serviceId, characteristicId: this.characteristicId, value: buffer, complete:()=>{ //buffer本身是arraybuffer(arraybuffer要转换为16进制) console.log('十六进制',this.ab2hex(buffer)); let sixNumber=this.ab2hex(buffer); console.log('十进制',this.hex2int(sixNumber)); } }) },

这里我对手机发送的数据进行了一个进制转换。到目前为止就可以实现手机与蓝牙设备的通信了。当然自己实际操作肯定不能这么顺利。各种问题还是会有,目前我把我遇到的坑都写了出来,后续也欢迎补充。

10.最终效果展示



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有