前情:在微信小程序中连接蓝牙电子计重桌秤,(电子秤品牌:坤宏),直接通过蓝牙获取当前称重的重量数据,然后显示在界面上。
ps:记录的时候,还在开发阶段,得到了数据,数据有会实时变化,但是数据和真实称重对不上,
ps:记录的时候,还在开发阶段,得到了数据,数据有会实时变化,但是数据和真实称重对不上,
⚠️注意
- 此次,只涉及读取数据,没有写入数据,具体 API 查看小程序官方文档
- 确保手机蓝牙已经打开,并且可以搜索到该电子秤的蓝牙设备,android 可以搜到,ios 搜不到
- 微信小程序中搜索到的蓝牙设备很多,deviceId 在 android 上显示为蓝牙设备主服务的 mac 地址,在 ios 上显示为蓝牙设备主服务的 uuid
- 最终得到的结果是 ArrayBuffer 型数据,需要先转为16进制字符串,再转为10进制数据
(1)初始化
初始化蓝牙模块 --- wx.openBluetoothAdapter
// 定义数据 data: { devices: [], // 搜索到的蓝牙设备 deviceId 数组 deviceId: '', // 目标蓝牙设备 deviceId services: [] // 设备服务列表 serviceId 数组 serviceId: '', characteristics: [] // 特征值列表 characteristicId: '' // 选择某一个特征值 value: '' // 16 进制数据值 } // 蓝牙 API 调用步骤 openBluetoothAdapter() { wx.openBluetoothAdapter({ // (1) success: res => { console.log('openBluetoothAdapter初始化蓝牙模块成功:', res) this.startBluetoothDevicesDiscovery() // (2) 开始搜索 }, fail: err => { console.log('openBluetoothAdapter初始化蓝牙模块失败:', err) if (err.errCode === 10001) { // 当前蓝牙适配器不可用 wx.onBluetoothAdapterStateChange( res => { if (res.available) { this.startBluetoothDevicesDiscovery() } }) } } }) }
(2)搜索蓝牙设备
搜寻附近的蓝牙外围设备 --- wx.startBluetoothDevicesDiscovery
- 入参 services 作用要搜索的蓝牙设备主 service 的 uuid 列表,某些蓝牙设备会广播自己的主 service 的 uuid,如果设置此参数,则只搜索广播包括对应 uuid 的主服务的蓝牙设备,可以通过该参数过滤掉周边不需要处理的其他蓝牙设备
- 入参 allowDuplicatesKey 作用是否允许重复上报同一设备,如果允许重复上报,则 wx.onBlueToothDeviceFound 方法会多次上报同一设备,但是 RSSI 值会有不同,默认为 false
eg: services: ['FEE7'] 主服务的 UUID 是 FEE7,传入这个参数,只搜索主服务 UUID 为 FEE7 的设备,该设备是微信硬件平台的蓝牙智能灯
⚠️ 此操作比较耗费系统资源,需要在搜索并连接到设备后调用 wx.stopBluetoothDevicesDiscovery 方法停止搜索
startBluetoothDevicesDiscovery() { wx.startBluetoothDevicesDiscovery({ success: res => { console.log('startBluetoothDevicesDiscovery开始搜索外围设备成功:', res) this.getBluetoothDevices() // (3) 获取蓝牙列表 }, fail: err => { console.log('startBluetoothDevicesDiscovery搜索外围设备失败:', err) } }) }
(3)获取蓝牙设备
获取在蓝牙模块生效期间所有已发现的蓝牙设备,包括已经连接成功的蓝牙设备 --- wx.getBluetoothDevices
getBluetoothDevices() { wx.getBluetoothDevices({ success: res => { console.log('getBluetoothDevices获取蓝牙设备成功:', res) this.setData({ devices: res. devices || [] // uuid 对应的的已连接设备列表 }) this.createBLEConnection(); // (4) 与目标设备建立连接 }, fail: err => { console.log('getBluetoothDevices获取蓝牙设备失败:', err) } }) }
(4)建立连接
与目标蓝牙设备建立连接,需要是低功耗蓝牙设备 --- wx.createBLEConnection
⚠️ 如果微信小程序此前搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,不用重新搜索
createBLEConnection() { // 如果是第一次建立连接,可以通过名称匹配,获取相应设备的 deviceId let devices = this.data.devices; devices.forEach(item => { if(item.name == 'kunHong') { this.setData({ deviceId: item.deviceId }) } }) // 建立连接 wx.createBLEConnection({ deviceId: this.data.deviceId, success: res => { console.log('createBLEConnection与目标蓝牙连接成功:', res) this.getBLEDeviceServices() // (5)获取服务 }, fail: err => { console.log('createBLEConnection与目标蓝牙连接失败:', err) } }) }
(5)获取蓝牙设备服务
获取蓝牙设备所有主服务的 uuid --- wx.getBLEDeviceServices
- 入参 deviceId 为 wx.getBluetoothDevices 中获取的目标蓝牙设备的 deviceId
⚠️开发过程中,主服务 serviceId 和 主服务的特征值 characteristics 都是选取的实际操作过程中,得到的类似于该目标蓝牙设备的 id,但是小程序官方文档的 demo,遍历了所有的列表(serviceId 和 characteristics),需要区分一下
getBLEDeviceServices() { wx.getBLEDeviceServices({ deviceId: this.data.deviceId, success: res => { console.log('getBLEDeviceServices获取蓝牙设备服务', res) // getBluetoothDevices 获取的有 deviceId 和 advertisServiceUUIDs,可以在这里获取的服务列表中选择一个一样的作为后续 API 请求的服务id,这个 id 需要满足是否可读 this.setData({ services: res.services, serviceId: res.services[0].uuid // 假设是第一个 }) this.getBLEDeviceCharacteristics() // (6) 获取特征值 // 官方 demo for(var i = 0; i < res.services.length; i++) { // 该服务是否为主服务 if(res.services[i].isPrimary) { this.getBLEDeviceCharacteristics(res.services[i].uuid) } } }, fail: err => { console.log('getBLEDeviceServices获取蓝牙设备服务失败:', err) } }) }
(6)获取特征值
获取蓝牙设备某个服务中所有特征值 --- wx.getBLEDeviceCharacteristics
- 入参 deviceId 为 wx.getBluetoothDevices 中获取的目标蓝牙设备的 deviceId
- 入参 serviceId 为蓝牙服务 uuid ,通过 wx.getBLEDeviceServices 获取
getBLEDeviceCharacteristics(serviceId) { wx.getBLEDeviceCharacteristics({ deviceId: this.data.deviceId, serviceId: this.data.serviceId, success: res => { console.log('getBLEDeviceCharacteristics获取蓝牙服务特征值成功:', res) this.setData({ characteristics: res. characteristics, characteristics: res. characteristics[0].uuid }) this.notifyBLECharacteristicValueChange(); // (7)启用 notify 功能 // 官方 demo for(var i = 0; i < res.characteristics.length; i++) { // 是否可读 if(res.characteristics[i].read) { // 读取数据 wx.readBLECharacteristicValue({ deviceId: this.data.deviceId, serviceId: serviceid, characteristicId: res.characteristicId[i].uuid }) }, if(res.characteristics[i].properties.notify || res.characteristics[i].properties.indicate) { // 启用功能 wx.notifyBLECharacteristicValueChange({ deviceId, serviceId, characteristicId: item.uuid, state: true, }) } } }, fail: err => { console.log('getBLEDeviceCharacteristics获取蓝牙服务特征值失败:', err) } }) this.onBLECharacteristicValueChange() // (8)监听特征值变化 this.readBLECharacteristicValue(); // (9)读取数据 }
(7)启用 notify 功能
启用低功耗蓝牙特征值变化时的 notify 功能,订阅特征值
⚠️必须设备的特征值支持 notify 或者 indicate 才可以成功启用
notifyBLECharacteristicValueChange() { wx.notifyBLECharacteristicValueChange({ deviceId: this.data.deviceId, serviceId: this.data.serviceId, characteristicId: this.data. characteristicId, state: true // 是否启用 notify (四个字段全部必填) }) }
(8)监听特征值变化
监听低功耗蓝牙设备特征值的变化事件 --- wx.onBLECharacteristicValueChange
⚠️必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification(通知)
// 先监听一下,保证第一时间获取数据 onBLECharacteristicValueChange() { wx.onBLECharacteristicValueChange( characteristic => { console.log('onBLECharacteristicValueChange从目标蓝牙设备监听到的数据值:', characteristic) this.setData({ value: this.ab2hex(abcharacteristic.value) // (10) 转为 16 进制 }) }) }
(9)读取数据
读取低功耗蓝牙设备的特征值的二进制数据值 --- wx.readBLECharacteristicValue
⚠️必须目标蓝牙设备的特征值支持 read 才可以成功调用,并且单独使用 readBLECharacteristicValue 并不能获取到真正的特征值,只能返回获取特征值的状态,即是否成功获取到值,真正的值需要使用 wx.onBLECharacteristicValueChange() 执行回调才可以在 wx.onBLECharacteristicValueChange() 这个 API 中获得读取到的特征值
readBLECharacteristicValue() { wx.readBLECharacteristicValue({ deviceId: this.data.deviceId, serviceId: this.data.serviceId, characteristicId: this.data.charecteristicId, success: res => { console.log('readBLECharacteristicValue读取特征值成功:', res) }, fail: err => { console.log('readBLECharacteristicValue读取特征值失败:', err) } }) }
(10)转为 16 进制
官方文档中介绍了 ArrayBuffer 转为 16 进制的方法
// ArrayBuffer转16进制字符串示例 ab2hex(buffer) { let hexArr = Array.prototype.map.call( new Uint8Array(buffer), function(bit) { return ('00' + bit.toString(16)).slice(-2) } ) return hexArr.join(''); }