微信小程序对接蓝牙设备,通讯、搜索、发送与接收

这篇博客详细介绍了如何使用微信小程序通过蓝牙与硬件设备进行交互,包括初始化蓝牙、查找和连接蓝牙设备、发送数据以及监听设备数据变化的过程。示例代码展示了从查找设备到连接设备,再到发送指令和接收应答的完整流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文档

微信小程序蓝牙通讯

开始

1.初始化

initBlue: function () {
    var that = this
    wx.openBluetoothAdapter({
      success: function (res) {
        that.findBlue()
      },
      fail: function (res) {
        wx.showToast({
          title: '请打开手机蓝牙!',
          icon: 'none',
          duration: 2000
        })
      }
    })
  }

2.查找蓝牙设备

用于微信小程序中的蓝牙设备搜索和连接功能的实现。它由两个主要函数组成:findBluegetBlue,分别负责开始蓝牙设备发现过程和处理发现到的蓝牙设备。

findBlue 函数

findBlue() {
  var that = this
  wx.startBluetoothDevicesDiscovery({
    allowDuplicatesKey: false, // 不允许重复的设备(即不保留历史搜索结果)
    interval: 0,               // 搜索间隔为0,表示立即开始搜索
    success: function (res) {  // 成功回调
      that.getBlue()           // 调用 getBlue 进行设备搜索
    }
  })
}
  • findBlue 方法通过调用微信小程序提供的 wx.startBluetoothDevicesDiscovery API 来启动蓝牙设备的搜索。
  • 设置了不允许重复设备 (allowDuplicatesKey: false) 和立即开始搜索 (interval: 0)。
  • 如果成功启动了蓝牙设备搜索,则会调用 getBlue 方法来执行进一步的操作。

getBlue 函数

getBlue() {
  var that = this
  var searchResult = false
  var index = 0

  wx.showLoading({ // 显示加载提示
    title: '搜索设备...',
  })

  // 定时搜索
  var interval = setInterval(function () {
    wx.getBluetoothDevices({
      success: function (res) { // 成功获取到设备列表后的回调
        index = index + 1
        if (!searchResult) {
          for (var i = 0; i < res.devices.length; i++) {
            // 根据设备名称匹配   这里的meterId是需要匹配的设备名称
            if ((res.devices[i].name + "").substr(0, 16) == that.data.meterId ||
              (res.devices[i].localName + "").substr(0, 16) == that.data.meterId) {
              that.setData({
                // 获取蓝牙设备的deviceId
                deviceId: res.devices[i].deviceId,
              })

              searchResult = true
              if (searchResult) {
                clearInterval(interval) // 停止定时器
              }
              // 连接蓝牙
              that.connetBlue(res.devices[i].deviceId)
              return
            }
          }
        }

        if (index > 20) { // 如果尝试次数超过20次
          clearInterval(interval) // 停止定时器
          wx.hideLoading()       // 隐藏加载提示
          wx.showToast({         // 显示提示信息
            title: '未找到设备,请重试!',
            icon: 'none',
            duration: 2000
          })
          wx.stopBluetoothDevicesDiscovery() // 关闭蓝牙搜索
        }
      }
    })
  }, 500) // 每隔500毫秒检查一次设备列表
}
  • getBlue 方法首先显示一个加载指示器告知用户正在搜索设备。
  • 使用 setInterval 创建一个定时器,每500毫秒调用一次 wx.getBluetoothDevices 来获取当前已发现的蓝牙设备列表。
  • 对每个设备进行循环遍历,检查其名称或本地名称是否与 that.data.meterId 匹配(这里假设 meterId 是你想要连接的特定蓝牙设备的名称前缀)。
  • 如果找到了匹配的设备,将该设备的 deviceId 存储在组件的数据中,并停止定时器,然后尝试连接该设备。
  • 如果经过20次尝试后仍未找到匹配的设备,将隐藏加载指示器,并向用户显示一条消息通知他们没有找到设备,同时停止蓝牙搜索。

