it-swarm.com.de

Android 4.3: Verbindung mit mehreren Bluetooth Low Energy-Geräten herstellen

Meine Frage ist: Kann Android 4.3 (Client) aktive Verbindungen mit mehreren BLE-Geräten (Servern) haben? Wenn ja, wie kann ich das erreichen?

Was ich bisher getan habe

Ich versuche zu ermitteln, welchen Durchsatz Sie mit BLE und der Android 4.3 BLE-API erreichen können. Außerdem versuche ich auch herauszufinden, wie viele Geräte gleichzeitig angeschlossen und aktiv sein können. Ich benutze einen Nexus 7 (2013), Android 4.4 als Master und TI CC2540 Keyfob als Slaves.

Ich habe eine einfache Serversoftware für die Slaves geschrieben, die 10000 20Byte-Pakete über BLE-Benachrichtigungen überträgt. Ich habe meine Android App auf dem Application Accelerator von der Bluetooth SIG basiert.

Es funktioniert gut für ein Gerät und ich kann bei einem Verbindungsintervall von 7,5 ms einen Nutzdaten-Durchsatz von 56 kBits erreichen. Um sich mit mehreren Slaves zu verbinden, folgte ich dem Rat eines nordischen Angestellten, der in der Nordic Developer Zone schrieb:

Ja, es ist möglich, mehrere Slaves mit einer einzigen App zu bedienen. Sie müssten jeden Slave mit einer BluetoothGatt-Instanz behandeln. Sie benötigen außerdem einen bestimmten BluetoothGattCallback für jeden Slave, zu dem Sie eine Verbindung herstellen.

Also habe ich es versucht und es funktioniert teilweise. Ich kann mich mit mehreren Slaves verbinden. Ich kann mich auch für Benachrichtigungen bei mehreren Slaves anmelden. Das Problem beginnt, wenn ich den Test starte. Ich erhalte zunächst Benachrichtigungen von allen Slaves, aber nach ein paar Verbindungsintervallen werden nur die Benachrichtigungen von einem Gerät durchgelassen. Nach etwa 10 Sekunden trennen sich die anderen Slaves, da sie scheinbar das Verbindungszeitlimit erreichen. Manchmal erhalte ich gleich zu Beginn des Tests nur Benachrichtigungen von einem Slave.

Ich habe auch versucht, auf das Attribut über eine Leseoperation mit demselben Ergebnis zuzugreifen. Nach ein paar Lesevorgängen kamen nur die Antworten von einem Gerät durch.

Mir ist bekannt, dass es in diesem Forum einige ähnliche Fragen gibt: Unterstützt Android 4.3 mehrere BLE-Geräteverbindungen? , Hat die native Android BLE GATT-Implementierung synchronen Charakter? oder Mehrfachverbindung brechen . Aber keine dieser Antworten hat mir klar gemacht, ob es möglich ist und wie es geht.

Für einen Rat wäre ich sehr dankbar.

34
Andreas Mueller

Ich vermute, dass jeder, der Verzögerungen hinzufügt, nur zulässt, dass das BLE-System die angeforderte Aktion abschließt, bevor Sie eine weitere einreichen. Das BLE-System von Android hat keine Warteschlangen. Wenn Sie tun 

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

dann wird der erste Schreibvorgang sofort mit dem zweiten überschrieben. Ja, es ist wirklich dumm und die Dokumentation sollte dies wahrscheinlich erwähnen.

Wenn Sie eine Wartezeit einfügen, kann der erste Vorgang abgeschlossen werden, bevor der zweite Vorgang ausgeführt wird. Das ist ein riesiger hässlicher Hack. Eine bessere Lösung ist die Implementierung einer eigenen Warteschlange (wie Google dies tun sollte). Glücklicherweise hat Nordic einen für uns veröffentlicht.

https://github.com/NordicSemiconductor/Puck-central-Android/tree/master/PuckCentral/app/src/main/Java/no/nordicsemi/puckcentral/bluetooth/gatt

Bearbeiten: Übrigens ist dies das universelle Verhalten für BLE-APIs. WebBluetooth verhält sich auf dieselbe Weise (aber Javascript vereinfacht die Verwendung), und ich glaube, dass die BLE-API von iOS dasselbe Verhalten aufweist.

21
Timmmm

Aufrufen der bluetooth-lowenergy Problem auf  Android : Ich verwende immer noch Verzögerungen.

Das Konzept: Nach jeder größeren Aktion, die den BluetoothGattCallback (z. B. Verbindung, Service Discovery, Write, Read) auslöst, ist ein Dealy erforderlich. P.S. Schauen Sie sich das Google-Beispiel auf BLE API Level 19-Beispiel für Konnektivität an um zu verstehen, wie Broadcasts gesendet werden sollen, und erhalten Sie ein allgemeines Verständnis usw.

