# Bluetooth Low Energy (BLE)

Host mode: Base library 1.1.0 (WeChat Guest iOS 6.5.6, Android 6.5.7) is supported.

From machine mode: base library 2.10.3 Start support.

Bluetooth Low Power is a protocol supported since Bluetooth 4.0, which has extremely low power consumption and faster transmission speeds, but a smaller amount of data transferred than classic Bluetooth. It is commonly used in various smart electronics products with high battery life requirements and only small data transfer, such as smart wearables, smart home appliances, sensors, etc., and has a wide range of applications.

# 1. Roles / Work Modes

The Bluetooth Low Power Protocol defines several roles, or working modes, for devices.Weixin Mini Program Bluetooth currently supports the following:

# 1) Central equipment / mainframe (Central)

The central device can scan the peripheral equipment and connect with it when it finds the peripheral equipment, then it can use the service provided by the peripheral equipment.

In general, the phone will act as the central device, the use of peripheral equipment to provide data processing or display and so on.Weixin Mini Program The Bluetooth Low Energy interface is provided by default with the phone as the central device.

# 2) Peripheral equipment / slave

The peripheral equipment is always on the air, waiting to be searched and connected by the central equipment. Devices such as smart bracelets, sensors, etc.

If the peripheral equipment is set to unconnectable when it is broadcasted, it is also called broadcaster mode. A common example is the Bluetooth Beacon device.

# Be careful

In Weixin Mini Program, a Bluetooth device can be in both host and slave modes.On Android devices, just call wx.openBluetoothAdapter for the first time;On iOS devices, two different modearguments are required to initialize the Bluetooth adapter for the central and peripheral equipment, respectively.It is recommended that the host and remote modes be initialized separately. wx.closeBluetoothAdapter will turn off both modes of BluetoothAdapter.

# 2. Communication protocol

After two Bluetooth low energy devices establish a connection, the data interaction between the two parties is based on GATT (Generic Attribute Profile). According to the specification, a profile can be defined to describe the service provided by the Bluetooth device.

In the entire communication process, there are several major concepts:

  • Profile : A profile is a collection of services predefined by the Bluetooth standard that do not exist in a Bluetooth device.If Bluetooth devices are to be compatible with each other, they only need to support the same profile. A Bluetooth device can support multiple profiles.
  • Service (Service) : Service is a service provided by Bluetooth devices, a device can provide multiple services, such as power information service, system information service, etc.Each service is uniquely identified by a UUID.
  • Characteristic : Each Service contains 0 to more Characteristics.For example, the electricity information service will have a Characteristic to represent the electricity data.A characteristic consists of a value and 0 or more descriptors . When communicating with a Bluetooth device, it is mainly done by reading and writing the Characteristic value. Each Characteristic is uniquely identified by a UUID.
  • Descriptor : A descriptor is a defined attribute that describes a particular value.For example, a Descriptor can specify a human-readable description, a range of values for a particular value, or a unit of measurement specific to a particular value. Each descriptor is uniquely identified by a UUID.

As the figure below shows, we can simply understand that each Bluetooth device may provide multiple services, each service may have multiple characteristics, and we can read and write the value of the corresponding characteristic according to the Bluetooth device's protocol to communicate with it.

GATT

# 3. UUID (Universally Unique Identifier)

According to the Bluetooth 4.2 protocol specification (Vol 3, Part B, section 2.5.1 UUID), the UUID is a 128-bit unique identifier used to identify services and characteristics.

In order to reduce the burden of storing and transmitting 128-bit UUID values, the Bluetooth Technology Alliance has pre-allocated a batch of UUIDs. This batch of UUIDs has a common part, known as the Bluetooth Base UUID, which is [...]00000000-0000-1000-8000-00805F9B34FB。Therefore, pre-assigned UUIDs can also be represented in 16-bit or 32-bit, with 16-bit UUIDs most commonly used. Using 16 / 32-bit UUID reduces the load on storage and transmission. Developer-defined UUIDs should be careful not to conflict with pre-assigned UUIDs.

In Weixin Mini Program, wx.startBluetoothDevicesDiscovery and wx.getConnectedBluetoothDevices Supports 16 / 32 / 128-bit UUID. In other interface parameters,

  • IOS supports direct use of 16-bit and 128-bit UUID;
  • Android 8.0.9 version, support for direct use of 16 / 32 / 128 bit UUID;
  • The following versions of Android 8.0.9 only support 128 bits of UUID, requiring developers to manually fill the 128 bits. The way to fill up is as follows
    128位UUID = 16位UUID * 2^96 + Bluetooth Base UUID
    128位UUID = 32位UUID * 2^96 + Bluetooth Base UUID
    
    例如
    0x180F -> 0000180F-0000-1000-8000-00805F9B34FB
    

所有接口的返回值统一为 128 位 UUID。

# 4. 中心设备的使用流程

# 4.1 初始化蓝牙模块

在使用蓝牙接口前,必须首先调用 wx.openBluetoothAdapter 初始化蓝牙适配器模块,其他接口必须在初始化后成功方可调用。

当蓝牙开关未开启或手机不支持蓝牙时,会返回错误 (errCode=10001)。此时小程序蓝牙模块已经初始化完成,可通过 wx.onBluetoothAdapterStateChange 监听手机蓝牙状态的改变,也可以调用蓝牙模块的所有API。开发者在开发中应该考虑兼容用户在使用小程序过程中打开/关闭蓝牙开关的情况,并给出必要的提示,提高可用性。

