Android BLE API: GATT Notification not received
It seems like you forgot to write the Descriptor which tells your BLE device to go in this mode. See the code lines that deal with descriptor at http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#notification
Without setting this descriptor, you never receive updates to a characteristic. Calling setCharacteristicNotification
is not enough. This is a common mistake.
code snipped
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 - I have the same issues. In my case, I have 3 notifying characteristics and a handful of read/write characteristics.
What I did find is that there is some dependency between writeGattDescriptor
and readCharacteristic
. All of the writeGattDescriptors must come first and complete before you issue any readCharacteristic calls.
Here is my solution using Queues
. Now I am getting notifications and everything else works fine:
Create two Queues like this:
private Queue<BluetoothGattDescriptor> descriptorWriteQueue = new LinkedList<BluetoothGattDescriptor>();private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();
Then write all of your descriptors immediately after discovery with this method:
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); }}
and this callback:
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()); };
The method for reading a characteristic normally then looks like this:
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); }
and my read callback:
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()); }
When setting the value to the descriptor instead of putting descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
, put descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
. The callbacks for onCharacteristicChanged are called now.