如何使用BLE实现设备间的通信

发布于 2021-6-21 11:23
浏览
1收藏

1. 介绍

BLE 是蓝牙低功耗的简称(Bluetooth Low Energy),生活中的很多场景都用到了BLE,如计步器、心率监视器、灯光控制、智能锁等。
BLE 设备间通信时会分为不同的角色:

  • 中心设备和外围设备:中心设备负责扫描外围设备、发现广播。外围设备负责发送广播。
  • GATT(Generic Attribute Profile,通用属性配置文件)服务端与 GATT 客户端:两台设备建立连接后,其中一台作为 GATT 服务端,另一台作为 GATT 客户端。

说明
使用BLE实现设备间通信,需要蓝牙版本在4.0或以上,而且,您需要确保蓝牙已启用。

本教程将结合以下内容讲解如何使用BLE实现设备间通信:

  1. 权限申请
  2. BLE广播和扫描
  3. GATT服务端、客户端的创建
  4. 建立GATT连接、发现服务
  5. 设备间通信

2. 权限申请

在"entry\src\main\config.json"文件中的"reqPermissions"字段中声明以下3个权限:

  1. ohos.permission.USE_BLUETOOTH:允许应用查看蓝牙的配置。
  2. ohos.permission.DISCOVER_BLUETOOTH:允许应用配置本地蓝牙,并允许其查找远端设备且与之配对连接。
  3. 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 {  
    @Override  
    public void onStart(Intent intent) {  
        String[] permissions = {"ohos.permission.LOCATION"}; 
        requestPermissionsFromUser(permissions, 0);  
        super.onStart(intent);   
    }  
}

3. BLE广播和扫描

  1. 外围设备进行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()) 
       .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);
  1. 中心设备进行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 list) { 
           // 使用组扫描时在此对扫描结果进行处理 
       } 
   } 
    
   // 获取中心设备管理回调 
   private MyBleCentralManagerCallback centralManagerCallback = new MyBleCentralManagerCallback(); 
    
   // 获取中心设备管理对象 
   private BleCentralManager centralManager = new BleCentralManager(this, centralManagerCallback); 
    
   // 创建扫描过滤器 
   private List filters = new ArrayList<>(); 
    
   // 开始扫描带有过滤器的指定BLE设备 
   centralManager.startScan(filters);

4. GATT服务端、客户端的创建

  1. 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 | 16, 
                   GattCharacteristic.PROPERTY_READ 
                           | GattCharacteristic.PROPERTY_WRITE 
                           | GattCharacteristic.PROPERTY_WRITE_NO_RESPONSE); 
    
   // 创建第2个GattCharacteristic实例,用于接收中心设备发送的数据 
   private GattCharacteristic writeCharacteristic = 
           new GattCharacteristic( 
                   UUID.fromString(WRITE_CHARACTER_UUID), 
                   1 | 16, 
                   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); 
       } 
   }
  1. 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();

5. 建立GATT连接、发现服务

  1. 中心设备扫描成功后,在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(); 
               } 
           } 
       } 
   }
  1. 中心设备获取外围设备实例后,调用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(); 
       } 
   }
  1. 中心设备发现服务后触发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; 
           } 
       } 
   }

6. 设备间通信

  1. 中心设备接收数据。启用特征通知后,当外围设备调用notifyCharacteristicChanged()接口通知特征更新时,中心设备会触发characteristicChangedEvent()回调,在回调中通过characteristic.getValue()可以获取到外围设备发送的数据。代码示例如下:
public void characteristicChangedEvent(GattCharacteristic characteristic) { 
    super.characteristicChangedEvent(characteristic); 
    // 获取外围设备发送的数据:characteristic.getValue() 
}


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

说明
BLE设备间通信对数据大小有限制,一次性传输的数据最大不超过20字节,超过部分将无法传输。

7. 最终实现效果

  1. 外围设备(BlePeripheral)和中心设备(BleCentral)操作界面初始效果
    如何使用BLE实现设备间的通信-开源基础软件社区
    如何使用BLE实现设备间的通信-开源基础软件社区

  2. 外围设备开始BLE广播、中心设备开始BLE扫描
    如何使用BLE实现设备间的通信-开源基础软件社区

如何使用BLE实现设备间的通信-开源基础软件社区
3. 连接设备
如何使用BLE实现设备间的通信-开源基础软件社区

