由于最近的项目在做小程序蓝牙通讯这块的内容,所以将实现的过程在这简单的记录下。
1、首先要初始化蓝牙-检查蓝牙适配器是否打开
TypeScript
async initBluetooth(): Promise<void> {
return new Promise((resolve: any, reject: any) => {
const _this = this
if (BluetoothController.adapterOpend) {
console.log("蓝牙适配器已打开")
resolve(true)
return
}
wx.openBluetoothAdapter({
success(res) {
BluetoothController.adapterOpend = true
console.log("=====*****蓝牙适配器已打开")
resolve(true)
},
fail(error) { //用来判断蓝牙是否已打开
reject(error)
BluetoothController.adapterOpend = false
console.log("=====*****蓝牙适初始化失败", error)
}
})
})
}
//使用案例
initBluetooth().then()
2、开始搜寻附近的蓝牙外围设备
TypeScript
/**
* @param options
* options.keywords 蓝牙名称筛选关键字
* options.deviceid 可选参数,蓝牙设备id,连接用
*/
async searchAroundBLE(options: any): Promise<void> {
return new Promise((resolve: any, reject: any) => {
const _this = this
if (_this.startDiscovery) {
console.log("已开启蓝牙扫描,勿重复开启")
resolve(true)
return
}
_this.startDiscovery = true
wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
services: options.services,
success(res) {
console.log('搜索成功', res);
resolve(true)
},
fail(error) {
reject(error)
_this.startDiscovery = false
},
})
})
}
//使用案例
searchAroundBLE({ 'keywords': [''], services: [] }).then()
3、监听搜索到的设备
TypeScript
/**
* @param options
* options.keywords 蓝牙名称筛选关键字
* options.deviceid 可选参数,蓝牙设备id,连接用
*/
async onBluetoothDeviceFound(options: any): Promise<void> {
return new Promise((resolve: any, reject: any) => {
let _this = this
let { keywords } = options
// 超时自动结束
_this.findTimer = setTimeout(() => {
clearTimeout(_this.findTimer)
if (!_this.connectStatus) {
reject({
success: false
})
console.log('蓝牙扫描超时,自动关闭任务')
}
}, 3000); //时间根据自己的需求定
let arr: any = []
wx.onBluetoothDeviceFound(res => {
let devices = res.devices;
devices.forEach((device) => {
if (!device.name && !device.localName) {
return
}
// 获取设备MAC地址,并根据关键字过滤
let systemInfo = wx.getSystemInfoSync()
let iosDevice = systemInfo.system.toLowerCase().indexOf("ios") > -1;
let deviceMac = iosDevice ? uuid2Mac(device.advertisData) : device.deviceId
// keywords
if (keywords && keywords.length > 0) {
let key = keywords[0]
if (device.name.indexOf(key) >= 0) {
arr.push({
...device,
deviceMac
})
}
if (arr.length) {
let foundDevices = arr
_this.deviceList = foundDevices
resolve({
data: arr,
success: true
})
}
}
})
})
})
}
/**
* 统一安卓与IOS端deviceId展示
* @param advertisData
* 在安卓设备中,获取到的 deviceId 为设备 MAC 地址,iOS 上则为设备 uuid,
* 因此为了展示一致需要将ios的展示进行输入(当然IOS的连接还是得用获取到的uuid)
*/
function uuid2Mac(advertisData: any) {
if (advertisData) {
let mac = Array.prototype.map
.call(new Uint8Array(bf), (x) => ("00" + x.toString(16)).slice(-2))
.join(":");
mac = mac.toUpperCase();
return mac;
}
}
使用案例: onBluetoothDeviceFound({ 'keywords': [''] }).then() //keywords根据自己的需求去定,
4、处理蓝牙连接(搜索到设备开始蓝牙链接)
TypeScript
/**
* @param {string} options.deviceId 蓝牙设备id,连接用
*/
async createBLEConnection(options: any): Promise<void> {
return new Promise((resolve: any, reject: any) => {
let { deviceId } = options,_this = this
_this.deviceId = deviceId
if (_this.connectStatus) {
wx.closeBLEConnection({
deviceId
})
}
let timeout = 3000 //根据自己需求去定
console.log('开始连接蓝牙--', deviceId)
_this.stopBLEDevicesTask()
wx.createBLEConnection({
deviceId,
timeout,
success(res) {
console.log('蓝牙连接成功-----', deviceId)
_this.connectStatus = true
resolve(true)
},
fail() {
_this.connectStatus = false
reject(false)
}
})
})
}
使用案例: createBLEConnection({ deviceId: tmp[0].deviceId }).then()
5、连接成功后,要获取蓝牙的所有服务 进而根据项目需求的服务 去获取对应的特征 来进行读写操作
TypeScript
/**
* @param deviceId 蓝牙设备Id,来自createBLEConnection调用
*/
async getBLEDeviceServices(deviceId: any): Promise<void> {
return new Promise((resolve: any, reject: any) => {
let _this = this
wx.getBLEDeviceServices({
deviceId,
success(res) {
/**
* 16 位 UUID 从对接文档中获取(注意都是0000开头,接着的4位数字为16进制的uuid,所有服务只有4位uuid不一样)
注意有多个服务,不同服务的操作不一样,单个服务只能执行单个操作,所以这里需要建立多个连接
*/
for (let i = 0; i < res.services.length; i++) {
// 注意uuid的大小写
if (
res.services[i].isPrimary &&
res.services[i].uuid == "0000D0FF-0000-1000-8000-008cbhschs" //设备id,根据自己的项目去走
) {
// _this.getBLEDeviceCharacteristics(res.services[i].uuid);
resolve({
data: res.services[i].uuid,
sucess: true
})
return;
}
}
},
fail: (res) => {
reject({
sucess: false,
data: res.errMsg
})
console.log("服务获取失败------------->", res.errMsg);
},
});
})
}
async getBLEDeviceCharacteristics(serviceId: any): Promise<void> {
return new Promise((resolve: any, reject: any) => {
let _this = this
wx.getBLEDeviceCharacteristics({
deviceId: _this.deviceId, //设备id根据自己项目的去赋值
serviceId, //服务id
success: (res) => {
// 设备特征列表
let characteristics = res.characteristics;
for (let i = 0; i < characteristics.length; i++) {
let item = characteristics[i];
if (item.properties.write) {
this.serviceId = serviceId
this.characteristicId = item.uuid
}
if (item.properties.read) {
wx.readBLECharacteristicValue({
deviceId: _this.deviceId,
serviceId,
characteristicId: item.uuid
});
}
if (item.properties.write) {
// resolve({
// data: 1,
// sucess: true
// })
}
if (item.properties.notify || item.properties.indicate) {
/* 启用蓝牙低功耗设备特征值变化时的 notify 功能,订阅特征。注 意:必须设备的特征支持 notify 或者 indicate 才可以成功调用。
另外,必须先启用 wx.notifyBLECharacteristicValueChange 才能监听到设备 characteristicValueChange 事件*/
wx.notifyBLECharacteristicValueChange({
deviceId: _this.deviceId,
serviceId,
characteristicId: item.uuid,
state: true,
});
resolve({
data: item.properties,
sucess: true
})
}
}
},
fail(error: any) {
reject({
sucess: false,
data: error
})
}
});
})
}
使用案例: let { data } = await getBLEDeviceServices(tmp[0].deviceId) //data就是所有服务内容
if (data) {
// 2、获取蓝牙低功耗设备某个服务中所有特征 (characteristic)
let ble_chart = await getBLEDeviceCharacteristics(data)
if (ble_chart.sucess) {
let item = ble_chart.data
// 该特征是否支持 write 操作
if (item.write) {
// resolve(true)
this.handleWriteSucess(); //处理写操作
}
// 该特征是否支持 notify ,indicate操作 ,开启监听订阅特征消息
if (item.notify || item.indicate) {
this.watchBleData()//监听蓝牙数据
}
}
}
6、读写成功后开始根据蓝牙协议进行操作(蓝牙协议有设备方提供)
TypeScript
handleWriteSucess() //写成功后的操作
watchBleData() {
wx.onBLECharacteristicValueChange(async (res) => { //监听蓝牙数据变化
let resHex = ab2hex(res.value).toLocaleUpperCase() //包数据, 根据蓝牙协议去进行处理
let { data } = writeBLECharacteristicValue('对应指令', '脚电极')
})
}
/**
* ArrayBuffer转16进度字符串
* @param buffer
*/
// ArrayBuffer转16进度字符串
ab2hex(buffer: any) {
var hexArr = Array.prototype.map.call(new Uint8Array(buffer), function (bit) {
return ("00" + bit.toString(16)).slice(-2);
});
return hexArr.join("");
}
7、处理蓝牙写指令
TypeScript
/**
* 4. 发送蓝牙指令。蓝牙指令超出20字符时需要截断多次发送
* @param {string} cmdStr 蓝牙指令
* @param {string} cmdName 蓝牙指令名称------可选用于打印调试
*/
async writeBLECharacteristicValue(cmdStr: any, cmdName: string): Promise<void> {
// // console.log("发送蓝牙指令------------》", cmdStr, cmdName);
return new Promise((resolve: any, reject: any) => {
let _this = this
let data = cmdStr.split(',')
let buffer = new ArrayBuffer(data.length);
let dataViewLast = new DataView(buffer);
for (let i = 0; i < data.length; i++) {
dataViewLast.setUint8(i, data[i]);
}
let param: any = {
deviceId: _this.deviceId,
serviceId: _this.serviceId,
characteristicId: _this.characteristicId, //在获取特性那有
value: dataViewLast.buffer,
};
console.log("param", param)
wx.writeBLECharacteristicValue({
...param,
success: function () {
console.log("发送指令成功", cmdName);
resolve({
data: {
cmdName,
sucess: true
}
})
},
fail: function (error: any) {
reject({
data: {
data: error,
sucess: true
}
})
},
})
})
}
注意点:
在判断蓝牙是否打开之前,还要先判断下手机上位置是否打开
let locationEnabled = wx.getSystemInfoSync().locationEnabled