一开始是根据uniapp提供的蓝牙api写的蓝牙方法,之后发现复用性,以及一些状态的监听存在缺陷,之后整理成了类。这样复用性以及状态监听的问题就解决了。
单例模式是为了保证蓝牙长连接,只有一个蓝牙实例
// 单例模式
if (Bluetooth.instance) {
return Bluetooth.instance;
}
Bluetooth.instance = this;
根据需求定义变量
const serviceUUID = “”; // 主服务的UUID
const notifyUUID = “”; // 读
const writeUUID = “”; // 写
这一部分,连接蓝牙uuid是必须的,可以考虑接受参数的形式,或者在类里面写好,两种方式
根据需要传入相应的回调方法
successLinkFn:蓝牙连接成功的回调
dataChangeFn:成功接收蓝牙指令的回调
blueToothLinkStatusFn :监听蓝牙状态的回调
export class Bluetooth {
constructor(params) {
// 单例模式
if (Bluetooth.instance) {
return Bluetooth.instance;
}
this.bundleData = null; // 需要拼包的数据
this.BLTdeviceId = ""; // 设备的 id
this.deviceId = params.deviceId; // 设备号(设备序列号)
this.serviceUUID = params.serviceUUID || serviceUUID;
this.notifyUUID = params.notifyUUID || notifyUUID;
this.writeUUID = params.writeUUID || writeUUID;
this.successLinkFn = params.successLinkFn; // 蓝牙连接成功
this.dataChangeFn = params.dataChangeFn; // 接收蓝牙指令
this.blueToothLinkStatusFn = params.blueToothLinkStatusFn; // 蓝牙连接状态
this.initBluetooth();
Bluetooth.instance = this;
}
}
initBluetooth() {
let _this = this;
uni.openBluetoothAdapter({
success(res) {
console.log("初始化蓝牙成功", res);
_this.getBluetoothAdapterState();
},
fail(error) {
console.log(error);
},
});
}
// 判断手机蓝牙标识是否打开的状态
getBluetoothAdapterState() {
const _this = this;
uni.getBluetoothAdapterState({
success(res) {
//如果res.avaliable==false 说明没打开蓝牙 反之则打开
if (res.available) {
// 蓝牙打开-----开始搜寻附近蓝牙
_this.startBluetoothDevicesDiscovery();
} else {
uni.showModal({
content:
"当前手机蓝牙关闭,请在手机设置中开启蓝牙,并允许微信获取您的蓝牙权限",
confirmColor: "#4AC596",
confirmText: "确定",
showCancel: false,
success(res) {},
fail() {
// TODO 蓝牙打开失败
// 页面返回
uni.navigateBack({
delta: 1, //返回层数,2则上上页
});
},
});
}
},
});
}
// 搜寻附近的蓝牙外围设备
// 此操作比较耗费系统资源,请在搜索并连接到设备后调用 uni.stopBluetoothDevicesDiscovery 方法停止搜索。
startBluetoothDevicesDiscovery() {
let _this = this;
uni.startBluetoothDevicesDiscovery({
success(res) {
console.log(res);
_this.onBluetoothDeviceFound();
},
fail: function(res) {
uni.showToast({
title: "搜寻附近的蓝牙外围设备失败",
icon: "none",
duration: 1000,
});
console.log("搜寻附近的蓝牙外围设备失败");
},
});
}
根据传入的设备序列号(设备号)进行匹配
onBluetoothDeviceFound() {
let _this = this;
uni.onBluetoothDeviceFound(function(res) {
res.devices.forEach(function(device) {
let deviceName = device.localName || device.name;
if (_this.deviceId == deviceName) { //根据传入的设备序列号(设备号)进行匹配
console.log("发现蓝牙设备", device);
_this.BLTdeviceId = device.deviceId;
_this.startBluetoothDevicesDiscovery();
_this.connectDevice();
}
});
});
}
//连接蓝牙
connectDevice() {
let _this = this;
uni.createBLEConnection({
deviceId: _this.BLTdeviceId,
timeout: 3000,
success: function(res) {
uni.showToast({
icon: "success",
title: "蓝牙连接成功",
mask: true,
duration: 3000,
});
console.log("蓝牙连接成功");
_this.getBLEDeviceServices();
// 监听蓝牙状态
_this.blueToothLinkChange();
},
fail: function(res) {
console.log("连接失败" + JSON.stringify(res));
uni.showToast({
icon: "none",
title: "未成功连接设备,请重新搜索",
mask: true,
duration: 3000,
});
},
});
}
// 监听蓝牙状态
blueToothLinkChange() {
const _this = this;
uni.onBLEConnectionStateChange(function(res) {
// 该方法回调中可以用于处理连接意外断开等异常情况
console.log(
`device ${res.deviceId} state has changed, connected: ${res.connected}`
);
_this.blueToothLinkStatusFn && _this.blueToothLinkStatusFn(res);
});
}
// 获取服务
getBLEDeviceServices() {
uni.showToast({
icon: "none",
title: "数据处理中,请稍后",
duration: 3000,
mask: true,
});
let _this = this;
uni.getBLEDeviceServices({
deviceId: _this.BLTdeviceId,
success: function(res) {
for (let i = 0; i < res.services.length; i++) {
if (res.services[i].uuid == _this.serviceUUID) {
console.log("获取serviceId成功", res);
_this.getBLEDeviceCharacteristics();
break;
}
}
},
fail: function(res) {
console.log("获取serviceId失败", res);
uni.showToast({
icon: "none",
title: "未成功连接设备,请重新搜索",
mask: true,
duration: 3000,
});
},
});
}
// 获取特征值
getBLEDeviceCharacteristics() {
let _this = this;
// 如果是自动链接的话,wx.getBLEDeviceCharacteristics方法建议使用setTimeout延迟1秒后再执行
wx.getBLEDeviceCharacteristics({
deviceId: _this.BLTdeviceId,
serviceId: _this.serviceUUID,
success(res) {
console.log("获取特征值成功: ", res); // 可以在此判断特征值是否支持读写等操作,特征值其实也需要提前向硬件佬索取的
_this.notify();
},
fail(err) {
console.error(err);
_this.closeBLEConnection();
uni.showToast({
title: "获取特征值失败",
icon: "error",
});
},
});
}
// 开启消息监听,只使用一次
notify() {
let _this = this;
uni.notifyBLECharacteristicValueChange({
state: true,
deviceId: _this.BLTdeviceId, // 设备id
serviceId: _this.serviceUUID, // 监听指定的服务
characteristicId: _this.notifyUUID, // 监听对应的特征值
type: "notification",
success(res) {
console.log("已开启监听: ", res);
_this.successLinkFn && _this.successLinkFn("success");
},
fail(err) {
console.error(err);
_this.closeBLEConnection();
uni.showToast({
title: "监听失败",
icon: "error",
});
console.log("监听失败");
},
});
}
根据蓝牙协议处理需要发送的数据,处理数据属于业务代码,处理好了直接调用send方法即可。
// 发送数据,只要有指令发送就会使用
send(data) {
// 向蓝牙设备发送
const _this = this;
let buffer = Util.string2buffer(data);
const BLTdeviceId = wx.getStorageSync("BLTdeviceId");
wx.writeBLECharacteristicValue({
deviceId: _this.BLTdeviceId,
serviceId: _this.serviceUUID,
characteristicId: _this.writeUUID,
value: buffer,
writeType: "writeNoResponse",
success(res) {
console.log("指令发送成功:", res);
setTimeout(() => {
_this.listenValueChange();
}, 500);
},
fail(err) {
console.error(err);
_this.closeBLEConnection();
},
});
}
// 监听消息变化
listenValueChange() {
const _this = this;
uni.onBLECharacteristicValueChange((res) => {
const data = Util.ab2hex(res.value);
console.log("监听消息变化:", data);
// 返回的数据有拆包的情况,所以需要判断data是否有帧头与帧尾,如无则需要拼接
// 作为业务代码在回调方法中处理数据
_this.dataChangeFn && _this.dataChangeFn(data, res);
});
}
stopBluetoothDevicesDiscovery() {
wx.stopBluetoothDevicesDiscovery({
success(res) {
console.log("蓝牙设备停止搜索:", res);
},
});
}
// 断开蓝牙连接
closeBLEConnection() {
uni.closeBLEConnection({
deviceId: _this.deviceId,
});
uni.closeBluetoothAdapter();
}
在需要连接蓝牙的页面,调用Bluetooth的类,根据需要传入参数以及回调方式即可,可以在回调函数里面处理蓝牙数据,监听蓝牙连接状态
// 蓝牙连接
handleBlueToothLink() {
let _this = this
let params = {
deviceId: _this.deviceId,
serviceUUID: "", // 主服务的UUID
notifyUUID: "", // 读
writeUUID: "", // 写
successLinkFn: function(res) {
_this.successLinkFn(res);
},
dataChangeFn: function(data, res) {
_this.dataChangeFn(data, res);
},
blueToothLinkStatusFn: function(res) {
_this.blueToothStatusChange(res);
},
};
_this.blueTooth = new Bluetooth(params);
},
在页面上点击按钮,发送对应的蓝牙指令,需要将指令处理为蓝牙协议需要的数据格式。
handlerClick(instruct, dataLen, data) {
this.blueTooth.send(Util.instructData(instruct, dataLen, data));
},
由于处理蓝牙协议使用的方法很多 ,单独写在了util.js方法中进行调用,包含进制转换,帧头和帧尾的处理,补零,数据处理等方法。(本文中提供ArrayBuffer转换的方法,有需要的话,再提供)。
蓝牙传输需要ArrayBuffer格式的数据
// 将字符串转换成ArrayBufer
string2buffer(str) {
let val = ""
if(!str) return;
let length = str.length;
let index = 0;
let array = []
while(index < length){
array.push(str.substring(index,index+2));
index = index + 2;
}
val = array.join(",");
// 将16进制转化为ArrayBuffer
return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
})).buffer
},
// 将ArrayBuffer转换成字符串
ab2hex(buffer) {
var hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('');
},
// ArrayBuffer转字符串
arrayBufferToString(buffer) {
return String.fromCharCode.apply(null, new Uint8Array(buffer));
},
蓝牙方法都是调用的uniapp提供的蓝牙api,按照文档来编写的。
类的封装是为了代码的复用性和组件化。以后再遇到蓝牙需求,可以直接使用BlueTooth的类。