uni-app 蓝牙通信

本文简介

点赞 + 关注 + 收藏 = 学会了


这是一次真实的 蓝牙收发数据 的全过程讲解。


本文使用 uni-app + Vue3 的方式进行开发,以手机app的方式运行(微信小程序同样可行)。

uni-app 提供了 蓝牙低功耗蓝牙api ,和微信小程序提供的 api 是一样的,所以本文的讲解也适用于微信小程序


本文只实现 蓝牙收发数据 功能,至于样式,我懒得调~

蓝牙相关功能我会逐步讲解。如果你基础好,又急的话,可以直接跳到 『完整代码』的章节查看,那里没废话。


uni-app 蓝牙通信_第1张图片

花了几块钱巨款买回来的蓝牙学习套装~



环境说明

  • 开发工具:HBuilder X 3.4.7.20220422
  • uni-app + Vue3
  • 以安卓App的方式运行(iOS和小程序同理)



思路

蓝牙收发数据的逻辑和我们常用的 AJAX 进行的网络请求是有一丢丢不同的。

其中较大的区别是:蓝牙接收数据不是那么的稳定,相比起网络请求,蓝牙更容易出现丢包的情况。


在开发中,AJAX 发起的请求不管成功还是失败,浏览器基本都会给你一个答复。但 uni-app 提供的 api 来看,蓝牙接收数据会显得更加“异步”



大致思路

使用蓝牙进行数据传输的大概思路如下:

  1. 初始化:打开蓝牙模块
  2. 搜寻:检测附近存在的设备
  3. 连接:找到目标设备进行
  4. 监听:开启监听功能,接收其他设备传过来的数据
  5. 发送指令:不管发送数据还是读取数据,都可以理解为向外发送指令



实现

上面整理出使用蓝牙传输数据的5大动作,但每个动作其实都是由 uni-app 提供的一个或者多个 api 组合而成。


初始化阶段

使用蓝牙之前,需要初始化蓝牙模块,这是最最最开始就要做的!

使用 uni.openBluetoothAdapter 这个 api 就可以初始化蓝牙模块。其他蓝牙相关 API 必须在 uni.openBluetoothAdapter 调用之后使用。否则 API 会返回错误( errCode=10000 )。

错误代码可以查阅 《错误码文档》

代码示例



如果你手机开启了蓝牙,点击页面上的按钮后,控制台就会输出如下内容

初始化蓝牙成功
{"errMsg":"openBluetoothAdapter:ok"}

如果手机没开启蓝牙,就会返回如下内容

初始化蓝牙失败
{"errMsg":"openBluetoothAdapter:fail not available","code":10001}

根据文档提示,10001代表当前蓝牙适配器不可用

file

file

如果你的控制台能打印出 {"errMsg":"openBluetoothAdapter:ok"} 证明第一步已经成功了。

接下来可以开始搜索附近蓝牙设备。



搜寻附近设备

这一步需要2个 api 配合完成。所以可以分解成以下2步:

  1. 开启搜寻功能uni.startBluetoothDevicesDiscovery
  2. 监听搜寻到新设备uni.onBluetoothDeviceFound


开发蓝牙相关功能时,操作逻辑更像是推送,所以“开启搜索”和“监听新设备”是分开操作的。


uni.startBluetoothDevicesDiscovery 可以让设备开始搜索附近蓝牙设备,但这个方法比较耗费系统资源,建议在连接到设备之后就使用 uni.stopBluetoothDevicesDiscovery 停止继续搜索。

uni.startBluetoothDevicesDiscovery 方法里可以传入一个对象,该对象接收几个参数,但初学的话我们只关注 successfail。如果你的项目中硬件佬有提供 service 的 uuid 给你的话,你也可以在 services 里传入。其他参数可以查看官方文档的介绍。


在使用 uni.startBluetoothDevicesDiscovery (开始搜索)后,可以使用 uni.onBluetoothDeviceFound 进行监听,这个方法里面接收一个回调函数。


代码示例





上面代码的逻辑是,如果开启 “寻找附近设备” 功能成功,接着就开启 “监听寻找到新设备的事件”

搜索到的设备会返回以下数据:

