rxandroidble пишет только отправляет первые 20B

Я пытаюсь записать>20 байтов данных по заданной (пользовательской) характеристике. В следующем журнале я попытался записать 85 байтов:

код:

connectionObservable
                .flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(
                        wChar.uuid(),
                        wChar.bytes()))
                .observeOn(mainThread())
                .subscribe(
                        bytes -> wChar.success(),
                        this::onWriteFailure
                );

результат: на стороне сервера (nrf52) я вижу EXEC_WRITE, но отправляются только первые 20B.

это логкат:

D / RxBle # ClientOperationQueue: QUEUED ConnectOperation(17461182) D / RxBle # ClientOperationQueue: STARTED ConnectOperation(17461182) D / RxBle # ClientOperationQueue: QUEUED ConnectOperation(218660306) D/RxBle#ClientOperationQue Операция 0: Операция Bluetooth: операция D606000000000000000000000000000000000000002D (0)Обновление_переключения: Операция Bluetooth 0: Операция вызова: DOWN_RU_RU_RU_RU: Действие: onConnectionStateChange NewState = 2 состояние = 0 D / RxBle # BluetoothGatt: onConnectionStateChange NewState = 2 состояние = 0 Д / RxBle # ClientOperationQueue: ЗАКОНЧИЛИ ConnectOperation(218660306) Д / RxBle # ClientOperationQueue: ЗАКОНЧИЛИ ConnectOperation(17461182) D/RxBle#ConnectionOperationQueue: QUEUED ServiceDiscoveryOperation(125599796) D/RxBle#ConnectionOperationQueue: STARTED ServiceDiscoveryOperation(125599796) D / RxBle # BluetoothGatt: onServicesDiscovered status = 0 D/RxBle#ConnectionOperationQueue: QUEUED Характеристика ReadOperation(2626026) Операция D / RxBueRue_DQueRue_RueBue_Rue_Rue_Rue_Rue_Rue_Rue_Rue_RueBue_RueBue_RueBue. ConnectionOperationQueue: STARTED CharacteristicReadOperation(2626026) D/RxBle#BluetoothGatt: onCharacteristicRead характеристикой =0000fa03-0278-03be-4447-091eba91df8e состояние = 0 Д / RxBle # ConnectionOperationQueue: ЗАКОНЧИЛИ CharacteristicReadOperation(2626026) Д / RxBle # ClientOperationQueue: QUEUED ConnectOperation(158692575) Д / RxBle # ClientOperationQueue: НАЧАТЬ ConnectOperation(158692575) Д /RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0 D/RxBle# ClientOperationQueue: ЗАВЕРШЕНО ConnectOperation(158692575) D/RxBle#ConnectionOperationQueue: QUEUED ServiceDiscoveryOperation(20778996) Служба D / RxBlend_RueBoD_Roged_Rue_Dext_WD_RgBD #Roged_Dext_Rue_Dext_Rue_RueBoD_Roged_Rue_Rue_RueBoD_Or_D_WGT_Rue_Rue_Rue_Rue_DateBerver.: onServicesDiscovered status = 0
D/RxBle#ConnectionOperationQueue: QUEUED CharacteristicWriteOperation(51009974) D / RxBle # ConnectionOperationQueue: FINISHED ServiceDiscoveryOperation(20778996) D/RxBle#ConnectionOperationQueue: STARTED CharacteristicWriteOperation(51009974)
> D / RxBle # BluetoothGatt: onCharacteristicWrite характеристика =0000fa04-0278-03be-4447-091eba91df8e status = 0

D / RxBle # ConnectionOperationQueue: FINISHED CharacteristicWriteOperation(51009974)

Я также попытался использовать длинную процедуру rxAndroidBlewrite:

connectionObservable
                .flatMap(rxBleConnection -> {
                            rxBleConnection.setupNotification(wChar.uuid()); 
                            return rxBleConnection.createNewLongWriteBuilder()
                                    .setCharacteristicUuid(wChar.uuid()) 
                                    .setBytes(array)
                                    .build();
                        }
                )
                .subscribe(
                        bytes -> wChar.success(),
                        this::onWriteFailure
                );

и он отправляет несколько последовательных команд записи, но это не процедура длинной записи (с n ATT_prepare и 1 ATT_exec), это независимая запись:

D/RxBle#ConnectionOperationQueue: QUEUED CharacteristicLongWriteOperation(74131396) D/RxBle#ConnectionOperationQueue: FINISHED ServiceDiscoveryOperation(250008320) D / RxBle # ConnectionOperationQueue: STARTED CharacteristicLongWriteOperation(74131396)

D / RxBle # BluetoothGatt: onCharacteristicWrite характеристика =0000fa04-0278-03be-4447-091eba91df8e status = 0

D / RxBle # BluetoothGatt: onCharacteristicWrite характеристика =0000fa04-0278-03be-4447-091eba91df8e status = 0

D / RxBle # BluetoothGatt: onCharacteristicWrite характеристика =0000fa04-0278-03be-4447-091eba91df8e status = 0

D / RxBle # BluetoothGatt: onCharacteristicWrite характеристика =0000fa04-0278-03be-4447-091eba91df8e status = 0

D / RxBle # BluetoothGatt: onCharacteristicWrite характеристика =0000fa04-0278-03be-4447-091eba91df8e status = 0

D / RxBle # ConnectionOperationQueue: FINISHED CharacteristicLongWriteOperation(74131396)

Конечно, мне удалось перестроить на сервере или изменить MTU, но я хочу использовать записи BLE в очереди, которые обычно поддерживаются моим центральным (rxandroidble) и моим периферийным устройством (nrf52)

3 ответа

Решение

Для клиентской части (Android) просто используйте стандартную процедуру записи, и все будет хорошо. Внутренне, это разделит это на многократные Запросы Подготовки записи, сопровождаемые Запросом Выполнения записи. То есть используйте свой первый подход.

Чтобы помочь с путаницей, есть два уровня: ГАТТ и АТТ. Уровень ATT определяет концепцию, называемую "Запись в очередь", которая состоит из Подготовить запрос / ответ на запись и Выполнить запрос / ответ на запись. Идея состоит в том, что все подготовленные записи (каждая из которых ограничена размером MTU-5, имеют параметр смещения и дескриптор ATT) помещаются в очередь на периферийной стороне. Записи не фиксируются на периферийном устройстве до тех пор, пока не будет выполнена запись. (Выполнение записи имеет флаг, который также можно использовать для отмены всей очереди.)

На уровне GATT у нас есть что-то под названием "Запись длинных значений характеристик". Это процедура, которая будет использоваться, когда должно быть записано значение характеристики, превышающее то, что может быть помещено в один запрос на запись. Эта процедура определена для использования функций "Запись в очередь" в ATT, поэтому она будет разбивать значение на несколько сегментов, отправлять их все в пакетах подготовленной записи и, наконец, будет отправлен пакет "Выполнить запись". Следует избегать "записи длинных значений характеристик" любой ценой из-за больших издержек (одна передача в оба конца на один пакет, ответные пакеты длинные, потому что они содержат копию значения, и необходим один последний запрос на выполнение записи). Вместо этого увеличение MTU до максимума намного лучше, поскольку, если вам повезет, вы можете отправить все в одном событии подключения.

Надежная запись - это также функция уровня GATT, которая использует функцию "Запись в очередь" в ATT. Идея состоит в том, что пользователь должен иметь возможность выполнять несколько атомарных записей для потенциально более чем одной характеристики в одной операции. Слово "надежный" происходит от того, что клиент должен проверять, что значения были отправлены правильно. Каждый подготовленный ответ на запись включает в себя полученное значение из подготовленного запроса на запись, и клиент должен сравнить их и увидеть, что они равны, а если нет, отменить. Теперь в Android невозможно выполнить эту проверку в текущей реализации API+, поэтому, хотя операция все еще работает, она на самом деле не настолько "надежна", как это должно быть (но в любом случае у вас есть CRC для всех пакетов BLE, поэтому я не думаю, что будет проблема). Обратите внимание, что если вы следуете правилам GATT, вы можете выполнять только надежную запись в характеристики (не в дескрипторы) и только в характеристики, декларирующие это свойство.

С периферийной стороны невозможно по-настоящему узнать, являются ли входящие подготовленные запросы на запись частью операции "Запись длинных признаков" или операции "Надежная запись". Но в любом случае, большинство стеков BLE не объединяют извлеченные части и затем доставляют одну запись при получении выполнения записи. Вместо этого они показывают API довольно низкого уровня, по моему мнению; часто просто более или менее пересылают пакеты ATT.

Для программного устройства Nordic Semiconductor самый простой метод, который у них есть, - это использовать "GATTS Queued Writes: стек обрабатывается, никакие атрибуты не требуют авторизации" http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s132.api.v5.0.0/group___b_l_e___g_a_t_t_s___q_u_e_u_e_d___w_r_i_t_e___b_u_f___n_o_a_u_t_h___m_s_c.html. Таким образом, он помещает в очередь все подготовленные записи в предоставленном приложением буфере и уведомляет приложение, когда прибывает выполнение записи. Затем приложение должно проанализировать все (более или менее) необработанные пакеты запроса предварительной записи ATT, которые стек поместил в буфер. Эта структура здесь определена Обратите внимание, что буфер представляет собой список (массив с объединенными этими структурами), а не одно значение. Список заканчивается элементом, содержащим BLE_GATT_HANDLE_INVALID в качестве дескриптора. Я думаю, что ваша ошибка в том, что вы анализируете только первый элемент в этом списке.

Если вы ссылаетесь на записи в очереди, то в Android API это называется надежной записью. Этот API в настоящее время не реализован вRxAndroidBle и вам нужно будет сделать это путем реализации RxBleCustomOperation API с помощью ярлыка на роднойBluetoothGattCallback, Даже тогда кажется, что родной Android API не полностью функционален в этом вопросе.

RxAndroidBle длинная запись не использует подготовленные записи, но несколько стандартных записей. Это на самом деле может быть лучше описано в Javadoc...

Существуют разные мнения о том, что такое длинная запись. Отличный ответ@ Эмиля в этом вопросе проясняет это очень хорошо.

Я провел несколько тестов, используя nRF51822 с Softdevice S110 из SDK 8.1.0.

Кажется, что длинная запись - это просто подготовленная запись - Android управляет ею для пользователя.

С периферийной стороны это кажется более сложным для реализации, поскольку Softdevice информирует приложение о том, что подготовленная запись завершила и что данные готовы для анализа (они не присоединены к самому событию записи BLE). Анализ данных относится к логике приложения, так как, по-видимому, нет различий между длинной записью и подготовленной / надежной записью, которая может принимать во внимание запись в более чем одну характеристику за раз, и что может быть некоторая согласованность, связанная с бизнес-логикой вопросы (должен ли быть принят определенный набор записей или нет).

Вывод: Android vanilla API (и RxAndroidBle) поддерживает так называемую длинную запись из коробки, делая несколько подготовленных / помещенных в очередь записей под капотом. Это зависит от прошивки периферийного устройства, чтобы справиться с этим должным образом

Спецификация Bluetooth 4.0, которая включала в себя введение BLE, гласит, что за одну характеристику одновременно может быть передано максимум 20 байтов. Если вам нужно отправить больше данных, вам придется отправлять 20 байтов за раз в некотором цикле.

Так что на самом деле это не проблема с RxAndroidBle, просто ограничение технологии.

Смотрите здесь: /questions/43387728/maksimalnaya-dlina-paketa-dlya-bluetooth-le/43387731#43387731

Другие вопросы по тегам