Skip to content

约 1474 字大约 5 分钟

项目小记uni-app蓝牙

2025-01-31

之前接了个与硬件建立蓝牙通讯的项目,学到了一些 uni-app 蓝牙操作的知识,也踩了一些坑,特此记录一下防止忘记。

在蓝牙模块调用时,采用的是 Promise 链式调用的形式,因此下文所提供的代码片段均为 Promise 形式。

超绝promise链

文章参考:uni-app 官网蓝牙低功耗蓝牙

Step1 初始化蓝牙适配器

使用uni.openBluetoothAdapter()函数初始化蓝牙适配器,uni.getBluetoothAdapterState()函数获取蓝牙适配器状态。

// Step1 初始化蓝牙
function initBlue() {
  return new Promise((resolve, reject) => {
    uni.openBluetoothAdapter({
      success(res) {
        console.log('初始化蓝牙成功')
        uni.getBluetoothAdapterState({
          success(r) {
            console.log('蓝牙状态', r.available)
            if (r.available) {
              resolve()
            }
            else {
              uni.showToast({
                title: '请开启蓝牙',
                icon: 'error',
                duration: 2000,
              })
              reject()
            }
          },
          fail(err) {
            uni.showToast({
              title: '请开启蓝牙',
              icon: 'error',
              duration: 2000,
            })
            reject()
          },
        })
      },
      fail(err) {
        console.log('初始化蓝牙失败')
        console.error(err)
        uni.showToast({
          title: '请开启蓝牙',
          icon: 'error',
          duration: 2000,
        })
        reject()
      },
    })
  })
}

Step2 开始搜索附近的蓝牙设备

使用uni.startBluetoothDevicesDiscovery()函数启动搜索附近的蓝牙设备,uni.onBluetoothDeviceFound()函数定义搜索到新设备时的回调函数。

// Step2 开始搜寻附近设备
function discovery() {
  return new Promise((resolve, reject) => {
    uni.startBluetoothDevicesDiscovery({
      success(res) {
        uni.showLoading({
          title: '正在搜索设备',
          icon: 'none',
        })
        // 开启监听回调
        uni.onBluetoothDeviceFound((devices) => {
          let obj = devices.devices[0]
          console.log(obj.name)
          if (obj.name === conf.deviceName) {
            // 找到目标设备
            // 设置设备ID到同步缓存中
            uni.setStorageSync('deviceId', obj.deviceId)
            uni.hideLoading()
            resolve()
          }
        })
        console.log('搜索蓝牙外围设备完成', res)
      },
      fail(err) {
        console.log(err)
      },
    })
  })
}

Step3 连接蓝牙设备

使用uni.createBLEConnection(deviceId)函数连接到目标蓝牙设备。

// Step3 连接设备
function connect() {
  return new Promise((resolve, reject) => {
    uni.createBLEConnection({
      deviceId: uni.getStorageSync('deviceId'), // 设备id
      success(res) {
        console.log('连接成功')
        console.log(res)
        uni.showToast({
          title: '连接成功',
          icon: 'success',
        })
        // Step4 蓝牙连接成功后关闭蓝牙搜索
        stopDiscovery()
        resolve()
      },
      fail(err) {
        uni.showToast({
          title: '连接失败',
          icon: 'error',
        })
        console.log('蓝牙连接失败')
        console.log(err)
        reject()
      },
    })
  })
}

重要

由于 Setp2 中搜索蓝牙设备的功能比较耗费系统资源,因此在搜索并连接到设备后务必停止搜索

Step4 停止搜索

使用uni.stopBluetoothDevicesDiscovery()函数停止搜索附近的蓝牙设备。

// Step4 停止搜索
function stopDiscovery() {
  uni.stopBluetoothDevicesDiscovery({
    success(res) {
      console.log('停止成功')
      console.log(res)
    },
    fail(err) {
      console.log('停止失败')
      console.error(err)
    },
  })
}

Step5 获取蓝牙设备所有服务

使用uni.getBLEDeviceServices(deviceId)函数获取蓝牙设备所有服务。

// Step5 获取蓝牙设备所有服务
function getServices() {
  return new Promise((resolve, reject) => {
    uni.getBLEDeviceServices({
      deviceId: uni.getStorageSync('deviceId'),
      success(res) {
        console.log(res)
        resolve()
      },
      fail(err) {
        console.error(err)
        reject()
      },
    })
  })
}