注意事项

  • that 变量是为了保存对当前上下文对象的引用,因为在这个例子中,this 的值会在异步回调函数中改变。
  • wx.startBluetoothDevicesDiscovery 和 wx.getBluetoothDevices 是微信小程序提供的用于操作蓝牙设备的API。
  • setData 方法是用来更新组件状态的方法,在微信小程序中,它用于安全地更新页面上的数据绑定。
  • connetBlue 方法在这里被提到但没有给出定义;它应该是用来处理与蓝牙设备建立连接的逻辑。

这段代码展示了如何使用微信小程序的API来进行蓝牙设备的搜索和连接。它实现了简单的设备查找逻辑,并在找到目标设备后停止搜索并尝试建立连接。如果在一定时间内找不到设备,则会提示用户未找到设备。

 findBlue() {
    var that = this
    wx.startBluetoothDevicesDiscovery({
      allowDuplicatesKey: false,
      interval: 0,
      success: function (res) {
        that.getBlue()
      }
    })
  },

  getBlue() {
    var that = this
    var searchResult = false
    var index = 0

    wx.showLoading({
      title: '搜索设备...',
    })

    //定时搜索
    var interval = setInterval(function () {
      wx.getBluetoothDevices({
        success: function (res) {
          index = index + 1
          if (!searchResult) {
            for (var i = 0; i < res.devices.length; i++) {
              //根据设备名称匹配   这里的meterId是需要匹配的设备名称
              if ((res.devices[i].name + "").substr(0, 16) == that.data.meterId ||
                (res.devices[i].localName + "").substr(0, 16) == that.data.meterId) {
                that.setData({
                  //获取蓝牙设备的deviceId
                  deviceId: res.devices[i].deviceId,
                })

                searchResult = true
                if (searchResult) {
                  clearInterval(interval)
                }
                //连接蓝牙
                that.connetBlue(res.devices[i].deviceId)
                return
              }
            }
          }

          if (index > 20) {
            clearInterval(interval)
            wx.hideLoading()
            wx.showToast({
              title: '未找到设备,请重试!',
              icon: 'none',
              duration: 2000
            })
            wx.stopBluetoothDevicesDiscovery() //关闭蓝牙搜索
          }
        }
      })
    }, 500)
  }

3.连接蓝牙设备

微信小程序中用于连接蓝牙低功耗(BLE)设备,并获取其服务和特征值的实现。它由三个主要函数组成:connetBluegetServiceIdgetCharacteId,分别负责连接设备、获取服务ID和获取特征值。

connetBlue 函数

connetBlue(deviceId) {
  var that = this

  wx.hideLoading()
  wx.showLoading({
    title: '连接设备...',
  })

  wx.createBLEConnection({
    deviceId: deviceId,
    success: function (res) {
      wx.stopBluetoothDevicesDiscovery() //关闭蓝牙搜索
      that.getServiceId() //获取服务ID
    },
    fail: function () {
      wx.hideLoading()
      wx.showToast({
        title: '连接设备失败!',
        icon: 'none',
        duration: 2000
      })
      wx.stopBluetoothDevicesDiscovery() //关闭蓝牙搜索
    }
  })
}
  • connetBlue 方法接收一个 deviceId 参数,代表要连接的蓝牙设备的唯一标识符。
  • 它首先隐藏任何现有的加载指示器,然后显示一个新的加载指示器告知用户正在尝试连接设备。
  • 使用 wx.createBLEConnection API 尝试建立与指定 deviceId 的蓝牙设备的连接。
  • 如果连接成功:
    • 停止蓝牙设备的搜索以节省资源。
    • 调用 getServiceId 方法来获取该设备的服务信息。
  • 如果连接失败:
    • 隐藏加载指示器。
    • 显示一个错误提示给用户。
    • 同样停止蓝牙设备的搜索。

getServiceId 函数

getServiceId() {
  var that = this
  wx.getBLEDeviceServices({
    deviceId: that.data.deviceId,
    success: function (res) {
      var model = res.services[0]
      that.setData({
        serviceId: model.uuid
      })
      that.getCharacteId()
    }
  })
}
  • getServiceId 方法用于获取已连接蓝牙设备的服务列表。
  • 它调用了 wx.getBLEDeviceServices API 来获取服务。
  • 获取到的服务列表中,选择了第一个服务(这里假设只需要与第一个服务交互),并将它的 UUID 存储在组件数据中。
  • 然后调用 getCharacteId 方法来进一步获取该服务下的特征值。

