Zum Testen verwendetes Gerät: Nexus 4, Android 4.3
Die Verbindung funktioniert einwandfrei, aber die onCharacteristicChanged
Methode meines Rückrufs wird nie aufgerufen. Ich registriere mich jedoch für Benachrichtigungen mit setCharacteristicNotification(char, true)
inside onServicesDiscovered
und diese Funktion gibt sogar true zurück.
Geräteprotokoll (es gibt eigentlich keine Nachrichten an alle , wenn Meldungen sollten erscheinen / werden über das Bluetooth - Gerät gesendet):
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 unter iOS einwandfrei und die App funktioniert im Wesentlichen genauso wie unter Android (Registrierung für Benachrichtigungen usw.).
Hat jemand anderes dies mit einer möglichen Lösung erlebt?
@ Boni2k - Ich habe die gleichen Probleme. In meinem Fall habe ich 3 Benachrichtigungsmerkmale und eine Handvoll Lese- / Schreibmerkmale.
Was ich gefunden habe ist, dass es eine gewisse Abhängigkeit zwischen
writeGattDescriptor
und gibtreadCharacteristic
. Alle writeGattDescriptors müssen an erster Stelle stehen und vollständig sein, bevor Sie readCharacteristic-Aufrufe ausgeben.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 unmittelbar nach der Entdeckung 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 Lesen eines Merkmals sieht dann normalerweise folgendermaßen 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 gelesener 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()); }
quelle
GattManager
. Siehe github.com/NordicSemiconductor/puck-central-androidWenn Sie den Wert auf den Deskriptor setzen
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
, anstatt ihn zu setzen , setzen Siedescriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
. Die Rückrufe für onCharacteristicChanged werden jetzt aufgerufen.quelle
Ich gehe davon aus (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)
Ich nehme an, 2 fehlt. In diesem Fall wird meiner Meinung nach eine Benachrichtigung auf niedriger Ebene ausgelöst, die jedoch niemals an die Anwendungsschicht gemeldet wird.
quelle
In früheren Versionen von Android traten Probleme auf, die Benachrichtigungen erhielten (ein Hinweis, der registriert wurde), und es trat danach immer ein seltsames Trennungsereignis auf. Wie sich herausstellte, haben wir uns für Benachrichtigungen zu fünf Merkmalen registriert.
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 erhöhte diese Grenze auf 7.
Durch die Reduzierung der Anzahl der Registrierungen in früheren Versionen konnten wir diese Einschränkung umgehen.
quelle
Nun, dieser API-Name führt sicherlich zu einigen Verwirrungen für App-Entwickler, wenn er / sie nicht der Bluetooth-Hintergrundprogrammierer war.
Zitieren Sie aus Sicht der Bluetooth-Kernspezifikation aus der Kernspezifikation 4.2, Band 3, Teil G, Abschnitt 3.3.3.3 "Konfiguration der Client-Eigenschaften":
und Abschnitt 4.10
Dies besagt eindeutig, dass, wenn der Client die Benachrichtigung (oder Anzeige, die eine Antwort benötigt) vom Server erhalten möchte, das Bit "Benachrichtigung" auf 1 schreiben sollte ("Bit" Anzeige "auch ansonsten auf 1).
Der Name "setCharacteristicNotification" gibt uns jedoch einen Hinweis darauf, dass der Client Benachrichtigungen erhalten würde, wenn wir die Parameter dieser API als TURE festlegen. Leider hat diese API nur das lokale Bit gesetzt, um die Benachrichtigung zuzulassen, die an Apps gesendet wird, wenn eine Remote-Benachrichtigung eingeht. 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 also zählte, war die Deskriptor-Schreibaktion.
quelle
Hier ist eine einfache Möglichkeit, aber lassen Sie mich wissen, wenn Sie Nachteile feststellen.
Schritt 1 Deklarieren Sie 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 Rückruf onServicesDiscovered:
@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 andere, 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; } }
quelle
Dieser arbeitet für mich:
Rufen Sie diese Funktion auf Ihrem Peripheriegerät auf, um das Master-Gerät über eine Änderung einer Eigenschaft zu informieren:
private BluetoothGattServer server; //init.... //on BluetoothGattServerCallback... //call this after change the characteristic server.notifyCharacteristicChanged(device, characteristic, false);
in Ihrem Master-Gerät: Aktivieren Sie setCharacteristicNotification, nachdem Sie den Dienst erkannt haben:
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); services = mGatt.getServices(); for(BluetoothGattService service : services){ if( service.getUuid().equals(SERVICE_UUID)) { characteristicData = service.getCharacteristic(CHAR_UUID); for (BluetoothGattDescriptor descriptor : characteristicData.getDescriptors()) { descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); mGatt.writeDescriptor(descriptor); } gatt.setCharacteristicNotification(characteristicData, true); } } if (dialog.isShowing()){ mHandler.post(new Runnable() { @Override public void run() { dialog.hide(); } }); } }
Jetzt können Sie überprüfen, ob sich Ihr charakteristischer Wert ändert, z. B. die Funktion onCharacteristicRead (dies funktioniert auch für die Funktion onCharacteristicChanged):
@Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { Log.i("onCharacteristicRead", characteristic.toString()); byte[] value=characteristic.getValue(); String v = new String(value); Log.i("onCharacteristicRead", "Value: " + v); }
quelle
Ich hatte noch einen weiteren Grund, den ich hinzufügen möchte, da er mich den ganzen Tag verrückt gemacht hat:
Auf meinem Samsung Note 3 habe ich keine Benachrichtigungen über geänderte Werte erhalten, während der gleiche Code auf jedem anderen Gerät funktioniert hat, mit dem ich getestet habe.
Durch einen Neustart des Geräts wurden alle Probleme behoben. Offensichtlich, aber wenn Sie in dem Problem sind, vergessen Sie zu denken.
quelle
Ich habe die Probleme mit Benachrichtigungen für BLE auch auf Android erlebt. Es gibt jedoch eine voll funktionsfähige Demo, die einen Bluetooth-Wrapper enthält
BluetoothAdapter
. Der Wrapper wird aufgerufenBleWrapper
und mit der Demo-Anwendung BLEDemo geliefert, die im Application Accelerator- Paket enthalten ist. Hier herunterladen: https://developer.bluetooth.org/Pages/Bluetooth-Android-Developers.aspx . Sie müssen sich vor dem Herunterladen mit Ihrer E-Mail-Adresse oben rechts registrieren. Die Projektlizenz ermöglicht die kostenlose Nutzung, Codeänderung und Veröffentlichung.Meiner Erfahrung nach verarbeitet die Android-Demoanwendung BLE-Benachrichtigungsabonnements sehr gut. Ich habe noch nicht zu viel in den Code eingetaucht, um zu sehen, wie der Wrapper tatsächlich verpackt wird.
Im Play Store ist eine Android-App verfügbar, mit der die Application Accelerator- Demo angepasst werden kann . Da die Benutzeroberfläche fast gleich aussieht, wird sie vermutlich auch verwendet
BleWrapper
. Laden Sie die App hier herunter: https://play.google.com/store/apps/details?id=com.macdom.ble.blescannerquelle