2014-01-20 3 views
31

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

То, что я сделал до сих пор

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

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

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

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

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

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

Я знаю, что на этом форуме есть несколько подобных вопросов: Does Android 4.3 support multiple BLE device connections?, Has native Android BLE GATT implementation synchronous nature? или Ble multiple connection. Но ни один из этих ответов не дал мне понять, если это возможно и как это сделать.

Я буду очень признателен за консультацию.

+0

Вам обязательно нужно подключиться? Если вы обеспокоены скрытием данных и надежностью или полным сбоем, возможно, вы можете просто поместить их в широковещательные пакеты и просмотреть их. –

+0

Спасибо за ваш повтор. Я должен использовать подключенный режим по соображениям надежности. –

+0

У меня есть поиск по всей сети, чтобы найти примеры, как сделать то, что вы сделали в своей тестовой программе @ Andreas Mueller. Не могли бы вы быть добрыми, чтобы помочь мне, и показать мне, как отправлять уведомления с Android на чип CC2540 с помощью модифицированного «ускорителя приложений»? (возможно, ссылка на ваш проект?) – HenrikS

ответ

18

Я подозреваю, что все, добавляющие задержки, просто позволяют системе 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

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

+0

все Android-смартфоны поддерживают одновременное подключение нескольких gatt? Могу ли я узнать телефон, который вы протестировали, и версию Android API. Предположим, что это и трактовка базового Bluetooth-чипа на телефоне. – Raulp

+0

Да, все телефоны поддерживают несколько соединений gatt. См. [Этот вопрос] (https://stackoverflow.com/questions/34400182/android-limit-of-simultaneous-ble-connections). – Timmmm

0

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

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

Edit: Reference for notification limitation

+0

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

+0

Затем я попытался бы быстро изменить цикл опроса вместо уведомлений и посмотреть, стабилизирует ли это соединение вообще. Я пытаюсь получить эквивалентную установку, собранную для тестирования, но это может быть сделано не сегодня. –

+0

Спасибо.Я был бы очень признателен. –

7

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

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

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

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

EDIT

Используйте ждать, как это так, что Android dindn't reaise 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; 
    } 
+0

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

+0

Добро пожаловать;) – Rain

+1

Задержки - это взлом, который обходит настоящую проблему. См. Мой ответ. – Timmmm

2

Дождь прямо в своем ответе, что вам нужно для задержки почти все, когда вы работаете с 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 достаточно быстро выполнить команды почти мгновенно, если вы не используете задержку между ними, вы можете получить ошибки или некогерентные значения ...

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

+0

Например, мне потребовалось 500 мс задержки, чтобы все работало на 100%. – Rain

11

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

Концепция: после каждого крупного действия, которое провоцирует BluetoothGattCallback (например, coninction, service discovery, write, read), необходимо иметь дело. Постскриптум посмотреть на примере Google на BLE API level 19 sample for connectivity, чтобы понять, как Трансляции следует отправить и получить общее представление и т.д ...

Во-первых, 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 и управление ею там.

+0

Thnks для обмена ссылкой на Android-пример. Иногда мы перемещаемся по миру, но забываем взглянуть на примеры Android. :) –

+5

Использование обратного вызова Gatt - ТОЛЬКО правильный способ обработки операций BLE. Я очень удивлен, увидев столько сообщений о случайных задержках. Эти методы асинхронны - кто знает, сколько времени они могут принять. Это и есть цель обратного вызова Gatt. Да, это очень раздражает, чтобы эффективно реализовать этот обратный вызов, чтобы он хорошо играл с остальной частью вашей системы, но это единственный способ пойти. – MattC

Смежные вопросы