如何使用BLE实现设备间的通信-开源基础软件社区
4. 外围设备发送数据、中心设备接收数据
如何使用BLE实现设备间的通信-开源基础软件社区

如何使用BLE实现设备间的通信-开源基础软件社区
5. 中心设备发送数据、外围设备接收数据
如何使用BLE实现设备间的通信-开源基础软件社区

如何使用BLE实现设备间的通信-开源基础软件社区

8. 完整示例代码

代码结构解读

为了方便您的学习,我们提供了的BLE示例工程的完整代码,代码的工程结构如下:

  • slice:应用页面
    • BleCentralAbilitySlice是中心设备操作页面。
    • BlePeripheralAbilitySlice是外围设备操作页面。
    • MainAbilitySlice是应用起始页面,提供中心、外围设备操作界面入口。
  • BleCentralAbility、BlePeripheralAbility、MainAbility、MyApplication:DevEco Studio生成,在MainAbility中对敏感权限进行显式声明,其他文件无需变更。
  • resources:存放工程使用到的资源文件
    • resources\base\graphic:存放textfield_element.xml用于设置输入框样式。
    • resources\base\layout:布局文件
      ability_main.xml用于展示页面选择按钮。
      ability_ble_central.xml用于展示中心设备操作按钮、文本、输入框。
      ability_ble_peripheral.xml用于展示外围设备操作按钮、文本、输入框。

如何使用BLE实现设备间的通信-开源基础软件社区

编写布局与样式

首先在HUAWEI DevEco Studio创建一个Phone的Empty Feature Ability(Java)模板工程,然后新建名称为BleCentralAbility和BlePeripheralAbility的Empty Page Ability(Java)。

  1. 修改ability_main.xml布局内容,主要包含2个按钮,用于实现页面跳转。代码如下:
<?xml version="1.0" encoding="utf-8"?> 
<DirectionalLayout 
    xmlns:ohos="http://schemas.huawei.com/res/ohos" 
    ohos:height="match_parent" 
    ohos:width="match_parent" 
    ohos:alignment="center" 
    ohos:orientation="vertical"> 
 
    <Button 
        ohos:id="$+id:ble_central" 
        ohos:height="50vp" 
        ohos:width="match_parent" 
        ohos:background_element="#33A1C9" 
        ohos:layout_alignment="center" 
        ohos:margin="20vp" 
        ohos:text="中心设备操作界面" 
        ohos:text_size="18fp"/> 
 
    <Button 
        ohos:id="$+id:ble_peripheral" 
        ohos:height="50vp" 
        ohos:width="match_parent" 
        ohos:background_element="#33A1C9" 
        ohos:layout_alignment="center" 
        ohos:margin="20vp" 
        ohos:text="外围设备操作界面" 
        ohos:text_size="18fp"/> 
</DirectionalLayout>

  1. 修改ability_ble_central.xml布局内容,页面首行绘制一个Text展示扫描到的目标设备,Text右侧绘制一个Button用于开始扫描或停止扫描。第二行绘制一个Text展示当前设备是否与外围设备连接,Text右侧绘制一个Button用于连接设备或断开连接。第三行绘制一个TextField用于输入数据内容,TextField右侧绘制一个Button用于发送数据。最下方区域用于展示外围设备发送的数据。代码如下:
 <?xml version="1.0" encoding="utf-8"?> 
