蓝牙配网-连接设备
注:这两天接手了一个设备运维端小程序,需要在上面加一个新功能,蓝牙配网,就是通过扫描设备二维码,拿到该设备的uuid、设备名等信息,然后通过蓝牙连接上该设备并往设备中写入数据、读取设备特征值、解析等。小白一个,所以在写的时候遇到了一些坑,想着记录下来
微信小程序官方文档上有相关的蓝牙API详细描写,我在这就不多说了
- 使用蓝牙功能去连接一个蓝牙设备,我们需要知道这个设备的名称,这个设备名称我是通过扫码得到的,得到这个设备的详细信息以后,我们就需要初始化蓝牙模块
//初始化蓝牙模块
clickbtn() {
var that = this
// 初始化蓝牙模块
wx.openBluetoothAdapter({
success: function (res) {
// 获取蓝牙适配器状态
that.getBluetoothAdapterState()
},
fail: function (res) {
if (res.errCode == 10001) {
//手机蓝牙不可用
// console.log('手机蓝牙不可用')
wx.showToast({
title: '手机蓝牙不可用,请开启蓝牙再试~',
icon: 'none',
duration: 2000
})
}
}
})
},
在初始化蓝牙模块的时候我遇到了第一个问题,可谓出师不利,在我初始化手机上蓝牙模块的时候,wx.openBluetoothAdapter这个api一直调用失败,说我手机没有打开蓝牙,可是我检查了好几遍,手机上的蓝牙我是开启了的,代码也没啥问题,手动狗头。最后面向百度编程,才得知,当你使用苹果手机的时候(安卓的没试过),光打开设置了里面的蓝牙是不够的,还得将设置–>微信–>蓝牙打开才OK。
- 获取本机蓝牙适配器状态
//获取本机蓝牙适配器状态
//检测本机蓝牙是否可用
getBluetoothAdapterState() {
var that = this
//获取本机蓝牙适配器状态
wx.getBluetoothAdapterState({
success: function (res2) {
if (res2.errMsg == 'getBluetoothAdapterState:ok') {
//搜寻附近的蓝牙设备
that.startBluetoothDevicesDiscovery()
}
//{errMsg: "getBluetoothAdapterState:ok", available: true, discovering: false}
},
fail: function (res2) {
// console.log('获取本机蓝牙适配器状态', res2)
},
})
},
3.开始搜寻附近的蓝牙设备
//开始搜寻附近的蓝牙外围设备
startBluetoothDevicesDiscovery() {
var that = this;
// console.log('开始搜寻附近的蓝牙外围设备')
wx.startBluetoothDevicesDiscovery({
powerLevel: "high",
success: function (res) {
console.log('开始搜寻附近的蓝牙外围设备', res)
if (res.errCode == 0) {
// 监听寻找到新设备的事件
that.onBluetoothDeviceFound()
}
},
fail: function (res) {
console.log('开始搜寻附近的蓝牙外围设备', res)
}
})
},
4.监听寻找到新设备的事件
//监听寻找到新设备的事件
onBluetoothDeviceFound() {
var that = this
wx.onBluetoothDeviceFound((res) => {
console.log(res);
res.devices.forEach(device => {
if (!device.name && !device.localName) {
return
}
const foundDevices = this.data.devices
const idx = that.inArray(foundDevices, 'deviceId', device.deviceId)
console.log(idx);
const data = {}
if (idx === -1) {
data[`devices[${foundDevices.length}]`] = device
} else {
data[`devices[${idx}]`] = device
}
this.setData(data)
})
})
// 筛选数据自动连接对应设备
var deviceName = 'FT_' + this.data.bluetoothName;
for (var i = 0; i < that.data.devices.length; i++) {
if (that.data.devices[i].localName == deviceName) {
//筛选完成,连接蓝牙
that.createBLEConnection(that.data.devices[i].deviceId)
// console.log(that.data.devices[i])
this.setData({
newBluetoothData: [that.data.devices[i]],
myDeviceId: [that.data.devices[i].deviceId],
})
wx.showToast({
title: '匹配设备成功~',
icon: 'success',
duration: 2000
})
clearInterval(this.data.TimeFn)
return
}
}
},
5.连接蓝牙
createBLEConnection(deviceId) {
var that = this
// console.log('连接蓝牙', that.data.deviceId)
for (var i = 0; i < that.data.devices.length; i++) {
//连接蓝牙前先关闭已连接蓝牙
wx.createBLEConnection({
deviceId: deviceId,
success: function (res) {
// console.log('连接蓝牙成功', res)
//获取连接设备的service服务
that.getBLEDeviceServices(deviceId);
//停止搜寻附近的蓝牙外围设备
// wx.stopBluetoothDevicesDiscovery({
// success: function(res) {
// console.log('停止搜寻附近的蓝牙外围设备',res)
// },
// fail:function(res){
// }
// })
},
fail: function (res) {
console.log('连接蓝牙失败', res)
},
})
},
- 获取连接设备的service服务
getBLEDeviceServices(deviceId) {
var that = this
wx.getBLEDeviceServices({
deviceId: deviceId,
success: function (res) {
that.setData({
service: res.services
})
console.log('获取连接设备的service服务', res)
// that.getBLEDeviceCharacteristics(deviceId, res.services[0]['uuid'])
for (let i = 0; i < res.services.length; i++) {
if (res.services[i].isPrimary && res.services[i].uuid == '0000AA00-0000-1000-8000-00805F9B34FB') {
that.setData({
myId: deviceId,
serviceId: res.services[i].uuid
})
//获取连接设备的所有特征值
that.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
// return
}
}
},
fail: function (res) {
console.log('获取蓝牙失败', res)
}
})
},
- 获取连接设备的所有特征值
getBLEDeviceCharacteristics(deviceId, serviceId) {
var that = this
wx.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: function (res) {
console.log('获取连接设备的所有特征值', res);
if (res.errCode == 0) {
that.setData({
characteristicId: res.characteristics,
// clickFlag: true,
showModal: false, //loading关闭
wifiFlag: true // wifi弹窗开启
})
// that.onBLECharacteristicValueChange()
for (var i = 0; i < res.characteristics.length; i++) {
//启用低功耗蓝牙设备特征值变化时的 notify 功能
that.notifyBLECharacteristicValueChange(deviceId, serviceId, that.data.characteristicId[i]['uuid'])
// 获取可以写入的设备的特征值uuid
if (that.data.characteristicId[i]['properties']['write']) {
that.setData({
uuId: that.data.characteristicId[i]['uuid']
})
}
}
} else {
console.log('获取特征值失败')
}
},
})
},
- 启用低功耗蓝牙设备特征值变化时的 notify 功能
notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId) {
var that = this
wx.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
state: true,
success: function (res) {
console.log('启用低功耗蓝牙设备特征值变化时的 notify 功能', res)
// console.log('启用低功耗蓝牙设备特征值变化时的 notify 功能', that.data.uuId)
/*用来监听手机蓝牙设备的数据变化*/
that.onBLECharacteristicValueChange()
},
fail: function (res) {
console.log('启用低功耗蓝牙设备特征值变化时的 notify 功能失败', res)
}
})
},
- 监听低功耗蓝牙设备的特征值变化事件(必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification)这个方法我其实是没有用到的,我在第七步连接蓝牙成功并且获取到连接设备的特征值以后就打开wifi弹窗进行蓝牙配网了,完整的蓝牙连接设备我会在文章末尾放一个前辈的连接,我是参考他的写的
onBLECharacteristicValueChange() {
var that = this
wx.onBLECharacteristicValueChange(function (res) {
console.log('监听低功耗蓝牙设备的特征值变化事件', res)
// console.log(`characteristic ${res.characteristicId} has changed, now is ${res.value}`)
// console.log(that.ab2hex(res.value))
// console.log(that.hexCharCodeToStr(that.ab2hex(res.value)))
var message = that.ab2hex(res.value)
that.readOrder(message)
})
},
- 连接wifi
// 连接wifi
wifiConnect() {
if (!this.data.wifiAccount || !this.data.wifiPassword) {
wx.showToast({
title: '不能为空',
icon: 'error',
duration: 2000
})
return false
}
// 处理要写入设备的值
this.getBluetoothValue()
},
11.获取要写入蓝牙设备的值
// 获取要写入蓝牙设备的值
getBluetoothValue() {
// 1.0x50,2.wifi账号,3.0x8B,4.wifi密码,5.0xCF 6.服务器IP 端口号 子网信息
let that = this
let {
useValueOne,
wifiAccount,
useValueTwo,
wifiPassword,
useValueThree,
serverIp,
portNumber,
subnetInfo
} = that.data
// 字符串转16进制
let a = that.strToHexCharCode(wifiAccount) // wifi账号
let b = that.strToHexCharCode(wifiPassword) // wifi密码
let c = that.strToHexCharCode(subnetInfo)// 服务器子网
//按硬件的需求 将3个定值 以及 wifi账号和密码拼成16进制字符串
let hesCaarCode = useValueOne + a + useValueTwo + b + useValueThree + serverIp + portNumber + c
// 2个字符等于一个字节 往设备写入数据一次只能发一个包20个字节
var len = parseInt(hesCaarCode.length / 40) + 1
console.log(len);
// 向设备写入数据 有几个包就发几次
for (var i = 0; i < len; i++) {
// 截取每一个包的数据
var code = hesCaarCode.substr(i * 40, 40)
that.writeBLECharacteristicValue(that.codebuffer(code))
}
},
- 发送数据(我是根据硬件的需求往设备里面写入数据)
//发送数据
writeBLECharacteristicValue(value) {
var that = this
//连接蓝牙是记录session
// console.log('写入设备', that.data.myId)
// console.log('写入服务', that.data.serviceId)
// console.log('写入特征值', that.data.uuId.toString())
console.log(value);
wx.writeBLECharacteristicValue({
deviceId: that.data.myId,
serviceId: that.data.serviceId,
characteristicId: that.data.uuId.toString(),
value: value,
success: function (res) {
if (res.errCode == 0) {
that.setData({
wifiFlag: false,
})
wx.showToast({
title: '连接成功',
icon: 'success',
duration: 2000
})
// // 暂时断开
// var closeTimeBlue = setInterval(() => {
// that.closeBlueFn();
// wx.navigateBack({
// delta: 1,
// })
// clearInterval(closeTimeBlue)
// }, 5000);
} else {
wx.showToast({
title: '连接失败',
icon: 'none',
duration: 2000
})
}
},
fail: function (err) {
// console.log('写入数据返回值失败', err)
},
})
},
到这里我这个蓝牙配网功能就初步完成了,后续会和硬件对接,有问题会及时更新,然后在附上几个小方法
//字符串转16进制
strToHexCharCode(str) {
if (str === "")
return "";
var hexCharCode = [];
// hexCharCode.push("0x");
for (var i = 0; i < str.length; i++) {
hexCharCode.push((str.charCodeAt(i)).toString(16));
}
return hexCharCode.join("");
},
//将16进制转ArrayBuffer
codebuffer: function (val) {
// 将16进制转化为ArrayBuffer
return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
})).buffer
},
//16进制转字符串
hexCharCodeToStr(hexCharCodeStr) {
var trimedStr = hexCharCodeStr.trim();
var rawStr =
trimedStr.substr(0, 2).toLowerCase() === "0x" ?
trimedStr.substr(2) :
trimedStr;
var len = rawStr.length;
if (len % 2 !== 0) {
alert("Illegal Format ASCII Code!");
return "";
}
var curCharCode;
var resultStr = [];
for (var i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16); // ASCII Code Value
resultStr.push(String.fromCharCode(curCharCode));
}
return resultStr.join("");
},
为什么我上面往设备发送数据的时候要分包
BLE 4.0 中发送一个数据包只能包含 20 字节的数据,大于 20 字节只能分包发送。微信小程序提供的 API 中似乎没有自动分包的功能,这就只能自己手动分包了。调试中发现,在 iOS 系统中调用 wx.writeBLECharacteristicValue 发送数据包,回调 success 后紧接着发送下一个数据包,很少出现问题,可以很快全部发送完毕。而安卓系统中,发送一个数据包成功后紧接着发送下一个,很大概率会出现发送失败的情况,在中间稍做延时再发送下一个就可以解决这个问题(不同安卓手机的时间长短也不一致),照顾下一些比较奇葩的手机,大概需要延时 250 ms 。不太好的但是比较科学的办法是,只要成功发送一个数据包则发送下一个,否则不断重发,具体就是
wx.writeBLECharacteristicValue 回调 fail 则重新发送,直至发送完毕
下面这个链接是我一个前辈的博客,对我这个功能的开发帮组非常大,比我的更完善
https://www.cnblogs.com/guhonghao/p/9947144.html