一、蓝牙简介
① 概念
- 蓝牙是短距离无线通信的一种方式,支持蓝牙的两个设备必须配对后才能通信。HarmonyOS 蓝牙主要分为传统蓝牙和低功耗蓝牙(通常称为 BLE,Bluetooth Low Energy)。
- 传统蓝牙指的是蓝牙版本 3.0 以下的蓝牙,低功耗蓝牙指的是蓝牙版本 4.0 以上的蓝牙。
- 当前蓝牙的配对方式有两种:蓝牙协议 2.0 以下支持 PIN 码(Personal Identification Number,个人识别码)配对,蓝牙协议 2.1 以上支持简单配对。
② 传统蓝牙
- 传统蓝牙本机管理:打开和关闭蓝牙、设置和获取本机蓝牙名称、扫描和取消扫描周边蓝牙设备、获取本机蓝牙 profile 对其他设备的连接状态、获取本机蓝牙已配对的蓝牙设备列表。
- 传统蓝牙远端设备操作:查询远端蓝牙设备名称和 MAC 地址、设备类型和配对状态,以及向远端蓝牙设备发起配对。
③ BLE
- BLE 设备交互时会分为不同的角色:
- 中心设备和外围设备:中心设备负责扫描外围设备、发现广播。外围设备负责发送广播。
- GATT(Generic Attribute Profile,通用属性配置文件)服务端与 GATT 客户端:两台设备建立连接后,其中一台作为 GATT 服务端,另一台作为 GATT 客户端。
- HarmonyOS 低功耗蓝牙提供的功能有:
- BLE 扫描和广播:根据指定状态获取外围设备、启动或停止 BLE 扫描、广播。
- BLE 中心设备与外围设备进行数据交互:BLE 外围设备和中心设备建立 GATT 连接后,中心设备可以查询外围设备支持的各种数据,向外围设备发起数据请求,并向其写入特征值数据。
- BLE 外围设备数据管理:BLE 外围设备作为服务端,可以接收来自中心设备(客户端)的 GATT 连接请求,应答来自中心设备的特征值内容读取和写入请求,并向中心设备提供数据。同时外围设备还可以主动向中心设备发送数据。
④ 权限申请
- 调用蓝牙的打开接口需要 ohos.permission.USE_BLUETOOTH 权限。
- 调用蓝牙扫描接口需要 ohos.permission.LOCATION 权限和 ohos.permission.DISCOVER_BLUETOOTH 权限。
二、传统蓝牙本机管理
① 应用场景
- 传统蓝牙本机管理主要是针对蓝牙本机的基本操作,包括打开和关闭蓝牙、设置和获取本机蓝牙名称、扫描和取消扫描周边蓝牙设备、获取本机蓝牙profile对其他设备的连接状态、获取本机蓝牙已配对的蓝牙设备列表。
② API 说明
- 蓝牙本机管理类 BluetoothHost 的主要接口:
接口名 | 功能描述 |
getDefaultHost(Context context) | 获取BluetoothHost实例,去管理本机蓝牙操作 |
enableBt() | 打开本机蓝牙 |
disableBt() | 关闭本机蓝牙 |
setLocalName(String name) | 设置本机蓝牙名称 |
getLocalName() | 获取本机蓝牙名称 |
getBtState() | 获取本机蓝牙状态 |
startBtDiscovery() | 发起蓝牙设备扫描 |
cancelBtDiscovery() | 取消蓝牙设备扫描 |
isBtDiscovering() | 检查蓝牙是否在扫描设备中 |
getProfileConnState(int profile) | 获取本机蓝牙profile对其他设备的连接状态 |
getPairedDevices() | 获取本机蓝牙已配对的蓝牙设备列 |
③ 打开蓝牙
- 调用 BluetoothHost 的 getDefaultHost(Context context) 接口,获取 BluetoothHost 实例,管理本机蓝牙操作。
- 调用 enableBt() 接口,打开蓝牙。
- 调用 getBtState(),查询蓝牙是否打开。
// 获取蓝牙本机管理对象
BluetoothHost bluetoothHost = BluetoothHost.getDefaultHost(context);
// 调用打开接口
bluetoothHost.enableBt();
// 调用获取蓝牙开关状态接口
int state = bluetoothHost.getBtState();
④ 蓝牙扫描
- 开始蓝牙扫描前要先注册广播 BluetoothRemoteDevice.EVENT_DEVICE_DISCOVERED。
- 调用 startBtDiscovery() 接口开始进行扫描外围设备。
- 如果想要获取扫描到的设备,必须在注册广播时继承实现 CommonEventSubscriber 类的 onReceiveEvent(CommonEventData data) 方法,并接收 EVENT_DEVICE_DISCOVERED 广播。
// 开始扫描
bluetoothHost.startBtDiscovery();
// 接收系统广播
public class MyCommonEventSubscriber extends CommonEventSubscriber {
@Override
public void onReceiveEvent(CommonEventData data) {
if (data == null) {
return;
}
Intent info = data.getIntent();
if (info == null) {
return;
}
// 获取系统广播的action
String action = info.getAction();
// 判断是否为扫描到设备的广播
if (BluetoothRemoteDevice.EVENT_DEVICE_DISCOVERED.equals(action)) {
IntentParams myParam = info.getParams();
BluetoothRemoteDevice device = (BluetoothRemoteDevice) myParam.getParam(BluetoothRemoteDevice.REMOTE_DEVICE_PARAM_DEVICE);
}
}
}
三、传统蓝牙远端设备操作
① 应用场景
- 传统蓝牙远端管理操作主要是针对远端蓝牙设备的基本操作,包括获取远端蓝牙设备地址、类型、名称和配对状态,以及向远端设备发起配对。
② API 说明
- 蓝牙远端设备管理类 BluetoothRemoteDevice 的主要接口:
接口名 | 功能描述 |
getDeviceAddr() | 获取远端蓝牙设备地址 |
getDeviceClass() | 获取远端蓝牙设备类型 |
getDeviceName() | 获取远端蓝牙设备名称 |
getPairState() | 获取远端设备配对状态 |
startPair() | 向远端设备发起配对 |
③ 开发流程
- 调用 BluetoothHost 的 getDefaultHost(Context context) 接口,获取 BluetoothHost 实例,管理本机蓝牙操作。
- 调用 enableBt() 接口,打开蓝牙。
- 调用 startBtDiscovery(),扫描设备。
- 调用 startPair(),发起配对。
// 获取蓝牙本机管理对象
BluetoothHost bluetoothHost = BluetoothHost.getDefaultHost(context);
// 调用打开接口
bluetoothHost.enableBt();
// 调用扫描接口
bluetoothHost.startBtDiscovery();
//设置界面会显示出扫描结果列表,点击蓝牙设备去配对
BluetoothRemoteDevice device = bluetoothHost.getRemoteDev(TEST_ADDRESS);
device.startPair();
四、BLE 扫描和广播
① 应用场景
- 通过 BLE 扫描和广播提供的开放能力,可以根据指定状态获取外围设备、启动或停止 BLE 扫描、广播。
② API 说明
- BLE 中心设备管理类 BleCentralManager 的主要接口:
接口名 | 功能描述 |
startScan(List filters) | 进行BLE蓝牙扫描,并使用filters对结果进行过滤 |
stopScan() | 停止BLE蓝牙扫描 |
getDevicesByStates(int[] states) | 根据状态获取连接的外围设备 |
BleCentralManager(Context context, BleCentralManagerCallback callback) | 获取中心设备管理对象 |
- 中心设备管理回调类 BleCentralManagerCallback 的主要接口:
接口名 | 功能描述 |
scanResultEvent(BleScanResult result) | 扫描到BLE设备的结果回调 |
groupScanResultsEvent(List scanResults) | 扫描到一组BLE设备的结果回调 |
scanFailedEvent(int resultCode) | 启动扫描失败的回调 |
- BLE 广播相关的 BleAdvertiser 类和 BleAdvertiseCallback 类的主要接口:
接口名 | 功能描述 |
BleAdvertiser(Context context, BleAdvertiseCallback callback) | 用于获取广播操作对象 |
startAdvertising(BleAdvertiseSettings settings, BleAdvertiseData advData, BleAdvertiseData scanResponse) | 进行BLE广播,第一个参数为广播参数,第二个为广播数据,第三个参数是扫描和广播数据参数的响应 |
stopAdvertising() | 停止BLE广播 |
startResultEvent(int result) | 广播回调结果 |
③ 中心设备进行 BLE 扫描
- 进行 BLE 扫描之前先要继承 BleCentralManagerCallback 类实现 scanResultEvent 和 scanFailedEvent 回调函数,用于接收扫描结果。
- 调用 BleCentralManager(BleCentralManagerCallback callback) 接口获取中心设备管理对象。
- 获取扫描过滤器,过滤器为空时为不使用过滤器扫描,然后调用 startScan() 开始扫描 BLE 设备,在回调中获取扫描到的 BLE 设备。
// 实现扫描回调
public class ScanCallback implements BleCentralManagerCallback {
List<BleScanResult> results = new ArrayList<BleScanResult>();
@Override
public void scanResultEvent(BleScanResult resultCode) {
// 对扫描结果进行处理
results.add(resultCode);
}
@Override
public void scanFailedEvent(int resultCode) {
HiLog.warn(TAG,"Start Scan failed, Code: %{public}d", resultCode);
}
@Override
public void groupScanResultsEvent(final List<BleScanResult> scanResults){
// 对扫描结果进行处理
}
}
// 获取中心设备管理对象
private ScanCallback centralManagerCallback = new ScanCallback();
private BleCentralManager centralManager = new BleCentralManager(context, centralManagerCallback);
// 创建扫描过滤器然后开始扫描
List<BleScanFilter> filters = new ArrayList<BleScanFilter>();
centralManager.startScan(filters);
④ 外围设备进行 BLE 广播
- 进行 BLE 广播前需要先继承 advertiseCallback 类实现 startResultEvent 回调,用于获取广播结果。
- 调用接口 BleAdvertiser(Context context, BleAdvertiseCallback callback) 获取广播对象,构造广播参数和广播数据。
- 调用 startAdvertising(BleAdvertiseSettings settings, BleAdvertiseData advData, BleAdvertiseData scanResponse) 接口开始 BLE 广播。
// 实现BLE广播回调
private BleAdvertiseCallback advertiseCallback = new BleAdvertiseCallback() {
@Override
public void startResultEvent(int result) {
if(result == BleAdvertiseCallback.RESULT_SUCC){
// 开始BLE广播成功
}else {
// 开始BLE广播失败
}
}
};
// 获取BLE广播对象
private BleAdvertiser advertiser = new BleAdvertiser(this,advertiseCallback);
// 创建BLE广播参数和数据
private BleAdvertiseData data = new BleAdvertiseData.Builder()
.addServiceUuid(SequenceUuid.uuidFromString(Server_UUID)) // 添加服务的UUID
.addServiceData(SequenceUuid.uuidFromString(Server_UUID), new byte[]{0x11}) // 添加广播数据内容
.build();
private BleAdvertiseSettings advertiseSettings = new BleAdvertiseSettings.Builder()
.setConnectable(true) // 设置是否可连接广播
.setInterval(BleAdvertiseSettings.INTERVAL_SLOT_DEFAULT) // 设置广播间隔
.setTxPower(BleAdvertiseSettings.TX_POWER_DEFAULT) // 设置广播功率
.build();
// 开始广播
advertiser.startAdvertising(advertiseSettings, data, null);
五、BLE 中心设备与外围设备进行数据交互
① 应用场景
- BLE 外围设备和中心设备建立 GATT 连接,通过该连接中心设备可以获取外围设备支持的 Service、Characteristic、Descriptor、RSSI 等数据。同时,中心设备可以向外围设备进行数据请求,并向外围设备写入 Characteristic、Descriptor 等特征值数据。
② API 说明
- 低功耗蓝牙外围设备操作类 BlePeripheralDevice 的接口说明如下:
接口名 | 功能描述 |
connect(boolean isAutoConnect, BlePeripheraCallback callback) | 重新连接GATT外围设备,isAutoConnect表示是否自动进行连接 |
discoverServices() | 搜索外围设备支持的服务,特征和描述 |
getServices() | 获取外围设备支持的所有GATT服务 |
getService(UUID uuid) | 根据UUID获取外围设备支持的某个GATT服务 |
disconnect() | 与外围设备断开BLE连接 |
close() | 关闭蓝牙GATT客户端 |
readCharacteristic(GattCharacteristic characteristic) | 读取外围设备GATT特征 |
writeCharacteristic(GattCharacteristic characteristic) | 写指定外围设备的GATT特征值 |
setNotifyCharacteristic(GattCharacteristic characteristic, boolean enable) | 设置指定GATT特征通知的使能/去使能 |
readDescriptor (GattDescriptor descriptor) | 读取外围设备GATT描述值 |
writeDescriptor(GattDescriptor descriptor) | 写指定外围设备的GATT描述值 |
readRemoteRssiValue() | 读取已连接外围设备的RSSI |
requestBleConnectionPriority(int connPriority) | 请求链接参数更新 |
requestBleMtuSize(int mtu) | 请求用于给定连接的MTU大小 |
- 低功耗蓝牙外围设备操作回调类 BlePeripheralCallback 的接口说明如下:
接口名 | 功能描述 |
servicesDiscoveredEvent(int status) | 外围设备服务发生更新触发的回调 |
connectionStateChangedEvent(int connectionState) | 外围设备GATT连接状态发生变化时的回调 |
characteristicReadEvent(GattCharacteristic characteristic, int ret) | GATT特征值读操作回调 |
characteristicWriteEvent(GattCharacteristic characteristic, int ret) | GATT特征值写操作回调 |
characteristicChangedEvent(GattCharacteristic characteristic) | 外围设备特征通知触发的回调 |
descriptorReadEvent(GattDescriptor descriptor, int ret) | GATT描述值读操作回调 |
descriptorWriteEvent(GattDescriptor descriptor, int ret) | GATT描述值写操作回调 |
readRemoteRssiEvent(int rssi, int ret) | 外围设备发来读取RSSI的回调 |
mtuUpdateEvent(int mtu, int ret) | GATT设备链接的MTU变化通知的回调 |
③ 开发流程
- 调用 startScan() 接口启动 BLE 扫描来获取外围设备。
- 获取到外围设备后,调用 connect(boolean isAutoConnect, BlePeripheraCallback callback) 建立与外围 BLE 设备的 GATT 连接,boolean 参数 isAutoConnect 用于设置是否允许设备在可发现距离内自动建立 GATT 连接。
- 启动 GATT 连接后,会触发 connectionStateChangedEvent(int connectionState) 回调,根据回调结果判断是否连接 GATT 成功。
- 在 GATT 连接成功时,中心设备可以调用 discoverServices() 接口,获取外围设备支持的 Services、Characteristics 等特征值,在回调 servicesDiscoveredEvent(int status) 中获取外围设备支持的服务和特征值,并根据 UUID 判断是什么服务。
- 根据获取到的服务和特征值,调用 read 和 write 方法可以读取或者写入对应特征值数据。
private List<GattService> services;
// 创建扫描过滤器然后开始扫描
List<BleScanFilter> filters = new ArrayList<BleScanFilter>();
centralManager.startScan(filters);
// 与外围设备建立连接,允许自动回连,连接会触发connectionStateChangedEvent回调
MyBlePeripheralCallback callback = new MyBlePeripheralCallback();
peripheraDevice.connect(true, callback);
// 连接后读取外围设备RSSI值,获取后触发readRemoteRssiEvent()回调
peripheraDevice.readRemoteRssiValue();
// 实现外围设备操作回调
private class MyBlePeripheralCallback extends BlePeripheralCallback {
@Override
public void connectionStateChangedEvent(int connectionState) { // GATT连接状态变化回调
if (connectionState == ProfileBase.STATE_CONNECTED){
peripheraDevice.discoverServices(); // 连接成功获取外围设备的Service
}
}
@Override
public void servicesDiscoveredEvent(int status) { // 获取外围设备Service的回调
if (status == BlePeripheralDevice.OPERATION_SUCC){
services = peripheraDevice.getServices(); // 获取Service成功后获服务列表
for (GattService service : services){
// 对每个服务进行相应操作
}
}
}
@Override
public void characteristicChangedEvent(GattCharacteristic charecteristic) { // 外围设备主动向中心设备发送特征值通知时触发回调
// 根据通知的charecteristic获取特征值携带的数据
}
@Override
public void characteristicWriteEvent(GattCharacteristic charecteristic, int ret) {
if (ret == BlePeripheralDevice.OPERATION_SUCC){
// 向外围设备写特征值数据成功后的操作
}
}
@Override
public void characteristicReadEvent(GattCharacteristic charecteristic, int ret) {
if (ret == BlePeripheralDevice.OPERATION_SUCC){
// 向外围设备写特征值数据成功后的操作
}
}
@Override
public void descriptorReadEvent(GattDescriptor descriptor, int ret) {
// 向外围设备读描述值数据成功后的操作
}
@Override
public void descriptorWriteEvent(GattDescriptor descriptor, int ret) {
// 向外围设备写描述值数据成功后的操作
}
@Override
public void readRemoteRssiEvent(int rssi, int ret) {
if (ret == BlePeripheralDevice.OPERATION_SUCC){
// 读取外围设备RSSI值成功后的操作,对端RSSI值为rssi
}
}
}
六、BLE 外围设备数据管理
① 应用场景
- BLE 外围设备作为服务端,可以接收来自中心设备(客户端)的 GATT 连接请求,应答来自中心设备的特征值内容读取和写入请求,并向中心设备提供数据,从而实现信息交互和消息同步。同时外围设备还可以主动向中心设备发送数据。
② API 说明
- 低功耗蓝牙外围设备操作类 BlePeripheralManager 的接口说明如下:
接口名 | 功能描述 |
BlePeripheralManager(Context context, BlePeripheralManagerCallback callback, int transport) | 获取外围设备管理回调对象 |
getServices() | 获取外围设备的所有服务 |
addService(GattService service) | 将GATT服务加入服务端 |
cancelConnection(BlePeripheralDevice device) | 取消与中心设备的GATT连接 |
clearServices() | 删除所有的GATT服务 |
close() | 关闭GATT服务端 |
getDevicesByStates(int[] states) | 通过状态获取连接的中心设备列表 |
notifyCharacteristicChanged(BlePeripheralDevice device, GattCharacteristic characteristic, boolean confirm) | 通知中心设备特征值出现变化 |
removeGattService(GattService service) | 移除特定的服务 |
sendResponse(BlePeripheralDevice device, int requestId, int status, int offset, byte[] value) | 发送回复给中心设备 |
- 低功耗蓝牙外围设备管理回调类 BlePeripheralManagerCallback 的接口说明如下:
接口名 | 功能描述 |
receiveCharacteristicReadEvent(BlePeripheralDevice device, int requestId, int offset, GattCharacteristic characteristic) | 收到中心设备对特征值的读取请求回调 |
receiveCharacteristicWriteEvent(BlePeripheralDevice device, int requestId, GattCharacteristic characteristic, boolean isPrep, boolean needRsp, int offset, byte[] value) | 收到中心设备对特征值的写入请求 |
connectionStateChangeEvent(BlePeripheralDevice device, int interval, int latency, int timeout, int status) | 中心设备连接事件回调 |
receiveDescriptorReadEvent(BlePeripheralDevice device, int transId, int offset, GattDescriptor descriptor) | 收到中心设备对描述值的读取请求回调 |
receiveDescriptorWriteRequestEvent(BlePeripheralDevice device, int transId, GattDescriptor descriptor, boolean isPrep, boolean needRsp, int offset, byte[] value) | 收到中心设备对描述值的写入请求回调 |
executeWriteEvent(BlePeripheralDevice device, int requestId, boolean execute) | 向中心设备执行写入操作的回调 |
mtuUpdateEvent(BlePeripheralDevice device, int mtu) | 中心设备MTU值变化的回调 |
notificationSentEvent(BlePeripheralDevice device, int status) | 向中心设备发送通知的回调 |
serviceAddedEvent(int status, GattService service) | 向外围设备添加服务结果回调 |
③ 开发流程
- 调用 BlePeripheralManager(Context context, BlePeripheralManagerCallback callback, int transport) 接口创建外围设备服务端并开启服务。
- 调用 GattService(UUID uuid, boolean isPrimary) 接口创建服务对象,向外围设备添加服务。
- 从回调接口 onCharacteristicWriteRequest 中获取中心设备发送来的消息,调用 notifyCharacteristicChanged 接口向中心设备发送通知。
private BlePeripheralDevice blePeripheralDevice;
private MyBlePeripheralManagerCallback peripheralManagerCallback = new MyBlePeripheralManagerCallback();
// 创建外围设备服务端并开启服务
private BlePeripheralManager peripheralManager = new BlePeripheralManager(this, peripheralManagerCallback, 0);
// 创建服务对象,向外围设备添加服务
private GattService service = new GattService(UUID.fromString(Service_UUID), true);
private GattCharacteristic writeCharacteristic = new GattCharacteristic(Character_UUID,
GattCharacteristic.PROPERTY_WRITE, GattCharacteristic.PROPERTY_WRITE);
service.addCharacteristic(writeCharacteristic);
peripheralManager.addServerService(service);
// 向中心设备发送通知
writeCharacteristic.setValue(data);
peripheralManager.notifyCharacteristicChanged(blePeripheralDevice, writeCharacteristic, false);
// 外围设备管理回调
public class MyBlePeripheralManagerCallback extends BlePeripheralManagerCallback {
// 中心设备向外围设备写入数据回调
@Override
public void receiveCharacteristicWriteEvent(BlePeripheralDevice device, int requestId,
GattCharacteristic characteristic, boolean isPrep, boolean needRsp, int offset, byte[] value){
// 中心设备写入外围设备的数据为value,对数据进行处理
}
// 外围设备连接状态变化回调
@Override
public void connectionStateChangeEvent(BlePeripheralDevice device, int interval, int latency, int timeout, int status){
if (status == ProfileBase.STATE_CONNECTED) {
// 中心设备连接服务端成功
blePeripheralDevice = device;
}
}
// 向中心设备发送通知回调
@Override
public void notificationSentEvent(BlePeripheralDevice device, int status){
if (status == BlePeripheralDevice.OPERATION_SUCC) {
// 向对中心设备发送通知成功回调
}
}
}