Android 4.3: как подключиться к нескольким устройствам с низким энергопотреблением Bluetooth

Мой вопрос: может ли Android 4.3 (клиент) иметь активные соединения с несколькими устройствами (серверами) BLE? Если да, то как мне этого добиться?

Чем я занимался до сих пор

Я пытаюсь оценить, какой пропускной способности вы можете достичь с помощью BLE и Android 4.3 BLE API. Кроме того, я также пытаюсь выяснить, сколько устройств может быть подключено и активно одновременно. Я использую Nexus 7 (2013), Android 4.4 в качестве главного и TI CC2540 Keyfob в качестве подчиненных.

Я написал простое серверное программное обеспечение для ведомых устройств, которое передает 10000 20-байтовых пакетов через уведомления BLE. Я основал свое приложение для Android на Application Accelerator от Bluetooth SIG.

Он хорошо работает для одного устройства, и я могу достичь пропускной способности полезной нагрузки около 56 кбит при интервале подключения 7,5 мс. Чтобы подключиться к нескольким ведомым устройствам, я последовал совету сотрудника из Северной Европы, который написал в Nordic Developer Zone:

Да, с помощью одного приложения можно обрабатывать несколько ведомых устройств. Вам нужно будет обрабатывать каждое ведомое устройство с помощью одного экземпляра BluetoothGatt. Вам также понадобится специальный BluetoothGattCallback для каждого подчиненного устройства, к которому вы подключаетесь.

Я попробовал это, и это частично сработало. Я могу подключиться к нескольким ведомым устройствам. Я также могу зарегистрироваться для получения уведомлений на нескольких ведомых устройствах. Проблема начинается, когда я запускаю тест. Сначала я получаю уведомления от всех ведомых устройств, но после пары интервалов подключения приходят только уведомления от одного устройства. Примерно через 10 секунд другие подчиненные устройства отключаются, потому что кажется, что они достигли тайм-аута соединения. Иногда я получаю сразу с самого начала теста только уведомления от одного ведомого.

Я также попытался получить доступ к атрибуту через операцию чтения с тем же результатом. После нескольких прочтений пришли ответы только с одного устройства.

Мне известно, что на этом форуме есть несколько похожих вопросов: Поддерживает ли Android 4.3 несколько подключений устройств BLE?, Имеет ли собственная реализация BLE GATT для Android синхронный характер? или Ble multiple connection. Но ни один из этих ответов не дал мне понять, возможно ли это и как это сделать.

Буду очень признателен за совет.


person Andreas Mueller    schedule 20.01.2014    source источник
comment
Обязательно нужно подключение? Если вас беспокоит сокрытие данных и надежный или полный отказ, возможно, вы могли бы просто поместить их в широковещательные пакеты и сканировать их.   -  person Chris Stratton    schedule 16.03.2014
comment
Спасибо за ваш повтор. Мне нужно использовать подключенный режим из соображений надежности   -  person Andreas Mueller    schedule 17.03.2014
comment
Я поискал по всей сети примеры того, как сделать то, что вы делали в вашей тестовой программе @Andreas Mueller. Не могли бы вы помочь мне и показать, как отправлять уведомления с Android на чип CC2540 с измененным кодом ускорителя приложений? (может быть ссылка на ваш проект?)   -  person HenrikS    schedule 26.05.2014


Ответы (5)


Я подозреваю, что каждый, кто добавляет задержки, просто позволяет системе BLE выполнить действие, которое вы просили, прежде чем отправить другое. Система BLE Android не имеет очереди. Если вы это сделаете

BluetoothGatt g;
g.writeDescriptor(a);
g.writeDescriptor(b);

тогда первая операция записи будет немедленно перезаписана второй. Да, это действительно глупо, и в документации, вероятно, стоит упомянуть об этом.

Если вы вставляете ожидание, оно позволяет завершить первую операцию перед выполнением второй. Однако это огромный уродливый хакер. Лучшее решение - реализовать свою собственную очередь (как должна быть у Google). К счастью, Nordic выпустили для нас одну.

https://github.com/NordicSemiconductor/puck-central-android/tree/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt

Изменить: кстати, это универсальное поведение для BLE API. WebBluetooth ведет себя так же (но Javascript действительно упрощает использование), и я считаю, что iOS BLE API также ведет себя так же.

person Timmmm    schedule 26.05.2015
comment
Все ли Android-смартфоны поддерживают несколько соединений Gatt одновременно? Могу ли я узнать телефон, который вы тестировали, и версию Android API. Я полагаю, что это также характерные черты базового чипа Bluetooth в телефоне. - person Raulp; 21.12.2016
comment
Да, все телефоны поддерживают несколько подключений к Gatt. См. этот вопрос. - person Timmmm; 21.12.2016
comment
Вы можете создать очередь дескрипторов и просто вызвать первый g.writeDescriptor (a); затем в onDescriptorWrite вы продолжаете работу с оставшимися дескрипторами, пока все не будут записаны. - person Pedro Antonio; 04.02.2021