Step6 获取蓝牙设备某个服务中所有特征值

使用uni.getBLEDeviceCharacteristics(deviceId, serviceId)函数获取蓝牙设备某个服务中所有特征值。

// Step6 获取蓝牙设备某个服务中所有特征值
function getCharacteristics() {
  return new Promise((resolve, reject) => {
    uni.getBLEDeviceCharacteristics({
      deviceId: uni.getStorageSync('deviceId'),
      serviceId: conf.serviceId,
      success(res) {
        console.log(res)
        resolve()
      },
      fail(err) {
        console.error(err)
        reject()
      },
    })
  })
}

Step7 开启消息监听

使用uni.notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId, ...)函数开启消息监听。

// Step7 开启消息监听
function notify(handleFunc) {
  // 这里传入一个回调函数,用于处理接收到的数据。
  uni.notifyBLECharacteristicValueChange({
    deviceId: uni.getStorageSync('deviceId'), // 设备id
    serviceId: conf.serviceId, // 监听指定的服务
    characteristicId: conf.characteristicId, // 监听对应的特征值
    state: true,
    success(res) {
      console.log(res)
      listenValueChange(handleFunc) // 开启监听消息变化,并传入回调函数。
    },
    fail(err) {
      console.error(err)
      uni.showToast({
        title: '监听失败',
        icon: 'error',
      })
    },
  })
}

Step8 监听消息变化

使用uni.onBLECharacteristicValueChange()函数监听消息变化并根据需求做相应处理。

// Step8 监听消息变化
function listenValueChange(handleFunc) {
  // 这里传入一个回调函数,用于处理接收到的数据。
  uni.onBLECharacteristicValueChange((res) => {
    let resArray = ab2array(res.value) // 拿到数字数组
    // Something to do...
  })
}
// ArrayBuffer 转数组
function ab2array(buffer) {
  return Array.from(new Uint8Array(buffer))
}

Step9 发送数据

使用uni.writeBLECharacteristicValue(deviceId, serviceId, characteristicId, value)函数向蓝牙设备发送数据。

// Step9 发送数据
function sendCommand(data) {
  uni.writeBLECharacteristicValue({
    deviceId: uni.getStorageSync('deviceId'),
    serviceId: conf.serviceId,
    characteristicId: conf.characteristicId,
    // 注意:value 是ArrayBuffer类型,需要进行转换。
    value: array2ab(data),
    success(res) {
      console.log(`写入成功${res.errMsg}`)
    },
    fail(err) {
      console.error(err)
      uni.showToast({
        title: '指令发送失败',
        icon: 'error',
      })
    },
  })
}
// 数组转Arraybuffer
function array2ab(numbers) {
  // 创建一个ArrayBuffer,其大小等于数字数组的长度
  const buffer = new ArrayBuffer(numbers.length)
  // 创建一个Uint8Array视图来操作buffer
  const uint8View = new Uint8Array(buffer)
  // 将数字填充到Uint8Array中
  for (let i = 0; i < numbers.length; i++) {
    uint8View[i] = numbers[i]
  }
  // 返回ArrayBuffer
  return buffer
}

Step10 断开蓝牙连接

使用uni.closeBLEConnection(deviceId)函数断开蓝牙连接。

// Step10 断开蓝牙连接
function stopConnection() {
  return new Promise((resolve, reject) => {
    uni.closeBLEConnection({
      deviceId: uni.getStorageSync('deviceId'), // 设备id
      success(r) {
        resolve()
      },
      fail(r) {
        console.log(r)
        reject()
      },
    })
  })
}

踩过的坑

为什么收到的数据是乱码(不符合预期)?

  1. 协议文档写错了:可能是文档看错了或者硬件那边把协议改了却没有更新文档。
  2. 数据类型不对:可能是收到的数据没有进行转换,直接在 ArrayBuffer 上面进行了数据解析操作。
  3. 硬件波特率设置有问题:可能是硬件那边的波特率设置有问题,导致数据收发不正常。经过测试踩坑,波特率设置成 9600 时, 数据收发正常。

为什么采用 Promise 链的形式?

  1. 逻辑清晰:Promise 链的形式可以让代码看起来更加清晰,易于理解。
  2. 避免上下文影响 :一些函数的调用会影响到下文,若只是按顺序结构编写逻辑则有可能发生获取服务 先于蓝牙连接的情况,导致程序报错。

贡献者