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

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

код:

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

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

это логарифм:

D / RxBle # ClientOperationQueue: QUEUED ConnectOperation (17461182) D / RxBle # ClientOperationQueue: STARTED ConnectOperation (17461182) D / RxBle # ClientOperationQueue: QUEUED ConnectOperation (218660306) D / RxBle # ClientOperation ConnectOperation (218660306) D / RxBle # ClientOperation ConnectOperation onConnectionStateChange newState = 2 status = 0 D / RxBle # BluetoothGatt: onConnectionStateChange newState = 2 status = 0 D / RxBle # ClientOperationQueue: FINISHED ConnectOperation (218660306) D / RxBle # ClientOperationQueue: FINISHEDConnectOperationDecovery: D / RxBlee # ClientOperationQueue: # RINISHEDConnectOperationService (17) (125599796) D / RxBle # ConnectionOperationQueue: STARTED ServiceDiscoveryOperation (125599796) D / RxBle # BluetoothGatt: onServicesDiscovered status = 0 D / RxBle # ConnectionOperationQueue: QUEUED CharacteristicReadOperation (2626026) D / RxBueEDOperation ConnectionOperationQueue: STARTED CharacteristicReadOpera tion (2626026) D / RxBle # BluetoothGatt: onCharacteristicRead характеристика = 0000fa03-0278-03be-4447-091eba91df8e status = 0 D / RxBle # ConnectionOperationQueue: FINISHED CharacteristicReadOperation (2626026) D / RxBle # ClientOperation8UEDQueue25 #ClientOperationQueue: STARTED ConnectOperation (158692575) D / RxBle # BluetoothGatt: onConnectionStateChange newState = 2 status = 0 D / RxBle # ClientOperationQueue: FINISHED ConnectOperation (158692575) D / RxBle # ConnectionOperationOperation ServiceDiscoveryOperation (20778996)> D / RxBle # BluetoothGatt: onServicesDiscovered status = 0
D / RxBle # ConnectionOperationQueue: QUEUED CharacteristicWriteOperation (51009974) D / RxBle # ConnectionOperationOperationQueue: FINISHED ServiceDiscoveryOperation74 )
> D / RxBle # BluetoothGatt: onCharacteristic Записать характеристику = 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)


person yacine    schedule 11.01.2018    source источник
comment
соответствующий пост на nordic devzone   -  person yacine    schedule 12.01.2018
comment
Любые новости? Вам удалось заставить его работать?   -  person Dariusz Seweryn    schedule 30.01.2018
comment
спасибо, сейчас я завершаю переход на Nordic SDK14 и буду тестировать снова. Я выложу результаты   -  person yacine    schedule 31.01.2018


Ответы (3)


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

Чтобы избежать путаницы, есть два уровня: GATT и ATT. Уровень ATT определяет концепцию под названием «Запись в очереди», которая состоит из подготовки запроса / ответа на запись и выполнения запроса / ответа на запись. Идея состоит в том, что все подготовленные записи (каждая из которых имеет размер MTU-5, имеют параметр смещения и дескриптор ATT) помещаются в очередь на периферийной стороне. Записи не фиксируются на периферийном устройстве до тех пор, пока не произойдет Execute Write. (У Execute Write есть флаг, который также можно использовать для отмены всей очереди.)

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

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

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

Для программного устройства Nordic Semiconductor самый простой способ - использовать «Запись в очереди GATTS: обработка стека, атрибуты не требуют авторизации» 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 . Таким образом, он помещает в очередь все подготовленные записи в буфере, предоставляемом вашим приложением, и уведомляет приложение о поступлении команды Execute Write. Затем приложение должно проанализировать все (более или менее) необработанные пакеты ATT Prepared Write Request, которые стек поместил в буфер. Эта структура определяется здесь _m_d___ ОБРАТИТЕ ВНИМАНИЕ, что буфер представляет собой список (массив с объединенными структурами), а не одно значение. Список завершается элементом, содержащим BLE_GATT_HANDLE_INVALID в качестве дескриптора. Я думаю, ваша ошибка в том, что вы разбираете только первый элемент в этом списке.

person Emil    schedule 24.01.2018
comment
Спасибо, Эмиль, это действительно важно, и нигде больше не нашел информации. Благодарность ! код работы находится здесь: devzone. nordicsemi.com/f/nordic-qa/29231/ - person yacine; 07.02.2018

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

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

