uniapp 实现 ble蓝牙同时连接多台蓝牙设备,支持app、苹果(ios)和安卓手机,以及ios连接蓝牙后的一些坑
首先对 uniapp BLE蓝牙API进行封装
这里我封装了一个类:bluetoothService.js
代码:
import { throttle } from 'lodash'
export default class Bluetooth {
constructor() {
this.device = {};
this.connected = false;
// 使用箭头函数绑定类实例的上下文,并在构造函数中初始化
// 你可以在这里传入蓝牙信息进行保存
this.throttledUpdate = throttle(async (params) => {
// 多设备上传数据建议做下节流处理
console.log(params)
}
}, 700); // 节流: 规定时间内最多更新一次
}
init() {
return new Promise((resolve, reject) => {
uni.openBluetoothAdapter({
success: (res) => {
resolve(res)
console.log("初始化成功", res)
},
fail: (err) => {
console.log("初始化失败:", err)
reject(err)
},
})
})
}
closeBluetoothAdapter() {
return new Promise((resolve, reject) => {
uni.closeBluetoothAdapter({
success: (res) => {
resolve(res)
},
fail: (err) => {
reject(err)
}
})
})
}
searchDevices() {
return new Promise((resolve, reject) => {
uni.startBluetoothDevicesDiscovery({
success: () => {
console.log('开始搜索蓝牙设备');
uni.onBluetoothDeviceFound((res) => {
if (res.devices[0]?.name.startsWith("SW")) { // 过滤条件只获取含SW开头名称的蓝牙,大家按需修改修改
console.log(res)
// 这里是设备,这里扫描到的设备,可以保存起来
resolve(res.devices)
}
});
},
fail: (err) => {
console.error('搜索蓝牙设备失败:', err);
reject(err)
},
});
})
}
stopSearchDevices() {
return new Promise((resolve, reject) => {
uni.stopBluetoothDevicesDiscovery({
success: () => {
console.log('停止搜索蓝牙设备');
resolve('停止搜索蓝牙设备')
},
fail: (err) => {
console.error('停止搜索蓝牙设备失败:', err);
reject(err);
}
});
})
}
connect(deviceId) {
return new Promise((resolve, reject) => {
uni.createBLEConnection({
deviceId: deviceId,
success: (res) => {
this.device.deviceId = deviceId;
this.connected = true;
resolve(res)
},
fail: (err) => {
console.error('蓝牙连接失败:', deviceId, err); // 处理连接失败
reject(err)
},
complete: () => {
// uni.hideLoading();
this.stopSearchDevices()
}
});
});
}
// 断开连接
disconnect() {
return new Promise((resolve, reject) => {
if (this.connected) {
uni.closeBLEConnection({
deviceId: this.device.deviceId,
success: () => {
this.connected = false;
this.device = {};
resolve();
},
fail: (err) => {
reject(err);
}
});
} else {
resolve();
}
});
}
// 监听蓝牙连接状态
listenBLEStateChange() {
return new Promise((resolve, reject) => {
uni.onBLEConnectionStateChange((res) => {
console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`);
if (res.connected) {
this.connected = true;
resolve(res);
} else {
this.connected = false;
reject(res)
}
});
});
}
// 获取服务UUID
getServices() {
return new Promise((resolve, reject) => {
uni.getBLEDeviceServices({
deviceId: this.device.deviceId,
success: (res) => {
console.log('获取服务成功:', res.services);
// 这里你们需要根据你们的设备修改
// this.device.serviceId = res.services[2]?.uuid;
this.device.serviceId = uni.getSystemInfoSync().platform === "android" ? res.services[2]?.uuid : res.services[1]?.uuid;
resolve(res)
},
fail: (err) => {
console.error('获取服务失败:', err);
reject(err)
},
});
})
}
getCharacteristics() {
return new Promise((resolve, reject) => {
uni.getBLEDeviceCharacteristics({
deviceId: this.device.deviceId,
serviceId: this.device.serviceId,
success: (res) => {
console.log('获取特征值成功:', res);
// 找到对应的特征值UUID
res.characteristics.forEach((item) => {
if (item.properties.notify === true) {
this.device.characteristicsNotify = item.uuid // 监听特征
}
if (item.properties.write === true) {
this.device.characteristicsWrite = item.uuid // 写入特征
}
})
resolve(this.device)
},
fail: (err) => {
console.error('获取特征值失败:', err);
reject(err)
// 处理获取特征值失败
},
});
})
}
// 开启监听
startNotify() {
return new Promise((resolve, reject) => {
uni.notifyBLECharacteristicValueChange({
state: true,
deviceId: this.device.deviceId,
serviceId: this.device.serviceId,
characteristicId: this.device.characteristicsNotify,
success: () => {
this.listenBLEStateChange(); // 监听蓝牙状态
resolve(this.device);
},
fail: (err) => {
console.error('开启notify失败:', err);
},
});
})
}
// 接收数据
receiveData() {
return new Promise((resolve, reject) => {
uni.onBLECharacteristicValueChange((res) => {
console.log("蓝牙上传的数据:", this.ab2hex(res.value))
resolve(this.ab2hex(res.value));
});
});
}
// 发送数据
sendData(data) {
return new Promise((resolve, reject) => {
if (this.connected) {
uni.writeBLECharacteristicValue({
deviceId: this.device.deviceId,
serviceId: this.device.serviceId,
characteristicId: this.device.characteristicsWrite,
value: data,
success: () => {
resolve(this.device);
},
fail: (err) => {
console.log("蓝牙指令写入失败:", err)
reject(err);
}
});
} else {
reject('蓝牙未连接');
}
});
}
// 监听信号
listenRSSI(deviceId, receiveData) {
return new Promise((resolve, reject) => {
uni.getBLEDeviceRSSI({
deviceId: deviceId,
success: (res) => {
resolve(res.RSSI);
},
fail: (err) => {
console.error("获取 RSSI 失败:", err);
reject(err);
}
});
});
}
ab2hex(buffer) {
// ArrayBuffer转16进度字符串示例
const hexArr = Array.prototype.map.call(new Uint8Array(buffer), function (bit) {
return ("00" + bit.toString(16)).slice(-2)
})
return hexArr.join("").toUpperCase()
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
同时连接多台蓝牙设备的连接操作方法:
// 封装异步操作函数(uniapp连接蓝牙获取服务需要延迟, 否则会出现无法获取服务情况)
async connectAndConfigureDevice(deviceId) {
try {
const bluetooth = new Bluetooth()
await bluetooth.connect(deviceId)
await bluetooth.delay(1000)
await bluetooth.getServices()
await bluetooth.delay(1000)
await bluetooth.getCharacteristics()
await bluetooth.delay(1000)
await bluetooth.startNotify()
await bluetooth.delay(1000)
bluetooth.receiveData()
return bluetooth
} catch (err) {
console.log(err); // 连接出现错误
}
},
async doConnect() {
// deviceList 是你保存的每个蓝牙设备信息的数组
if (this.deviceList.length > 0) {
// 循环连接每个设备
for (const device of this.deviceList) {
const bluetooth = await this.connectAndConfigureDevice(device.deviceId); // 传入deviceId进行连接
if (bluetooth) {
console.log("连接成功"); // 这里可以把每个连接成功蓝牙实例(bluetooth)的信息保存起来,建议保存到vuex中,方便后续对某个蓝牙设备的操作
}
}
}
}
},
ios有一个坑,需要配置后台运行能力,否则切换后台蓝牙会暂停数据上传
这是由于ios系统限制导致的,需要配置后台运行能力,在Hbuilderx中配置即可,如下图
uniapp官方说明:uni-app官网
"audio"表示后台播放音乐能力,"location"表示后台定位能力,'bluetooth-central'表示后台蓝牙功能。
更多后台能力配置参考苹果官网UIBackgroundModes文档