<DirectionalLayout 
    xmlns:ohos="http://schemas.huawei.com/res/ohos" 
    ohos:height="match_parent" 
    ohos:width="match_parent" 
    ohos:background_element="#F0FFFF" 
    ohos:orientation="vertical"> 
 
    <DirectionalLayout 
        ohos:height="60vp" 
        ohos:width="match_parent" 
        ohos:orientation="horizontal"> 
 
        <Text 
            ohos:id="$+id:device_info" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:margin="8vp" 
            ohos:text="设备:暂无设备" 
            ohos:text_alignment="left" 
            ohos:text_size="18fp" 
            ohos:weight="3"/> 
 
        <Button 
            ohos:id="$+id:scan" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:background_element="#33A1C9" 
            ohos:margin="5vp" 
            ohos:text="开始扫描" 
            ohos:text_alignment="center" 
            ohos:text_size="18fp" 
            ohos:weight="1"/> 
    </DirectionalLayout> 
 
    <DirectionalLayout 
        ohos:height="60vp" 
        ohos:width="match_parent" 
        ohos:orientation="horizontal"> 
 
        <Text 
            ohos:id="$+id:status" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:margin="8vp" 
            ohos:text="状态:未连接" 
            ohos:text_alignment="left" 
            ohos:text_size="18fp" 
            ohos:weight="3"/> 
 
        <Button 
            ohos:id="$+id:connect" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:background_element="#33A1C9" 
            ohos:margin="5vp" 
            ohos:text="连接设备" 
            ohos:text_alignment="center" 
            ohos:text_size="18fp" 
            ohos:weight="1"/> 
    </DirectionalLayout> 
 
    <DirectionalLayout 
        ohos:height="60vp" 
        ohos:width="match_parent" 
        ohos:orientation="horizontal"> 
 
        <TextField 
            ohos:id="$+id:input" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:background_element="$graphic:textfield_element" 
            ohos:hint="请输入" 
            ohos:margin="8vp" 
            ohos:text_alignment="vertical_center|left" 
            ohos:text_size="18fp" 
            ohos:weight="3"/> 
 
        <Button 
            ohos:id="$+id:send" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:background_element="#33A1C9" 
            ohos:margin="5vp" 
            ohos:text="发送数据" 
            ohos:text_alignment="center" 
            ohos:text_size="18fp" 
            ohos:weight="1"/> 
    </DirectionalLayout> 
 
    <DirectionalLayout 
        ohos:height="400vp" 
        ohos:width="match_parent" 
        ohos:background_element="#FFFFCD" 
        ohos:margin="10vp" 
        ohos:orientation="vertical"> 
 
        <Text 
            ohos:height="40vp" 
            ohos:width="match_parent" 
            ohos:text="外围设备:" 
            ohos:text_size="18fp"/> 
 
        <Text 
            ohos:id="$+id:data" 
            ohos:height="320vp" 
            ohos:width="match_parent" 
            ohos:text_alignment="top|left" 
            ohos:text_size="18fp"/> 
    </DirectionalLayout> 
</DirectionalLayout>


  1. 修改ability_ble_peripheral.xml布局内容,页面首行绘制一个Text展示当前设备是否开启广播或者是否与中心设备连接,Text右侧绘制一个Button用于开始广播或停止广播。第二行绘制一个TextField用于输入数据内容,TextField右侧绘制一个Button用于发送数据。最下方区域用于展示中心设备发送的数据。代码如下:
   <?xml version="1.0" encoding="utf-8"?> 
<DirectionalLayout 
    xmlns:ohos="http://schemas.huawei.com/res/ohos" 
    ohos:height="match_parent" 
    ohos:width="match_parent" 
    ohos:background_element="#F0FFFF" 
    ohos:orientation="vertical"> 
 
    <DirectionalLayout 
        ohos:height="50vp" 
        ohos:width="match_parent" 
        ohos:orientation="horizontal"> 
 
        <Text 
            ohos:id="$+id:status" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:margin="8vp" 
            ohos:text="状态:未开始广播" 
            ohos:text_alignment="left" 
            ohos:text_size="18fp" 
            ohos:weight="3"/> 
 
        <Button 
            ohos:id="$+id:advertise" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:background_element="#33A1C9" 
            ohos:margin="5vp" 
            ohos:text="开始广播" 
            ohos:text_alignment="center" 
            ohos:text_size="18fp" 
            ohos:weight="1"/> 
    </DirectionalLayout> 
 
    <DirectionalLayout 
        ohos:height="50vp" 
        ohos:width="match_parent" 
        ohos:orientation="horizontal"> 
 
        <TextField 
            ohos:id="$+id:input" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:background_element="$graphic:textfield_element" 
            ohos:hint="请输入" 
            ohos:margin="8vp" 
            ohos:text_alignment="vertical_center|left" 
            ohos:text_size="18fp" 
            ohos:weight="3"/> 
 
        <Button 
            ohos:id="$+id:send" 
            ohos:height="match_parent" 
            ohos:width="0vp" 
            ohos:background_element="#33A1C9" 
            ohos:margin="5vp" 
            ohos:text="发送数据" 
            ohos:text_alignment="center" 
            ohos:text_size="18fp" 
            ohos:weight="1"/> 
    </DirectionalLayout> 
 
    <DirectionalLayout 
        ohos:height="400vp" 
        ohos:width="match_parent" 
        ohos:background_element="#FFFFCD" 
        ohos:margin="10vp" 
        ohos:orientation="vertical"> 
 
        <Text 
            ohos:height="40vp" 
            ohos:width="match_parent" 
            ohos:text="中心设备:" 
            ohos:text_size="18fp"/> 
 
        <Text 
            ohos:id="$+id:data" 
            ohos:height="320vp" 
            ohos:width="match_parent" 
            ohos:text_alignment="top|left" 
            ohos:text_size="18fp"/> 
    </DirectionalLayout> 
