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);
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow