Android
Bluetooth Low Energy
Szukaj…
Wprowadzenie
Dokumentacja ta stanowi rozszerzenie w stosunku do oryginalnej dokumentacji i będzie koncentrować się na najnowszym interfejsie API LE LE Bluetooth wprowadzonym w systemie Android 5.0 (API 21). Omówione zostaną zarówno role centralne, jak i peryferyjne, a także sposób rozpoczęcia operacji skanowania i reklamy.
Znajdowanie urządzeń BLE
Do korzystania z interfejsów API Bluetooth wymagane są następujące uprawnienia:
android.permission.BLUETOOTH android.permission.BLUETOOTH_ADMIN
Jeśli kierujesz reklamy na urządzenia z Androidem 6.0 ( poziom API 23 ) lub nowszym i chcesz wykonywać operacje skanowania / reklamy, potrzebujesz pozwolenia na lokalizację:
android.permission.ACCESS_FINE_LOCATION
lub
android.permission.ACCESS_COARSE_LOCATION
Uwaga. - Urządzenia z Androidem 6.0 (API Level 23) lub wyższym również muszą mieć włączone Usługi lokalizacji.
Obiekt BluetoothAdapter jest wymagany do rozpoczęcia operacji skanowania / reklamy:
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
Metoda startScan (ScanCallback callback)
klasy BluetoothLeScanner jest najbardziej podstawowym sposobem rozpoczęcia operacji skanowania. Obiekt ScanCallback
jest wymagany do otrzymania wyników:
bluetoothAdapter.getBluetoothLeScanner().startScan(new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Log.i(TAG, "Remote device name: " + result.getDevice().getName());
}
});
Łączenie z serwerem GATT
Po znalezieniu pożądanego obiektu BluetoothDevice można się z nim połączyć za pomocą metody connectGatt()
, która przyjmuje jako parametry obiekt Context, boolean wskazujący, czy automatycznie łączyć się z urządzeniem BLE oraz referencję BluetoothGattCallback, w której zdarzenia połączenia i operacje klienta wyniki zostaną dostarczone:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
device.connectGatt(context, false, bluetoothGattCallback, BluetoothDevice.TRANSPORT_AUTO);
} else {
device.connectGatt(context, false, bluetoothGattCallback);
}
Zastąp onConnectionStateChange
w BluetoothGattCallback, aby odbierać połączenia i zdarzenia rozłączenia:
BluetoothGattCallback bluetoothGattCallback =
new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "Connected to GATT server.");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
}
}
};
Pisanie i czytanie z cech
Po nawiązaniu połączenia z serwerem Gatt będziesz wchodzić w interakcje z nim, pisząc i czytając cechy serwera. Aby to zrobić, najpierw musisz dowiedzieć się, jakie usługi są dostępne na tym serwerze i jakie cechy są dostępne w każdej usłudze:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "Connected to GATT server.");
gatt.discoverServices();
}
. . .
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> services = gatt.getServices();
for (BluetoothGattService service : services) {
List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
for (BluetoothGattCharacteristic characteristic : characteristics) {
///Once you have a characteristic object, you can perform read/write
//operations with it
}
}
}
}
Podstawowa operacja zapisu wygląda następująco:
characteristic.setValue(newValue);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
gatt.writeCharacteristic(characteristic);
Po zakończeniu procesu zapisu zostanie onCharacteristicWrite
metoda onCharacteristicWrite
BluetoothGattCallback
:
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.d(TAG, "Characteristic " + characteristic.getUuid() + " written);
}
Podstawowa operacja zapisu wygląda następująco:
gatt.readCharacteristic(characteristic);
Po zakończeniu procesu zapisu zostanie onCharacteristicRead
metoda onCharacteristicRead
BluetoothGattCallback
:
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
byte[] value = characteristic.getValue();
}
Subskrybowanie powiadomień z serwera Gatt
Możesz poprosić o powiadomienie z serwera Gatt, gdy wartość właściwości zostanie zmieniona:
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
Wszystkie powiadomienia z serwera będą odbierane za onCharacteristicChanged
metody onCharacteristicChanged
urządzenia BluetoothGattCallback:
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
byte[] newValue = characteristic.getValue();
}
Reklama urządzenia BLE
Za pomocą reklamy Bluetooth LE można przesyłać pakiety danych do wszystkich pobliskich urządzeń bez konieczności wcześniejszego nawiązania połączenia. Pamiętaj, że istnieje ścisły limit 31 bajtów danych reklamowych. Reklama urządzenia to także pierwszy krok w kierunku umożliwienia innym użytkownikom nawiązania z Tobą połączenia.
Ponieważ nie wszystkie urządzenia obsługują reklamę Bluetooth LE, pierwszym krokiem jest sprawdzenie, czy urządzenie ma wszystkie niezbędne wymagania do obsługi. Następnie możesz zainicjować obiekt BluetoothLeAdvertiser
i za jego pomocą możesz rozpocząć operacje reklamowe:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && bluetoothAdapter.isMultipleAdvertisementSupported())
{
BluetoothLeAdvertiser advertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
//Define a service UUID according to your needs
dataBuilder.addServiceUuid(SERVICE_UUID);
dataBuilder.setIncludeDeviceName(true);
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
settingsBuilder.setTimeout(0);
//Use the connectable flag if you intend on opening a Gatt Server
//to allow remote connections to your device.
settingsBuilder.setConnectable(true);
AdvertiseCallback advertiseCallback=new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.i(TAG, "onStartSuccess: ");
}
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.e(TAG, "onStartFailure: "+errorCode );
}
};
advertising.startAdvertising(settingsBuilder.build(),dataBuilder.build(),advertiseCallback);
}
Korzystanie z serwera Gatt
Aby urządzenie działało jako urządzenie peryferyjne, najpierw musisz otworzyć BluetoothGattServer
i wypełnić go co najmniej jednym BluetoothGattService
i jednym BluetoothGattCharacteristic
:
BluetoothGattServer server=bluetoothManager.openGattServer(context, bluetoothGattServerCallback);
BluetoothGattService service = new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
Jest to przykład cechy BluetoothGattCharakterystyka z pełnymi uprawnieniami do zapisu, odczytu i powiadamiania. W zależności od potrzeb możesz dostosować uprawnienia, które przyznajesz tej właściwości:
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_UUID,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE |
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
characteristic.addDescriptor(new BluetoothGattDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"), BluetoothGattCharacteristic.PERMISSION_WRITE));
service.addCharacteristic(characteristic);
server.addService(service);
BluetoothGattServerCallback
odpowiada za odbieranie wszystkich zdarzeń związanych z BluetoothGattServer
:
BluetoothGattServerCallback bluetoothGattServerCallback= new BluetoothGattServerCallback() {
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
}
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
}
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
}
@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
}
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
}
Ilekroć otrzymasz prośbę o zapis / odczyt do cechy lub deskryptora, musisz wysłać odpowiedź, aby prośba została pomyślnie zrealizowana:
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
server.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, YOUR_RESPONSE);
}