См. Здесь: https://stackoverflow.com/a/38914831/4321774

person vdelricco    schedule 11.01.2018
comment
спасибо, но я думаю, что это неправда: в BLE есть способ управлять длинными записями: community. nxp.com/thread/332032#441552 - person yacine; 15.01.2018
comment
Отчасти правда. На уровне ATT вы можете отправлять только 20 байтов (или то, что MTU позволяет для конкретной операции), но уровень GATT (который реализует Android) определяет процедуры для атомарного выполнения более длительных операций записи с использованием нескольких пакетов ATT. Поэтому, когда вы выполняете обычную характеристику записи в Android, он будет использовать этот метод автоматически, если значение слишком велико для одного пакета. - person Emil; 25.01.2018
comment
Очаровательно, я этого не знал. Спасибо за ответ @Emil - person vdelricco; 25.01.2018

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

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

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

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

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

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

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

person Dariusz Seweryn    schedule 11.01.2018
comment
привет, Дариуш (я фанат :-)), я читал этот скандинавский пост уже несколько раз. он говорит еще кое-что, что на Android достаточно вызвать writeCharacteristic (...) с данными длиной более 20 байт, чтобы он заработал. Он должен работать на главной панели управления nRF. Android позаботится об этом автоматически. и именно поэтому я не понимаю, почему отправляется только 20B, когда я прошу android (через rxandroidble) написать 85B. - person yacine; 12.01.2018
comment
в нем также говорится, что микропрограммное обеспечение должно быть подготовлено к получению длительной записи, что я и сделал, и я думаю, что он работает хорошо, потому что мне удалось получить первые 20B с помощью правильной процедуры. - person yacine; 12.01.2018
comment
Что касается терминологии поставленные в очередь, подготовленные и длинные, я полностью заблудился после нескольких недель чтения форумов. все, что мне нужно сделать, это отправить, например, 100B длинных данных по одной данной характеристике. - person yacine; 12.01.2018
comment
Длинная запись RxAndroidBle использует не подготовленные записи, а несколько стандартных записей. На самом деле это можно было бы лучше описать в Javadoc ... вот что я наблюдаю. см. мой второй отрывок из журнала с несколькими последовательными записями. но если я сделаю это, я не смогу использовать северный стек для управления повторной сборкой. - person yacine; 12.01.2018
comment
Кажется, есть проблема либо на стороне прошивки, либо на стороне стека Android BLE. RxAndroidBle при использовании RxBleConnection.writeCharacteristic() не изменяет переданный byte[] и передает его (в какой-то момент) исходному BluetoothGattCharacteristic. Если и Android, и прошивка работают правильно, все должно работать. Моя виртуальная машина для разработки nRF просто умерла, но я постараюсь реализовать долгую запись на nRF51 и проверить это. - person Dariusz Seweryn; 12.01.2018
comment
привет, dariusw, извините за вопрос. но я все еще застрял с этой проблемой. - person yacine; 19.01.2018
comment
Мне не удалось протестировать его на этой неделе, хотя я исправил набор инструментов OSX для тестирования, поэтому я смогу сделать это на следующей неделе. - person Dariusz Seweryn; 19.01.2018
comment
@DariuszSeweryn см. Мой ответ stackoverflow.com/a/48432975/556495, который должен прояснить вашу путаницу в терминологии. - person Emil; 25.01.2018
comment
@Emil большое спасибо за подтверждение моих подозрений и за отличный ответ. @yacine Я провел тест с RxAndroidBle и nRF51 с S110, и долгая запись работала из коробки на стороне Android. Обработка этого на стороне периферийного устройства немного более обременительна (необходимость предоставления большего буфера и анализа полученных данных), но это уже другая тема. Я отредактировал свой ответ. - person Dariusz Seweryn; 25.01.2018
comment
Привет, @DariuszSewery, все работает! Это значит следовать советам Эмиля. Я думаю, это означает, что Android (а затем и RxAndroidBle) поддерживает длинную запись без звука. - person yacine; 07.02.2018
comment
Рад это слышать! Да, Android API (и RxAndroidBle) прозрачным образом используют эту функцию записи в очередь (как я упоминал out of the box). Хорошего дня! :) - person Dariusz Seweryn; 07.02.2018