# 4.2 扫描并发现蓝牙外围设备

蓝牙模块初始化成功后,一般需要通过 wx.startBluetoothDevicesDiscovery 扫描外围设备。当蓝牙外围设备被扫描到时,会回调 wx.onBluetoothDeviceFound 事件,返回扫描到的设备。扫描设备比较耗费系统资源,请在搜索到需要的设备后及时调用 wx.stopBluetoothDevicesDiscovery 停止搜索。

若之前已连接过某个设备,获取到了 deviceId,可跳过扫描步骤。

/ / Monitor scans to new device events
wx.onBluetoothDeviceFound((res) => {
  res.devices.forEach((device) => {
    / / Some filtering can be done here
    console.log('Device Found', device)
  })
  / / When you find the device you want to search, stop scanning in time
  wx.stopBluetoothDevicesDiscovery()
})

/ / initialize the Bluetooth module
wx.openBluetoothAdapter({
  mode: 'central',
  success: (res) => {
    / / Start looking for nearby Bluetooth peripheral equipment
    wx.startBluetoothDevicesDiscovery({
      allowDuplicatesKey: false,
    })
  },
  fail: (res) => {
    if (res.errCode !== 10001) return
    wx.onBluetoothAdapterStateChange((res) => {
      if (!res.available) return
      / / Start searching for nearby Bluetooth peripheral equipment
      wx.startBluetoothDevicesDiscovery({
        allowDuplicatesKey: false,
      })
    })
  }
})

# 4.3 连接设备

蓝牙低功耗设备间要进行通信,必须首先建立连接。

wx.createBLEConnection({
  DeviceId, / / Search for deviceId of the device
  success: () => {
    / / The connection was successful and the service was obtained
    wx.getBLEDeviceServices({
      deviceId,
    })
  }
})

# 4.4 获取蓝牙外围设备的服务

wx.getBLEDeviceServices({
  DeviceId, / / Search for deviceId of the device
  success: (res) => {
    for (let i = 0; i < res.services.length; i++) {
      if (res.services[i].isPrimary) {
        / / You can select a master service for communication based on specific business needs
      }
    }
  }
})

# 4.5 读写服务的特征值

wx.getBLEDeviceCharacteristics({
  DeviceId, / / Search for deviceId of the device
  ServiceId, / / a service found in the previous step
  success: (res) => {
    for (let i = 0; i < res.characteristics.length; i++) {
      let item = res.characteristics[i]
      If (item.properties.write) {/ / The special value can be written
        / / This example is to send a 0x00 hexadecimal data to a Bluetooth device
        / / When actually used, data should be sent according to the specific device protocol
        let buffer = new ArrayBuffer(1)
        let dataView = new DataView(buffer)
        dataView.setUint8(0, 0)
        wx.writeBLECharacteristicValue({
          deviceId,
          serviceId,
          characteristicId: item.uuid,
          value: buffer,
        })
      }
      If (item.properties.read) {/ / The special value is readable
        wx.readBLECharacteristicValue({
          deviceId,
          serviceId,
          characteristicId: item.uuid,
        })
      }
      if (item.properties.notify || item.properties.indicate) {
        / / wx.notifyBLECharacteristicValueChange must be enabled to listen to the device onBLECharacteristicValueChange event
        wx.notifyBLECharacteristicValueChange({
          deviceId,
          serviceId,
          characteristicId: item.uuid,
          state: true,
        })
      }
    }
  }
})
/ / Listen before the operation to ensure that the data is obtained the first time
wx.onBLECharacteristicValueChange((result) => {
  / / Disconnect the connection and turn off the Bluetooth adapter at the appropriate time after use is complete
  wx.closeBLEConnection({
    deviceId,
  })
  wx.closeBluetoothAdapter({})
})

# 4.6 断开连接和关闭蓝牙适配器

使用完成后,应该在合适的时机断开连接,并关闭蓝牙适配器。

# 5. 注意事项

  • iOS 上,对特征值的 readwritenotify 操作,由于系统需要获取特征值实例,传入的 serviceIdcharacteristicId 必须由 wx.getBLEDeviceServiceswx.getBLEDeviceCharacteristics 中获取到后才能使用。建议统一在建立连接后先执行 wx.getBLEDeviceServiceswx.getBLEDeviceCharacteristics 后再进行与蓝牙设备的数据交互。
  • 考虑到蓝牙功能可以间接进行定位,安卓 6.0 及以上版本,无定位权限或定位开关未打开时,无法进行设备搜索。
  • 安卓上,部分机型获取设备服务时会多出 0000180000001801 UUID 的服务,这是系统行为,注意不要使用这两个服务。
  • 建立连接和关闭连接必须要成对调用。如果未能及时关闭连接释放资源,安卓上容易导致 state 133 GATT ERROR 的异常。
  • 在与蓝牙设备传输数据时,需要注意 MTU(最大传输单元)。如果数据量超过 MTU 会导致错误,建议根据蓝牙设备协议进行分片传输。安卓设备可以调用 wx.setBLEMTU 进行 MTU 协商。在 MTU 未知的情况下,建议使用 20 字节为单位传输。