Большинство действий, которые можно выполнять через BLE, являются асинхронными и требуют времени для завершения. Настройка уведомлений не является исключением - это двухэтапная процедура:
- настроить локальное уведомление
- написать дескриптор конфигурации характеристик клиента для характеристики, от которой требуется получать уведомления
Если ваше периферийное устройство сначала настроено на отправку уведомлений до того, как уведомления будут готовы к получению центральным сервером, то некоторые данные могут быть потеряны во время процедуры настройки уведомлений.
Может быть, есть способ выполнить setupNotification (), а затем записать характеристики, чтобы устройство начало отправлять уведомления?
Конечно (именно так обычно обрабатываются похожие сценарии) - существует несколько возможных реализаций. Один из них мог выглядеть так:
device.establishConnection(false) // establish the connection
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(log_uuid) // once the connection is available setup the notification
.flatMap(logDataObservable -> Observable.merge( // when the notification is setup start doing three things at once
rxBleConnection.writeCharacteristic(pword_uuid, pword).ignoreElements(), // writing the `pword` but ignore the result so the output of this .merge() will contain only log data
rxBleConnection.writeCharacteristic(mode_uuid, mode).ignoreElements(), // same as the line above but for `mode`
logDataObservable // observing the log data notifications
))
)
.subscribe(
bytes -> System.out.println(">>> data from device " + bytesToHex(bytes)),
throwable -> {
System.out.println("error");
System.out.println(throwable);
}
);
Редактировать:
Как было упомянуто в комментариях ниже, периферийное устройство не допускает никаких взаимодействий с BLE до установки режима и написания пароля. Как я уже писал выше, настройка уведомлений представляет собой двухэтапную процедуру с локальным шагом и удаленным (выполняемым на периферии), который выполняется перед режимом / паролем в приведенном выше фрагменте кода. Эти два шага можно разделить, используя режим NotificationSetupMode.COMPAT
и записав Client Characteristic Configuration Descriptor
вручную позже:
UUID clientCharacteristicConfigDescriptorUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
device.establishConnection(false) // establish the connection
.flatMap(
RxBleConnection::discoverServices, // once the connection is available discover the services of the peripheral
(rxBleConnection, rxBleDeviceServices) -> // and when we have the connection and services
rxBleDeviceServices.getCharacteristic(log_uuid) // we get the log characteristic (on which we will setup the notification and write the descriptor)
.flatMap(logDataCharacteristic -> // once the log characteristic is retrieved
rxBleConnection.setupNotification(logDataCharacteristic, NotificationSetupMode.COMPAT) // we setup the notification on it in the COMPAT mode (without writing the CCC descriptor)
.flatMap(logDataObservable -> Observable.merge( // when the notification is setup start doing four things at once
rxBleConnection.writeCharacteristic(pword_uuid, pword).ignoreElements(), // writing the `pword` but ignore the result so the output of this .merge() will contain only log data
rxBleConnection.writeCharacteristic(mode_uuid, mode).ignoreElements(), // same as the line above but for `mode`
rxBleConnection.writeDescriptor(logDataCharacteristic.getDescriptor(clientCharacteristicConfigDescriptorUuid), BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE).ignoreElements(), // and we write the CCC descriptor manually
logDataObservable // observing the log data notifications
))
)
)
.flatMap(observable -> observable) // flatMap to get the raw byte[]
.subscribe(
bytes -> System.out.println(">>> data from device " + bytesToHex(bytes)),
throwable -> {
System.out.println("error");
System.out.println(throwable);
}
);
Вызов rxBleConnection.discoverServices()
может быть опущен, если мы знаем службу характеристик журнала UUID
и используем функцию rxBleConnection.writeDescriptor(UUID serviceUuid, UUID characteristicUuid, UUID descriptorUuid
.
UUID clientCharacteristicConfigDescriptorUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
device.establishConnection(false) // establish the connection
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(log_uuid, NotificationSetupMode.COMPAT) // once the connection is available setup the notification w/o setting Client Characteristic Config Descriptor
.flatMap(logDataObservable -> Observable.merge( // when the notification is setup start doing three things at once
rxBleConnection.writeCharacteristic(pword_uuid, pword).ignoreElements(), // writing the `pword` but ignore the result so the output of this .merge() will contain only log data
rxBleConnection.writeCharacteristic(mode_uuid, mode).ignoreElements(), // same as the line above but for `mode`
rxBleConnection.writeDescriptor(log_service_uuid, log_uuid, clientCharacteristicConfigDescriptorUuid).ignoreElements(), // same as the above line but for writing the CCC descriptor
logDataObservable // observing the log data notifications
))
)
.subscribe(
bytes -> System.out.println(">>> data from device " + bytesToHex(bytes)),
throwable -> {
System.out.println("error");
System.out.println(throwable);
}
);
Изменить 2:
Начиная с версии 1.8.0
существует новый NotificationSetupMode.QUICK_SETUP
, который сначала включает внутренние уведомления, а затем записывает значение дескриптора CCC.
rxBleConnection.setupNotification(log_uuid, NotificationSetupMode.QUICK_SETUP)
Плюсы:
Observable<byte[]>
испускается до записи дескриптора, что позволяет отслеживать уведомления с самого начала (если начало записывает дескриптор)
Минусы:
- Невозможно определить, когда именно был записан дескриптор.
person
Dariusz Seweryn
schedule
13.09.2017