{
    "devices": [{
        "deviceId": "B4:10:7B:C4:83:14",
        "name": "蓝牙设备名",
        "RSSI": -58,
        "localName": "",
        "advertisServiceUUIDs": ["0000FFF0-0000-1000-8000-00805F9B34FB"],
        "advertisData": {}
    }]
}


每监听到一个新的设备,我都会将其添加到 蓝牙设备列表(blueDeviceList) 里,最后讲这个列表的数据渲染到页面上。

file



连接目标设备

连接目标设备只需要1个 api 就能完成。但根据文档提示,我们连接后还需要关闭 “搜索附近设备” 的功能,这个很好理解,既然找到了,再继续找就是浪费资源。


流程如下:

  1. 获取设备ID:根据 uni.onBluetoothDeviceFound 回调,拿到设备ID
  2. 连接设备:使用设备ID进行连接 uni.createBLEConnection
  3. 停止搜索uni.stopBluetoothDevicesDiscovery


我给每条搜索到的蓝牙结果添加一个 click 事件,会向目标设备发送连接请求。

我的设备名称是 leihou ,所以我点击了这条。


代码示例






连接成功后在控制台会输出

连接成功
{"errMsg":"createBLEConnection:ok"}


在连接成功后就立刻调用 uni.stopBluetoothDevicesDiscovery 方法停止继续搜索附近其他设备,停止成功后会输出

停止成功
{"errMsg":"stopBluetoothDevicesDiscovery:ok"}


uni-app 蓝牙通信_第2张图片

连接成功后,设备也亮起了绿灯。



监听

在连接完设备后,就要先开启监听数据的功能。这样才能接收到发送读写指令后设备给你回调的信息。

要开启监听,首先需要知道蓝牙设备提供了那些服务,然后通过服务获取特征值,特征值会告诉你哪个可读,哪个可写。最后根据特征值进行消息监听


步骤如下:

  1. 获取蓝牙设备服务uni.getBLEDeviceServices
  2. 获取特征值uni.getBLEDeviceCharacteristics
  3. 开启消息监听uni.notifyBLECharacteristicValueChange
  4. 接收消息监听传来的数据uni.onBLECharacteristicValueChange


正常情况下,硬件佬会提前把蓝牙设备的指定服务还有特征值告诉你。

比如我这个设备的蓝牙服务是:0000FFE0-0000-1000-8000-00805F9B34FB

特征值是:0000FFE1-0000-1000-8000-00805F9B34FB


第一步,获取蓝牙服务



此时点击按钮,将会获取到已连接设备的所有服务。

我的设备有以下几个服务。你在工作中拿到的 服务uuid 和我的是不一样的,数量也不一定相同。

可以发现,我拿到的结果里有 0000FFE0-0000-1000-8000-00805F9B34FB 这条服务。

{
    "services": [{
        "uuid": "00001800-0000-1000-8000-00805F9B34FB",
        "isPrimary": true
    }, {
        "uuid": "00001801-0000-1000-8000-00805F9B34FB",
        "isPrimary": true
    }, {
        "uuid": "0000180A-0000-1000-8000-00805F9B34FB",
        "isPrimary": true
    }, {
        "uuid": "0000FFF0-0000-1000-8000-00805F9B34FB",
        "isPrimary": true
    }, {
        "uuid": "0000FFE0-0000-1000-8000-00805F9B34FB",
        "isPrimary": true
    }],
    "errMsg": "getBLEDeviceServices:ok"
}


第二步,获取指定服务的特征值

获取特征值,需要传 设备ID服务ID

在上两步我拿到了 设备IDB4:10:7B:C4:83:14服务ID0000FFE0-0000-1000-8000-00805F9B34FB



最后成功输出

{
    "characteristics": [{
        "uuid": "0000FFE1-0000-1000-8000-00805F9B34FB",
        "properties": {
            "read": true,
            "write": true,
            "notify": true,
            "indicate": false
        }
    }],
    "errMsg": "getBLEDeviceCharacteristics:ok"
}

characteristics 字段里保存了该服务的所有特征值,我的设备这个服务只有1个特征值,并且读、写、消息推送都为 true

你的设备可能不止一条特征值,需要监听那条特征值这需要你和硬件佬协商的(通常也是硬件佬直接和你说要监听哪条)。