getCharacteId 函数

getCharacteId() {
  var that = this

  wx.getBLEDeviceCharacteristics({
    deviceId: that.data.deviceId,
    serviceId: that.data.serviceId,
    success: function (res) {
      for (var i = 0; i < res.characteristics.length; i++) {
        var model = res.characteristics[i]
        if (model.properties.notify == true) {
          that.setData({
            notifyId: model.uuid
          })

          // 开启监听服务。监听设备数据变化
          that.startNotice(model.uuid)
        }
        if (model.properties.write == true) {
          that.setData({
            writeId: model.uuid
          })
        }
      }

      // 检查是否有可写权限
      if (that.data.writeId != '') {
        wx.hideLoading()
        wx.showLoading({
          title: '通讯中...',
        })

        // 这里可以完成需要下发到设备上的指令的生成
        that.sendCmd(value)
      } else {
        wx.showToast({
          title: '失败: 设备无写入权限!',
          icon: 'none',
          duration: 2000
        })
      }
    }
  })
}
  • getCharacteId 方法用于获取特定服务下的所有特征值。
  • 它调用 wx.getBLEDeviceCharacteristics API 来获取特征值列表。
  • 对每个特征值进行检查:
    • 如果特征值支持通知(notify 属性为 true),则保存该特征值的 UUID 到 notifyId 并启动监听服务。
    • 如果特征值支持写入(write 属性为 true),则保存该特征值的 UUID 到 writeId
  • 检查是否找到了具有写入权限的特征值:
    • 如果找到了,显示新的加载指示器并准备发送命令到设备。
    • 如果没有找到,则向用户显示一条消息,说明设备没有写入权限。

注意事项

  • that 变量是为了保存对当前上下文对象的引用,因为在异步回调函数中,this 的值会改变。
  • setData 方法是用来更新组件状态的方法,在微信小程序中,它用于安全地更新页面上的数据绑定。
  • startNotice 和 sendCmd 方法在这里被提到但没有给出定义;它们应该是用来处理开启通知和发送命令到蓝牙设备的逻辑。
  • 在实际应用中,value 应该是具体要发送给设备的数据或命令,这部分代码未提供具体实现。
  • wx.createBLEConnectionwx.getBLEDeviceServices, 和 wx.getBLEDeviceCharacteristics 是微信小程序提供的用于操作蓝牙设备的API。
  • 为了确保最佳用户体验,开发者应当处理好各种可能的异常情况,并提供适当的反馈给用户。
connetBlue(deviceId) {
    var that = this

    wx.hideLoading()
    wx.showLoading({
      title: '连接设备...',
    })

    wx.createBLEConnection({
      deviceId: deviceId,
      success: function (res) {
        wx.stopBluetoothDevicesDiscovery() //关闭蓝牙搜索
        that.getServiceId() //获取服务ID
      },
      fail: function () {
        wx.hideLoading()
        wx.showToast({
          title: '连接设备失败!',
          icon: 'none',
          duration: 2000
        })
        wx.stopBluetoothDevicesDiscovery() //关闭蓝牙搜索
      }
    })
  },

  getServiceId() {
    var that = this
    wx.getBLEDeviceServices({
      deviceId: that.data.deviceId,
      success: function (res) {
        var model = res.services[0]
        that.setData({
          serviceId: model.uuid
        })
        that.getCharacteId()
      }
    })
  },

  getCharacteId() {
    var that = this

    wx.getBLEDeviceCharacteristics({
      deviceId: that.data.deviceId,
      serviceId: that.data.serviceId,
      success: function (res) {
        for (var i = 0; i < res.characteristics.length; i++) {
          var model = res.characteristics[i]
          if (model.properties.notify == true) {
            that.setData({
              notifyId: model.uuid
            })

            //重点:开启监听服务。监听设备数据变化
            that.startNotice(model.uuid)
          }
          if (model.properties.write == true) {
            that.setData({
              writeId: model.uuid
            })
          }
        }

        //有可写权限
        if (that.data.writeId != '') {
          wx.hideLoading()
          wx.showLoading({
            title: '通讯中...',
          })

          //这里可以完成需要下发到设备上的指令的生成
          that.sendCmd(value)
        } else {
          wx.showToast({
            title: '失败: 设备无写入权限!',
            icon: 'none',
            duration: 2000
          })
        }
      }
    })
  }

