Reading the documentation, one would think that setCharacteristicNotification enables notifications for a BLE characteristic:
Enable or disable notifications/indications for a given
characteristic.
But this method doesn't seem to do this? Reading the BLE documentation on receiving BLE notifications, it turns out to be a multi-step process where you have to call this method and then write a file into a descriptor.
If this is the case, then what does setCharacteristicNotification by itself do?
The descriptor write is needed in order to tell the remote device to send notifications. setCharactersticNotification only tells the Bluetooth stack that it should forward any received notification to the app.
Interesting read at Why does setCharacteristicNotification() not actually enable notifications? . The answerers there dig through the source code and docs to find that:
"setCharacteristicNotification only prepares the local service to receive notifications."
I would suggest a wrapper function for set notifications, because, in addition to the documentation being not-so-clear, it is a confusing concept to enable notification reception locally as well as enable notification sending on the peripheral. I would suggest something like what the kind answerers at Enabling Bluetooth characteristic Notification in Android (Bluetooth Low Energy ) Not Working use:
public boolean setCharacteristicNotification(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic characteristic,boolean enable) {
Logger.d("setCharacteristicNotification");
bluetoothGatt.setCharacteristicNotification(characteristic, enable);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID);
descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : new byte[]{0x00, 0x00});
return bluetoothGatt.writeDescriptor(descriptor); //descriptor write operation successfully started?
}
Related
Can any one please help.
I am developing a Bluetooth Low Energy App
Works are done:
Devices Scan
Device Connection
Write to remote Device Raspberry Pi
The remote device has service id:
6e400001-b5a3-f393-e0a9-e50e24dcca9e
Write Strings is:
6e400002-b5a3-f393-e0a9-e50e24dcca9e
Notify String is:
6e400003-b5a3-f393-e0a9-e50e24dcca9e
All above custom services working well on IOS and on Android all done instead of listening to remote device notification.
I can write with:
gatt.writeCharacteristic(characteristicWrite);
I can enable notification with:
gatt.setCharacteristicNotification(characteristicNotify, true);
But when I try to fetch descriptor the descriptor is always null.
I tried:
UUID uuid = UUID.fromString("00000001-0000-1000-8000-00805f9b34fb");
BluetoothGattDescriptor descriptor =
characteristic.getDescriptor(uuid);
UUID uuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
BluetoothGattDescriptor descriptor =
characteristic.getDescriptor(uuid);
UUID uuid = convertFromInteger(0x2902); BluetoothGattDescriptor
descriptor = characteristic.getDescriptor(uuid);
UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e");
UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e");
UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e");
in all cases the descriptor is return null and I cannot set descriptor notification value.
The server guy told the descriptor value not configured on server device, same device working fine on IOS but on Android notification not receiving.
I tested same device with this app:
https://play.google.com/store/apps/details?id=no.nordicsemi.android.nrftoolbox
I can recieve notification with the help of this app.
Above app listening on:00002902-0000-1000-8000-00805f9b34fb for descriptor
But with same UUID I can not getting notification.
Sorry for my English.
I have found my answer after two days research.
Please see reference app for testing any BLE device.
The app change request MTU and then enable notification.
My steps to write and listen notification given below:
First I tired to request MTU and then write with notification value. After first try with the help of notification token the descriptor is written successfully.
After that step I can write and listen with given device.
My be any one facing same problem so please consider above given application and check application logs. Every step will be clear by given app.
On Android when you want to receive BLE notifications for characteristic changes you use something like the following:
BluetoothGattCharacteristic characteristic = ...
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor =
characteristic.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
My question is: what is the gatt.setCharacteristicNotification() doing?
My assumption is that the above code represents:
Tell Android to pay attention for notifications from characteristic X
Tell the BLE device to start broadcasting change notifications for X
It seems logical, but I haven't seen it documented anywhere - instead the docs simply tell you to do what I wrote above. I'd like to confirm the purpose of setCharacteristicNotification() in this process.
I'm trying to create an Android 5 (lollipop) app that acts as a Bluetooth Low Energy (BLE) peripheral. The app runs on a Nexus 9 which supports BLE peripheral mode. So far I've managed to advertise a service and allow another BLE device connect to it successfully. However, it is fails when trying to read the characteristic value.
I've checked that the characteristic has the read property and that the read permission is set.
When using the LightBlue iOS app (https://itunes.apple.com/gb/app/lightblue-bluetooth-low-energy/id557428110?mt=8) I manage to discover and connect to my Nexus and see the characteristic uuid but the value doesn't show.
Any help would be highly appreciated.
First check the characteristic data you are advertising in the peripheral mode usually there are three modes
BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattCharacteristic.PROPERTY_READ,
BluetoothGattCharacteristic.PROPERTY_NOTIFY;
and you can build characteristic with all modes using
BluetoothGattCharacteristic.PROPERTY_WRITE |BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY;
Once done look for onCharacteristicWriteRequest() in BluetoothGattServerCallback() that you gave while building characteristic. When central want to send data it can write data to the characteristic with WRITE mode and you will have onCharacteristicWriteRequest() callback method triggered at peripheral side and you will have data in byte[] and make sure send response using btGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
by checking the responseNeeded bool value in callback method. In this way the data is transferred from central to peripheral.
And to send data from peripheral to central use notification charatertertistc
BluetoothGattCharacteristic bgc = bluetoothGattService
.getCharacteristic(chartersticUUID);
bgc.setValue(bnd.data);
btGattServer.notifyCharacteristicChanged(centralbluetoothdevice, bgc, false);
.
The BluetoothLeGatt Android BLE example contains the following code:
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to Heart Rate Measurement.
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
My question is basically, why is the marked code specific to Heart Rate Measurement? It seems like having a Client Characteristic Config Descriptor (CCCD) characteristic is the standard way to control characteristic notification, so why doesn't setCharacteristicNotification() take care of writing to it? And since it doesn't do that, what does setCharacteristicNotification() actually do?
I'm pretty new to BLE and there aren't any explanations of it on the internet that don't assume that you already understand it all! So don't assume I know what a CCCD or whatever is! It was difficult enough finding out what CCCD even stands for!
Edit: See also this answer which supports my understanding of CCCDs (and makes me continue to wonder why you have to write to them manually in Android when there is a function that looks like it should do that for you): https://devzone.nordicsemi.com/index.php/what-does-cccd-mean
I think is a litte bit late for give an answer but today I had the same doubt and I found a clear answer.
Using setCharacteristicNotification() you enable notification localy (on android device) and setting CCC descriptor to ENABLE_NOTIFICATION_VALUE you enable notification on ble peripheral. In fact for enabling CCC notification you have to use setValue() and writeDescriptor() that are methods used for writing characteristics (in this case characteristics descriptors) to remote device.
I found this on: http://processors.wiki.ti.com/index.php/SensorTag_User_Guide
Here is an excerpt from the O'Reilly book "Getting Started With Bluetooth Low Energy":
To enable notifications on Android, you normally have to locally
enable the notification for the particular characteristic you are
interested in.
Once that’s done, you also have to enable notifications on the peer
device by writing to the device’s client characteristic configuration
descriptor (CCCD)
I believe this answers your question.
So
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
refers to the first part
and
mBluetoothGatt.writeDescriptor(descriptor);
refers to the 2nd.
For future peoples coming across this, here's the best answer I could find:
By writing to the Client Characteristic Config descriptor, you, the client, are telling the BLE server to switch configurations. (This made no sense to me either initially, but in english:)
This tells the BLE device to switch modes (configurations) to actively gather and report changes to this characteristic instead of changing and only reporting when requested.
It's poorly named, but digging through the docs it appears this is also going to be used for other possible characteristic changes that the client might request: hence the confusing name.
https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
This begs the question, why call BluetoothGatt.setCharacteristicNotification() if we're only going to duplicate our efforts by modifying the descriptor?! Digging through the BluetoothGatt source shows us that setCharacteristicNotification only prepares the local service to receive notifications, not to enable persistent updates.
I know it looks silly, but setting CCCD value is the only way you can tell the API whether you are going to turn on notification or indication.
Currently there is no setCharacteristicIndication. To enable indication, you have to call setCharacteristicNotification (confusing) and then write BluetoothGattDescriptor.ENABLE_INDICATION_VALUE to CCCD, similar to what you did to enable Notification.
All the other answers do not really answer the question.
My guess would be that the Android BLE team took the assumption an application could have more than one BluetoothGattCallback().
By splitting the notification enabling (and disabling) in two steps it would allow the BluetoothGattCallback observers to listen for GATT notification (only invoke setCharacteristicNotification()) - and leave only one BluetoothGattCallback implemention doing the write operations to the GATT server aka the BLE peripheral.
"The Client Characteristic Configuration descriptor defines how the characteristic may be configured by a specific client."
i am trying to read temperature value from health profile.health thermometer service. according to official google ble devlopment page when i try to notify a health thermometer characteristic to read temperature i try to write descriptor value (Client Characteristic Configuration) using writeDescriptor a callback method of writeDescriptor return status 5 according to google it's GATT_INSUFFICIENT_AUTHENTICATION.
so i comment the code of writedescriptor and try to call the method mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); but onCharacteristicChanged method never called. when i talk to my ios devloper team. they said that they never write a descriptor value they just set the notification to true and they get temperature value from the same chip.
to write a descriptor value is necessary for android developer to get notification ???
so at last i try to run the official bluetooth app from the bluetooth.com site for test purpose they just disable all three buttons notify read and write???
thank you and sorry for my English
[UPDATE]
find out some log which may cause some bonding issues please help me if you have any solution for that.
Short-Term Key generated still log as error
11-08 11:26:44.392: E/bt-smp(1014): STK Generated
after that bond state change dramatically may this cause insufficient authentication
11-08 11:26:49.437: I/BluetoothBondStateMachine(1014): bondStateChangeCallback: Status: 0 Address: 00:16:A4:C0:FF:EE newState: 2
11-08 11:26:49.437: D/BtGatt.btif(1014): btif_gattc_upstreams_evt: Event 9
11-08 11:26:49.437: E/BluetoothBondStateMachine(1014): In stable state, received invalid newState: 12
[UPDATE]
after upgrading kitkat 4.4 on nexus 7 . add two method
device.createBond();
device.setPairingConfirmation(true);
works for me now i can read the encrypted characteristic. but still unstable. but some success i got.
Yes, in Android for enabling BLE notification, you must both call mBluetoothGatt.setCharacteristicNotification(characteristic, enabled) and write suitable value to descriptor 0x2902 of that characteristic.
I am not sure why you get GATT_INSUFFICIENT_AUTHENTICATION, this may be caused by the implementation of the thermometer.
EDIT :
From the new information in the comment and also the screen captured provided, there are a few things you may want to check :
The characteristic is a indication characteristic, but not a notification characteristic. The value you write to the descriptor should be BluetoothGattDescriptor.ENABLE_INDICATION_VALUE, but not BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE. Notice that you still need to turn on the indication by setCharacteristicNotification(). (Confusing terminology here, but it is necessary as per the docs)
For the unstable Bluetooth stack on Android, try to restart the Bluetooth, and turn of WiFi. This will increase the stability. (Although not 100% solving the problem)
In my case the problem was that BluetoothGatt object can accept only one pending operation. Solution was to do read/write operations in sequence, waiting for the completion callback of first before requesting second read/write operation.