</DirectionalLayout>
  1. 创建textfield_element.xml用来定义TextField背景属性。代码如下:
 <?xml version="1.0" encoding="utf-8"?> 
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos" 
       ohos:shape="rectangle"> 
    <corners 
        ohos:radius="50"/> 
    <solid 
        ohos:color="#D1FCFF"/> 
</shape>

业务逻辑代码

  1. 在MainAbility类中对敏感权限ohos.permission.LOCATION进行显式声明。代码如下:
 public class MainAbility extends Ability { 
    @Override 
    public void onStart(Intent intent) { 
        String[] permissions = {"ohos.permission.LOCATION"}; 
        requestPermissionsFromUser(permissions, 0); 
        super.onStart(intent); 
        super.setMainRoute(MainAbilitySlice.class.getName()); 
    } 
}
  1. 在MainAbilitySlice类中开启蓝牙并实现点击按钮跳转到"中心设备操作界面"和"外围设备操作界面"。代码如下:
  public class MainAbilitySlice extends AbilitySlice { 
    // BUNDLE_NAME、ABILITY_NAME开发者需要替换为自己项目对应的名称 
    private static final String BUNDLE_NAME = "com.huawei.blebluetooth"; 
    private static final String CENTRAL_ABILITY_NAME = "com.huawei.blebluetooth.BleCentralAbility"; 
    private static final String PERIPHERAL_ABILITY_NAME = "com.huawei.blebluetooth.BlePeripheralAbility"; 
 
    // 获取 BluetoothHost 实例,管理本机蓝牙操作 
    private BluetoothHost bluetooth = BluetoothHost.getDefaultHost(this); 
 
    @Override 
    public void onStart(Intent intent) { 
        super.onStart(intent); 
        super.setUIContent(ResourceTable.Layout_ability_main); 
        initComponent(); 
 
        if (bluetooth.getBtState() != BluetoothHost.STATE_ON) { 
            bluetooth.enableBt(); 
        } 
    } 
 
    private void initComponent() { 
        findComponentById(ResourceTable.Id_ble_central).setClickedListener(new Component.ClickedListener() { 
            @Override 
            public void onClick(Component component) { 
                Intent intent = new Intent(); 
                Operation operation = new Intent.OperationBuilder() 
                        .withDeviceId("") 
                        .withBundleName(BUNDLE_NAME) 
                        .withAbilityName(CENTRAL_ABILITY_NAME) 
                        .build(); 
                intent.setOperation(operation); 
                startAbility(intent); 
            } 
        }); 
 
        findComponentById(ResourceTable.Id_ble_peripheral).setClickedListener(new Component.ClickedListener() { 
            @Override 
            public void onClick(Component component) { 
                Intent intent = new Intent(); 
                Operation operation = new Intent.OperationBuilder() 
                        .withDeviceId("") 
                        .withBundleName(BUNDLE_NAME) 
                        .withAbilityName(PERIPHERAL_ABILITY_NAME) 
                        .build(); 
                intent.setOperation(operation); 
                startAbility(intent); 
            } 
        }); 
    } 
}


  1. 在BleCentralAbilitySlice类中实现中心设备BLE扫描、设备连接、数据发送、数据接收等功能。代码如下:
public class BleCentralAbilitySlice extends AbilitySlice { 
    private static final String SERVICE_UUID = "00001887-0000-1000-8000-00805f9b34fb"; 
    private static final String NOTIFY_CHARACTER_UUID = "00002a10-0000-1000-8000-00805f9b34fb"; 
    private static final String WRITE_CHARACTER_UUID = "00002a11-0000-1000-8000-00805f9b34fb"; 
    private BlePeripheralDevice peripheralDevice = null; 
    private GattCharacteristic writeCharacteristic; 
    private boolean isConnected = false; 
    private boolean isScanning = false; 
    private Text deviceText; 
    private Text statusText; 
    private TextField field; 
    private Text dataText; 
    private Button scanButton; 
    private Button connectButton; 
    private Button sendButton; 
 