4.发送数据

微信小程序中用于向已连接的蓝牙低功耗(BLE)设备发送命令或数据的具体实现。它由两个主要函数组成:sendCmdwriteCharacterToDevice,分别负责准备发送的数据和实际执行写入操作

sendCmd 函数

sendCmd(cmdValue) {
  var that = this

  if (cmdValue != '' && cmdValue != null && cmdValue != undefined) {
    that.writeCharacterToDevice(util.stringToArrayBuffer(cmdValue))
  } else {
    wx.showToast({
      title: '获取设备指令失败',
      icon: 'none',
      duration: 2000
    })
  }
}
  • sendCmd 方法接收一个参数 cmdValue,代表要发送给设备的命令或数据。
  • 它首先检查 cmdValue 是否既不是空字符串也不是 null 或 undefined,以确保有有效的数据可以发送。
  • 如果 cmdValue 是有效的,它会调用辅助函数 util.stringToArrayBuffer 将字符串转换为数组缓冲区(ArrayBuffer),然后调用 writeCharacterToDevice 方法来将数据写入到蓝牙设备。
  • 如果 cmdValue 不是有效值,则显示一条消息给用户,告知他们获取设备指令失败。

writeCharacterToDevice 函数

writeCharacterToDevice(buffer) {
  var that = this

  wx.writeBLECharacteristicValue({
    deviceId: that.data.deviceId,
    serviceId: that.data.serviceId,
    characteristicId: that.data.writeId,
    value: buffer,
    fail: function () {
      wx.showToast({
        title: '写入设备数据失败!',
        icon: 'none',
        duration: 2000
      })
    },
    success: function () {
      wx.showLoading({
        title: '指令发送成功',
        duration: 2000
      })
    }
  })
}
  • writeCharacterToDevice 方法接收一个参数 buffer,即要写入蓝牙设备的数据,该数据已经被转换成数组缓冲区格式。
  • 它使用 wx.writeBLECharacteristicValue API 向指定的蓝牙设备特征值写入数据。
  • 写入操作需要提供以下信息:
    • deviceId: 设备的唯一标识符。
    • serviceId: 服务的UUID。
    • characteristicId: 特征值的UUID,这里应该是之前找到的支持写入操作的那个特征值。
    • value: 要写入的数据,即传入的 buffer
  • 如果写入失败:
    • 显示一个错误提示给用户,说明写入设备数据失败。
  • 如果写入成功:
    • 显示一个短暂的加载指示器告知用户指令发送成功,持续时间为2秒。

注意事项

  • that 变量是为了保存对当前上下文对象的引用,因为在异步回调函数中,this 的值会改变。
  • util.stringToArrayBuffer 是一个假设存在的辅助函数,用于将字符串转换为数组缓冲区,以便能够通过蓝牙API发送。你需要确保这个函数已经在项目的某个地方定义并且可用。
  • setData 方法在原代码片段中没有直接出现,但它是微信小程序中用于更新页面状态的方法;在这个场景下,我们通过 that 来访问组件的状态,例如 deviceIdserviceId, 和 writeId
  • wx.writeBLECharacteristicValue 是微信小程序提供的用于与BLE设备通信的API之一,允许应用程序向特定的特征值写入数据。
  • 成功和失败的回调处理提供了即时反馈给用户,增强了用户体验。
  • showToast 和 showLoading 都是微信小程序提供的UI反馈方法,用来向用户展示短时间的消息提示或加载状态
 sendCmd(cmdValue) {
    var that = this

    if (cmdValue != '' && cmdValue != null && cmdValue != undefined) {
      that.writeCharacterToDevice(util.stringToArrayBuffer(cmdValue))
    } else {
      wx.showToast({
        title: '获取设备指令失败',
        icon: 'none',
        duration: 2000
      })
    }
  }

 writeCharacterToDevice(buffer) {
    var that = this

    wx.writeBLECharacteristicValue({
      deviceId: that.data.deviceId,
      serviceId: that.data.serviceId,
      characteristicId: that.data.writeId,
      value: buffer,
      fail: function () {
        wx.showToast({
          title: '写入设备数据失败!',
          icon: 'none',
          duration: 2000
        })

      },
      success: function () {
        wx.showLoading({
          title: '指令发送成功',
          duration: 2000
        })
      }
    })
  }

