一、蓝牙简介
① 概念
  • 蓝牙是短距离无线通信的一种方式,支持蓝牙的两个设备必须配对后才能通信。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) {
	            // 向对中心设备发送通知成功回调
	        }
	    }
	}