1. 介绍 BLE 是蓝牙低功耗的简称(Bluetooth Low Energy),生活中的很多场景都用到了BLE,如计步器、心率监视器、灯光控制、智能锁等。
BLE 设备间通信时会分为不同的角色:
- 中心设备和外围设备:中心设备负责扫描外围设备、发现广播。外围设备负责发送广播。
- GATT(Generic Attribute Profile,通用属性配置文件)服务端与 GATT 客户端:两台设备建立连接后,其中一台作为 GATT 服务端,另一台作为 GATT 客户端。
说明
使用BLE实现设备间通信,需要蓝牙版本在4.0或以上,而且,您需要确保蓝牙已启用。
本教程将结合以下内容讲解如何使用BLE实现设备间通信
2. 搭建HarmonyOS环境我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。
- 安装DevEco Studio,详情请参考下载和安装软件。
- 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
- 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
- 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
- 开发者可以参考以下链接,完成设备调试的相关配置:
3. 权限申请 在"entrysrcmainconfig.json"文件中的"reqPermissions"字段中声明以下3个权限:
- ohos.permission.USE_BLUETOOTH:允许应用查看蓝牙的配置。
- ohos.permission.DISCOVER_BLUETOOTH:允许应用配置本地蓝牙,并允许其查找远端设备且与之配对连接。
- ohos.permission.LOCATION:允许应用在前台运行时获取位置信息。
代码示例如下:
- "module": {
- ......
- "reqPermissions": [
- {
- "name": "ohos.permission.USE_BLUETOOTH"
- },
- {
- "name": "ohos.permission.DISCOVER_BLUETOOTH"
- },
- {
- "name": "ohos.permission.LOCATION",
- "reason": "$string:permreason_location",
- "usedScene": {
- "ability": [
- ".BleCentralAbility"
- ],
- "when": "inuse"
- }
- }
- ]
- }
复制代码此外,ohos.permission.LOCATION属于敏感权限,需要在代码中显式声明。代码示例如下:
- public class MainAbility extends Ability {
- [url=home.php?mod=space&uid=2735960]@Override[/url]
- public void onStart(Intent intent) {
- String[] permissions = {"ohos.permission.LOCATION"};
- requestPermissionsFromUser(permissions, 0);
- super.onStart(intent);
- }
- }
复制代码 4. BLE广播和扫描- 外围设备进行BLE广播需要做的准备工作有:实现BLE广播回调、获取BLE广播对象、创建广播数据和设置广播参数,然后调用startAdvertising()接口开始BLE广播。代码示例如下:
- // 实现BLE广播回调
- private class MyBleAdvertiseCallback extends BleAdvertiseCallback {
- @Override
- public void startResultEvent(int result) {
- if (result == BleAdvertiseCallback.RESULT_SUCC) {
- // 开始BLE广播成功
- }
- }
- }
-
- // 创建广播数据
- private BleAdvertiseData advertiseData = new BleAdvertiseData.Builder()
- .addServiceData(SequenceUuid.uuidFromString(SERVICE_UUID), "12".getBytes(StandardCharsets.UTF_8))
- .addServiceUuid(SequenceUuid.uuidFromString(SERVICE_UUID))
- .build();
-
- // 设置广播参数
- private BleAdvertiseSettings advertiseSettings = new BleAdvertiseSettings.Builder()
- .setConnectable(true)
- .setInterval(BleAdvertiseSettings.INTERVAL_SLOT_MIN)
- .setTxPower(BleAdvertiseSettings.TX_POWER_MAX)
- .build();
-
- // 获取BLE广播回调
- private MyBleAdvertiseCallback advertiseCallback = new MyBleAdvertiseCallback();
-
- // 获取BLE广播对象
- private BleAdvertiser advertiser = new BleAdvertiser(this, advertiseCallback);
-
- // 开始BLE广播
- advertiser.startAdvertising(advertiseSettings, advertiseData, null);
复制代码
- 中心设备进行BLE扫描需要做的准备工作有:实现中心设备管理回调、获取中心设备管理对象、创建扫描过滤器,然后调用startScan()开始扫描BLE设备。代码示例如下:
- // 实现中心设备管理回调
- private class MyBleCentralManagerCallback implements BleCentralManagerCallback {
- // 扫描结果的回调
- @Override
- public void scanResultEvent(BleScanResult bleScanResult) {
- // 根据扫描结果获取外围设备实例
- }
-
- // 扫描失败回调
- @Override
- public void scanFailedEvent(int i) {
- // 扫描失败
- }
-
- // 组扫描成功回调
- @Override
- public void groupScanResultsEvent(List<BleScanResult> list) {
- // 使用组扫描时在此对扫描结果进行处理
- }
- }
-
- // 获取中心设备管理回调
- private MyBleCentralManagerCallback centralManagerCallback = new MyBleCentralManagerCallback();
-
- // 获取中心设备管理对象
- private BleCentralManager centralManager = new BleCentralManager(this, centralManagerCallback);
-
- // 创建扫描过滤器
- private List<BleScanFilter> filters = new ArrayList<>();
-
- // 开始扫描带有过滤器的指定BLE设备
- centralManager.startScan(filters);
复制代码 5. GATT服务端、客户端的创建
- GATT服务端在BLE外围设备中创建,首先实现外围设备管理回调,其次获取外围设备管理对象,接着创建具有指定UUID的GattService、GattCharacteristic实例,最后在BLE广播成功后将GattService添加到外围设备管理对象中。代码示例如下:
- // 实现外围设备管理回调
- private class MyBlePeripheralManagerCallback extends BlePeripheralManagerCallback {
- // 连接状态变更的回调
- @Override
- public void connectionStateChangeEvent(
- BlePeripheralDevice device, int interval, int latency, int timeout, int status) {
- if (status == BlePeripheralDevice.OPERATION_SUCC) {
- // 连接成功
- }
- }
- // 远程GATT客户端已请求编写特征的回调
- @Override
- public void receiveCharacteristicWriteEvent(
- BlePeripheralDevice device,
- int transId,
- GattCharacteristic characteristic,
- boolean isPrep,
- boolean needRsp,
- int offset,
- byte[] value) {
- // 获取中心设备写入的数据
- }
- }
- // 获取外围设备管理回调
- private MyBlePeripheralManagerCallback peripheralManagerCallback = new MyBlePeripheralManagerCallback();
- // 获取外围设备管理对象
- private BlePeripheralManager blePeripheralManager = new BlePeripheralManager(this, peripheralManagerCallback, 1);
- // 创建具有指定UUID的GattService实例
- private GattService gattService = new GattService(UUID.fromString(SERVICE_UUID), true);
- // 创建第1个GattCharacteristic实例,用于向中心设备发送数据
- private GattCharacteristic notifyCharacteristic =
- new GattCharacteristic(
- UUID.fromString(NOTIFY_CHARACTER_UUID),
- 1 | GATT_CHARACTERISTIC_PERMISSIONS,
- GattCharacteristic.PROPERTY_READ
- | GattCharacteristic.PROPERTY_WRITE
- | GattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
- // 创建第2个GattCharacteristic实例,用于接收中心设备发送的数据
- private GattCharacteristic writeCharacteristic =
- new GattCharacteristic(
- UUID.fromString(WRITE_CHARACTER_UUID),
- 1 | GATT_CHARACTERISTIC_PERMISSIONS,
- GattCharacteristic.PROPERTY_READ
- | GattCharacteristic.PROPERTY_WRITE
- | GattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
- public void startResultEvent(int result) {
- if (result == BleAdvertiseCallback.RESULT_SUCC) {
- // 为GattService添加一个或多个特征
- gattService.addCharacteristic(notifyCharacteristic);
- gattService.addCharacteristic(writeCharacteristic);
- // 删除所有服务
- blePeripheralManager.clearServices();
- // 向外围设备管理对象添加GATT服务
- blePeripheralManager.addService(gattService);
- }
- }
复制代码 - GATT客户端在BLE中心设备中创建,需要实现外围设备操作回调。代码示例如下:
- // 实现外围设备操作回调
- private class MyBlePeripheralCallback extends BlePeripheralCallback {
- // 在外围设备上发现服务的回调
- @Override
- public void servicesDiscoveredEvent(int status) {
- super.servicesDiscoveredEvent(status);
- // 发现服务操作成功后启用特征通知并获取GattCharacteristic实例
- }
- // 连接状态变更的回调
- @Override
- public void connectionStateChangeEvent(int connectionState) {
- super.connectionStateChangeEvent(connectionState);
- // 连接成功在外围设备上发现GATT服务
- }
- // 特征变更的回调
- @Override
- public void characteristicChangedEvent(GattCharacteristic characteristic) {
- super.characteristicChangedEvent(characteristic);
- // 接收外围设备发送的数据
- }
- }
- // 获取外围设备操作回调
- private MyBlePeripheralCallback blePeripheralCallback = new MyBlePeripheralCallback();
复制代码
6. 建立GATT连接、发现服务- 中心设备扫描成功后,在scanResultEvent()回调中根据扫描结果获取广播数据中的SERVICE_UUID,再根据SERVICE_UUID来获取外围设备实例。代码示例如下:
- public void scanResultEvent(BleScanResult bleScanResult) {
- if (peripheralDevice == null) {
- // 获取广播数据中的服务uuids
- List<UUID> uuids = bleScanResult.getServiceUuids();
- for (UUID uuid : uuids) {
- if (SERVICE_UUID.equals(uuid.toString())) {
- // 根据扫描结果获取外围设备实例
- peripheralDevice = bleScanResult.getPeripheralDevice();
- }
- }
- }
- }
复制代码 - 中心设备获取外围设备实例后,调用connect()接口与外围设备建立GATT连接,连接成功后调用discoverServices()接口在BLE外围设备上发现GATT服务。代码示例如下:
- // 与外围设备建立Gatt连接
- peripheralDevice.connect(false, blePeripheralCallback);
- public void connectionStateChangeEvent(int connectionState) {
- super.connectionStateChangeEvent(connectionState);
- if (connectionState == ProfileBase.STATE_CONNECTED) {
- isConnected = true;
- // 连接成功在外围设备上发现GATT服务
- peripheralDevice.discoverServices();
- }
- }
复制代码 - 中心设备发现服务后触发servicesDiscoveredEvent()回调,如果操作成功则对服务中的特征进行UUID匹配。如果UUID等于NOTIFY_CHARACTER_UUID就调用setNotifyCharacteristic()启用特征通知;如果UUID等于WRITE_CHARACTER_UUID就获取该GattCharacteristic实例。代码示例如下:
- public void servicesDiscoveredEvent(int status) {
- super.servicesDiscoveredEvent(status);
- if (status == BlePeripheralDevice.OPERATION_SUCC) {
- for (GattService service : peripheralDevice.getServices()) {
- checkGattCharacteristic(service);
- }
- }
- }
- private void checkGattCharacteristic(GattService service) {
- for (GattCharacteristic tmpChara : service.getCharacteristics()) {
- if (tmpChara.getUuid().equals(UUID.fromString(NOTIFY_CHARACTER_UUID))) {
- // 启用特征通知
- peripheralDevice.setNotifyCharacteristic(tmpChara, true);
- }
- if (tmpChara.getUuid().equals(UUID.fromString(WRITE_CHARACTER_UUID))) {
- // 获取GattCharacteristic
- writeCharacteristic = tmpChara;
- }
- }
- }
复制代码 7. 设备间通信- 中心设备接收数据。启用特征通知后,当外围设备调用notifyCharacteristicChanged()接口通知特征更新时,中心设备会触发characteristicChangedEvent()回调,在回调中通过characteristic.getValue()可以获取到外围设备发送的数据。代码示例如下:
- public void characteristicChangedEvent(GattCharacteristic characteristic) {
- super.characteristicChangedEvent(characteristic);
- // 获取外围设备发送的数据:characteristic.getValue()
- }
复制代码 - 中心设备发送数据。获取到writeCharacteristic实例后,通过调用writeCharacteristic.setValue()接口将数据写入到writeCharacteristic中,然后调用peripheralDevice.writeCharacteristic()接口向外围设备写入特征。代码示例如下:
- // 向外围设备发送数据
- writeCharacteristic.setValue("我是中心设备".getBytes(StandardCharsets.UTF_8));
- peripheralDevice.writeCharacteristic(writeCharacteristic);
复制代码 - 外围设备接收数据。建立GATT连接后,当中心设备调用writeCharacteristic()接口写入特征时,外围设备会触发receiveCharacteristicWriteEvent()回调接收数据,其中参数byte[] value就是中心设备写入的数据。代码示例如下:
- public void receiveCharacteristicWriteEvent(
- BlePeripheralDevice device,
- int transId,
- GattCharacteristic characteristic,
- boolean isPrep,
- boolean needRsp,
- int offset,
- byte[] value) {
- // 获取中心设备写入的数据:value
- }
复制代码 - 外围设备发送数据。调用notifyCharacteristic.setValue()接口将数据写入notifyCharacteristic中,再调用notifyCharacteristicChanged()接口通知中心设备特征已更新。代码示例如下:
- // 向中心设备发送数据
- notifyCharacteristic.setValue("我是外围设备".getBytes(StandardCharsets.UTF_8));
- blePeripheralManager.notifyCharacteristicChanged(peripheralDevice, notifyCharacteristic, false);
复制代码
说明
BLE设备间通信对数据大小有限制,一次性传输的数据最大不超过20字节,超过部分将无法传输。
8. 最终实现效果- 外围设备(BlePeripheral)和中心设备(BleCentral)操作界面初始效果
- 外围设备开始BLE广播、中心设备开始BLE扫描
- 连接设备
- 外围设备发送数据、中心设备接收数据
- 中心设备发送数据、外围设备接收数据