5.工具函数


  function stringToArrayBuffer(str) {
  var bytes = new Array();
  var len, c;
  len = str.length;
  for (var i = 0; i < len; i++) {
    c = str.charCodeAt(i);
    if (c >= 0x010000 && c <= 0x10FFFF) {
      bytes.push(((c >> 18) & 0x07) | 0xF0);
      bytes.push(((c >> 12) & 0x3F) | 0x80);
      bytes.push(((c >> 6) & 0x3F) | 0x80);
      bytes.push((c & 0x3F) | 0x80);
    } else if (c >= 0x000800 && c <= 0x00FFFF) {
      bytes.push(((c >> 12) & 0x0F) | 0xE0);
      bytes.push(((c >> 6) & 0x3F) | 0x80);
      bytes.push((c & 0x3F) | 0x80);
    } else if (c >= 0x000080 && c <= 0x0007FF) {
      bytes.push(((c >> 6) & 0x1F) | 0xC0);
      bytes.push((c & 0x3F) | 0x80);
    } else {
      bytes.push(c & 0xFF);
    }
  }
  var array = new Int8Array(bytes.length);
  for (var i in bytes) {
    array[i] = bytes[i];
  }
  return array.buffer;
}

function arrayBufferToString(arr) {
  if (typeof arr === 'string') {
    return arr;
  }
  var dataview = new DataView(arr);
  var ints = new Uint8Array(arr.byteLength);
  for (var i = 0; i < ints.length; i++) {
    ints[i] = dataview.getUint8(i);
  }
  arr = ints;
  var str = '',
    _arr = arr;
  for (var i = 0; i < _arr.length; i++) {
    var one = _arr[i].toString(2),
      v = one.match(/^1+?(?=0)/);
    if (v && one.length == 8) {
      var bytesLength = v[0].length;
      var store = _arr[i].toString(2).slice(7 - bytesLength);
      for (var st = 1; st < bytesLength; st++) {
        store += _arr[st + i].toString(2).slice(2);
      }
      str += String.fromCharCode(parseInt(store, 2));
      i += bytesLength - 1;
    } else {
      str += String.fromCharCode(_arr[i]);
    }
  }
  return str;
}

6.开启事件监听

定义了一个名为 startNotice 的方法,用于启用对指定特征值的通知,并处理当该特征值的数据发生变化时的响应逻辑。

方法签名

/**
 * 监听水表数据变化
 * @param {string} uuid - 水表ID(实际上是特征值的UUID)
 */
  • 注释:指明了这个方法的作用是监听水表数据的变化,并指出了参数 uuid 的含义,即水表ID,但实际上这里的 uuid 应该是指定的特征值的UUID。

startNotice 方法

