Android BLE API: notification GATT non reçue

Appareil utilisé pour tester: Nexus 4, Android 4.3

La connexion fonctionne bien, mais la méthode onCharacteristicChanged de mon rappel n'est jamais appelée. Cependant, je m'inscris pour les notifications à l'aide de setCharacteristicNotification(char, true) dans onServicesDiscovered et cette fonction renvoie même true.

  • Texte AndroidAllCaps dans Theme
  • Comment stocker les données d'application Android sur la carte SIM en utilisant NFC?
  • Comment intégrer la fonctionnalité "J'aime" et "Commentaire" à l'aide d'Android Facebook SDK?
  • Envoyer l'emplacement actuel au serveur périodiquement dans Android
  • La liste de suggestion d'autocomplèteTextView augmente
  • Est-il possible d'exécuter des applications .APK / Android sur des appareils iPad / iPhone?
  • Journal des périphériques (il n'y a pas de messages sur le tout lorsque les notifications doivent apparaître / sont envoyées via le périphérique Bluetooth):

     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 

    Les notifications GATT fonctionnent correctement avec iOS et l'application fait essentiellement comme sur Android (enregistrement pour notification, etc.).

    Est-ce que quelqu'un d'autre a connu cela avec une solution possible?

  • Android WebView -> Affichage WebArchive
  • Est-il possible de rendre l'arrière-plan du menu Options Android non translucide?
  • ScrollView dans les deux sens, Android
  • Afficher la sélection de l'année d'abord dans la vue Calendrier Android
  • TextView coupe le texte lorsqu'il est assez long
  • Comment puis-je importer la bibliothèque de conception matérielle sur Android Studio?
  • 10 Solutions collect form web for “Android BLE API: notification GATT non reçue”

    Il semble que vous avez oublié d'écrire le descripteur qui indique à votre appareil BLE d'accéder à ce mode. Consultez les lignes de code traitant du descripteur à http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#notification

    Sans définir ce descripteur, vous ne recevez jamais de mise à jour d'une caractéristique. L'établissement d'appel La setCharacteristicNotification n'est pas suffisant. C'est une erreur courante.

    Code couché

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

    @ Boni2k – J'ai les mêmes problèmes. Dans mon cas, j'ai 3 caractéristiques de notification et une poignée de caractéristiques de lecture / écriture.

    Ce que j'ai trouvé, c'est qu'il existe une certaine dépendance entre writeGattDescriptor et readCharacteristic . Tous les scripts writeGattDescript doivent être les premiers et complétés avant d'émettre des appels ReadCharacteristic.

    Voici ma solution en utilisant les Queues . Maintenant, je reçois des notifications et tout le reste fonctionne bien:

    Créez deux files d'attente comme ceci:

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

    Ensuite, écris tous vos descripteurs immédiatement après la découverte avec cette méthode:

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

    Et ce rappel:

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

    La méthode de lecture d'une caractéristique normalement alors ressemble à ceci:

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

    Et mon rappel de lecture:

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

    Lorsque vous définissez la valeur dans le descripteur au lieu de mettre descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) , mettez descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) . Les rappels pour onCharacteristicChanged sont appelés maintenant.

    Des problèmes expérimentés dans les versions antérieures d'Android recevant des notifications (une indication qui a été enregistrée) et ont toujours eu un étrange événement de déconnexion après. Il s'avère que c'est parce que nous nous sommes inscrits pour des notifications sur cinq caractéristiques.

    L'erreur découverte dans LogCat était:

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

    Avant 4.4.2, le nombre d'enregistrements a été plafonné à 4! 4.4.2 a augmenté cette limite à 7.

    En réduisant le nombre d'enregistrements dans des versions antérieures, nous avons pu contourner cette limitation.

    Je suppose (vous n'avez pas fourni votre code source) que vous ne l'avez pas implémenté comme Google voulait :

    (1)

     mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); 

    et alors

    (2)

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

    Je suppose que 2 est manquant. Dans ce cas, je crois que la notification de bas niveau sera déclenchée, mais ils ne seront jamais signalés à la couche d'application.

    Eh bien, ce nom de l'API conduit certainement des confusions au développeur de l'application si il / elle n'était pas le programmeur de fond Bluetooth.

    Du point de vue de la spécification du noyau Bluetooth, citez la spécification core 4.2 Vol 3, Partie G, section 3.3.3.3 "Configuration caractéristique du client":

    La valeur du descripteur caractéristique est un champ de bit. Lorsqu'un bit est réglé, cette action doit être activée, sinon elle ne sera pas utilisée.

    Et section 4.10

    Les notifications peuvent être configurées à l'aide du descripteur de configuration de la caractéristique du client (voir la section 3.3.3.3).

    Ce qui indique clairement que si le client souhaite recevoir la notification (ou l'indication, qui nécessite une réponse) du serveur, devrait écrire le bit "Notification" sur 1 (bit "Indication" également sur 1 autrement).

    Cependant, le nom "setCharacteristicNotification" nous donne un indice, c'est que si nous définissons les paramètres de cette API comme TURE, le client recevrait des notifications; Malheureusement, cette API ne définit que le bit local pour permettre la notification envoyée aux applications en cas de notification à distance. Voir le code de 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; }' 

    Alors, ce qui importait, c'était l'action d'écriture du descripteur.

    J'ai eu une autre raison que j'aimerais ajouter car il me rendait fou toute la journée:

    Sur mon Note 3 de Samsung, je n'ai pas reçu de notifications de valeurs modifiées alors que le même code fonctionnait sur tout autre appareil avec lequel j'ai testé.

    Le redémarrage du périphérique a résolu tous les problèmes. Évidemment, mais quand vous êtes dans le problème, vous oubliez de penser.

    Voici un moyen simple de le faire, mais faites-moi savoir si vous voyez des inconvénients.

    Étape 1 Déclarez les variables booléennes

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

    Étape 2 abonnez-vous à la première caractéristique du rappel de service 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; } 

    Étape 3

    Abonnez-vous à d'autres personnes après les incendies de rappel OnCharacteristicChanged

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

    Celui-ci travaille pour moi:

    Pour avertir le périphérique maître que certaines caractéristiques changent, appelez cette fonction sur votre périphérique:

     private BluetoothGattServer server; //init.... //on BluetoothGattServerCallback... //call this after change the characteristic server.notifyCharacteristicChanged(device, characteristic, false); 

    Dans votre appareil principal: activez setCharacteristicNotification après avoir découvert le service:

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

    Maintenant vous pouvez vérifier que votre valeur caractéristique est un changement, par exemple sur la fonction OnCharacteristicRead (cela fonctionne également sur la fonction onCharacteristicChanged aussi):

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

    J'ai également eu des problèmes avec les notifications pour BLE sur Android. Cependant, il existe une démo entièrement fonctionnelle qui inclut une enveloppe bluetooth autour de BluetoothAdapter . L'emballage est appelé BleWrapper et est livré avec l'application de démonstration appelée BLEDemo contenue dans le package Application Accelerator . Téléchargez ici: https://developer.bluetooth.org/Pages/Bluetooth-Android-Developers.aspx . Vous devez vous inscrire avec votre adresse e-mail en haut à droite avant de télécharger. La licence du projet permet une utilisation gratuite, une modification de code et une publication.

    À mon avis, l'application de démonstration Android gère très bien les abonnements à la notification BLE. Je n'ai pas encore plongé trop dans le code pour voir comment l'enveloppe réelle.

    Il existe une application Android disponible dans Play Store qui est une personnalisation de la démo de l' accélérateur d'application . Comme l'interface utilisateur ressemble presque à la même chose, je suppose qu'elle utilise également BleWrapper . Téléchargez l'application ici: https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner

    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.