回复
如何使用BLE实现设备间的通信
奶盖
发布于 2021-6-21 11:23
浏览
2收藏
1. 介绍
BLE 是蓝牙低功耗的简称(Bluetooth Low Energy),生活中的很多场景都用到了BLE,如计步器、心率监视器、灯光控制、智能锁等。
BLE 设备间通信时会分为不同的角色:
- 中心设备和外围设备:中心设备负责扫描外围设备、发现广播。外围设备负责发送广播。
- GATT(Generic Attribute Profile,通用属性配置文件)服务端与 GATT 客户端:两台设备建立连接后,其中一台作为 GATT 服务端,另一台作为 GATT 客户端。
说明
使用BLE实现设备间通信,需要蓝牙版本在4.0或以上,而且,您需要确保蓝牙已启用。
本教程将结合以下内容讲解如何使用BLE实现设备间通信:
- 权限申请
- BLE广播和扫描
- GATT服务端、客户端的创建
- 建立GATT连接、发现服务
- 设备间通信
2. 权限申请
在"entry\src\main\config.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 {
@Override
public void onStart(Intent intent) {
String[] permissions = {"ohos.permission.LOCATION"};
requestPermissionsFromUser(permissions, 0);
super.onStart(intent);
}
}
3. 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())
.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 list) {
// 使用组扫描时在此对扫描结果进行处理
}
}
// 获取中心设备管理回调
private MyBleCentralManagerCallback centralManagerCallback = new MyBleCentralManagerCallback();
// 获取中心设备管理对象
private BleCentralManager centralManager = new BleCentralManager(this, centralManagerCallback);
// 创建扫描过滤器
private List filters = new ArrayList<>();
// 开始扫描带有过滤器的指定BLE设备
centralManager.startScan(filters);
4. 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 | 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);
}
}
- 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连接、发现服务
- 中心设备扫描成功后,在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;
}
}
}
6. 设备间通信
- 中心设备接收数据。启用特征通知后,当外围设备调用notifyCharacteristicChanged()接口通知特征更新时,中心设备会触发characteristicChangedEvent()回调,在回调中通过characteristic.getValue()可以获取到外围设备发送的数据。代码示例如下:
public void characteristicChangedEvent(GattCharacteristic characteristic) {
super.characteristicChangedEvent(characteristic);
// 获取外围设备发送的数据:characteristic.getValue()
}
- 中心设备发送数据。获取到writeCharacteristic实例后,通过调用writeCharacteristic.setValue()接口将数据写入到writeCharacteristic中,然后调用peripheralDevice.writeCharacteristic()接口向外围设备写入特征。代码示例如下:
// 向外围设备发送数据
writeCharacteristic.setValue("我是中心设备".getBytes());
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());
blePeripheralManager.notifyCharacteristicChanged(peripheralDevice, notifyCharacteristic, false);
说明
BLE设备间通信对数据大小有限制,一次性传输的数据最大不超过20字节,超过部分将无法传输。
7. 最终实现效果
-
外围设备(BlePeripheral)和中心设备(BleCentral)操作界面初始效果
-
外围设备开始BLE广播、中心设备开始BLE扫描
3. 连接设备
4. 外围设备发送数据、中心设备接收数据
5. 中心设备发送数据、外围设备接收数据
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用于展示外围设备操作按钮、文本、输入框。
编写布局与样式
首先在HUAWEI DevEco Studio创建一个Phone的Empty Feature Ability(Java)模板工程,然后新建名称为BleCentralAbility和BlePeripheralAbility的Empty Page Ability(Java)。
- 修改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>
- 修改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>
- 修改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>
- 创建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>
业务逻辑代码
- 在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());
}
}
- 在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);
}
});
}
}
- 在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);
}
});
}
}
- 在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修改
赞
2
收藏 2
回复
相关推荐