小程序低功耗蓝牙控制开门
整体流程
- 初始化蓝牙模块
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地址 - 我使用的是通过设备名称来进行匹配
- 安卓和IOS返回的deviceId不一样,安卓返回的是mac地址,IOS返回的是UUID,如果想通过mac地址来匹配设备,可以让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() // 连接蓝牙
}
}
})
}
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
来查看所有已搜索到的蓝牙,在这个里面做判断来连接蓝牙设备
- 解决方法:我在搜索蓝牙设备之前做了一个定时器,然后用
- 我上面的步骤没有把这些坑的解决步骤加上,如果碰见此类问题,可以自己在合适的位置修改一下
二维码
我自己做了一个小程序的蓝牙调试器,下面是小程序码,欢迎大家体验