Подтвердить что ты не робот

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

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

Что я сделал до сих пор

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

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

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

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

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

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

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

Я был бы очень благодарен за совет.

4b9b3361

Ответ 1

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

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

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

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

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

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

Ответ 2

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

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

Во-первых, scan (или scan) для BluetoothDevices, заполните 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 (намерение) может потребоваться сделать следующим образом:

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 (на этот раз с другим действием String), и теперь вы можете начать чтение, запись и включение уведомлений/указаний... 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 и управление ею там.

Ответ 3

Я разрабатываю приложение с функциями 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;
    }

Ответ 4

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

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

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

или

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

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

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

Ответ 5

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

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

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