Skip to content

小程序增加蓝牙通信功能


小程序中的蓝牙能力

点击查看详情

在小程序中,要使用蓝牙能力(Beacon 除外)必须首先调用 wx.openBluetoothAdapter 初始化蓝牙适配器模块,其生效周期为调用 wx.openBluetoothAdapter 至调用 wx.closeBluetoothAdapter 或小程序被销毁为止。只有在小程序蓝牙适配器模块生效期间,开发者才能够正常调用蓝牙相关的小程序 API,并收到蓝牙模块相关的事件回调(绑定监听不受此限制)。

小程序对蓝牙支持情况如下:

经典蓝牙:iOS 因系统限制暂无法提供,安卓目前已在规划中。

蓝牙低功耗 (BLE):

  • 主机模式:基础库 1.1.0(微信客户端 iOS 6.5.6,Android 6.5.7)开始支持。
  • 从机模式:基础库 2.10.3 开始支持。
  • 蓝牙信标 (Beacon):基础库 1.2.0 开始支持。

前置条件

  • 蓝牙模块需要是 低功耗蓝牙(BLE) ,不然小程序搜索不到(将导致无法继续进行开发),可购买我们的设备 来进行开发。
  • 开发者工具上只能模拟部分蓝牙接口能力,完整功能请使用真机调试。

官方文档

微信小程序蓝牙开发文档

微信小程序提供的操作蓝牙的 API

微信小程序目前有蓝牙 API 共 18 个

操作蓝牙适配器的 API

wx.openBluetoothAdapter 初始化蓝牙适配器
wx.closeBluetoothAdapter 关闭蓝牙模块
wx.getBluetoothAdapterState 获取本机蓝牙适配器状态
wx.onBluetoothAdapterStateChange 监听蓝牙适配器状态变化事件

连接前使用的 API

wx.startBluetoothDevicesDiscovery 开始搜寻附近的蓝牙外围设备
wx.stopBluetoothDevicesDiscovery 停止搜寻附近的蓝牙外围设备
wx.getBluetoothDevices 获取所有已发现的蓝牙设备
wx.onBluetoothDeviceFound 监听寻找到新设备的事件

连接和断开时使用的 API

wx.createBLEConnection 连接低功耗蓝牙设备
wx.closeBLEConnection 断开与低功耗蓝牙设备的连接

连接成功后使用的 API

wx.getConnectedBluetoothDevices 根据 uuid 获取处于已连接状态的设备
wx.getBLEDeviceServices 获取蓝牙设备所有 service(服务)
wx.getBLEDeviceCharacteristics  获取蓝牙设备所有 characteristic(特征值)
wx.readBLECharacteristicValue  读取低功耗蓝牙设备的特征值的二进制数据值
wx.writeBLECharacteristicValue 向低功耗蓝牙设备特征值中写入二进制数据
wx.notifyBLECharacteristicValueChange  启用低功耗蓝牙设备特征值变化时的 notify 功能
wx.onBLECharacteristicValueChange 监听低功耗蓝牙设备的特征值变化
wx.onBLEConnectionStateChange 监听低功耗蓝牙连接的错误事件

基本操作流程

最基本的操作流程是:初始化蓝牙适配器 → 开始搜寻附近的蓝牙外围设备 → 监听寻找到新设备的事件 → 连接低功耗蓝牙设备 → 获取蓝牙设备所有 service 和 characteristic → 读取或写入低功耗蓝牙设备的特征值的二进制数据值。

蓝牙组件代码

由于我们 index 中代码量比较多,不利于维护,所以我们将蓝牙功能单独抽离成一个 component(组件),方便管理和复用。

新建组件

新建一个 components 目录,在该目录下新建一个目录为 bluetooth-comp,然后在 bluetooth-comp 点击"新建 Component"按钮,输入 index 并点击确定。

页面结构部分

