点赞 + 关注 + 收藏 = 学会了
这是一次真实的 蓝牙收发数据 的全过程讲解。
本文使用 uni-app
+ Vue3
的方式进行开发,以手机app的方式运行(微信小程序同样可行)。
uni-app
提供了 蓝牙 和 低功耗蓝牙 的 api
,和微信小程序提供的 api
是一样的,所以本文的讲解也适用于微信小程序。
本文只实现 蓝牙收发数据 功能,至于样式,我懒得调~
蓝牙相关功能我会逐步讲解。如果你基础好,又急的话,可以直接跳到 『完整代码』的章节查看,那里没废话。
花了几块钱巨款买回来的蓝牙学习套装~
HBuilder X 3.4.7.20220422
uni-app
+ Vue3
蓝牙收发数据的逻辑和我们常用的 AJAX
进行的网络请求是有一丢丢不同的。
其中较大的区别是:蓝牙接收数据不是那么的稳定,相比起网络请求,蓝牙更容易出现丢包的情况。
在开发中,AJAX
发起的请求不管成功还是失败,浏览器基本都会给你一个答复。但 uni-app
提供的 api
来看,蓝牙接收数据会显得更加“异步”。
使用蓝牙进行数据传输的大概思路如下:
上面整理出使用蓝牙传输数据的5大动作,但每个动作其实都是由 uni-app
提供的一个或者多个 api
组合而成。
使用蓝牙之前,需要初始化蓝牙模块,这是最最最开始就要做的!
使用 uni.openBluetoothAdapter
这个 api
就可以初始化蓝牙模块。其他蓝牙相关 API 必须在 uni.openBluetoothAdapter
调用之后使用。否则 API 会返回错误( errCode=10000
)。
错误代码可以查阅 《错误码文档》
代码示例
如果你手机开启了蓝牙,点击页面上的按钮后,控制台就会输出如下内容
初始化蓝牙成功
{"errMsg":"openBluetoothAdapter:ok"}
如果手机没开启蓝牙,就会返回如下内容
初始化蓝牙失败
{"errMsg":"openBluetoothAdapter:fail not available","code":10001}
根据文档提示,10001代表当前蓝牙适配器不可用。
如果你的控制台能打印出 {"errMsg":"openBluetoothAdapter:ok"}
证明第一步已经成功了。
接下来可以开始搜索附近蓝牙设备。
这一步需要2个 api
配合完成。所以可以分解成以下2步:
uni.startBluetoothDevicesDiscovery
uni.onBluetoothDeviceFound
开发蓝牙相关功能时,操作逻辑更像是推送,所以“开启搜索”和“监听新设备”是分开操作的。
uni.startBluetoothDevicesDiscovery
可以让设备开始搜索附近蓝牙设备,但这个方法比较耗费系统资源,建议在连接到设备之后就使用 uni.stopBluetoothDevicesDiscovery
停止继续搜索。
uni.startBluetoothDevicesDiscovery
方法里可以传入一个对象,该对象接收几个参数,但初学的话我们只关注 success
和 fail
。如果你的项目中硬件佬有提供 service 的 uuid
给你的话,你也可以在 services
里传入。其他参数可以查看官方文档的介绍。
在使用 uni.startBluetoothDevicesDiscovery
(开始搜索)后,可以使用 uni.onBluetoothDeviceFound
进行监听,这个方法里面接收一个回调函数。
代码示例
id: {{ item.deviceId }}
name: {{ item.name }}
上面代码的逻辑是,如果开启 “寻找附近设备” 功能成功,接着就开启 “监听寻找到新设备的事件” 。
搜索到的设备会返回以下数据:
{
"devices": [{
"deviceId": "B4:10:7B:C4:83:14",
"name": "蓝牙设备名",
"RSSI": -58,
"localName": "",
"advertisServiceUUIDs": ["0000FFF0-0000-1000-8000-00805F9B34FB"],
"advertisData": {}
}]
}
每监听到一个新的设备,我都会将其添加到 蓝牙设备列表(blueDeviceList) 里,最后讲这个列表的数据渲染到页面上。
连接目标设备只需要1个 api
就能完成。但根据文档提示,我们连接后还需要关闭 “搜索附近设备” 的功能,这个很好理解,既然找到了,再继续找就是浪费资源。
流程如下:
uni.onBluetoothDeviceFound
回调,拿到设备IDuni.createBLEConnection
uni.stopBluetoothDevicesDiscovery
我给每条搜索到的蓝牙结果添加一个 click
事件,会向目标设备发送连接请求。
我的设备名称是 leihou
,所以我点击了这条。
代码示例
id: {{ item.deviceId }}
name: {{ item.name }}
连接成功后在控制台会输出
连接成功
{"errMsg":"createBLEConnection:ok"}
在连接成功后就立刻调用 uni.stopBluetoothDevicesDiscovery
方法停止继续搜索附近其他设备,停止成功后会输出
停止成功
{"errMsg":"stopBluetoothDevicesDiscovery:ok"}
连接成功后,设备也亮起了绿灯。
在连接完设备后,就要先开启监听数据的功能。这样才能接收到发送读写指令后设备给你回调的信息。
要开启监听,首先需要知道蓝牙设备提供了那些服务,然后通过服务获取特征值,特征值会告诉你哪个可读,哪个可写。最后根据特征值进行消息监听。
步骤如下:
uni.getBLEDeviceServices
uni.getBLEDeviceCharacteristics
uni.notifyBLECharacteristicValueChange
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。
在上两步我拿到了 设备ID 为 B4:10:7B:C4:83:14
,服务ID 为 0000FFE0-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
App端收到的数据转成 16进制 后的结果:6c6569686f75
再从 16进制 转成 ASCII码 后的结果:leihou
终于到最后一步了。
从 uni-app
和 微信小程序
提供的蓝牙api
来看,发送指令只要有2个方法:
uni.writeBLECharacteristicValue
:向低功耗蓝牙设备特征值中写入二进制数据。uni.readBLECharacteristicValue
:读取低功耗蓝牙设备的特征值的二进制数据值。这里需要理清一个概念,本节的内容为 “发送指令”,也就是说,从你的app或小程序向其他蓝牙设备发送指令,而这个指令分2种情况,一种是你要发送一些数据给蓝牙设备,另一种情况是你叫蓝牙设备给你发点信息。
这两种情况我们需要分开讨论,先讲讲 uni.writeBLECharacteristicValue
。
uni.writeBLECharacteristicValue
从文档可以看出,这个 api
是可以发送一些数据给蓝牙设备,但发送的值要转成 ArrayBuffer
。
代码示例
此时,如果 uni.writeBLECharacteristicValue
走 success
,证明你已经把数据向外成功发送了,但不代表设备一定就收到了。
通常设备收到你发送过去的信息,会返回一条消息给你,而这个回调消息会在 uni.onBLECharacteristicValueChange
触发,也就是 第【9】步 那里。但这是蓝牙设备那边控制的,你作为前端佬,人家“已读不回”你也拿人家没办法。
在 “监听” 部分,我们使用了 uni.getBLEDeviceCharacteristics
获取设备的特征值,我的设备提供的特征值支持 read
,所以可以使用 uni.readBLECharacteristicValue
向蓝牙设备发送一条 “读取” 指令。然后在 uni.onBLECharacteristicValueChange
里可以接收设备发送过来的数据。
代码示例
使用 “读取” 的方式向设备发送指令,是不需要另外传值的。
此时我的设备返回 00
这个数据是硬件那边设置的。
在日常工作中,uni.readBLECharacteristicValue
的作用主要是读取数据,但使用场景不算很多。
我在工作中遇到的场景是:蓝牙设备提供了几个接口,而且传过来的数据比较大,比如传图片给app这边。我就会先用 uni.writeBLECharacteristicValue
告诉设备我现在需要取什么接口的数据,然后用 uni.readBLECharacteristicValue
发送读取数据的请求,如果数据量比较大,就要重复使用 uni.readBLECharacteristicValue
进行读取。比如上面的例子,我读第一次的时候返回 00
,读第二次就返回 01
……
最后再提醒一下,uni.readBLECharacteristicValue
只负责发送读取的请求,并且里面的 success
和 fail
只是返回你本次发送请求的动作是否成功,至于对面的蓝牙设备有没有收到这个指令你是不清楚的。
最后需要通过 uni.getBLEDeviceCharacteristics
监听设备传过来的数据。
id: {{ item.deviceId }}
name: {{ item.name }}
监听到的内容:{{ message }}
监听到的内容(十六进制):{{ messageHex }}
以上就是本文的完整代码。
uni-app 蓝牙文档
uni-app 低功耗蓝牙文档
微信小程序 蓝牙文档
微信小程序 低功耗蓝牙文档
DataView 使用方法
uni-app App端半屏连续扫码
记住:点赞 + 关注 + 收藏 = 学会了
点赞 + 关注 + 收藏 = 学会了