Android BLE API: GATT Notificación no recibida

Dispositivo utilizado para pruebas: Nexus 4, Android 4.3

La conexión funciona bien pero el método onCharacteristicChanged de mi devolución de llamada nunca se llama. Sin embargo, estoy registrando notificaciones usando setCharacteristicNotification(char, true) dentro de onServicesDiscovered y esa función devuelve true.

Registro del dispositivo (en realidad no hay mensajes en absoluto cuando las notificaciones deben aparecer / se envían a través del dispositivo 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 

Las notificaciones de GATT funcionan bien usando iOS y la aplicación básicamente hace lo mismo que en Android (registrarse para notificación, etc.).

¿Alguien más ha experimentado esto con una posible solución?

Parece que te olvidaste de escribir el Descriptor que le dice a tu dispositivo BLE que vaya en este modo. Consulte las líneas de código que se ocupan del descriptor en http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#notification

Sin establecer este descriptor, nunca recibirá actualizaciones de una característica. Llamar setCharacteristicNotification no es suficiente. Este es un error común.

Código cortado

 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 – Tengo los mismos problemas. En mi caso, tengo 3 características de notificación y un puñado de características de lectura / escritura.

Lo que encontré es que hay alguna dependencia entre writeGattDescriptor y readCharacteristic . Todos los writeGattDescriptors deben venir primero y terminar antes de que emita cualquier readCharacteristic llamadas.

Aquí está mi solución usando Queues . Ahora estoy recibiendo notificaciones y todo lo demás funciona bien:

Crear dos colas como esta:

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

A continuación, escriba todos sus descriptores inmediatamente después del descubrimiento con este método:

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

Y esta devolución de llamada:

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

El método para leer una característica normalmente se ve así:

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

Y mi devolución de llamada de lectura:

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

Al establecer el valor en el descriptor en lugar de poner descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) , ponga descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) . Las llamadas de retorno para onCharacteristicChanged se llaman ahora.

Problemas experimentados en versiones anteriores de Android recibiendo notificaciones (una indicación que se registró) y siempre tuvo un extraño evento de desconexión después. Como resultado, esto fue porque nos registramos para notificaciones sobre cinco características.

El error descubierto en LogCat fue:

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

Antes de 4.4.2, el número de registros fue limitado a 4! 4.4.2 aumentó este límite a 7.

Al reducir el número de registros en versiones anteriores, pudimos evitar esta limitación.

Supongo (no proporcionaste tu código fuente) que no lo implementaste como quería Google :

(1)

 mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); 

y entonces

(2)

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

Supongo que falta 2. En ese caso, creo que en la notificación de bajo nivel se activará, pero nunca se informará a la capa de aplicación.

Bueno, este nombre de API seguramente llevará algunas confusiones al desarrollador de aplicaciones si él / ella no era el programador de fondo de Bluetooth.

Desde la perspectiva de la especificación del núcleo de Bluetooth, cite de la especificación central 4.2 Vol 3, Parte G sección 3.3.3.3 "Configuración de la característica del cliente":

El valor descriptor característico es un campo de bits. Cuando se establece un bit, dicha acción se habilitará, de lo contrario no se utilizará.

Y la sección 4.10

Las notificaciones se pueden configurar utilizando el descriptor de configuración de características de cliente (consulte la sección 3.3.3.3).

Que indica claramente que si el cliente desea recibir la notificación (o indicación, que necesita respuesta) del servidor, debe escribir el bit "Notificación" a 1 ("Indicación" bit también a 1 de lo contrario).

Sin embargo, el nombre "setCharacteristicNotification" nos da una pista es que si establecemos los parámetros de esta API como TURE, el cliente recibirá notificaciones; Lamentablemente esta API sólo establece el bit local para permitir la notificación enviada a las aplicaciones en caso de notificación remota viene. Ver código 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; }' 

Así que lo que importa era la acción de escritura del descriptor.

Tuve otra razón que me gustaría añadir ya que me volvió loco todo el día:

En mi Samsung Note 3 No recibí notificaciones de valores modificados mientras el mismo código funcionaba en cualquier otro dispositivo con el que probé.

Reiniciar el dispositivo resuelto todos los problemas. Obvio, pero cuando estás en el problema, te olvidas de pensar.

Aquí hay una manera sencilla de hacerlo, pero avísame si ves algún inconveniente.

Paso 1 Declare variables booleanas

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

Paso 2 suscribirse a la primera característica en la devolución de llamada 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; } 

Paso 3

Suscribirse a otros después de los incendios de devolución de llamada 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; } } 

Este está trabajando para mí:

Para notificar al dispositivo maestro que alguna característica está cambiando, llame a esta función en su pheripheral:

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

En su dispositivo maestro: active setCharacteristicNotification después de descubrir el servicio:

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

Ahora usted puede comprobar su valor de característica es el cambio, por ejemplo onCharacteristicRead función (esto también trabajando en onCharacteristicChanged función también):

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

He experimentado los problemas con las notificaciones de BLE en Android también. Sin embargo, hay una demostración totalmente funcional que incluye un envoltorio bluetooth alrededor de BluetoothAdapter . El envoltorio se llama BleWrapper y se envía con la aplicación de demostración llamada BLEDemo contenida en el paquete Application Accelerator . Descargue aquí: https://developer.bluetooth.org/Pages/Bluetooth-Android-Developers.aspx . Debe registrarse con su dirección de correo electrónico en la parte superior derecha antes de descargar. La licencia del proyecto permite el uso gratuito, modificación de código y publicación.

Según mi experiencia, la aplicación de demostración de Android gestiona las suscripciones de notificaciones BLE muy bien. Todavía no he buceado demasiado en el código para ver cómo envuelve realmente envuelve.

Hay una aplicación de Android disponible en Play Store que es una personalización de la demostración del acelerador de aplicaciones . Como la interfaz de usuario se ve casi la misma supongo que también utiliza BleWrapper . Descarga la aplicación aquí: https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner

  • Error Android Bluetooth LE: Error al registrar la devolución de llamada
  • BluetoothGatt: la negociación de nuevos MTU tiene éxito pero el nuevo tamaño no se puede utilizar (diferencia de 3 bytes)
  • Android Cómo leer las propiedades de BLE Readable Writable Notifiable GATT Características
  • BLE GATT onCharacteristicChanged no se llama después de suscribirse a la notificación
  • Android BluetoothGatt - estado 133 - registro de devolución de llamada
  • Bluetooth Bluetooth de bajo consumo de energía
  • Android, ¿Cómo puedo hacer que el dispositivo BLE esté conectado a un dispositivo (vinculado)
  • Android - No se pudo conectar al dispositivo bluetooth en Lollipop
  • ¿Cómo mejorar el rendimiento de GATT de baja energía de Bluetooth de Android?
  • Samsung ble api no puede recibir notificación de múltiples características del GATT
  • BLE con Android 5.0: ¿Cómo conseguir que un dispositivo actúe como Central AND Server?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.