蓝牙通信能力封装
一开始是根据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 = ""; // 设备的 idthis.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, // 设备idserviceId: _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 = thislet params = {deviceId: _this.deviceId,serviceUUID: "", // 主服务的UUIDnotifyUUID: "", // 读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进制转化为ArrayBufferreturn 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的类。