第三、四步,开启消息监听 并 接收消息监听传来的数据

根据已经拿到的 设备ID服务ID特征值,就可以开启对应的监听功能。

使用 uni.notifyBLECharacteristicValueChange 开启消息监听;

并在 uni.onBLECharacteristicValueChange 方法触发监听到的消息。



listenValueChange 方法是用来接收设备传过来的消息。

上面的例子中,res 的结果是

{
    "deviceId": "B4:10:7B:C4:83:14",
    "serviceId": "0000FFE0-0000-1000-8000-00805F9B34FB",
    "characteristicId": "0000FFE1-0000-1000-8000-00805F9B34FB",
    "value": {}
}

设备传过来的内容就放在 value 字段里,但因为该字段的类型是 ArrayBuffer,所以无法在控制台用肉眼直接观察。于是就通过 ab2hex 方法将该值转成 16进制 ,最后再用 hexCharCodeToStr 方法将 16进制 转成 ASCII码


我从设备里发送一段字符串过来:leihou

file

App端收到的数据转成 16进制 后的结果:6c6569686f75

再从 16进制 转成 ASCII码 后的结果:leihou



发送指令

终于到最后一步了。

uni-app微信小程序 提供的蓝牙api 来看,发送指令只要有2个方法:


这里需要理清一个概念,本节的内容为 “发送指令”,也就是说,从你的app或小程序向其他蓝牙设备发送指令,而这个指令分2种情况,一种是你要发送一些数据给蓝牙设备,另一种情况是你叫蓝牙设备给你发点信息。


uni.writeBLECharacteristicValue

这两种情况我们需要分开讨论,先讲讲 uni.writeBLECharacteristicValue

uni.writeBLECharacteristicValue 从文档可以看出,这个 api 是可以发送一些数据给蓝牙设备,但发送的值要转成 ArrayBuffer


代码示例



此时,如果 uni.writeBLECharacteristicValuesuccess ,证明你已经把数据向外成功发送了,但不代表设备一定就收到了。

通常设备收到你发送过去的信息,会返回一条消息给你,而这个回调消息会在 uni.onBLECharacteristicValueChange 触发,也就是 第【9】步 那里。但这是蓝牙设备那边控制的,你作为前端佬,人家“已读不回”你也拿人家没办法。

file



uni.readBLECharacteristicValue

“监听” 部分,我们使用了 uni.getBLEDeviceCharacteristics 获取设备的特征值,我的设备提供的特征值支持 read ,所以可以使用 uni.readBLECharacteristicValue 向蓝牙设备发送一条 “读取” 指令。然后在 uni.onBLECharacteristicValueChange 里可以接收设备发送过来的数据。


代码示例



使用 “读取” 的方式向设备发送指令,是不需要另外传值的。


此时我的设备返回 00

file

这个数据是硬件那边设置的。


在日常工作中,uni.readBLECharacteristicValue 的作用主要是读取数据,但使用场景不算很多。

我在工作中遇到的场景是:蓝牙设备提供了几个接口,而且传过来的数据比较大,比如传图片给app这边。我就会先用 uni.writeBLECharacteristicValue 告诉设备我现在需要取什么接口的数据,然后用 uni.readBLECharacteristicValue 发送读取数据的请求,如果数据量比较大,就要重复使用 uni.readBLECharacteristicValue 进行读取。比如上面的例子,我读第一次的时候返回 00 ,读第二次就返回 01 ……


最后再提醒一下,uni.readBLECharacteristicValue 只负责发送读取的请求,并且里面的 successfail 只是返回你本次发送请求的动作是否成功,至于对面的蓝牙设备有没有收到这个指令你是不清楚的。

最后需要通过 uni.getBLEDeviceCharacteristics 监听设备传过来的数据。



完整代码





file

uni-app 蓝牙通信_第3张图片


以上就是本文的完整代码。



相关文档

uni-app 蓝牙文档

uni-app 低功耗蓝牙文档


微信小程序 蓝牙文档

微信小程序 低功耗蓝牙文档



DataView 使用方法

推荐阅读

uni-app App端半屏连续扫码


记住:点赞 + 关注 + 收藏 = 学会了

点赞 + 关注 + 收藏 = 学会了

你可能感兴趣的