startNotice(uuid) {
  var that = this

  wx.notifyBLECharacteristicValueChange({
    state: true, // 启用 notify 功能
    deviceId: that.data.deviceId,
    serviceId: that.data.serviceId,
    characteristicId: uuid,
    success: function (res) {
      var deviceResult = ''
      // 特征数据变化
      wx.onBLECharacteristicValueChange(function (res) {
        deviceResult = deviceResult + util.arrayBufferToString(res.value) // 应答数据比较长,会触发多次
        // 如果应答帧总长度大于数据包长度,并且包含“16”,则说明是完整的应答帧
        if (deviceResult.lastIndexOf('16') >= (deviceResult.length - 8) && deviceResult.indexOf('68') >= 0) {
          // 解析应答帧  这里开始对设备应答数据进行处理了
          
          deviceResult = ''
        }
      })
    }
  })
}
启用通知
  • wx.notifyBLECharacteristicValueChange:这是一个微信小程序提供的API,用于启用或禁用指定特征值的通知功能。
  • 参数包括:
    • state: 设置为 true 表示启用通知。
    • deviceId: 设备的唯一标识符。
    • serviceId: 服务的UUID。
    • characteristicId: 特征值的UUID,这里传入的是 uuid 参数,代表要监听的特征值。
  • 成功回调函数会在启用了通知后执行。
处理数据变化
  • 在成功启用了通知之后,使用 wx.onBLECharacteristicValueChange 注册一个监听器,用于监听特征值数据的变化。
  • 当特征值的数据发生变化时,wx.onBLECharacteristicValueChange 的回调函数会被触发,接收一个新的 res 对象,其中包含最新的数据。
  • util.arrayBufferToString(res.value) 将接收到的数组缓冲区格式的数据转换成字符串,并将其附加到 deviceResult 变量中。这一步骤可能被执行多次,因为一次完整的应答可能会被分多次发送。
  • 然后检查 deviceResult 是否包含了特定的结束标记 "16" 和起始标记 "68",以确定是否已经收到了完整的应答帧。如果满足条件,则认为接收到的是完整的一帧数据,并可以在此处解析和处理这些数据。
  • 最后,重置 deviceResult 为空字符串,以便为下一轮的数据接收做准备。

注意事项

  • that 变量是为了保存对当前上下文对象的引用,因为在异步回调函数中,this 的值会改变。
  • util.arrayBufferToString 是一个假设存在的辅助函数,用于将数组缓冲区转换为字符串,你需要确保这个函数已经在项目的某个地方定义并且可用。
  • deviceResult 的构建方式假定了数据是以字符串形式表示的,并且每帧数据都以 "68" 开头并以 "16" 结尾。这种协议可能是特定于所使用的水表设备的通信协议,因此在实际应用中需要根据具体设备的通信规范来调整。
  • 数据完整性检查基于最后8个字符包含 "16" 和任意位置包含 "68" 的假设,这可能不是所有情况的最佳实践;在实际开发中,应该依据具体的通信协议文档来准确判断数据帧的边界。
  • 如果应答帧的结构复杂或者存在更多变种,你可能需要更复杂的逻辑来解析和验证接收到的数据。
  • wx.notifyBLECharacteristicValueChange 和 wx.onBLECharacteristicValueChange 是微信小程序提供的用于与BLE设备通信的API,允许应用程序监听来自设备的数据变化。

这段代码展示了如何设置监听以接收来自特定蓝牙设备的数据更新,并实现了一个简单的机制来拼接和解析可能被分段发送的数据帧。对于任何想要利用微信小程序与BLE设备交互并处理其返回数据的开发者来说,这是一个非常重要的部分

  /**
   * 监听水表数据变化
   * @param {水表ID} uuid 
   */
  startNotice(uuid) {
    var that = this

    wx.notifyBLECharacteristicValueChange({
      state: true, // 启用 notify 功能
      deviceId: that.data.deviceId,
      serviceId: that.data.serviceId,
      characteristicId: uuid,
      success: function (res) {
        var deviceResult = ''
        //特征数据变化
        wx.onBLECharacteristicValueChange(function (res) {
          deviceResult = deviceResult + util.arrayBufferToString(res.value) //应答数据比较长,会触发多次
          //如果应答帧总长度大于数据包长度,并且包含“16”,则说明是完整的应答帧
          if (deviceResult.lastIndexOf('16') >= (deviceResult.length - 8) && deviceResult.indexOf('68') >= 0) {
            //解析应答帧  这里开始对设备应答数据进行处理了
            
            deviceResult = ''
          }
        })
      }
    })
  },

设备应答数据也可以发送到后台服务进行处理,根据具体业务而定

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

道友老李

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值