Как получать все уведомления в RxAndroidBle
Я пытаюсь связаться с регистратором / датчиком данных BLE, используя rxBleAndroid, работающий как на телефоне Android, так и на Raspberry Pi с использованием Android Things.
Однако в настоящее время у меня возникает проблема, из-за которой мое приложение никогда не получает до 5 первых уведомлений.
Я проверил, что устройство BLE фактически успешно отправляет все ожидаемые уведомления. Я сделал это через приложение nRF Connect, и там все работает как положено.
Когда я делаю это через приложение nRF Connect, я предпринимаю следующие шаги:
- Написать в пароль характеристики, чтобы разблокировать устройство
- Запись в характеристику режима для перевода устройства в правильный режим
- Подписаться на уведомления (и уведомления сразу начинают работать)
Когда я делаю это через RxAndroidBle, я подозреваю, что.subscribe() недостаточно быстро устанавливается.
Есть ли какой-нибудь способ сделать setupNotification(), а затем написать характеристики, чтобы сообщить устройству, чтобы начать отправку уведомлений?
Вот мой текущий код:
rxBleClient = RxBleClient.create(this);
RxBleDevice device = rxBleClient.getBleDevice(mac_address);
device.establishConnection(false)
.flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(pword_uuid, pword)
.flatMap(ignored1 -> rxBleConnection.writeCharacteristic(mode_uuid, mode))
.flatMap(ignored2 -> rxBleConnection.setupNotification(log_uuid))
)
.flatMap(notificationObservable -> notificationObservable)
.subscribe(
bytes -> {
System.out.println(">>> data from device " + bytesToHex(bytes));
},
throwable -> {
System.out.println("error");
System.out.println(throwable);
});
1 ответ
Большинство действий, которые можно выполнять над 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);
}
);