    // 实现外围设备操作回调 
    private class MyBlePeripheralCallback extends BlePeripheralCallback { 
        // 在外围设备上发现服务的回调 
        @Override 
        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; 
                } 
            } 
        } 
 
        // 连接状态变更的回调 
        @Override 
        public void connectionStateChangeEvent(int connectionState) { 
            super.connectionStateChangeEvent(connectionState); 
 
            if (connectionState == ProfileBase.STATE_CONNECTED && !isConnected) { 
                isConnected = true; 
                // 连接成功在外围设备上发现GATT服务 
                peripheralDevice.discoverServices(); 
                updateComponent(statusText, "状态:已连接"); 
            } 
        } 
 
        // 特征变更的回调 
        @Override 
        public void characteristicChangedEvent(GattCharacteristic characteristic) { 
            super.characteristicChangedEvent(characteristic); 
            // 接收外围设备发送的数据 
            updateComponent(dataText, new String(characteristic.getValue())); 
        } 
    } 
 
    // 获取外围设备操作回调 
    private MyBlePeripheralCallback blePeripheralCallback = new MyBlePeripheralCallback(); 
 
    // 实现中心设备管理回调 
    private class MyBleCentralManagerCallback implements BleCentralManagerCallback { 
        // 扫描结果的回调 
        @Override 
        public void scanResultEvent(BleScanResult bleScanResult) { 
            // 根据扫描结果获取外围设备实例 
            if (peripheralDevice == null) { 
                // 获取广播数据中的服务uuids 
                List uuids = bleScanResult.getServiceUuids(); 
                for (UUID uuid : uuids) { 
                    if (SERVICE_UUID.equals(uuid.toString())) { 
                        peripheralDevice = bleScanResult.getPeripheralDevice(); 
                        int length = peripheralDevice.toString().length(); 
                        String deviceId = peripheralDevice.toString().substring(length - 7, length); 
                        updateComponent(deviceText, "设备:" + deviceId); 
                    } 
                } 
            } 
        } 
 
        // 扫描失败回调 
        @Override 
        public void scanFailedEvent(int i) { 
            updateComponent(deviceText, "设备:扫描失败,请重新扫描!"); 
        } 
 
        // 组扫描成功回调 
        @Override 
        public void groupScanResultsEvent(List list) { 
            // 使用组扫描时在此对扫描结果进行处理 
        } 
    } 
 
    // 获取中心设备管理回调 
    private MyBleCentralManagerCallback centralManagerCallback = new MyBleCentralManagerCallback(); 
 
    // 获取中心设备管理对象 
    private BleCentralManager centralManager = new BleCentralManager(this, centralManagerCallback); 
 
    // 创建扫描过滤器 
    private List filters = new ArrayList<>(); 
 
    @Override 
    public void onStart(Intent intent) { 
        super.onStart(intent); 
        super.setUIContent(ResourceTable.Layout_ability_ble_central); 
        initComponent(); 
        initClickedListener(); 
    } 
 
    // 初始化组件 
    private void initComponent() { 
        if (findComponentById(ResourceTable.Id_device_info) instanceof Text) { 
            deviceText = (Text) findComponentById(ResourceTable.Id_device_info); 
        } 
        if (findComponentById(ResourceTable.Id_status) instanceof Text) { 
            statusText = (Text) findComponentById(ResourceTable.Id_status); 
        } 
        if (findComponentById(ResourceTable.Id_data) instanceof Text) { 
            dataText = (Text) findComponentById(ResourceTable.Id_data); 
        } 
        if (findComponentById(ResourceTable.Id_input) instanceof TextField) { 
            field = (TextField) findComponentById(ResourceTable.Id_input); 
        } 
        if (findComponentById(ResourceTable.Id_scan) instanceof Button) { 
            scanButton = (Button) findComponentById(ResourceTable.Id_scan); 
        } 
        if (findComponentById(ResourceTable.Id_connect) instanceof Button) { 
            connectButton = (Button) findComponentById(ResourceTable.Id_connect); 
        } 
        if (findComponentById(ResourceTable.Id_send) instanceof Button) { 
            sendButton = (Button) findComponentById(ResourceTable.Id_send); 
        } 
    } 
 
    private void updateComponent(Text text, String content) { 
        getUITaskDispatcher().syncDispatch(new Runnable() { 
            @Override 
            public void run() { 
                text.setText(content); 
            } 
        }); 
    } 
 
    // 初始化点击回调 
    private void initClickedListener() { 
        scanButton.setClickedListener(new Component.ClickedListener() { 
            @Override 
            public void onClick(Component component) { 
                if (!isScanning) { 
                    isScanning = true; 
                    scanButton.setText("停止扫描"); 
                    deviceText.setText("设备:正在扫描..."); 
                    // 开始扫描带有过滤器的指定BLE设备 
                    centralManager.startScan(filters); 
                } else { 
                    isScanning = false; 
                    scanButton.setText("开始扫描"); 
                    deviceText.setText("设备:暂无设备"); 
                    // 停止扫描 
                    centralManager.stopScan(); 
                } 
            } 
        }); 
 
        connectButton.setClickedListener(new Component.ClickedListener() { 
            @Override 
            public void onClick(Component component) { 
                if (peripheralDevice == null) { 
                    statusText.setText("状态:请先扫描获取设备信息"); 
                    return; 
                } 
 
                if (!isConnected) { 
                    connectButton.setText("断开连接"); 
                    statusText.setText("状态:连接中..."); 
                    // 连接到BLE外围设备 
                    peripheralDevice.connect(false, blePeripheralCallback); 
                } else { 
                    isConnected = false; 
                    connectButton.setText("连接设备"); 
                    statusText.setText("状态:未连接"); 
                    deviceText.setText("设备:暂无设备"); 
                    writeCharacteristic.setValue("Disconnect".getBytes()); 
                    peripheralDevice.writeCharacteristic(writeCharacteristic); 
                    // 断开连接 
                    peripheralDevice.disconnect(); 
                    peripheralDevice = null; 
                } 
            } 
        }); 
 
        sendButton.setClickedListener(new Component.ClickedListener() { 
            @Override 
            public void onClick(Component component) { 
                if (field.getText().isEmpty() || (peripheralDevice == null) || !isConnected) { 
                    return; 
                } 
                // 向外围设备发送数据 
                writeCharacteristic.setValue(field.getText().getBytes()); 
                peripheralDevice.writeCharacteristic(writeCharacteristic); 
            } 
        }); 
    } 
}


  1. 在BlePeripheralAbilitySlice类中实现外围设备BLE广播、数据发送、数据接收等功能。代码如下:
   public class BlePeripheralAbilitySlice extends AbilitySlice { 
       private static final String SERVICE_UUID = "00001887-0000-1000-8000-00805f9b34fb"; 
       private static final String NOTIFY_CHARACTER_UUID = "00002a10-0000-1000-8000-00805f9b34fb"; 
       private static final String WRITE_CHARACTER_UUID = "00002a11-0000-1000-8000-00805f9b34fb"; 
       private BlePeripheralDevice peripheralDevice = null; 
       private boolean isAdvertising = false; 
       private boolean isConnected = false; 
       private Text statusText; 
       private TextField field; 
       private Text dataText; 
       private Button advertiseButton; 
       private Button sendButton; 
    
       // 实现外围设备管理回调 
       private class MyBlePeripheralManagerCallback extends BlePeripheralManagerCallback { 
           // 连接状态变更的回调 
           @Override 
           public void connectionStateChangeEvent( 
                   BlePeripheralDevice device, int interval, int latency, int timeout, int status) { 
               if (status == BlePeripheralDevice.OPERATION_SUCC && !isConnected) { 
                   isConnected = true; 
                   peripheralDevice = device; 
                   updateComponent(statusText, "状态:已连接"); 
               } 
           } 
    
           // 远程GATT客户端已请求编写特征的回调 
           @Override 
           public void receiveCharacteristicWriteEvent( 
                   BlePeripheralDevice device, 
                   int transId, 
                   GattCharacteristic characteristic, 
                   boolean isPrep, 
                   boolean needRsp, 
                   int offset, 
                   byte[] value) { 
               if (Arrays.equals("Disconnect".getBytes(), value)) { 
                   isConnected = false; 
                   peripheralDevice = null; 
                   updateComponent(statusText, "状态:已广播,等待连接"); 
                   return; 
               } 
    
               // 接收中心设备写入的数据 
               updateComponent(dataText, new String(value, Charset.defaultCharset())); 
           } 
       } 
    
       // 获取外围设备管理回调 
       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 | 16, 
                       GattCharacteristic.PROPERTY_READ 
                               | GattCharacteristic.PROPERTY_WRITE 
                               | GattCharacteristic.PROPERTY_WRITE_NO_RESPONSE); 
    
       // 创建第2个GattCharacteristic实例,用于接收中心设备发送的数据 
       private GattCharacteristic writeCharacteristic = 
               new GattCharacteristic( 
                       UUID.fromString(WRITE_CHARACTER_UUID), 
                       1 | 16, 
                       GattCharacteristic.PROPERTY_READ 
                               | GattCharacteristic.PROPERTY_WRITE 
                               | GattCharacteristic.PROPERTY_WRITE_NO_RESPONSE); 
    
       // 实现BLE广播回调 
       private class MyBleAdvertiseCallback extends BleAdvertiseCallback { 
           // 开始广播回调 
           @Override 
           public void startResultEvent(int result) { 
               if (result == BleAdvertiseCallback.RESULT_SUCC) { 
                   // 为GattService添加一个或多个特征 
                   gattService.addCharacteristic(notifyCharacteristic); 
                   gattService.addCharacteristic(writeCharacteristic); 
                   // 删除所有服务 
                   blePeripheralManager.clearServices(); 
                   // 向外围设备管理对象添加GATT服务 
                   blePeripheralManager.addService(gattService); 
               } 
           } 
       } 
    
       // 创建广播数据 
       private BleAdvertiseData advertiseData = new BleAdvertiseData.Builder() 
               .addServiceData(SequenceUuid.uuidFromString(SERVICE_UUID), "12".getBytes()) 
               .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); 
    
       @Override 
       public void onStart(Intent intent) { 
           super.onStart(intent); 
           super.setUIContent(ResourceTable.Layout_ability_ble_peripheral); 
           initComponent(); 
           initClickedListener(); 
       } 
    
       // 初始化组件 
       private void initComponent() { 
           if (findComponentById(ResourceTable.Id_status) instanceof Text) { 
               statusText = (Text) findComponentById(ResourceTable.Id_status); 
           } 
           if (findComponentById(ResourceTable.Id_data) instanceof Text) { 
               dataText = (Text) findComponentById(ResourceTable.Id_data); 
           } 
           if (findComponentById(ResourceTable.Id_input) instanceof TextField) { 
               field = (TextField) findComponentById(ResourceTable.Id_input); 
           } 
           if (findComponentById(ResourceTable.Id_advertise) instanceof Button) { 
               advertiseButton = (Button) findComponentById(ResourceTable.Id_advertise); 
           } 
           if (findComponentById(ResourceTable.Id_send) instanceof Button) { 
               sendButton = (Button) findComponentById(ResourceTable.Id_send); 
           } 
       } 
    
       // 初始化点击回调 
       private void initClickedListener() { 
           advertiseButton.setClickedListener(new Component.ClickedListener() { 
               @Override 
               public void onClick(Component component) { 
                   if (!isAdvertising) { 
                       advertiseButton.setText("停止广播"); 
                       statusText.setText("状态:已广播,等待连接"); 
                       // 开始BLE广播 
                       advertiser.startAdvertising(advertiseSettings, advertiseData, null); 
                       isAdvertising = true; 
                   } else { 
                       advertiseButton.setText("开始广播"); 
                       statusText.setText("状态:已停止广播"); 
                       // 停止BLE广播 
                       advertiser.stopAdvertising(); 
                       isAdvertising = false; 
                   } 
               } 
           }); 
    
           sendButton.setClickedListener(new Component.ClickedListener() { 
               @Override 
               public void onClick(Component component) { 
                   if (field.getText().isEmpty() || (blePeripheralManager == null) || !isConnected) { 
                       return; 
                   } 
    
                   // 向中心设备发送数据 
                   notifyCharacteristic.setValue(field.getText().getBytes()); 
                   blePeripheralManager.notifyCharacteristicChanged(peripheralDevice, notifyCharacteristic, false); 
               } 
           }); 
       } 
    
       private void updateComponent(Text text, String content) { 
           getUITaskDispatcher().syncDispatch(new Runnable() { 
               @Override 
               public void run() { 
                   text.setText(content); 
               } 
           }); 
       } 
   }

说明
以上代码示例仅供参考使用,产品化的代码需要考虑数据校验和国际化。

9. 恭喜您

通过本教程的学习,您已学会如何使用BLE实现设备间通信。

已于2022-5-5 14:19:25修改
1
收藏 1
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