Повторение проблемы bluetooth-lowenergy на : я все еще использую задержки.

Концепция: после каждого крупного действия, которое вызывает BluetoothGattCallback (например, соединение, обнаружение службы, запись, читайте) нужен деалий. P.S. взгляните на пример Google на BLE образец API уровня 19 для подключения понять, как следует отправлять трансляции, получить общее представление и т. д.

Во-первых, сканирование (или scan) для устройств Bluetooth, заполните connectionQueue нужные устройства и вызовите initConnection ().

Взгляните на следующий пример.

private Queue<BluetoothDevice> connectionQueue = new LinkedList<BluetoothDevice>();

public void initConnection(){
    if(connectionThread == null){
        connectionThread = new Thread(new Runnable() {
            @Override
            public void run() {
                connectionLoop();
                connectionThread.interrupt();
                connectionThread = null;
            }
        });

        connectionThread.start();
    }
}

private void connectionLoop(){
    while(!connectionQueue.isEmpty()){
        connectionQueue.poll().connectGatt(context, false, bleInterface.mGattCallback);
        try {
            Thread.sleep(250);
        } catch (InterruptedException e) {}
    }
}

Теперь, если все в порядке, вы установили подключения и BluetoothGattCallback.onConnectionStateChange (BluetoothGatt gatt, int status, int newState).

public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        switch(status){
            case BluetoothGatt.GATT_SUCCESS:
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    broadcastUpdate(BluetoothConstants.ACTION_GATT_CONNECTED, gatt);
                }else if(newState == BluetoothProfile.STATE_DISCONNECTED){
                    broadcastUpdate(BluetoothConstants.ACTION_GATT_DISCONNECTED, gatt);
                }
                break;
        }

    }
protected void broadcastUpdate(String action, BluetoothGatt gatt) {
    final Intent intent = new Intent(action);

    intent.putExtra(BluetoothConstants.EXTRA_MAC, gatt.getDevice().getAddress());

    sendBroadcast(intent);
}

P.S. sendBroadcast (intent) может потребоваться сделать следующим образом:

Context context = activity.getBaseContext();
context.sendBroadcast(intent);

Затем трансляцию принимает BroadcastReceiver .onReceive (...)

public BroadcastReceiver myUpdateReceiver = new BroadcastReceiver(){

    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if(BluetoothConstants.ACTION_GATT_CONNECTED.equals(action)){
            //Connection made, here you can make a decision: do you want to initiate service discovery.
            // P.S. If you are working with multiple devices, 
            // make sure that you start the service discovery 
            // after all desired connections are made
        }
        ....
    }
}

Сделав все, что вы хотите в приемнике вещания, я продолжаю вот как:

private Queue<BluetoothGatt> serviceDiscoveryQueue = new LinkedList<BluetoothGatt>();

private void initServiceDiscovery(){
    if(serviceDiscoveryThread == null){
        serviceDiscoveryThread = new Thread(new Runnable() {
            @Override
            public void run() {
                serviceDiscovery();

                serviceDiscoveryThread.interrupt();
                serviceDiscoveryThread = null;
            }
        });

        serviceDiscoveryThread.start();
    }
}

private void serviceDiscovery(){
    while(!serviceDiscoveryQueue.isEmpty()){
        serviceDiscoveryQueue.poll().discoverServices();
        try {
            Thread.sleep(250);
        } catch (InterruptedException e){}
    }
}

Опять же, после успешного обнаружения службы, BluetoothGattCallback.onServicesDiscovered (...) называется. Опять же, я отправляю намерение в BroadcastReceiver (на этот раз с другой строкой действия), и теперь вы можете начать читать, писать и включать уведомления / индикации ... P.S. Если вы работаете с несколькими устройствами, убедитесь, что вы начинаете чтение, запись и т. Д. После того, как все устройства сообщат, что их службы были обнаружены.

private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();

private void startThread(){

    if(initialisationThread == null){
        initialisationThread = new Thread(new Runnable() {
            @Override
            public void run() {
                loopQueues();

                initialisationThread.interrupt();
                initialisationThread = null;
            }
        });

        initialisationThread.start();
    }

}

private void loopQueues() {

    while(!characteristicReadQueue.isEmpty()){
        readCharacteristic(characteristicReadQueue.poll());
        try {
            Thread.sleep(BluetoothConstants.DELAY);
        } catch (InterruptedException e) {}
    }
    // A loop for starting indications and all other stuff goes here!
}

BluetoothGattCallback будет иметь все ваши входящие данные от датчика BLE. Хорошая практика - отправить широковещательное сообщение с данными на ваш BroadcastReceiver и обработать его там.

