Android BLE API: GATT-Benachrichtigung nicht erhalten



4 Answers

@ 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());
    }
Question

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?




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.




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.




Ich habe die Probleme mit Benachrichtigungen für BLE auch auf Android erlebt. Allerdings gibt es eine voll funktionsfähige Demo, die einen Bluetooth Wrapper um BluetoothAdapter . Der Wrapper heißt BleWrapper und wird mit der im Application Accelerator- Paket enthaltenen Demoanwendung namens BLEDemo ausgeliefert. Hier herunterladen: https://developer.bluetooth.org/Pages/Bluetooth-Android-Developers.aspx . Sie müssen sich vor dem Download oben rechts mit Ihrer E-Mail-Adresse registrieren. Die Lizenz des Projekts erlaubt die freie Nutzung, Code-Änderung und Veröffentlichung.

Nach meiner Erfahrung behandelt die Android-Demo-Anwendung BLE-Benachrichtigungsabonnements sehr gut. Ich habe noch nicht zu viel in den Code eingetaucht, um zu sehen, wie der Wrapper tatsächlich umhüllt.

Im Play Store ist eine Android-App verfügbar, bei der es sich um eine Anpassung der Application Accelerator- Demo handelt. Da die Benutzeroberfläche fast gleich aussieht, nehme ich an, dass auch BleWrapper verwendet BleWrapper . Laden Sie die App hier herunter: https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner




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.




Related