小程序低功耗蓝牙控制开门
整体流程
- 初始化蓝牙模块
openBluetoothAdapter
- 获取本机蓝牙适配器状态
getBluetoothAdapterState
- 搜索外围蓝牙设备
startBluetoothDevicesDiscovery
- 监听寻找到新设备
onBluetoothDeviceFound
- 连接蓝牙
createBLEConnection
- 获取蓝牙设备的服务
getBLEDeviceServices
- 获取服务中的特征值
getBLEDeviceCharacteristics
- 启用特征值变化时的notify功能
notifyBLECharacteristicValueChange
- 向蓝牙设备写入数据
writeBLECharacteristicValue
- 关闭蓝牙模块
closeBluetoothAdapter
1. 初始化蓝牙模块
- 初始化蓝牙模块使用的是:
wx.openBluetoothAdapter
,初始化之前对蓝牙功能做一个判断,看手机微信版本是否支持此功能 - 初始化之前需要关闭蓝牙模块:
wx.closeBluetoothAdapter
,否则容易搜索失败var _this = this if (!wx.openBluetoothAdapter) { wx.showModal({ title: '提示', showCancel: false, content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试', }) } else { wx.closeBluetoothAdapter({ success: res => { wx.openBluetoothAdapter({ // 初始化蓝牙模块 success: res => { console.log('初始化蓝牙成功') _this.getBluetoothAdapterState() }, fail: err => { console.log(err) } }) }, }) }
2. 获取本机蓝牙适配器状态
- 获取本机蓝牙适配器状态使用的是
wx.getBluetoothAdapterState
,调用成功后,会返回两个参数discovering
判断是否正在搜索设备available
判断蓝牙适配器是否可用getBluetoothAdapterState: function(){ var _this = this wx.getBluetoothAdapterState({ success: res => { if (res.available == false) { wx.showToast({ title: '设备无法开启蓝牙连接', icon: 'none', duration: 2000 }) wx.closeBluetoothAdapter() } else if (res.discovering == false) { _this.startBluetoothDevicesDiscovery() // 开启搜索外围设备 } else if (res.available){ _this.startBluetoothDevicesDiscovery() // 蓝牙适配器正常,去执行搜索外围设备 } } }) }
3. 搜索外围蓝牙设备
- 搜索外围蓝牙设备使用的是
wx.startBluetoothDevicesDiscovery
,连接设备后一定要使用wx.stopBluetoothDevicesDiscovery
停止搜索startBluetoothDevicesDiscovery: function(){ var _this = this wx.startBluetoothDevicesDiscovery({ allowDuplicatesKey: false, success: res => { if(!res.isDiscovering){ // 是否在搜索设备 _this.getBluetoothAdapterState() }else{ _this.onBluetoothDeviceFound() // 搜索成功后,执行监听设备的api } }, fail: err => { console.log("蓝牙搜寻失败") wx.stopBluetoothDevicesDiscovery() // 没有搜索到设备 wx.closeBluetoothAdapter() // 关闭蓝牙模块 } }) }
4. 监听寻找到新设备
- 监听寻找到新设备使用的是
wx.onBluetoothDeviceFound
,每搜到一个新设备就会触发一次,然后返回一个搜索到的设备列表,包含了设备名称和mac地址,一般都是使用设备名称和mac地址来匹配设备的- 安卓和IOS返回的deviceId不一样,安卓返回的是mac地址,IOS返回的是UUID,如果想通过mac地址来匹配设备,可以让mac地址存储在
advertisData
数据段中,然后解析这个数据段得到mac地址 - 我使用的是通过设备名称来进行匹配
onBluetoothDeviceFound: function(){ var _this = this wx.onBluetoothDeviceFound(res => { for(let i=0; i<res.devices.length; i++){ if(res.devices[i].name == "设备名称" || res.devices[i].localName == "设备名称"){ _this.setData({ deviceId: res.devices[i].deviceId // 把匹配设备的deviceId存到data中 }) wx.stopBluetoothDevicesDiscovery() // 匹配到设备后关闭搜索 _this.createBLEConnection() // 连接蓝牙 } } }) }
- 安卓和IOS返回的deviceId不一样,安卓返回的是mac地址,IOS返回的是UUID,如果想通过mac地址来匹配设备,可以让mac地址存储在
5. 连接蓝牙
- 连接蓝牙使用的是
wx.createBLEConnection
,连接蓝牙是通过deviceId
连接,deviceId
是通过wx.onBluetoothDeviceFound
获取的 - 连接蓝牙容易失败,所以可以定一个变量
count
用来计算连接的次数,如果超出特定的次数就判断为连接失败,关闭蓝牙模块createBLEConnection: function () { // 连接低功耗蓝牙 var _this = this wx.createBLEConnection({ deviceId: _this.data.deviceId, success: res => { _this.getBLEDeviceServices() }, fail: err => { console.log("连接失败") if( count < 6 ){ count++ _this.createBLEConnection() }else{ wx.closeBluetoothAdapter() // 连接失败关闭蓝牙模块 } } }) }
6. 获取蓝牙设备的服务
- 获取蓝牙设备的服务列表使用的是
wx.getBLEDeviceServices
getBLEDeviceServices: function () { var _this = this wx.getBLEDeviceServices({ deviceId: _this.data.deviceId, success: res => { for(let i=0; i<res.services.length; i++){ // 如果提前得知可以直接判断,如果不知道可以用蓝牙工具看一下服务所需的功能 if(res.services[i].uuid == _this.data.service){ _this.setData({ serviceId: res.services[i].uuid }) _this.getBLEDeviceCharacteristics() } } }, fail: err => { console.log("获取服务失败") wx.closeBluetoothAdapter() // 关闭蓝牙模块 } }) },
7. 获取服务中的特征值
- 获取服务中的特征值使用的是
wx.getBLEDeviceCharacteristics
getBLEDeviceCharacteristics: function () { // 获取服务中的特征值 var _this = this wx.getBLEDeviceCharacteristics({ deviceId: _this.data.deviceId, serviceId: _this.data.serviceId, success: res => { for(let i=0; i<res.characteristics.length; i++){ let model = res.characteristics[i] if ((model.properties.notify || model.properties.indicate) && (model.properties.read && model.properties.write)){ _this.setData({ characteristicId: model.uuid }) _this.notifyBLECharacteristicValueChange() } } }, fail: err => { console.log("获取服务中的特征值失败") wx.closeBluetoothAdapter() // 关闭蓝牙模块 } }) },
8. 启用特征值变化时的notify功能
- 启用特征值变化时的notify功能使用的是
wx.notifyBLECharacteristicValueChange
notifyBLECharacteristicValueChange: function () { // 启用蓝牙特征值变化时的notify功能 var _this = this wx.notifyBLECharacteristicValueChange({ deviceId: _this.data.deviceId, serviceId: _this.data.serviceId, characteristicId: _thisa.characteristicId, state: true, success: res => { wx.onBLECharacteristicValueChange(res => { console.log(如果要打印需要从arraybuffer格式转为字符串或16进制) }) _this.writeBLECharacteristicValue() // 向蓝牙设备写入数据 }, fail: err => { console.log("启用BLE蓝牙特征值变化时的notify功能错误") wx.closeBluetoothAdapter() // 关闭蓝牙模块 } })
9. 向蓝牙设备写入数据
- 向蓝牙设备写入数据
wx.writeBLECharacteristicValue
,这时候就是输入提前设定好的指令writeBLECharacteristicValue: function () { // 向蓝牙设备写入数据 var _this = this wx.writeBLECharacteristicValue({ deviceId: _this.data.deviceId, serviceId: _this.data.serviceId, characteristicId: _this.data.characteristicId, value: buffer, // 这个输入的指令,需要转换成ArrayBuffer success: res => { console.log("成功") wx.closeBluetoothAdapter() // 关闭蓝牙模块 }, fail: err => { console.log("输入指令失败") wx.closeBluetoothAdapter() // 关闭蓝牙模块 } }) }
转换格式
字符串转为arraybuffer
string2buffer: function (str) { // 首先将字符串转为16进制 let val = "" for (let i = 0; i < str.length; i++) { if (val === '') { val = str.charCodeAt(i).toString(16) } else { val += ',' + str.charCodeAt(i).toString(16) } } // 将16进制转化为ArrayBuffer return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) { return parseInt(h, 16) })).buffer }
arraybuffer转为字符串
function ab2str(u,f) { var b = new Blob([u]); var r = new FileReader(); r.readAsText(b, 'utf-8'); r.onload = function (){if(f)f.call(null,r.result)} }
蓝牙遇见的坑
- 1.苹果手机有时候输入指令会显示发送成功,但是设备并没有反应
解决方法:把需要输入的指令改成每10毫秒输入一个字节
- 如果改成每10毫秒输入一个字节,安卓手机就会频繁出现10008错误
- 针对这个问题我用了一个不太好的方法,我判断了一下手机的类型,如果是ios的就分10毫秒输入,如果是安卓的就一次性输入
- 2.在调用
wx.onBluetoothDeviceFound
这个api时ios会监听两次,然后就会导致最后设备会有两次指令输入- 解决方法:我在搜索蓝牙设备之前做了一个定时器,然后用
getBluetoothDevices
来查看所有已搜索到的蓝牙,在这个里面做判断来连接蓝牙设备
- 解决方法:我在搜索蓝牙设备之前做了一个定时器,然后用
- 我上面的步骤没有把这些坑的解决步骤加上,如果碰见此类问题,可以自己在合适的位置修改一下
二维码
我自己做了一个小程序的蓝牙调试器,下面是小程序码,欢迎大家体验