bluetooth bluetooth - Android BLE API:GATT-Benachrichtigung nicht erhalten




low energy (9)

Testgerät: Nexus 4, Android 4.3

Verbindung funktioniert gut, aber die onCharacteristicChanged Methode meines Rückrufs wird nie aufgerufen. Allerdings registriere ich mich für Benachrichtigungen mit setCharacteristicNotification(char, true) in onServicesDiscovered und diese Funktion gibt sogar true zurück.

Geräteprotokoll (Es gibt tatsächlich keine Nachrichten, wenn Benachrichtigungen über das Bluetooth-Gerät gesendet werden sollen / sollen):

07-28 18:15:06.936  16777-16809/de.ffuf.leica.sketch D/BluetoothGatt: setCharacteristicNotification() - uuid: 3ab10101-f831-4395-b29d-570977d5bf94 enable: true
07-28 18:15:06.936    4372-7645/com.android.bluetooth D/BtGatt.GattService: registerForNotification() - address=C9:79:25:34:19:6C enable: true
07-28 18:15:06.936    4372-7645/com.android.bluetooth D/BtGatt.btif: btif_gattc_reg_for_notification
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1018
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.GattService: onRegisterForNotifications() - address=null, status=0, registered=1, charUuid=3ab10101-f831-4395-b29d-570977d5bf94
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1016
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1018
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.GattService: onRegisterForNotifications() - address=null, status=0, registered=1, charUuid=3ab10102-f831-4395-b29d-570977d5bf94
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1016
07-28 18:15:06.946    4372-7684/com.android.bluetooth E/bt-btif: already has a pending command!!
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1013
07-28 18:15:06.946    4372-7684/com.android.bluetooth E/bt-btif: already has a pending command!!
07-28 18:15:06.946    4372-7645/com.android.bluetooth D/BtGatt.btif: btgattc_handle_event: Event 1013
07-28 18:15:06.946    4372-7684/com.android.bluetooth E/bt-btif: already has a pending command!!
07-28 18:15:06.976    4372-7645/com.android.bluetooth D/BtGatt.btif: btif_gattc_upstreams_evt: Event 9

GATT-Benachrichtigungen funktionieren gut mit iOS und die App funktioniert im Grunde wie bei Android (Registrierung für Benachrichtigungen usw.).

Hat jemand anderes dies mit einer möglichen Lösung erfahren?


Answers

Ich hatte noch einen anderen Grund, den ich hinzufügen möchte, da er mich den ganzen Tag verrückt machte:

Auf meinem Samsung Note 3 habe ich keine Benachrichtigungen über geänderte Werte erhalten, während derselbe Code auf jedem anderen Gerät funktioniert hat, mit dem ich getestet habe.

Neustart des Geräts löste alle Probleme. Offensichtlich, aber wenn Sie in dem Problem sind, vergessen Sie, daran zu denken.


Es scheint, als hätten Sie vergessen, den Deskriptor zu schreiben, der Ihrem BLE-Gerät sagt, dass es in diesem Modus gehen soll. Weitere Informationen finden Sie in den Codezeilen unter http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#notification

Ohne diesen Deskriptor zu erhalten, erhalten Sie niemals Aktualisierungen für ein Merkmal. Der Aufruf von setCharacteristicNotification ist nicht genug. Dies ist ein häufiger Fehler.

Code abgeschnitten

protected static final UUID CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

public boolean setCharacteristicNotification(BluetoothDevice device, UUID serviceUuid, UUID characteristicUuid,
        boolean enable) {
    if (IS_DEBUG)
        Log.d(TAG, "setCharacteristicNotification(device=" + device.getName() + device.getAddress() + ", UUID="
                + characteristicUuid + ", enable=" + enable + " )");
    BluetoothGatt gatt = mGattInstances.get(device.getAddress()); //I just hold the gatt instances I got from connect in this HashMap
    BluetoothGattCharacteristic characteristic = gatt.getService(serviceUuid).getCharacteristic(characteristicUuid);
    gatt.setCharacteristicNotification(characteristic, enable);
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID);
    descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : new byte[] { 0x00, 0x00 });
    return gatt.writeDescriptor(descriptor); //descriptor write operation successfully started? 
}

Wenn Sie den Wert auf den Deskriptor setzen, anstatt descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) setzen, setzen Sie descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) . Die Callbacks für onCharacteristicChanged werden jetzt aufgerufen.


Ich nehme an (Sie haben Ihren Quellcode nicht angegeben), dass Sie ihn nicht wie von Google gewünscht implementiert haben:

(1)

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

und dann

(2)

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);

Ich vermute, 2 fehlt. In diesem Fall glaube ich, dass Low-Level-Benachrichtigungen ausgelöst werden, aber sie werden nie an die Anwendungsebene gemeldet.


Hier ist ein einfacher Weg, um es zu tun, aber lassen Sie mich wissen, wenn Sie irgendwelche Nachteile sehen.

Schritt 1 Deklariere boolesche Variablen

private boolean char_1_subscribed = false;
private boolean char_2_subscribed = false;
private boolean char_3_subscribed = false;

Schritt 2 abonnieren Sie das erste Merkmal im onServicesDiscovered Callback:

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
    } else {
        Log.w(TAG, "onServicesDiscovered received: " + status);
    }
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if(!char_1_subscribed)
        subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_1)); char_1_subscribed = true;
}

Schritt 3

Abonnieren Sie alle anderen, nachdem der Rückruf onCharacteristicChanged ausgelöst wurde

@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
                                    BluetoothGattCharacteristic characteristic) {
    if(UUID_CHAR_1.equals(characteristic.getUuid()))
    {
        if(!char_1_subscribed)
            subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_2)); char_2_subscribed = true;
    }
    if(UUID_CHAR_2.equals(characteristic.getUuid()))
    {
        if(!char_3_subscribed)
            subscribeToNotification(gatt.getService(UUID_SERVICE).getCharacteristic(UUID_CHAR_3)); char_3_subscribed = true;
    }
}

Nun, dieser API-Name führte sicherlich zu einigen Verwirrungen beim App-Entwickler, wenn er nicht der Bluetooth-Hintergrundprogrammierer war.

Aus der Sicht der Bluetooth-Kernspezifikation, Zitat aus der Kernspezifikation 4.2, Band 3, Abschnitt G, Abschnitt 3.3.3.3 "Konfiguration der Client-Eigenschaften":

Der charakteristische Deskriptorwert ist ein Bitfeld. Wenn ein Bit gesetzt ist, muss diese Aktion aktiviert sein, andernfalls wird es nicht verwendet.

und Abschnitt 4.10

Benachrichtigungen können mit dem Deskriptor für die Beschreibung der Clientcharakteristik konfiguriert werden (siehe Abschnitt 3.3.3.3).

Dies bedeutet eindeutig, dass, wenn der Client die Benachrichtigung (oder eine Anzeige, die eine Antwort benötigt) vom Server erhalten möchte, das "Notification" -Bit auf 1 schreiben sollte ("Indication" -Bit auch auf 1, andernfalls).

Der Name "setCharacteristicNotification" gibt uns jedoch den Hinweis, dass der Client Benachrichtigungen erhalten würde, wenn wir die Parameter dieser API als TURE festlegen würden. Leider setzt diese API nur das lokale Bit, um die Benachrichtigung an Apps senden zu können, wenn eine Benachrichtigung per Fernzugriff erfolgt. Siehe Code von Bluedroid:

    /*******************************************************************************
    **
    ** Function         BTA_GATTC_RegisterForNotifications
    **
    ** Description      This function is called to register for notification of a service.
    **
    ** Parameters       client_if - client interface.
    **                  bda - target GATT server.
    **                  p_char_id - pointer to GATT characteristic ID.
    **
    ** Returns          OK if registration succeed, otherwise failed.
    **
    *******************************************************************************/

    tBTA_GATT_STATUS BTA_GATTC_RegisterForNotifications (tBTA_GATTC_IF client_if,
                                                         BD_ADDR bda,
                                                         tBTA_GATTC_CHAR_ID *p_char_id)

{
    tBTA_GATTC_RCB      *p_clreg;
    tBTA_GATT_STATUS    status = BTA_GATT_ILLEGAL_PARAMETER;
    UINT8               i;

    if (!p_char_id)
    {
        APPL_TRACE_ERROR("deregistration failed, unknow char id");
        return status;
    }

    if ((p_clreg = bta_gattc_cl_get_regcb(client_if)) != NULL)
    {
        for (i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++)
        {
            if ( p_clreg->notif_reg[i].in_use &&
                 !memcmp(p_clreg->notif_reg[i].remote_bda, bda, BD_ADDR_LEN) &&
                  bta_gattc_charid_compare(&p_clreg->notif_reg[i].char_id, p_char_id))
            {
                APPL_TRACE_WARNING("notification already registered");
                status = BTA_GATT_OK;
                break;
            }
        }
        if (status != BTA_GATT_OK)
        {
            for (i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++)
            {
                if (!p_clreg->notif_reg[i].in_use)
                {
                    memset((void *)&p_clreg->notif_reg[i], 0, sizeof(tBTA_GATTC_NOTIF_REG));

                    p_clreg->notif_reg[i].in_use = TRUE;
                    memcpy(p_clreg->notif_reg[i].remote_bda, bda, BD_ADDR_LEN);

                    p_clreg->notif_reg[i].char_id.srvc_id.is_primary = p_char_id->srvc_id.is_primary;
                    bta_gattc_cpygattid(&p_clreg->notif_reg[i].char_id.srvc_id.id, &p_char_id->srvc_id.id);
                    bta_gattc_cpygattid(&p_clreg->notif_reg[i].char_id.char_id, &p_char_id->char_id);

                    status = BTA_GATT_OK;
                    break;
                }
            }
            if (i == BTA_GATTC_NOTIF_REG_MAX)
            {
                status = BTA_GATT_NO_RESOURCES;
                APPL_TRACE_ERROR("Max Notification Reached, registration failed.");
            }
        }
    }
    else
    {
        APPL_TRACE_ERROR("Client_if: %d Not Registered", client_if);
    }

    return status;
}'

