Bluetooth Low Energy (BLE) is used in various scenarios, such as pedometers, heart rate monitors, light control, and smart locks.
When devices interact with each other through BLE, they play different roles as follows:
This codelab describes how to enable BLE communication between devices and covers the following content:
Applying for Permissions
BLE Advertising and Scanning
Creating a GATT Server and a GATT Client
Setting Up a GATT Connection and Discovering Services
Communication Between Devices
The procedure is as follows:
Declare the following permissions in the reqPermissions field in the entry\src\main\config.json file:
"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 is a sensitive permission. You must explicitly declare it in the code. The sample code is as follows:
public class MainAbility extends Ability {
@Override
public void onStart(Intent intent) {
String[] permissions = {"ohos.permission.LOCATION"};
requestPermissionsFromUser(permissions, 0);
super.onStart(intent);
}
}
// Implement the BleAdvertiseCallback class.
private class MyBleAdvertiseCallback extends BleAdvertiseCallback {
@Override
public void startResultEvent(int result) {
if (result == BleAdvertiseCallback.RESULT_SUCC) {
// Called when the BLE advertising is started successfully.
}
}
}
// Build a BleAdvertiseData instance.
private BleAdvertiseData advertiseData = new BleAdvertiseData.Builder()
.addServiceData(SequenceUuid.uuidFromString(SERVICE_UUID), "12".getBytes(StandardCharsets.UTF_8))
.addServiceUuid(SequenceUuid.uuidFromString(SERVICE_UUID))
.build();
// Build a BleAdvertiseSettings instance.
private BleAdvertiseSettings advertiseSettings = new BleAdvertiseSettings.Builder()
.setConnectable(true)
.setInterval(BleAdvertiseSettings.INTERVAL_SLOT_MIN)
.setTxPower(BleAdvertiseSettings.TX_POWER_MAX)
.build();
// Obtain a BleAdvertiseCallback.
private MyBleAdvertiseCallback advertiseCallback = new MyBleAdvertiseCallback();
// Obtain a BleAdvertiser object.
private BleAdvertiser advertiser = new BleAdvertiser(this, advertiseCallback);
// Start BLE advertising.
advertiser.startAdvertising(advertiseSettings, advertiseData, null);
// Implement the BleCentralManagerCallback class.
private class MyBleCentralManagerCallback implements BleCentralManagerCallback {
// Called to send a notification of the scan result after a BLE scan is complete.
@Override
public void scanResultEvent(BleScanResult bleScanResult) {
// Obtain the BlePeripheralDevice instance based on the scan result.
}
// Called when the scan fails.
@Override
public void scanFailedEvent(int i) {
// Scan failure event
}
// Called when a group of BLE scan results is reported.
@Override
public void groupScanResultsEvent(List list) {
// You can define how to handle the scan results in this callback.
}
}
// Obtain a BleCentralManagerCallback object.
private MyBleCentralManagerCallback centralManagerCallback = new MyBleCentralManagerCallback();
// Obtain a BleCentralManager object.
private BleCentralManager centralManager = new BleCentralManager(this, centralManagerCallback);
// Create BLE scan filters.
private List filters = new ArrayList<>();
// Start scanning for specified BLE devices with filters.
centralManager.startScan(filters);
// Implement the BlePeripheralManagerCallback class.
private class MyBlePeripheralManagerCallback extends BlePeripheralManagerCallback {
// Called when the connection state changes.
@Override
public void connectionStateChangeEvent(
BlePeripheralDevice device, int interval, int latency, int timeout, int status) {
if (status == BlePeripheralDevice.OPERATION_SUCC) {
// A connection has been set up.
}
}
// Called when the GATT client requests to write a characteristic.
@Override
public void receiveCharacteristicWriteEvent(
BlePeripheralDevice device,
int transId,
GattCharacteristic characteristic,
boolean isPrep,
boolean needRsp,
int offset,
byte[] value) {
// Obtain the data written by the central device (GATT client).
}
}
// Obtain a BlePeripheralManagerCallback object.
private MyBlePeripheralManagerCallback peripheralManagerCallback = new MyBlePeripheralManagerCallback();
// Obtain a BlePeripheralManager object.
private BlePeripheralManager blePeripheralManager = new BlePeripheralManager(this, peripheralManagerCallback, 1);
// Create a GattService object with a specified UUID.
private GattService gattService = new GattService(UUID.fromString(SERVICE_UUID), true);
// Create a GattCharacteristic object to send data to the central device.
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);
// Create another GattCharacteristic object to receive data from the central device.
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) {
// Add one or more characteristics to the GattService object.
gattService.addCharacteristic(notifyCharacteristic);
gattService.addCharacteristic(writeCharacteristic);
// Clear all services.
blePeripheralManager.clearServices();
// Add the GattService object to the BlePeripheralManager object.
blePeripheralManager.addService(gattService);
}
}
// Implement the BlePeripheralCallback class.
private class MyBlePeripheralCallback extends BlePeripheralCallback {
// Called when services are discovered on the peripheral device.
@Override
public void servicesDiscoveredEvent(int status) {
super.servicesDiscoveredEvent(status);
// After the service discovery operation succeeds, this callback is invoked to send a notification and obtain a GattCharacteristic object.
}
// Called when the connection state changes.
@Override
public void connectionStateChangeEvent(int connectionState) {
super.connectionStateChangeEvent(connectionState);
// After a connection is set up, discover GATT services on the peripheral device.
}
// Called when a characteristic changes.
@Override
public void characteristicChangedEvent(GattCharacteristic characteristic) {
super.characteristicChangedEvent(characteristic);
// Receive the data sent by the peripheral device.
}
}
// Obtain a BlePeripheralCallback object.
private MyBlePeripheralCallback blePeripheralCallback = new MyBlePeripheralCallback();
public void scanResultEvent(BleScanResult bleScanResult) {
if (peripheralDevice == null) {
// Obtain service UUIDs.
List<UUID> uuids = bleScanResult.getServiceUuids();
for (UUID uuid : uuids) {
if (SERVICE_UUID.equals(uuid.toString())) {
// Obtain the BlePeripheralDevice instance based on the scan result.
peripheralDevice = bleScanResult.getPeripheralDevice();
}
}
}
}
// Set up a GATT connection with the peripheral device.
peripheralDevice.connect(false, blePeripheralCallback);
public void connectionStateChangeEvent(int connectionState) {
super.connectionStateChangeEvent(connectionState);
if (connectionState == ProfileBase.STATE_CONNECTED) {
isConnected = true;
// After a connection is set up, discover GATT services on the peripheral device.
peripheralDevice.discoverServices();
}
}
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))) {
// Enable characteristic notifications.
peripheralDevice.setNotifyCharacteristic(tmpChara, true);
}
if (tmpChara.getUuid().equals(UUID.fromString(WRITE_CHARACTER_UUID))) {
// Obtain the GattCharacteristic instance.
writeCharacteristic = tmpChara;
}
}
}
public void characteristicChangedEvent(GattCharacteristic characteristic) {
super.characteristicChangedEvent(characteristic);
// Call the characteristic.getValue() method to obtain the data received from the peripheral device.
}
// Send data to the peripheral device.
writeCharacteristic.setValue("I am the central device".getBytes(StandardCharsets.UTF_8));
peripheralDevice.writeCharacteristic(writeCharacteristic);
public void receiveCharacteristicWriteEvent(
BlePeripheralDevice device,
int transId,
GattCharacteristic characteristic,
boolean isPrep,
boolean needRsp,
int offset,
byte[] value) {
// Obtain the data written by the central device.
}
// Send data to the central device.
notifyCharacteristic.setValue("I am the peripheral device".getBytes(StandardCharsets.UTF_8));
blePeripheralManager.notifyCharacteristicChanged(peripheralDevice, notifyCharacteristic, false);
|
|
|
|
|
|
|
|
|
|
Well done! You have completed this codelab and learned how to enable BLE communication between devices.
For your convenience, we provide the complete code for a sample BLE project. The code structure is as follows:

On HUAWEI DevEco Studio, create a project with Phone and Empty Feature Ability (Java) as the device type and template, respectively. Then create two Page abilities, BleCentralAbility and BlePeripheralAbility.
<?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="Central device page"
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="Peripheral device page"
ohos:text_size="18fp"/>
</DirectionalLayout>
<?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="Device: none"
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="Start scanning"
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="Status: disconnected"
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="Connect"
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="Enter"
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="Send data"
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="Peripheral device:"
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>
<?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="Status: not advertising"
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="Start advertising"
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="Enter"
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="Send data"
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="Central device:"
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>
<?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>
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());
}
}
public class MainAbilitySlice extends AbilitySlice {
// Replace " ABILITY_NAME" with the actual ability name in the project
private static final String CENTRAL_ABILITY_NAME = "com.huawei.blebluetooth.BleCentralAbility";
private static final String PERIPHERAL_ABILITY_NAME = "com.huawei.blebluetooth.BlePeripheralAbility";
// Obtain the BluetoothHost instance for managing Bluetooth on the local device.
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(getBundleName())
.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(getBundleName())
.withAbilityName(PERIPHERAL_ABILITY_NAME)
.build();
intent.setOperation(operation);
startAbility(intent);
}
});
}
}
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 static final int CUT_LENGTH = 7;
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;
/**
* Implement the BlePeripheralCallback class.
*
* @since 2021-02-09
*/
private class MyBlePeripheralCallback extends BlePeripheralCallback {
// Called when services are discovered on the peripheral device.
@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))) {
// Enable characteristic notifications.
peripheralDevice.setNotifyCharacteristic(tmpChara, true);
}
if (tmpChara.getUuid().equals(UUID.fromString(WRITE_CHARACTER_UUID))) {
// Obtain a GattCharacteristic instance.
writeCharacteristic = tmpChara;
}
}
}
// Called when the connection state changes.
@Override
public void connectionStateChangeEvent(int connectionState) {
super.connectionStateChangeEvent(connectionState);
if (connectionState == ProfileBase.STATE_CONNECTED && !isConnected) {
isConnected = true;
// After a connection is set up, discover GATT services on the peripheral device.
peripheralDevice.discoverServices();
updateComponent(statusText, "Status: connected");
}
}
// Called when a characteristic changes.
@Override
public void characteristicChangedEvent(GattCharacteristic characteristic) {
super.characteristicChangedEvent(characteristic);
// Receive the data sent by the peripheral device.
updateComponent(dataText, new String(characteristic.getValue()));
}
}
// Obtain a BlePeripheralCallback object.
private MyBlePeripheralCallback blePeripheralCallback = new MyBlePeripheralCallback();
/**
* Implement the BleCentralManagerCallback class.
*
* @since 2021-02-09
*/
private class MyBleCentralManagerCallback implements BleCentralManagerCallback {
// Called to send a notification of the scan result after a BLE scan is complete.
@Override
public void scanResultEvent(BleScanResult bleScanResult) {
// Obtain the BlePeripheralDevice instance based on the scan result.
if (peripheralDevice == null) {
// Obtain service UUIDs.
List<UUID> 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 - CUT_LENGTH, length);
updateComponent(deviceText, "Device:" + deviceId);
}
}
}
}
// Called when the scan fails.
@Override
public void scanFailedEvent(int event) {
updateComponent(deviceText, "Device: Scan failed. Please try again.");
}
// Called when a group of BLE scan results is reported.
@Override
public void groupScanResultsEvent(List<BleScanResult> list) {
// You can define how to handle the scan results in this callback.
}
}
// Obtain a BleCentralManagerCallback object.
private MyBleCentralManagerCallback centralManagerCallback = new MyBleCentralManagerCallback();
// Obtain a BleCentralManager object.
private BleCentralManager centralManager = new BleCentralManager(this, centralManagerCallback);
// Create BLE scan filters.
private List<BleScanFilter> filters = new ArrayList<>();
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_ble_central);
initComponent();
initClickedListener();
}
// Initialize a component.
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 startOrStopScan() {
if (!isScanning) {
isScanning = true;
scanButton.setText("Stop scanning");
deviceText.setText("Device: scanning...");
// Start scanning for specified BLE devices with filters.
centralManager.startScan(filters);
} else {
isScanning = false;
scanButton.setText("Start scanning");
deviceText.setText("Device: none");
// Stop scanning.
centralManager.stopScan();
}
}
// Initialize the click event listener.
private void initClickedListener() {
scanButton.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
startOrStopScan();
}
});
connectButton.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
if (peripheralDevice == null) {
statusText.setText("Status: scan to start obtaining device information");
return;
}
if (!isConnected) {
connectButton.setText("Disconnect");
statusText.setText("Status: connecting...";
// Connect to the BLE peripheral device.
peripheralDevice.connect(false, blePeripheralCallback);
} else {
isConnected = false;
connectButton.setText("Connect");
statusText.setText("Status: disconnected");
deviceText.setText("Device: none");
writeCharacteristic.setValue("Disconnect".getBytes(StandardCharsets.UTF_8));
peripheralDevice.writeCharacteristic(writeCharacteristic);
// Disconnect from the BLE peripheral device.
peripheralDevice.disconnect();
peripheralDevice = null;
}
}
});
sendButton.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
if (field.getText().isEmpty() || (peripheralDevice == null) || !isConnected) {
return;
}
// Send data to the peripheral device.
writeCharacteristic.setValue(field.getText().getBytes(StandardCharsets.UTF_8));
peripheralDevice.writeCharacteristic(writeCharacteristic);
}
});
}
}
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 static final int GATT_CHARACTERISTIC_PERMISSIONS = 16;
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;
/**
* Implement the BlePeripheralManagerCallback class.
*
* @since 2021-02-09
*/
private class MyBlePeripheralManagerCallback extends BlePeripheralManagerCallback {
// Called when the connection state changes.
@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, "Status: connected");
}
}
// Called when the GATT client requests to write a characteristic.
@Override
public void receiveCharacteristicWriteEvent(
BlePeripheralDevice device,
int transId,
GattCharacteristic characteristic,
boolean isPrep,
boolean needRsp,
int offset,
byte[] value) {
if (Arrays.equals("Disconnect".getBytes(StandardCharsets.UTF_8), value)) {
isConnected = false;
peripheralDevice = null;
updateComponent(statusText, "Status: advertising and waiting to be connected");
return;
}
// Obtain the data written by the central device.
updateComponent(dataText, new String(value, Charset.defaultCharset()));
}
}
// Obtain a BlePeripheralManagerCallback object.
private MyBlePeripheralManagerCallback peripheralManagerCallback = new MyBlePeripheralManagerCallback();
// Obtain a BlePeripheralManager object.
private BlePeripheralManager blePeripheralManager = new BlePeripheralManager(this, peripheralManagerCallback, 1);
// Create a GattService object with a specified UUID.
private GattService gattService = new GattService(UUID.fromString(SERVICE_UUID), true);
// Create a GattCharacteristic object to send data to the central device.
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);
// Create another GattCharacteristic object to send data to the central device.
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);
/**
* Implement the BleAdvertiseCallback class.
*
* @since 2021-02-09
*/
private class MyBleAdvertiseCallback extends BleAdvertiseCallback {
// Called when BLE advertising is started.
@Override
public void startResultEvent(int result) {
if (result == BleAdvertiseCallback.RESULT_SUCC) {
// Add one or more characteristics to the GattService object.
gattService.addCharacteristic(notifyCharacteristic);
gattService.addCharacteristic(writeCharacteristic);
// Clear all services.
blePeripheralManager.clearServices();
// Add the GattService object to the BlePeripheralManager object.
blePeripheralManager.addService(gattService);
}
}
}
// Build a BleAdvertiseData object.
private BleAdvertiseData advertiseData = new BleAdvertiseData.Builder()
.addServiceData(SequenceUuid.uuidFromString(SERVICE_UUID), "12".getBytes(StandardCharsets.UTF_8))
.addServiceUuid(SequenceUuid.uuidFromString(SERVICE_UUID))
.build();
// Build a BleAdvertiseSettings object.
private BleAdvertiseSettings advertiseSettings = new BleAdvertiseSettings.Builder()
.setConnectable(true)
.setInterval(BleAdvertiseSettings.INTERVAL_SLOT_MIN)
.setTxPower(BleAdvertiseSettings.TX_POWER_MAX)
.build();
// Obtain a BleAdvertiseCallback object.
private MyBleAdvertiseCallback advertiseCallback = new MyBleAdvertiseCallback();
// Obtain a BleAdvertiser object.
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();
}
// Initialize a component.
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);
}
}
// Initialize the click event listener.
private void initClickedListener() {
advertiseButton.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
if (!isAdvertising) {
advertiseButton.setText("Stop advertising");
statusText.setText("Status: advertising and waiting to be connected");
// Start BLE advertising.
advertiser.startAdvertising(advertiseSettings, advertiseData, null);
isAdvertising = true;
} else {
advertiseButton.setText("Start advertising");
statusText.setText("Status: advertising stopped");
// Stop BLE advertising.
advertiser.stopAdvertising();
isAdvertising = false;
}
}
});
sendButton.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
if (field.getText().isEmpty() || (blePeripheralManager == null) || !isConnected) {
return;
}
// Send data to the central device.
notifyCharacteristic.setValue(field.getText().getBytes(StandardCharsets.UTF_8));
blePeripheralManager.notifyCharacteristicChanged(peripheralDevice, notifyCharacteristic, false);
}
});
}
private void updateComponent(Text text, String content) {
getUITaskDispatcher().syncDispatch(new Runnable() {
@Override
public void run() {
text.setText(content);
}
});
}
}