小程序蓝牙API在iOS平台时无法获取蓝牙设备名称
前言
目前主流的蓝牙设备使用的都是原生APP来进行连接的,因此官方对于蓝牙API做的改进比较少,在iOS设备上还是存在许多小问题,但是在Android设备上按照官方文档操作基本没有任何问题。但是种所周知,作为小公司不可能花费两笔钱来开发APP。话不多说,这里我将主要讲一下我在开发中是如何解决这个问题的。
一、微信小程序蓝牙是如何运行的?
二、使用步骤
1.代码
1.话不多说上代码,首先就是初始化蓝牙适配器、获取蓝牙状态、开启扫描外围设备:
/**
* 开启蓝牙适配器
* 获取蓝牙适配器的状态
* 开启蓝牙扫描外围设备
* 监听蓝牙扫描到的设备
*/
openBluetoothAdapter(){
let that = this
wx.openBluetoothAdapter({
success(res){
// console.log("\n成功打开adapter")
wx.getBluetoothAdapterState({
success: (res) => {
if(res.available){
if(res.discovering){
that.onBluetoothDeviceFound()
}else{
wx.startBluetoothDevicesDiscovery({
//过滤serviceId
services:["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"],
//允许重复名字的设备
allowDuplicatesKey: true,
success(){
// console.log("\n开启扫描成功")
that.onBluetoothDeviceFound()
}
})
}
}
},
})
}
})
},
2.开启设备扫描监听:
/**
* 将扫描到的设备存起来
*/
onBluetoothDeviceFound() {
let that = this
wx.onBluetoothDeviceFound((res) => {
res.devices.forEach(device => {
if (!device.name && !device.localName) {
//将没有名字的设备命名为了“未知设备”
device.name = '未知设备'
// return
}
const foundDevices = that.data.devices
//console.log(foundDevices)
const idx = that.inArray(foundDevices,'deviceId',device.deviceId)
const data = {}
if (idx === -1) {
//新设备,直接存起来
data[`devices[${foundDevices.length}]`] = device
} else {
//老设备覆盖一遍
data[`devices[${idx}]`] = device
}
// console.log(data)
that.setData(data)
})
})
},
3.点击连接指定设备,同时开启设备连接状态的监听,随时获取连接状态,及时做出反应:
/**
* 点击连接设备
* @param {点击事件} e
*/
tapToCreatBLEConnection(e){
let that = this
//防止连接多个设备,导致通信干扰
if(that.data.connected){
wx.showToast({
title: '已连接到设备,若要切换设备请断开当前设备',
icon: 'none',
duration:2000
})
}else{
const ds = e.currentTarget.dataset
const deviceId = ds.deviceId
const name = ds.name
wx.createBLEConnection({
deviceId,
success: (res) => {
that.getBLEDeviceServices(deviceId)
//监听蓝牙设备是否掉线
wx.onBLEConnectionStateChange((res) => {
if(!res.connected){
that.setData({
//相关操作
connected: false,
})
}
})
that.stopBluetoothDevicesDiscovery()
}
})
}
},
4.获取设备服务列表:
/**
* 获取设备的服务列表
* @param {设备id} deviceId
*/
getBLEDeviceServices(deviceId) {
//获取服务列表
wx.getBLEDeviceServices({
deviceId,
success: (res) => {
for (let i = 0; i < res.services.length; i++) {
if (res.services[i].isPrimary) {
//获取服务权限并进行相应操作
this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
return
}
}
}
})
},
5.获取特征值,并开启特征值变化监听,保证第一时间获取到数据:
/**
* 获取设备的特征值、监听特征值变化,保证第一事件获取数据
* @param {设备id} deviceId
* @param {服务id} serviceId
*/
getBLEDeviceCharacteristics(deviceId, serviceId) {
let that = this
//获取服务具体权限
wx.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success: (res) => {
device_data.deviceId = deviceId
device_data.serviceId = serviceId
let item = res.characteristics[0]
device_data.characteristicId = item.uuid
//读取数据
wx.readBLECharacteristicValue({
deviceId,
serviceId,
characteristicId: item.uuid,
})
//开启notify
wx.notifyBLECharacteristicValueChange({
deviceId,
serviceId,
characteristicId: item.uuid,
state: true,
success:function(res){
// console.log("开启notify成功\n")
}
})
},
fail(res) {
console.error('getBLEDeviceCharacteristics', res)
}
})
// 操作之前先监听,保证第一时间获取数据
wx.onBLECharacteristicValueChange((characteristic) => {
//接收数据并进行存储
})
},
6.发送数据,提供一下ArrayBuffer与字符串相互转换的函数,之前找了挺久的:
/**
* ArrayBuffer转字符串
* @param {接收到的arrayBuffer} arrayBuffer
*/
function ab2str(arrayBuffer){
let unit8Arr = new Uint8Array(arrayBuffer);
let encodedString = String.fromCharCode.apply(null, unit8Arr);
return encodedString;
}
/**
* 将要发送的str转换成ArrayBuffer
* @param {需要发送的str} str
*/
function str2ab(str){
var buf = new ArrayBuffer(str.length)// 每个字符占用2个字节
var bufView = new Uint8Array(buf)
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i)
}
return buf
}
/**
* 发送数据
*/
bindSentMessage(){
wx.writeBLECharacteristicValue({
deviceId: device_data.deviceId,
serviceId: device_data.serviceId,
characteristicId: device_data.characteristicId,
value: buff,//发送的数据,要是ArrayBuffer类型的
})
},
2.iOS无法扫描到设备名称的解决办法
经过多次测试发现,一些设备的芯片型号和蓝牙协议的不同会导致iOS设备无法获取到设备的名称,无论是name还是LocalName都无法获取。我问了一下官方,官方的回答我就不说了,说了和不说一样。LocalName是广播数据中带的一个字段,我想可能是解析出了问题,想到官方API中好像还有其他几个广播字段我就都打印了一下,发现除了serviceId有东西其它都为空。没办法官方API也改不动啊,就从name下手,name这个字段是连接设备后才能获取到,于是就想到一个很笨的办法:通过serviceId过滤蓝牙设备,再将设备列表的所有设备一一建立连接,然后断开,再次扫描,虽然很难受,但是问题是解决了,等一手官方API的完善吧。(当然可能还有大佬知道有其它办法解决,望看到这篇文章的大佬能热心的给我指导指导)
备注
代码掺杂了很多业务逻辑,由于涉及到公司保密的义务,就都善了,所以代码想复制粘贴的话会有很大的问题。(/皮一下)