html
<!-- bluetooth-comp/index.wxml -->
<view style="margin: 26rpx">
  <button wx:if="{{!connected}}" bindtap="openBluetoothAdapter">开始扫描</button>
  <button wx:else bindtap="closeBLEConnection">断开连接 - {{name}}</button>

  <view class="devices_summary">已发现 {{devices.length}} 个外围设备:</view>
  <view
    wx:for="{{devices}}"
    wx:key="index"
    data-device-id="{{item.deviceId}}"
    data-name="{{item.name || item.localName}}"
    bindtap="createBLEConnection"
    class="device_item"
    hover-class="device_item_hover">
    <view style="font-size: 16px; color: #333">{{item.name}}</view>
    <view style="font-size: 10px">信号强度: {{item.RSSI}}dBm</view>
    <view style="font-size: 10px">UUID: {{item.deviceId}}</view>
  </view>
</view>

样式部分

css
/* bluetooth-comp/index.wxss */
.devices_summary {
  /* margin-top: 30px; */
  padding: 10px;
  font-size: 16px;
}

.device_item {
  border-bottom: 1px solid #eee;
  padding: 10px;
  color: #666;
}
.device_item_hover {
  background-color: rgba(0, 0, 0, 0.1);
}

逻辑部分

在 data 中定义变量

js
// bluetooth-comp/index.js
data:{
  devices: [],
  connected: false,
  chs: []
}

js 文件顶部定义工具方法

js
function inArray(arr, key, val) {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i][key] === val) {
      return i;
    }
  }
  return -1;
}

