小程序蓝牙通信

蓝牙通信能力封装

一开始是根据uniapp提供的蓝牙api写的蓝牙方法,之后发现复用性,以及一些状态的监听存在缺陷,之后整理成了类。这样复用性以及状态监听的问题就解决了。

蓝牙组件

创建蓝牙组件的类

小程序蓝牙通信_第1张图片

单例模式是为了保证蓝牙长连接,只有一个蓝牙实例

// 单例模式
    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的类。

你可能感兴趣的:(前端框架,小程序,前端,javascript,vue.js)