[文章]使用Bluetooth Low Energy(低功耗蓝牙)实现设备间通信

阅读量0
0
4
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:允许应用在前台运行时获取位置信息。
代码示例如下:
  1. "module": {  
  2. ......  
  3. "reqPermissions": [
  4.       {
  5.         "name": "ohos.permission.USE_BLUETOOTH"
  6.       },
  7.       {
  8.         "name": "ohos.permission.DISCOVER_BLUETOOTH"
  9.       },
  10.       {
  11.         "name": "ohos.permission.LOCATION",
  12.         "reason": "$string:permreason_location",
  13.         "usedScene": {
  14.           "ability": [
  15.             ".BleCentralAbility"
  16.           ],
  17.           "when": "inuse"
  18.         }
  19.       }
  20.    ]
  21. }
复制代码
此外,ohos.permission.LOCATION属于敏感权限,需要在代码中显式声明。代码示例如下:

  1. public class MainAbility extends Ability {  
  2.     [url=home.php?mod=space&uid=2735960]@Override[/url]  
  3.     public void onStart(Intent intent) {  
  4.         String[] permissions = {"ohos.permission.LOCATION"};
  5.         requestPermissionsFromUser(permissions, 0);  
  6.         super.onStart(intent);   
  7.     }  
  8. }