Was zählt, war die Schreibaktion des Deskriptors.


@ Boni2k - Ich habe die gleichen Probleme. In meinem Fall habe ich 3 Benachrichtigungsmerkmale und eine Handvoll Lese- / Schreibeigenschaften.

Was ich gefunden habe, ist, dass es eine Abhängigkeit zwischen writeGattDescriptor und readCharacteristic . Alle writeGattDescriptors müssen zuerst und vollständig sein, bevor Sie leseCharacteristic-Aufrufe ausführen.

Hier ist meine Lösung mit Queues . Jetzt bekomme ich Benachrichtigungen und alles andere funktioniert gut:

Erstellen Sie zwei Warteschlangen wie folgt:

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

Schreiben Sie dann alle Ihre Deskriptoren sofort nach der Erkennung mit dieser Methode:

public void writeGattDescriptor(BluetoothGattDescriptor d){
    //put the descriptor into the write queue
    descriptorWriteQueue.add(d);
    //if there is only 1 item in the queue, then write it.  If more than 1, we handle asynchronously in the callback above
    if(descriptorWriteQueue.size() == 1){   
        mBluetoothGatt.writeDescriptor(d);      
    }
}

und dieser Rückruf:

public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {         
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.d(TAG, "Callback: Wrote GATT Descriptor successfully.");           
        }           
        else{
            Log.d(TAG, "Callback: Error writing GATT Descriptor: "+ status);
        }
        descriptorWriteQueue.remove();  //pop the item that we just finishing writing
        //if there is more to write, do it!
        if(descriptorWriteQueue.size() > 0)
            mBluetoothGatt.writeDescriptor(descriptorWriteQueue.element());
        else if(readCharacteristicQueue.size() > 0)
            mBluetoothGatt.readCharacteristic(readQueue.element());
    };

Die Methode zum normalen Lesen eines Merkmals sieht dann so aus:

public void readCharacteristic(String characteristicName) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(kYourServiceUUIDString));
    BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(characteristicName));
    //put the characteristic into the read queue        
    readCharacteristicQueue.add(c);
    //if there is only 1 item in the queue, then read it.  If more than 1, we handle asynchronously in the callback above
    //GIVE PRECEDENCE to descriptor writes.  They must all finish first.
    if((readCharacteristicQueue.size() == 1) && (descriptorWriteQueue.size() == 0))
        mBluetoothGatt.readCharacteristic(c);              
}

und mein Lese-Rückruf:

public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic,
                                     int status) {
        readCharacteristicQueue.remove();
        if (status == BluetoothGatt.GATT_SUCCESS) {
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);                                
        }
        else{
            Log.d(TAG, "onCharacteristicRead error: " + status);
        }

        if(readCharacteristicQueue.size() > 0)
            mBluetoothGatt.readCharacteristic(readCharacteristicQueue.element());
    }

Erfahrene Probleme in früheren Versionen von Android, die Benachrichtigungen erhalten (eine Anzeige, die registriert wurde) und hatten danach immer ein merkwürdiges Disconnect-Ereignis. Es stellte sich heraus, dass wir uns für Benachrichtigungen zu fünf Merkmalen angemeldet hatten.

Der in LogCat entdeckte Fehler war:

02-05 16:14:24.990    1271-1601/? E/bt-btif Max Notification Reached, registration failed.

Vor 4.4.2 war die Anzahl der Registrierungen auf 4 begrenzt! 4.4.2 hat diese Grenze auf 7 erhöht.

Durch die Reduzierung der Registrierungen in früheren Versionen konnten wir diese Einschränkung umgehen.


Die Option -cpu-delay <delay> die in den Emulator- -cpu-delay <delay> beschrieben wird, kann hilfreich sein.





android bluetooth bluetooth-lowenergy android-4.3-jelly-bean gatt