阅读前须知:
1、若已知蓝牙设备的 deviceId、serviceId、以及读写特征值,可以直接从
第7点
开始阅读
2、文档中各函数的详细属性、回调函数等介绍内容,可以查看官网文档:uniapp 低功耗蓝牙连接
3、与蓝牙设备之间收发数据格式必须为 ArrayBuffer
4、本文中与蓝牙设备收发数据时,数据包的长度为20 字节
,不够 20 字节的补 0
1、初始化蓝牙模块
uni.openBluetoothAdapter({
success(res) {
console.log('初始化蓝牙成功')
console.log(res)
},
fail(err) {
console.log('初始化蓝牙失败')
console.error(err)
}
})
2、搜索附近可连接的蓝牙设备
uni.startBluetoothDevicesDiscovery()
比较耗费系统资源,建议搜索完成后调用stopBluetoothDevicesDiscovery()
结束搜索。
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false,
interval: 0,
success: (res) => {
console.log("执行搜索成功的回调")
},
fail: (error) => {
console.log(error)
}
})
若需要过滤搜索出来的蓝牙设备,可以设置services
属性进行过滤
3、搜索附近可连接的蓝牙设备
uni.getBluetoothDevices()
获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备
uni.getBluetoothDevices({
success: function(res) {
console.log(res)
},
fail: function() {
console.log("获取蓝牙设备列表失败")
}
})
到这一步,已经能拿到附近所有的蓝牙设备列表,下面开始进行连接。
4、连接低功耗蓝牙设备
前提条件:
- 蓝牙设备支持低功耗协议(BLE)
- 拿到设备的
deviceId
值
uni.createBLEConnection({
deviceId: this.deviceId, // 在第 3 步可以拿到蓝牙的 deviceId 的值
success: (res) => {
console.log("连接成功", res)
// 在这里可以执行获取蓝牙设备的所有服务,若获取失败可以使用 setTimeout
},
fail: (error) => {
console.log("连接失败", error)
}
})
5、获取蓝牙设备的所有服务
uni.getBLEDeviceServices({
deviceId: this.deviceId, // 在第 3 步可以拿到蓝牙的 deviceId 的值
success: (res) => {
console.log('成功获取蓝牙设备的所有服务', res)
// 这里可以拿到蓝牙设备的 serviceId
}
})
6、获取蓝牙设备某个服务中所有特征值(characteristic)
uni.getBLEDeviceCharacteristics({
deviceId: this.deviceId, // 在第 3 步可以拿到蓝牙的 deviceId 的值
serviceId: this.serviceId, // 在第 5 步可以拿到蓝牙的 serviceId 的值
success: (res) => {
console.log('获取 characteristic 成功', res)
// 在这里可以获取到这个服务的所有特征值,需要确定哪些特征值是此次使用的
// 一般这里可以拿到两个特征值:写characteristic、读characteristic
// 接下来这里可以根据拿到的“读characteristic”开启 notify
},
fail: (res) => {
console.log('获取 characteristic 失败', res)
}
})
7、启动 notify 功能监听蓝牙设备回传信息
注意:必须设备的特征值支持 notify 或者 indicate 才可以成功调用
uni.notifyBLECharacteristicValueChange({
state: true,
deviceId: this.deviceId, // 在第 3 步可以拿到蓝牙的 deviceId 的值
serviceId: this.serviceId, // 在第 5 步可以拿到蓝牙的 serviceId 的值
characteristicId: this.readCharacteristic, // 在第 6 步可以拿到蓝牙的 characteristic 的值,这里使用的是 “读characteristic”
success(res) {
console.log("开启 notify 功能成功...")
// 必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送
uni.onBLECharacteristicValueChange(res => {
let resHex = ab2hex(res.value) // 将蓝牙设备回传的内容进行解析
console.log("接收到回传内容:", resHex) // 若需要,可参考第 9 点解析响应包的源码
// 蓝牙设备会分多次回传数据,并且都可以在这里监听到
// 这里可以执行一个处理所有回传结果的函数
})
},
fail(err) {
console.log("监听失败,发生错误", err)
}
})
// ArrayBuffer 转 16 进制字符串示例
function ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
}
到这里,已经完成了与低功耗蓝牙设备连接、蓝牙设备回传数据的监听。
8、向蓝牙设备发送指令
假设,存在一个获取蓝牙设备电量的指令如下:
向蓝牙设备发送数据格式:
起始码 | 数据类型 | 数据 |
---|---|---|
0xSA | 0x01 | 0x00,0x00,…,0x00 |
蓝牙设备回传数据格式:
起始码 | 数据类型 | 结果 | 电量 | 空数据 |
---|---|---|---|---|
0xRA | 0x01 | 0x01 | 0x14 | 0x00,0x00,…,0x00 |
let buffer = generate_command(0xSA, 0x01)
uni.writeBLECharacteristicValue({
deviceId: this.deviceId, // 在第 3 步可以拿到蓝牙的 deviceId 的值
serviceId: this.serviceId, // 在第 5 步可以拿到蓝牙的 serviceId 的值
characteristicId: this.writeCharacteristicId, // 在第 6 步可以拿到蓝牙的 characteristic 的值,这里使用的是 “写characteristicId”
value: buffer, // buffer 的格式为 ArrayBuffer
success(res) {
console.log('指令下发成功', res)
},
fail(err) {
console.log('指令发送失败', err)
}
})
/**
* 生成 ArrayBuffer 格式的数据,可以直接用于给蓝牙设备发送数据
* @param {Object} startCode 起始码
* @param {Object} dataType 数据类型
* @param {Object} sentData 发送给蓝牙的数据数组,例如:[0x12,0x01]
*/
function generate_command(startCode, dataType, sentData) {
let buffer = new ArrayBuffer(20)
let dataView = new DataView(buffer)
dataView.setUint8(0, startCode)
dataView.setUint8(1, dataType)
let _beforeIndex = 2
if(sentData) {
for (var k = 0; k < sentData.length; k++) {
dataView.setUint8(k + _beforeIndex, sentData[k])
}
_beforeIndex += sentData.length
}
return buffer
}
当指令发送成功后,在第 7 点中可以接收到蓝牙设备的响应包。否则,都视作为蓝牙接收数据失败!
9、附加:解析蓝牙响应数据包
在第 7 点中接受到响应的数据包后,仍然不是可以直观看出数据真实信息的格式,因此需要再次转换。
// responseData 的值为第 7 点中的 resHex
function parse_blue_response(responseData) {
const dataList = responseData.match(/.{1,2}/g)
const [resCode, resType, resResult] = dataList.splice(0, 3)
// resCode:响应包起始码;
// resType:响应包数据类型
// resResult:响应包结果
// dataList:其他响应数据 + 空数据,Array 格式数据
return [resCode, resType, String(resResult), dataList]
}
10、附加:将上一步的 Array 解析成 number
function array16_to_number(arrayValue) {
const newArray = arrayValue.filter(item => String(item) !== '00' || String(item) !== '0')
const _number16 = newArray.map(hex => parseInt(hex, 16).toString(16)).join('')
return parseInt(`0x${_number16}`)
}
如第 8 点,指令返回的电量结果为0x14
,经过转换的得到值为20
!!!关于array16_to_number函数的bug说明
:uniapp解析蓝牙设备响应数据bug