复制代码
4. BLE广播和扫描
  • 外围设备进行BLE广播需要做的准备工作有:实现BLE广播回调、获取BLE广播对象、创建广播数据和设置广播参数,然后调用startAdvertising()接口开始BLE广播。代码示例如下:
    1. // 实现BLE广播回调
    2. private class MyBleAdvertiseCallback extends BleAdvertiseCallback {
    3. @Override
    4. public void startResultEvent(int result) {
    5.     if (result == BleAdvertiseCallback.RESULT_SUCC) {
    6.         // 开始BLE广播成功
    7.     }
    8. }
    9. }

    10. // 创建广播数据
    11. private BleAdvertiseData advertiseData = new BleAdvertiseData.Builder()
    12.     .addServiceData(SequenceUuid.uuidFromString(SERVICE_UUID), "12".getBytes(StandardCharsets.UTF_8))
    13.     .addServiceUuid(SequenceUuid.uuidFromString(SERVICE_UUID))
    14.     .build();

    15. // 设置广播参数
    16. private BleAdvertiseSettings advertiseSettings = new BleAdvertiseSettings.Builder()
    17.     .setConnectable(true)
    18.     .setInterval(BleAdvertiseSettings.INTERVAL_SLOT_MIN)
    19.     .setTxPower(BleAdvertiseSettings.TX_POWER_MAX)
    20.     .build();

    21. // 获取BLE广播回调
    22. private MyBleAdvertiseCallback advertiseCallback = new MyBleAdvertiseCallback();

    23. // 获取BLE广播对象
    24. private BleAdvertiser advertiser = new BleAdvertiser(this, advertiseCallback);

    25. // 开始BLE广播
    26. advertiser.startAdvertising(advertiseSettings, advertiseData, null);
    复制代码

  • 中心设备进行BLE扫描需要做的准备工作有:实现中心设备管理回调、获取中心设备管理对象、创建扫描过滤器,然后调用startScan()开始扫描BLE设备。代码示例如下:
    1. // 实现中心设备管理回调
    2. private class MyBleCentralManagerCallback implements BleCentralManagerCallback {
    3. // 扫描结果的回调
    4. @Override
    5. public void scanResultEvent(BleScanResult bleScanResult) {
    6.     // 根据扫描结果获取外围设备实例
    7. }

    8. // 扫描失败回调
    9. @Override
    10. public void scanFailedEvent(int i) {
    11.     // 扫描失败
    12. }

    13. // 组扫描成功回调
    14. @Override
    15. public void groupScanResultsEvent(List<BleScanResult> list) {
    16.     // 使用组扫描时在此对扫描结果进行处理
    17. }
    18. }

    19. // 获取中心设备管理回调
    20. private MyBleCentralManagerCallback centralManagerCallback = new MyBleCentralManagerCallback();

    21. // 获取中心设备管理对象
    22. private BleCentralManager centralManager = new BleCentralManager(this, centralManagerCallback);

    23. // 创建扫描过滤器
    24. private List<BleScanFilter> filters = new ArrayList<>();

    25. // 开始扫描带有过滤器的指定BLE设备
    26. centralManager.startScan(filters);
    复制代码
    5. GATT服务端、客户端的创建
    • GATT服务端在BLE外围设备中创建,首先实现外围设备管理回调,其次获取外围设备管理对象,接着创建具有指定UUID的GattService、GattCharacteristic实例,最后在BLE广播成功后将GattService添加到外围设备管理对象中。代码示例如下:
      1. // 实现外围设备管理回调
      2. private class MyBlePeripheralManagerCallback extends BlePeripheralManagerCallback {
      3.     // 连接状态变更的回调
      4.     @Override
      5.     public void connectionStateChangeEvent(
      6.             BlePeripheralDevice device, int interval, int latency, int timeout, int status) {
      7.         if (status == BlePeripheralDevice.OPERATION_SUCC) {
      8.             // 连接成功
      9.         }
      10.     }

      11.     // 远程GATT客户端已请求编写特征的回调
      12.     @Override
      13.     public void receiveCharacteristicWriteEvent(
      14.             BlePeripheralDevice device,
      15.             int transId,
      16.             GattCharacteristic characteristic,
      17.             boolean isPrep,
      18.             boolean needRsp,
      19.             int offset,
      20.             byte[] value) {
      21.         // 获取中心设备写入的数据
      22.     }
      23. }

      24. // 获取外围设备管理回调
      25. private MyBlePeripheralManagerCallback peripheralManagerCallback = new MyBlePeripheralManagerCallback();

      26. // 获取外围设备管理对象
      27. private BlePeripheralManager blePeripheralManager = new BlePeripheralManager(this, peripheralManagerCallback, 1);

      28. // 创建具有指定UUID的GattService实例
      29. private GattService gattService = new GattService(UUID.fromString(SERVICE_UUID), true);

      30. // 创建第1个GattCharacteristic实例,用于向中心设备发送数据
      31. private GattCharacteristic notifyCharacteristic =
      32.         new GattCharacteristic(
      33.                 UUID.fromString(NOTIFY_CHARACTER_UUID),
      34.                 1 | GATT_CHARACTERISTIC_PERMISSIONS,
      35.                 GattCharacteristic.PROPERTY_READ
      36.                         | GattCharacteristic.PROPERTY_WRITE
      37.                         | GattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);

      38. // 创建第2个GattCharacteristic实例,用于接收中心设备发送的数据
      39. private GattCharacteristic writeCharacteristic =
      40.         new GattCharacteristic(
      41.                 UUID.fromString(WRITE_CHARACTER_UUID),
      42.                 1 | GATT_CHARACTERISTIC_PERMISSIONS,
      43.                 GattCharacteristic.PROPERTY_READ
      44.                         | GattCharacteristic.PROPERTY_WRITE
      45.                         | GattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);

      46. public void startResultEvent(int result) {
      47.     if (result == BleAdvertiseCallback.RESULT_SUCC) {
      48.         // 为GattService添加一个或多个特征
      49.         gattService.addCharacteristic(notifyCharacteristic);
      50.         gattService.addCharacteristic(writeCharacteristic);

      51.         // 删除所有服务
      52.         blePeripheralManager.clearServices();

      53.         // 向外围设备管理对象添加GATT服务
      54.         blePeripheralManager.addService(gattService);
      55.     }
      56. }
      复制代码
    • GATT客户端在BLE中心设备中创建,需要实现外围设备操作回调。代码示例如下:

      1. // 实现外围设备操作回调
      2. private class MyBlePeripheralCallback extends BlePeripheralCallback {
      3.     // 在外围设备上发现服务的回调
      4.     @Override
      5.     public void servicesDiscoveredEvent(int status) {
      6.         super.servicesDiscoveredEvent(status);
      7.         // 发现服务操作成功后启用特征通知并获取GattCharacteristic实例
      8.     }

      9.     // 连接状态变更的回调
      10.     @Override
      11.     public void connectionStateChangeEvent(int connectionState) {
      12.         super.connectionStateChangeEvent(connectionState);
      13.         // 连接成功在外围设备上发现GATT服务
      14.     }

      15.     // 特征变更的回调
      16.     @Override
      17.     public void characteristicChangedEvent(GattCharacteristic characteristic) {
      18.         super.characteristicChangedEvent(characteristic);
      19.         // 接收外围设备发送的数据
      20.     }
      21. }

      22. // 获取外围设备操作回调
      23. private MyBlePeripheralCallback blePeripheralCallback = new MyBlePeripheralCallback();

      复制代码
    6. 建立GATT连接、发现服务
    • 中心设备扫描成功后,在scanResultEvent()回调中根据扫描结果获取广播数据中的SERVICE_UUID,再根据SERVICE_UUID来获取外围设备实例。代码示例如下:

      1. public void scanResultEvent(BleScanResult bleScanResult) {
      2.     if (peripheralDevice == null) {
      3.         // 获取广播数据中的服务uuids
      4.         List<UUID> uuids = bleScanResult.getServiceUuids();
      5.         for (UUID uuid : uuids) {
      6.             if (SERVICE_UUID.equals(uuid.toString())) {
      7.                 // 根据扫描结果获取外围设备实例
      8.                 peripheralDevice = bleScanResult.getPeripheralDevice();
      9.             }
      10.         }
      11.     }
      12. }
      复制代码
    • 中心设备获取外围设备实例后,调用connect()接口与外围设备建立GATT连接,连接成功后调用discoverServices()接口在BLE外围设备上发现GATT服务。代码示例如下:

      1. // 与外围设备建立Gatt连接
      2. peripheralDevice.connect(false, blePeripheralCallback);

      3. public void connectionStateChangeEvent(int connectionState) {
      4.     super.connectionStateChangeEvent(connectionState);
      5.     if (connectionState == ProfileBase.STATE_CONNECTED) {
      6.         isConnected = true;
      7.         // 连接成功在外围设备上发现GATT服务
      8.         peripheralDevice.discoverServices();
      9.     }
      10. }
      复制代码
    • 中心设备发现服务后触发servicesDiscoveredEvent()回调,如果操作成功则对服务中的特征进行UUID匹配。如果UUID等于NOTIFY_CHARACTER_UUID就调用setNotifyCharacteristic()启用特征通知;如果UUID等于WRITE_CHARACTER_UUID就获取该GattCharacteristic实例。代码示例如下:

    1. public void servicesDiscoveredEvent(int status) {
    2.     super.servicesDiscoveredEvent(status);
    3.     if (status == BlePeripheralDevice.OPERATION_SUCC) {
    4.         for (GattService service : peripheralDevice.getServices()) {
    5.             checkGattCharacteristic(service);
    6.         }
    7.     }
    8. }

    9. private void checkGattCharacteristic(GattService service) {
    10.     for (GattCharacteristic tmpChara : service.getCharacteristics()) {
    11.         if (tmpChara.getUuid().equals(UUID.fromString(NOTIFY_CHARACTER_UUID))) {
    12.             // 启用特征通知
    13.             peripheralDevice.setNotifyCharacteristic(tmpChara, true);
    14.         }

    15.         if (tmpChara.getUuid().equals(UUID.fromString(WRITE_CHARACTER_UUID))) {
    16.             // 获取GattCharacteristic
    17.             writeCharacteristic = tmpChara;
    18.         }
    19.     }
    20. }
    复制代码
    7. 设备间通信
    • 中心设备接收数据。启用特征通知后,当外围设备调用notifyCharacteristicChanged()接口通知特征更新时,中心设备会触发characteristicChangedEvent()回调,在回调中通过characteristic.getValue()可以获取到外围设备发送的数据。代码示例如下:

      1. public void characteristicChangedEvent(GattCharacteristic characteristic) {
      2.     super.characteristicChangedEvent(characteristic);
      3.     // 获取外围设备发送的数据:characteristic.getValue()
      4. }
      复制代码
    • 中心设备发送数据。获取到writeCharacteristic实例后,通过调用writeCharacteristic.setValue()接口将数据写入到writeCharacteristic中,然后调用peripheralDevice.writeCharacteristic()接口向外围设备写入特征。代码示例如下:

      1. // 向外围设备发送数据
      2. writeCharacteristic.setValue("我是中心设备".getBytes(StandardCharsets.UTF_8));
      3. peripheralDevice.writeCharacteristic(writeCharacteristic);
      复制代码
    • 外围设备接收数据。建立GATT连接后,当中心设备调用writeCharacteristic()接口写入特征时,外围设备会触发receiveCharacteristicWriteEvent()回调接收数据,其中参数byte[] value就是中心设备写入的数据。代码示例如下:
      1. public void receiveCharacteristicWriteEvent(
      2.     BlePeripheralDevice device,
      3.     int transId,
      4.     GattCharacteristic characteristic,
      5.     boolean isPrep,
      6.     boolean needRsp,
      7.     int offset,
      8.     byte[] value) {
      9. // 获取中心设备写入的数据:value
      10. }
      复制代码
    • 外围设备发送数据。调用notifyCharacteristic.setValue()接口将数据写入notifyCharacteristic中,再调用notifyCharacteristicChanged()接口通知中心设备特征已更新。代码示例如下:
      1. // 向中心设备发送数据            
      2. notifyCharacteristic.setValue("我是外围设备".getBytes(StandardCharsets.UTF_8));     
      3. blePeripheralManager.notifyCharacteristicChanged(peripheralDevice, notifyCharacteristic, false);
      复制代码

    说明
    BLE设备间通信对数据大小有限制,一次性传输的数据最大不超过20字节,超过部分将无法传输。
8. 最终实现效果
  • 外围设备(BlePeripheral)和中心设备(BleCentral)操作界面初始效果
  • 外围设备开始BLE广播、中心设备开始BLE扫描
  • 连接设备
  • 外围设备发送数据、中心设备接收数据
  • 中心设备发送数据、外围设备接收数据




完整示例代码.pdf
(181.47 KB, 下载次数: 4)


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友