person Rain    schedule 10.12.2014
comment
Спасибо за то, что поделился ссылкой на пример Android. Иногда мы путешествуем по миру, но забываем взглянуть на примеры Android .. :) - person Rahul Rastogi; 16.02.2015
comment
Использование обратного вызова Gatt - ЕДИНСТВЕННЫЙ правильный способ обработки операций BLE. Я очень удивлен, увидев так много сообщений о случайных задержках. Эти методы асинхронны - кто знает, сколько времени они могут занять. В этом вся цель обратного вызова Gatt. Да, очень неприятно эффективно реализовать этот обратный вызов, чтобы он хорошо работал с остальной частью вашей системы, но это единственный выход. - person SuperDeclarative; 29.05.2015
comment
Я пытаюсь справиться с двумя связями. Я уже реализовал большую часть этого самостоятельно. Однако, когда я настраиваю каждое устройство на получение уведомлений, это, кажется, происходит дважды на последнем устройстве, которое я подключаю. Кто-нибудь подтвердил, что этот ответ работает? Спасибо, я знаю, что это старый пост - person luckyging3r; 21.03.2018

Я сам разрабатываю приложение с функциями BLE. Мне удалось подключиться к нескольким устройствам и включить уведомления, чтобы реализовать задержки.

Поэтому я создаю новый поток (чтобы не блокировать поток пользовательского интерфейса) и в новом потоке подключаюсь и включаю уведомления.

Например, после BluetoothDevice.connectGatt (); вызвать Thread.sleep ();

И добавьте такую ​​же задержку для уведомлений о чтении / записи и включении / отключении уведомлений.

ИЗМЕНИТЬ

Используйте такой способ ожидания, чтобы Android не вызвал ошибку ANR

public static boolean waitIdle() {
        int i = 300;
        i /= 10;
        while (--i > 0) {
            if (true)
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

        }

        return i > 0;
    }
person Rain    schedule 16.03.2014
comment
Я изменил свой код так, что теперь он последовательно подключается и регистрируется для получения уведомлений на каждом ведомом устройстве. И это устранило все проблемы. Так что большое спасибо за вашу помощь. - person Andreas Mueller; 17.03.2014
comment
Задержки - это способ решения реальной проблемы. Смотрите мой ответ. - person Timmmm; 26.05.2015

Рейн прав в своем ответе, вам нужны задержки практически для всего, когда вы работаете с BLE в Android. Я разработал на нем несколько приложений, и это действительно необходимо. Используя их, вы избегаете множества сбоев.

В моем случае я использую задержки после каждой команды чтения / записи. Таким образом вы почти всегда получаете ответ от устройства BLE. Я делаю что-то вроде этого: (конечно, все делается в отдельном потоке, чтобы избежать большой работы в основном потоке)

 readCharacteristic(myChar);
 try {
    Thread.sleep(100);
 } catch (InterruptedException e) {
    e.printStackTrace();
 }
 myChar.getValue();

or:

 myChar.setValue(myByte);
 writeCharacteristic(myChar);
 try {
    Thread.sleep(100);
 } catch (InterruptedException e) {
    e.printStackTrace();
 }

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

Надеюсь, это поможет, даже если это не совсем ответ на ваш вопрос.

person kodartcha    schedule 13.08.2014
comment
Например, мне потребовалась задержка в 500 мс, чтобы все заработало на 100%. - person Rain; 13.08.2014
comment
Это очень плохое решение. Обратные вызовы нужны для доставки ответов. Вместо этого ждать случайной задержки действительно плохо, поскольку вы не знаете, действительно ли операция завершена (радиопомехи могут добавить непредсказуемого замедления). Кроме того, очевидно, что он не дает ответ как можно скорее. - person Emil; 05.12.2020
comment
@Emil, спасибо за ваш комментарий в ответе 6-летней давности, который даже не принят! С тех пор многое изменилось во фреймворке BLE ... - person kodartcha; 06.12.2020
comment
Не совсем. Эта часть API сегодня выглядит точно так же, как и 6 лет назад. - person Emil; 06.12.2020

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

Я бы попытался переключиться на цикл опроса (скажем, опросить элементы, о которых идет речь, 1 / сек) и посмотреть, повысится ли ваша стабильность. Я бы также подумал о переключении на другое ведомое устройство (скажем, HRM или TI SensorTag), чтобы узнать, есть ли проблема с кодом ведомой стороны (если вы не можете протестировать это на iOS или другой платформе и подтвердить, что это не так. часть вопроса).

Изменить: Ссылка для ограничение уведомлений

person Ben Von Handorf    schedule 20.01.2014
comment
Спасибо за ваш ответ. Я думаю, что код на ведомой стороне в порядке, потому что тест выполняется под iOS с 8 ведомыми устройствами. Я также попытался получить доступ к атрибуту через операцию чтения с тем же результатом. - person Andreas Mueller; 20.01.2014
comment
Затем я бы попытался быстро изменить цикл опроса вместо уведомлений и посмотреть, стабилизирует ли это соединение вообще. Я пытаюсь собрать эквивалентную установку для тестирования, но, возможно, этого не сделать сегодня. - person Ben Von Handorf; 20.01.2014
comment
Спасибо. Я был бы очень признателен за это. - person Andreas Mueller; 21.01.2014