scan (oder scan ) für Bluetooth - Geräte, füllen Sie die connectionQueue mit den gewünschten Geräten und rufen Sie initConnection () auf.

Schauen Sie sich das folgende Beispiel an.

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) {}
    }
}

Wenn alles in Ordnung ist, haben Sie Verbindungen hergestellt und BluetoothGattCallback.onConnectionStateChange (BluetoothGatt gatt, int status, int newState) wurde aufgerufen.

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) muss möglicherweise folgendermaßen ausgeführt werden:

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

Dann wird die Sendung von BroadcastReceiver.onReceive (...) empfangen.

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
        }
        ....
    }
}

Nachdem Sie im Rundfunkempfänger alles gemacht haben, was Sie wollen, gehe ich folgendermaßen vor

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){}
    }
}

Nach einer erfolgreichen Serviceerkennung wird BluetoothGattCallback.onServicesDiscovered (...) erneut aufgerufen. Ich sende erneut eine Absicht an den BroadcastReceiver (diesmal mit einer anderen Aktion String) und jetzt können Sie damit beginnen, Benachrichtigungen/Anzeigen zu lesen, zu schreiben und zu aktivieren ...P.S. Wenn Sie mit mehreren Geräten arbeiten, stellen Sie sicher, dass Sie mit dem Lesen, Schreiben usw. beginnen, nachdem alle Geräte gemeldet haben, dass ihre Dienste erkannt wurden.

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 empfängt alle eingehenden Daten vom BLE-Sensor. Es empfiehlt sich, eine Sendung mit den Daten an Ihren BroadcastReceiver zu senden und dort zu bearbeiten.

13
Rain

Ich entwickle selbst eine App mit BLE-Funktionen. Ich habe es geschafft, eine Verbindung zu mehreren Geräten herzustellen und Benachrichtigungen zu aktivieren, Verzögerungen zu implementieren.

Also erstelle ich einen neuen Thread (um UI-Thread nicht zu blockieren) und in dem neuen Thread Benachrichtigungen verbinden und aktivieren.

Zum Beispiel nach BluetoothDevice.connectGatt (); rufen Sie Thread.sleep () auf;

Fügen Sie dieselbe Verzögerung für Lesen/Schreiben und Aktivieren/Deaktivieren von Benachrichtigungen hinzu.

EDIT

Warten Sie so, damit Android nicht auf ANR reagiert

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;
    }
7
Rain

Regen ist in seiner Antwort richtig, Sie brauchen ziemlich viel Zeit, wenn Sie mit BLE in Android arbeiten. Ich habe mehrere Apps damit entwickelt und es ist wirklich notwendig. Durch die Verwendung vermeiden Sie viele Abstürze.

In meinem Fall verwende ich Verzögerungen nach jedem Lese-/Schreibbefehl. Auf diese Weise stellen Sie sicher, dass Sie fast immer die Antwort vom BLE-Gerät erhalten. Ich mache so etwas: (natürlich wird alles in einem separaten Thread erledigt, um zu viel Arbeit am Haupt-Thread zu vermeiden)

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

oder:

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

Dies ist sehr nützlich, wenn Sie mehrere Merkmale in einer Reihe lesen/schreiben ... Da Android schnell genug ist, um die Befehle fast sofort auszuführen, wenn Sie keine Verzögerung zwischen ihnen verwenden, werden möglicherweise Fehler oder inkohärente Werte angezeigt.

Hoffe es hilft auch wenn es nicht genau die Antwort auf deine Frage ist.

2
margabro

Leider sind die Benachrichtigungen im aktuellen Android BLE-Stack etwas fehlerhaft. Es gibt einige fest vorgegebene Grenzwerte, und ich habe sogar mit einem einzelnen Gerät Stabilitätsprobleme festgestellt. (Ich habe an einem Punkt gelesen, dass Sie nur 4 Benachrichtigungen haben könnten ... nicht sicher, ob dies auf allen Geräten oder pro Gerät geschieht. Der Versuch, die Quelle für diese Informationen jetzt zu finden.)

Ich würde versuchen, auf eine Abfrageschleife umzuschalten (sagen Sie, die betreffenden Fragen 1/Sek. Abfragen) und sehen, ob Ihre Stabilität zunimmt. Ich würde auch in Betracht ziehen, zu einem anderen Slave-Gerät (z. B. einem HRM oder dem TI SensorTag) zu wechseln, um zu sehen, ob möglicherweise ein Problem mit dem Slave-seitigen Code vorliegt (es sei denn, Sie können das gegen iOS oder eine andere Plattform testen und bestätigen, dass dies nicht der Fall ist Teil der Ausgabe).

Bearbeiten: Referenz für Benachrichtigungsbeschränkung

0
Ben Von Handorf