// 将字符串转为 ArrayBuffer
function str2ab(str) {
  let buf = new ArrayBuffer(str.length);
  let bufView = new Uint8Array(buf);
  for (var i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

在 methods 中定义方法

js
// bluetooth-comp/index.js
methods: {
  /* 初始化蓝牙模块 */
  openBluetoothAdapter() {
    // 先关闭蓝牙模块再开启 防止断开后点连接连接不上
    this.closeBluetoothAdapter();

    wx.openBluetoothAdapter({
      success: response => {
        console.log("初始化蓝牙模块成功:openBluetoothAdapter", response);
        this.startBluetoothDevicesDiscovery();
      },
      fail: err => {
        if (err.errCode === 10001) {
          /* 监听蓝牙适配器状态变化事件 */
          wx.onBluetoothAdapterStateChange(res => {
            console.log("监听蓝牙适配器状态变化事件:onBluetoothAdapterStateChange", res);
            res.available && this.startBluetoothDevicesDiscovery();
          });
        }
      },
    });
  },
  /* 获取本机蓝牙适配器状态 */
  getBluetoothAdapterState() {
    wx.getBluetoothAdapterState({
      success: res => {
        console.log("getBluetoothAdapterState", res);
        if (res.discovering) {
          // 是否正在搜索设备
          this.onBluetoothDeviceFound();
        } else if (res.available) {
          // 蓝牙适配器是否可用
          this.startBluetoothDevicesDiscovery();
        }
      },
    });
  },
  /* 开始搜寻附近的蓝牙外围设备 */
  startBluetoothDevicesDiscovery() {
    // 开始扫描参数
    if (this._discoveryStarted) return;

    this._discoveryStarted = true;
    wx.startBluetoothDevicesDiscovery({
      allowDuplicatesKey: true,
      success: response => {
        console.log("开始搜寻附近的蓝牙外围设备:startBluetoothDevicesDiscovery", response);
        this.onBluetoothDeviceFound();
      },
      fail: err => {
        console.log("搜索设备失败", err);
        wx.showToast({ title: "搜索设备失败", icon: "none" });
      },
    });
  },
  /* 停止搜寻附近的蓝牙外围设备。*/
  stopBluetoothDevicesDiscovery() {
    console.log("停止搜寻附近的蓝牙外围设备");
    wx.stopBluetoothDevicesDiscovery();
  },
  /* 监听搜索到新设备的事件 */
  onBluetoothDeviceFound() {
    wx.onBluetoothDeviceFound(res => {
      res.devices.forEach(device => {
        if (!device.name && !device.localName) {
          return;
        }

        const foundDevices = this.data.devices;
        const idx = inArray(foundDevices, "deviceId", device.deviceId);
        const data = {};
        if (idx === -1) {
          data[`devices[${foundDevices.length}]`] = device;
        } else {
          data[`devices[${idx}]`] = device;
        }
        this.setData(data);
      });
    });
  },
  /* 连接蓝牙低功耗设备。*/
  createBLEConnection(e) {
    const ds = e.currentTarget.dataset;
    const deviceId = ds.deviceId;
    const name = ds.name;
    wx.createBLEConnection({
      deviceId,
      success: () => {
        this.setData({ connected: true, name, deviceId });
        wx.showToast({ title: "连接蓝牙设备成功", icon: "none" });
        this.getBLEDeviceServices(deviceId);
      },
      fail: e => {
        console.log("连接失败", e.errMsg);
        wx.showToast({ title: "连接失败,错误信息: " + e.errMsg, icon: "none" });
      },
    });
    // 停止搜寻蓝牙设备
    this.stopBluetoothDevicesDiscovery();
  },
  /* 断开与蓝牙低功耗设备的连接。 */
  closeBLEConnection() {
    console.log("断开与蓝牙低功耗设备的连接");
    wx.showToast({ title: "已断开和蓝牙设备的连接", icon: "none" });
    wx.closeBLEConnection({ deviceId: this.data.deviceId });
    this.setData({ connected: false, chs: [], canWrite: false });
  },
  /* 获取蓝牙低功耗设备所有服务 (service) */
  getBLEDeviceServices(deviceId) {
    wx.getBLEDeviceServices({
      deviceId,
      success: res => {
        for (let i = 0; i < res.services.length; i++) {
          if (res.services[i].isPrimary) {
            this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid);
            return;
          }
        }
      },
    });
  },
  /* 获取蓝牙低功耗设备某个服务中所有特征 (characteristic)。 */
  getBLEDeviceCharacteristics(deviceId, serviceId) {
    wx.getBLEDeviceCharacteristics({
      deviceId,
      serviceId,
      success: res => {
        console.log("获取蓝牙低功耗设备某个服务中所有特征:getBLEDeviceCharacteristics");

        for (let i = 0; i < res.characteristics.length; i++) {
          let item = res.characteristics[i];
          if (item.properties.read) {
            wx.readBLECharacteristicValue({ deviceId, serviceId, characteristicId: item.uuid });
          }
          if (item.properties.write) {
            this.setData({ canWrite: true });
            this._deviceId = deviceId;
            this._serviceId = serviceId;
            this._characteristicId = item.uuid;
            //   this.writeBLECharacteristicValue();
          }
          if (item.properties.notify || item.properties.indicate) {
            wx.notifyBLECharacteristicValueChange({
              deviceId,
              serviceId,
              characteristicId: item.uuid,
              state: true,
              success(res) {
                console.log("notifyBLECharacteristicValueChange success", res);
              },
            });
          }
        }
      },
      fail(res) {
        console.error("getBLEDeviceCharacteristics", res);
      },
    });

    // 操作之前先监听,保证第一时间获取数据
    wx.onBLECharacteristicValueChange(characteristic => {
      // TODO 收到的信息为ArrayBuffer类型,可根据自己的需要转换 可发送给父组件用来回显
      console.log("收到原始的数据", characteristic, characteristic.value);
      // 测试向设备发送数据
      // this.writeBLECharacteristicValue(JSON.stringify({"FAN":"OFF"}))
    });
  },
  /* 向蓝牙低功耗设备特征值中写入二进制数据 */
  writeBLECharacteristicValue(jsonStr) {
    let arrayBufferValue = str2ab(jsonStr);
    console.log("发送数据给蓝牙", "原始字符串", jsonStr, "转换arrayBuffer", arrayBufferValue);

    wx.writeBLECharacteristicValue({
      deviceId: this._deviceId,
      serviceId: this._serviceId, // 微信文档上是错误的
      characteristicId: this._characteristicId,
      value: arrayBufferValue, // 只能发送arrayBuffer类型数据
      success(res) {
        console.log("消息发送成功", res.errMsg);
        wx.showToast({ title: "消息发送成功", icon: "none" });
      },
      fail(e) {
        console.log("发送消息失败", e);
        wx.showToast({ title: "发送消息失败,错误信息: " + e.errMsg, icon: "none" });
      },
    });
  },
  closeBluetoothAdapter() {
    console.log("关闭蓝牙模块");
    wx.closeBluetoothAdapter();
    this._discoveryStarted = false;
  },
};

最终效果

界面

搜索到蓝牙设备

真机调试结果

真机调试结果

powered by 天人之际工作室