蓝牙分类
蓝牙分为经典蓝牙(Bluetooth Classic) 和 低功耗蓝牙(Bluetooth Low Energy)。
现在大部分设备都是低功耗蓝牙,蓝牙智能(Bluetooth Smart) 也是低功耗蓝牙的一种。此外,还有兼容两者的蓝牙智能就绪(Bluetooth Smart Ready)。
经典蓝牙多数用来传递富内容,包含音视频等多媒体。
低功耗蓝牙则用来传输更小的数据,如用在现在的智能设备上;但是也可以通过技术手段,用来传递音频(LE Audio)。
蓝牙重要版本号:
蓝牙 4.0 开始引入 BLE 的概念。
蓝牙 5.1 开始引入定位服务。
为什么扫描蓝牙设备需要位置权限?
蓝牙 5.1 中新增了蓝牙寻向功能,结合现有的蓝牙定位方案,可以提高定位精确度至亚米级。这需要APP能够访问设备的定位信息以实现更精确的定位服务。
在 Android 6.0 / iOS 13 以后的系统中,进行 BLE(蓝牙低功耗)扫描需要申请位置权限,以支持发现附近的 Beacons 设备等蓝牙功能。这是因为蓝牙设备与手机连接后,可以认为自己的位置就是手机的位置,相当于间接获取了手机的位置信息,因此必须申请定位权限。
蓝牙服务与特征
要与蓝牙通信,需要获取蓝牙的服务(Service)与特征(Characteristic)。
服务(Service) 是蓝牙设备提供的功能集合,可以看作是设备上运行的一个应用程序或功能模块。服务定义了一组可以被其他设备访问的特征(Characteristic)。
特征(Characteristic) 是服务中的一个具体数据项,它包含了可以被读取(Read)、写入(Write)或通知(Notify)的数据。特征是服务的具体实现细节。
graph LR;
连接设备 --> 获取服务Service
获取服务Service --> 获取特征Characteristic
获取特征Characteristic --> 发送消息
获取特征Characteristic --> 监听返回消息
与服务、特征通信,需要使用 UUID 进行连接。
服务 UUID 和特征 UUID 的编码规则相同,通常为 0000****-0000-1000-8000-00805F9B34FB,简写为 0x****。
基础服务UUID 通常有 0x18**,如下面的 2 个服务。自定义的服务 UUID 规避这一编码即可。
Generic Access Service:00001800-0000-1000-8000-00805f9b34fb
Generic Attribute Service:00001801-0000-1000-8000-00805f9b34fb
基础特征UUID 通常有 0x2a**,如下面的 2 个特征。自定义的特征 UUID 规避这一编码即可。
Device Name:00002a00-0000-1000-8000-00805f9b34fb
Appearance:00002a01-0000-1000-8000-00805f9b34fb
蓝牙调试
可以使用开源库 nRF Connect 进行调试。
Android 版本: github.com/NordicSemic…
iOS 版本: github.com/NordicSemic…
桌面版本: github.com/NordicSemic…
蓝牙操作比较简单,但是各个 app 设计会有所不同,下面以安卓为例:
SCANNER 为扫描仪,会扫描附近的设备。可以选择连接、或绑定
BONDED 为已经绑定的设备;
点击 Connect 之后会新开 tab 显示当前连接设备。
当前连接的设备下,可以看到其作为 Client 和 Server 的相关服务。点击服务可以看到服务下的特征。我们作为主动发起方,需要将设备作为 Client 进行交互。
特征权限(Properties)
特征下的 Properties 是该特征的权限:
Notify
通知权限,需要先使能(enabled)才能获取其消息。
点击右侧的 三个箭头,可以切换 enabled 状态。
Indicate
指示权限,类似于通知,但需要确认
Read
仅读取权限,无法做操作。
Write
写权限,还可以细分:
Write Request:客户端可以使用Write Request来写入特征值,服务器会响应一个Write Response来确认写入操作。
Write without Response / Write Command:客户端可以使用 Write without Response/Write Command 来写入特征值,服务器不会响应任何确认。
Signed Write Command:客户端可以使用Signed Write Command来写入特征值,这需要一个签名来验证客户端的身份,增加了安全性。
Queued Write:允许客户端将多个写入操作排队,然后一次性发送给服务器,这有助于减少通信开销。
如果通过 Write without Response / Write Command 写入,有的硬件会把响应信息发在 Notify,这时候需要监听 Notify 的返回信息。
点击写权限右侧的发送箭头,出来写入值的弹窗,可以发送 16 进制的内容给设备。可以选择支持的类型包含字节数组、字节、UTF-8等。
开发流程中,可以对比 nRF Connect 发送消息的结果,看看我们的 app 发送是否正常。
蓝牙开发
蓝牙模块还是比较成熟的,现在各个客户端开发都有相关的 API / 库。
Android 原生
android.bluetooth:这是Android官方提供的蓝牙开发包,包含了一系列的类和接口,用于控制蓝牙A2DP配置文件、管理蓝牙适配器、与远程蓝牙设备进行通信等。
iOS 原生
CoreBluetooth.framework:这是苹果官方提供的用于与蓝牙低功耗(BLE)设备进行通信的解决方案。通过该框架,开发者可以实现iOS设备与蓝牙设备间的搜索、连接、数据交换等功能。
如果觉得原生的库操作复杂,可以看看是否有 Java / Kotlin / Object-C / Swift开源库支持,不过目前这些原生开源库都比较旧,还需要写写测试 demo 确认是否兼新版本。
React Native
react-native-ble-plx:这是一个功能全面且可靠的React Native BLE库,支持观察设备的蓝牙适配器状态、扫描BLE设备、连接到外围设备、发现服务/特征、读写特征、观察特征通知/指示、读取RSSI、协商MTU以及iOS后台模式等。
react-native-ble-manager:如果react-native-ble-plx看起来太复杂,react-native-ble-manager 提供了另一个优秀的选择。这个工具支持所有主要的BLE功能,如扫描设备、建立连接、传输数据、获取状态变化通知以及断开连接
react-native-bluetooth-classic:这个库专为React Native应用设计,旨在解决iOS蓝牙经典(非BLE)连接的问题,通过External Accessory框架提供稳定的接口。
个人踩过的坑:react native 的库只支持 base64 的消息传递,如果需要其他格式,建议使用原生完成。
Flutter
在Flutter中,有几个流行的蓝牙库可以用于实现蓝牙通信,以下是一些主要的蓝牙库:
flutter_blue:这是一个蓝牙插件,用于帮助开发者构建现代多平台应用。它支持扫描和连接到附近的蓝牙设备,并与之交互。
flutter_blue_plus:这是从 FlutterBlue 发展而来的蓝牙低功耗插件,支持 BLE 中央角色(最常见)。如果你需要 BLE 外设角色,你可能需要查看 FlutterBlePeripheral 或bluetooth_low_energy。
universal_ble:这是一个跨平台(Android/iOS/macOS/Windows/Linux/Web)的蓝牙低功耗(BLE)插件,支持扫描、连接、发现服务、读写数据、配对等功能。
这些库提供了与蓝牙设备进行通信所需的基本功能,包括扫描附近的BLE设备、连接到设备、发现服务和特征、读写特征值等。开发者可以根据项目需求选择合适的库进行集成。
Electron
Web Bluetooth API:Electron 支持 Web Bluetooth API,允许与蓝牙设备通信。开发者需要处理 select-bluetooth-device 事件以及可能的蓝牙配对处理。
总结
蓝牙技术分为经典蓝牙(Bluetooth Classic)和低功耗蓝牙(Bluetooth Low Energy,BLE),后者支持智能设备的小数据传输,并自蓝牙4.0版本引入低功耗蓝牙,从5.1版引入定位服务。现代应用在使用BLE扫描时需申请位置权限以提高精度。
与蓝牙通信需通过 UUID 获取服务和服务中的特征来实现数据的读取、写入或通知功能。
针对不同平台,存在多种开源库支持蓝牙开发,如 React Native 的 react-native-ble-plx 和 Flutter 的 flutter_blue 等,这些工具简化了开发流程,并提供了广泛的 API 接口用于与蓝牙设备交互。