Bluetooth Low Energy notifications - android

I want to receive characteristic updates notifications from server to client. Both server and client are my android apps. There are a question and a problem.
I have found a "solution" that I need to add this code to subscribe, but add where? On the server side or on the client side?
val CCC_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb"
deviceGatt.setCharacteristicNotification(characteristic, true)
val desc = characteristic.getDescriptor(UUID.fromString(CCC_DESCRIPTOR_UUID))
deviceGatt.writeDescriptor(desc, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
The problem is that there are no descriptors at all. When server hosts a characteristic I check and there are 0 descriptors. There should be a CCCD by default but there is no one. The same happens when client discovers a service. There are no descriptors connected to characteristic. Can I add one by myself?
Edited:
I have tried to implement according to advice. Without manual adding CCCD on the server side there are no descriptors visible on the client side. So, on the Server side we have:
gattServer!!.addService(generateGattService())
...
private fun generateGattService(): BluetoothGattService {
val service = BluetoothGattService(GAME_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY)
characteristic = BluetoothGattCharacteristic(
MESSAGE_UUID,
BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
BluetoothGattCharacteristic.PERMISSION_WRITE
)
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val cccd = BluetoothGattDescriptor(
CCC_DESCRIPTOR_UUID,
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
)
characteristic!!.addDescriptor(cccd)
service.addCharacteristic(characteristic)
return service
}
On the Client side we have:
private fun onConnected(discoveredGatt: BluetoothGatt){
val service = discoveredGatt.getService(GAME_UUID)
val messageCharacteristic = service.getCharacteristic(MESSAGE_UUID)
discoveredGatt.setCharacteristicNotification(messageCharacteristic, true)
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val desc = messageCharacteristic.getDescriptor(CCC_DESCRIPTOR_UUID)
//desc.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
//discoveredGatt.writeDescriptor(desc)
Log.i(TAG_CLIENT, "Connected C -> S")
}
There are 2 commented rows. Using debugger I got that this way there is a descriptor connected to characteristic. I can send messages C -> S but this descritor has null value. If we uncomment those 2 rows, there is a descriptor value but messages don't come from anyone to anyone.
P.S. Code to write C -> S:
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
characteristic.value = msg
deviceGatt.writeCharacteristic(characteristic)
Code to write S -> C:
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
characteristic.value = msg
gattServer.notifyCharacteristicChanged(deviceToNotify, characteristic, false)

See below the answers to your two questions:-
The setCharacteristicNotification() method is used to enable or disable notifications for a specific characteristic on a GATT server device. Therefore, this method should be added on the GATT client side, because it is the client that subscribes to notifications for a particular characteristic.
As can be seen from this answer, you need the following to provide a notifiable characteristic on the GATT server side:-
a) Create a Bluetooth characteristic from your UUID:
public static final UUID NEW_SERVICE = UUID.fromString("0000ffff-0000-1000-8000-00805f9b34fb");
public static final UUID NEW_VALUE = UUID.fromString("0000fffe-0000-1000-8000-00805f9b34fb");
BluetoothGattCharacteristic characteristic = gatt.getService(NEW_SERVICE).getCharacteristic(NEW_VALUE);
b) Enable notifications
gatt.setCharacteristicNotification(characteristic,true);
c) Finally, set the CCCD to allow server initiated updates:-
public static final UUID CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
BluetoothGattDescriptor desc = characteristic.getDescriptor(CONFIG_DESCRIPTOR);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
Below are some useful links for both GATT client and GATT server side:-
Why does setCharacteristicNotification() not enable notifications
The ultimate guide to Android BLE development
Making Android BLE work

Finally I have found a solution. Posting here for the case if someone face the same.
So there are steps to enable notifications:
Server hosts:
val service = BluetoothGattService(GAME_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY)
characteristic = BluetoothGattCharacteristic(
MESSAGE_UUID,
BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ or BluetoothGattCharacteristic.PERMISSION_WRITE)
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val cccd = BluetoothGattDescriptor(
CCC_DESCRIPTOR_UUID,
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE)
characteristic!!.addDescriptor(cccd)
service.addCharacteristic(characteristic)
Server GattCallbacck overrides onDescriptorReadRequest and onDescriptorWriteRequest with lines respectively
server.gattServer!!.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
and
server.gattServer!!.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null)
Client does:
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val desc = messageCharacteristic.getDescriptor(CCC_DESCRIPTOR_UUID)
discoveredGatt.setCharacteristicNotification(messageCharacteristic, true)
desc.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
discoveredGatt.writeDescriptor(desc)
GattCallback overrides DEPRECATED!!! version of onCharacteristicChanged:
override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?)
https://github.com/alexanderlavrushko/BLEProof-collection was very helpful but requires few scanning fixes to work.

Related

BLE Gatt onCharacteristicChanged method not called

We are facing an issue with BLE Gatt onCharacteristicChanged method. This method is not called.
We have enabled notification using below code:
mBluetoothGatt!!.setCharacteristicNotification("NOTIFY_SERVICE_CHARACTERISTIC", true)
val descriptor = NOTIFY_SERVICE_CHARACTERISTIC.getDescriptor(convertFromInteger(0x2902))
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
descriptor.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
var status = mBluetoothGatt!!.writeDescriptor(descriptor)
After this, We are sending code to BLE from onDescriptorWrite Method.
{WRITE_SERVICE_CHARACTERISTIC}.setValue({BYTE_ARRAY})
val status = mBluetoothGatt!!.writeCharacteristic(RxChar)
After sending code to BLE. it will response in onCharacteristicWrite Method. but we are not getting any response in onCharacteristicChanged Method via Notify Service.
Same commands are working in other BLE Scanner Application but my application facing an issue.
Please help us to solve the issue.
Thanks
You are overriding the notification settings of the descriptor with the indication setting:
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
descriptor.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
You probably only need notifications and can remove the enabling of indications:
mBluetoothGatt!!.setCharacteristicNotification("NOTIFY_SERVICE_CHARACTERISTIC", true)
val descriptor = NOTIFY_SERVICE_CHARACTERISTIC.getDescriptor(convertFromInteger(0x2902))
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
var status = mBluetoothGatt!!.writeDescriptor(descriptor)
If you truly need both notifications and indications you have to set the values like this:
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE | BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
The | operator does a bitwise OR operation and therefore combines both flags into one

Android BLE : Notification from multiple characteristic under one service

PROBLEM
I am trying to get notification from more than one characteristic through BLE, I've seen some solution on the internet that I need to wait until the onDescriptorWrite() callback is finished (Which i think i have done here?), but I can't do onDescriptorWrite() for the second time for FILE_TX (code down there) notification. All of this is performed under onServicesDiscovery() - when I established BLE connection.
Is there anything that I am doing wrong here?
You can only have one outstanding gatt operation at a time. In this case you do two writeDescriptor calls before waiting until the first has completed. You must wait for https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html#onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int) until you can send the next one.
This is the best answer I can find but how can you tell that the onDescriptorWrite has been completed?
I tried putting Thread.sleep(500) in between for a work around but i doesn't work too.
Under onServicesDiscovery - gattCallback
for (gattCharacteristic in gattCharacteristics) {
uuid = gattCharacteristic.uuid
// // Log.d("GATT", "$uuid")
if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_READ_FILE_TX)) {
gatt.setCharacteristicNotification(gattCharacteristic, true)
val descriptorfile: BluetoothGattDescriptor = gattCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID) ?: error("Required Client Characteristic Configuration not found")
descriptorfile.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
isSuccess = gatt.writeDescriptor(descriptorfile)
Log.d("tagfile", "FILE_TX Successful ? " + isSuccess)
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
Log.d("tagfile", "Found Transparent service File Tx characteristics")
}
else if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_TX)) {
gatt.setCharacteristicNotification(gattCharacteristic, true)
val descriptor: BluetoothGattDescriptor = gattCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID) ?: error("Required Client Characteristic Configuration not found")
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
isSuccess = gatt.writeDescriptor(descriptor)
Log.d("tagfile", "TX Successful ? " + isSuccess)
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
Log.d("tagfile", "Found Transparent service Tx characteristics")
}
if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_RX)) {
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
Log.d("tagfile", "Found Transparent service Rx characteristics")
}
else if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_READ_FILE_RX)) {
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
Log.d("tagfile", "Found Transparent service File Rx characteristics")
}
}
Under onDescriptorWrite - gattCallback
override fun onDescriptorWrite(
gatt: BluetoothGatt?,
descriptor: BluetoothGattDescriptor?,
status: Int
) {
Log.d("tagfile", "Status of gatt : " + status + " GATT FAILURE : " + BluetoothGatt.GATT_FAILURE)
}
RESULTS :
2020-01-24 09:41:51.359 8565-8587/com.example.ricco_ble D/tagfile: TX Successful ? true
2020-01-24 09:41:53.359 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service Tx characteristics
2020-01-24 09:41:53.360 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service Rx characteristics
2020-01-24 09:41:53.371 8565-8587/com.example.ricco_ble D/tagfile: FILE_TX Successful ? false
2020-01-24 09:41:53.371 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service File Tx characteristics
2020-01-24 09:41:53.372 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service File Rx characteristics
2020-01-24 09:41:53.424 8565-8587/com.example.ricco_ble D/tagfile: Status of gatt : 0 GATT FAILURE : 257
Instead of trying to build the system yourself, I recommend using the Android BLE Library from Nordic SemiConductor. I spent the better half of 4 months trying to do it myself. This library allows you to execute your calls using async with defined callbacks and not have to worry about issues like this.
I found it useful to follow two points when working with the Android BLE stack.
In any Gatt callbacks, only perform result copying, and schedule the next task to perform (not to execute). So that the callback function can be released as soon as possible.
Perform operation sequentially. Don't perform the next operation until you receive the callback from the previous operation.
From the code post above, you are nesting writing descriptors in serviceDiscovery callback. Also, you write the next descriptor before receiving the prior callback.
To have a more stable/predictable performance, you can refactor your code into something like
BluetoothGattCallback(){
onServiceDiscovery(gatt){
MainHandler.post((gatt)->setFirstNotification(gatt)) //do the scheduling, not direct execution here.
}
onDescriptorWrite(gatt){
if(the first descriptor write success) {
MainHandler.post((gatt)->setSecondNotification(gatt)) //do the scheduling
} else if (the second descriptor write success) {
MainHandler.post((gatt)->otherLogic(gatt)) //do the scheduling
}
}
}
fun setFirstNotification(gatt){
//your code to set descriptor value
}
fun setSecondNotification(gatt){
//your code to set descriptor value
}
fun otherLogic(gatt){
//your other code
}
This is roughly the idea how you would approach it if you want to build your communication app directly with the Android stack.

Android BLE Gatt Server with multiple services - onCharacteristicWriteRequest() no char for handle

I am trying to build a BLE Gatt Server with multiple custom services and multiple characteristics.
To begin with I used the Google Example: https://github.com/androidthings/sample-bluetooth-le-gattserver/tree/master/kotlin
This was straight forward and worked very well. I modified the UUIDs to fit mine and I could receive notifications and write to the chars with no problem.
This is where I define the services and chars:
fun createTimeService(): BluetoothGattService {
val service = BluetoothGattService(TIME_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY)
// Current Time characteristic
val currentTime = BluetoothGattCharacteristic(CURRENT_TIME,
//Read-only characteristic, supports notifications
BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ)
val configDescriptor = BluetoothGattDescriptor(CLIENT_CONFIG,
//Read/write descriptor
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE)
currentTime.addDescriptor(configDescriptor)
// Local Time Information characteristic
val localTime = BluetoothGattCharacteristic(LOCAL_TIME_INFO,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE)
service.addCharacteristic(currentTime)
service.addCharacteristic(localTime)
return service
}
fun createSerialService(): BluetoothGattService {
val service = BluetoothGattService(serialPortServiceID,
BluetoothGattService.SERVICE_TYPE_PRIMARY)
val serialData = BluetoothGattCharacteristic(serialDataCharacteristicID,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE)
service.addCharacteristic(serialData)
return service
}
And here I am applying them to my server:
private fun startServer() {
bluetoothGattServer = bluetoothManager.openGattServer(this, gattServerCallback)
bluetoothGattServer?.addService(TimeProfile.createTimeService())
?: Log.w(TAG, "Unable to create GATT server")
bluetoothGattServer?.addService(TimeProfile.createSerialService())
?: Log.w(TAG, "Unable to create GATT server")
// Initialize the local UI
updateLocalUi(System.currentTimeMillis())
}
I would expect that everything would be working like before after adding the second service. But now if I try to write/subscribe to any of the characteristics (doesn't matter in which service) I just receive this:
W/BluetoothGattServer: onCharacteristicWriteRequest() no char for handle 42
W/BluetoothGattServer: onDescriptorWriteRequest() no desc for handle 43
I found what was going wrong. Apparently you cannot just add all services at once like I did. Adding the second service before the first one was confirmed lead to an Exception setting the services to null.
In the end I solved this by adding only one service initially.
Then in the onServiceAdded() Callback of the BleGattServerCallback() I started one after another.

Android Central with GattServer

I'm trying to program Bluetooth low energy - AlertNotificationService (ANS). It is a bit weird service because typically services run on GAP peripheral. But ANS run on GAP central. So typical way of working shall be for example this:
Watch - GAP peripheral (broadcasting), GATT client
Phone - GAP central , GATT server
Basically it works for me but not always. And this instability is very strange to me. When I look with bluetooth LE analyzer I see that Android GATT server sometimes tells there are no characteristic in my profile...
It looks like this: "watch" ask for my GATT service (I know it is proprietary not ANS UUID)
Slave->Master ATT Rcvd Find By Type Value Request, GATT Primary Service Declaration 11:22:33:44:11:22:33:44:11:76:62:65:01:00:00:00
Phone says the service is there starting from handle 0x35
Master->Slave ATT Rcvd Find By Type Value Response Handle: 0x0035
Watch ask for charactersitisc from handle 0x35
Slave->Master ATT Rcvd Read By Type Request, GATT Characteristic Declaration, Handles: 0x0035..0xffff
But phone sometimes incorrectly says that there is no characteristic from that handle:
Master->Slave ATT Rcvd Error Response - Attribute Not Found, Handle: 0x0035
When I do add service and characteristic into GATT server I always get "true" from functions. I do it like this:
BluetoothGattService service =new BluetoothGattService(Vbe_AnsExt.UUID_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic characApp =
new BluetoothGattCharacteristic(Vbe_AnsExt.UUID_CharacApp,
BluetoothGattCharacteristic.PROPERTY_READ ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic characMsg =
new BluetoothGattCharacteristic(Vbe_AnsExt.UUID_CharacMsg,
BluetoothGattCharacteristic.PROPERTY_READ ,
BluetoothGattCharacteristic.PERMISSION_READ );
boolean ret;
ret = service.addCharacteristic(characApp);
Log.i("vbeInit_ASN_Ext_Server","addCharApp retruned: "+ret);
ret = service.addCharacteristic(characMsg);
Log.i("vbeInit_ASN_Ext_Server","addCharMsg retruned: "+ret);
ret = mGattServer.addService(service);
Log.i("vbeInit_ASN_Ext_Server","addService retruned: "+ret);
Any idea what might be the problem? I noticed that there is function BluetoothGattServer::connect(). I'm not sure how to use it. I'm using standard BluetoothDevice::connectGatt(). But I guess if I would do something wrong it shall work never - not sometimes...
I'm using Android M (6.0.1) on Samsung SM-G920F.
[UPDATE]
I noticed that after phone restart it always works. After application close and reopen it usually does not works. It does not works in different ways ->
sometimes no characteristic is found;
sometimes only first characteristic is found
sometimes only last(second) characteristic is found.
When I start Gatt Server I do it like this:
mGattServer = bluetoothManager.openGattServer(appContext, mGattServerCallback);
mGattServer.clearServices();
When I close app (onDestroy()) I do close gattserver:
mGattServer.close();
I also tried not to close the GATT server, but it did not help. Any ideas what might get wrong between closing and reopening?
So I may have found the reason. I'm adding 2 services: ANS and my service ANS_extension. It seems it helps when I wait after adding the service on call back onServiceAdded()
So now it looks like this:
public static UUID UUID_SupportedNewAlertCategory = UUID.fromString("00002a47-0000-1000-8000-00805f9b34fb");
public static UUID UUID_NewAlert = UUID.fromString("00002a46-0000-1000-8000-00805f9b34fb");
// descriptor used for enabling notify feature
public static UUID UUID_Descr_new = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
mGattServer = bluetoothManager.openGattServer(appContext, mGattServerCallback);
mGattServer.clearServices();
waitServiceAdded = true;
BluetoothGattService service = new BluetoothGattService(Vbe_Ans.UUID_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic characSupportedNewAlerCategory =
new BluetoothGattCharacteristic(Vbe_Ans.UUID_SupportedNewAlertCategory,
BluetoothGattCharacteristic.PROPERTY_READ ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic characNewAlert =
new BluetoothGattCharacteristic(Vbe_Ans.UUID_NewAlert,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ );
BluetoothGattDescriptor bgd = new BluetoothGattDescriptor(UUID_Descr_new,
BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
characNewAlert.addDescriptor(bgd);
...
ret = service.addCharacteristic(characSupportedNewAlerCategory);
ret = service.addCharacteristic(characNewAlert);
...
ret = mGattServer.addService(service);
while(waitServiceAdded);
waitServiceAdded = true;
//init second service similar way as previous
while(waitServiceAdded);
Then I clear waiting flag waitServiceAdded in GATT server call back. (I'm java newbie so you might want to use some thread mutex synchro to access boolean?)
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onServiceAdded (int status,
BluetoothGattService service){
waitServiceAdded = false;
}

Android Bluetooth Low Energy Getting response to specific request

There's something I'm not quite getting with using Gatt to communicate with a BLE device.
According to this:
https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback)
BluetoothGatt gatt = device.connectGatt(context,true,new BluetoothGattCallback(){....})
I can connect to a BLE device and give it a callbacks object to be notified on stuff like onCharacteristicRead and onCharacteristicWrite
What I'm not getting is how do which write corresponds to which read callback?
This method signatures are:
public void onCharacteristicRead (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
public void onCharacteristicWrite (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
so if I do:
BluetoothGattCharacteristic char = gatt.getService(UART_SERVICE_UUID).getCharacteristic(UART_TX_CHARACTERISTIC_UUID);
char1.setValue("command1");
gatt.writeCharacteristic(char);
char1.setValue("command2");
gatt.writeCharacteristic(char);
in the onCharacteristicRead callback, how do I know if the characteristic.getStringValue() is for command1 or command2?
Thanks!
There are a couple of important things to know about when using BluetoothGatt.writeCharacteristic() and related BluetoothGatt methods.
Please notice that writeCharacteristic() (and many other methods of BluetoothGatt) return a boolean. When the result is false, it means that the operation was not successfully initiated. When does that occur? See the next item.
Using aBluetoothGatt object, it is not possible to initiate two operations at the same time. The call to initiate the second will fail and return false. After calling writeCharacteristic(), the code must wait for the callback response (onCharacteristicWrite) before issuing another write. In the sample code for this question the second call to writeCharacteristic() will almost certainly return false for this reason.
In this way, if each BluetoothGatt operation waits for a callback on the previously issued command, you can successfully pair command initiations and callbacks -- really you are forced to.
In this implementation you should wait for the onCharacteristicWrite after the first write before issuing the next write operation. But this holds if you did not change the write type with setWriteType (int writeType) from the default WRITE_TYPE_DEFAULT, which calls back on write operations. You might need to keep state for these ops.
Another option is to have a command code (e.g. 1 byte) in the response so you can map it to the command.
private String heartRate;
private String temperature;
...
char.setValue("command1");
gatt.writeCharacteristic(char);
...
Then in onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) check if the heartRate variable is null. If it is, get the value of the characteristic and assign it to the heartRate variable. Then do characteristic.setValue("command2");
gatt.writeCharacteristic(characteristic); and it will call onCharacteristicWrite(...) again.
If the heartRate variable is not null, get the value and assign it to the temperature variable. You can also use the UUID of the passed characteristic to differentiate between what the value is for.
Bonus: A single characteristic can contain multiple descriptors. You can have two descriptors with their own UUID for that characteristic. You can then call characteristic.getDescriptor(UUIDDescriptor).getValue().
As I've already explained here, I'll give the answer I'd like to have when I've started "playing" with GATT BLE.
In order to create kind of an API (request with parameter(s) => response, as it's sais in the title of this question), follow these steps:
create a GATT service with:
a Write characteristic (unlike read characteristic, write one receives data) to manage request (with parameter(s) through the data payload)
a Read characteristic to manage reponse as notification
a Client Characteristic Configuration Descriptor for the Read characteristic to enable notification (standard 16-bit CCCD UUID: 0x2902).
Client write Client Characteristic Configuration Descriptor to enable notification/indication (once after ble connection)
Client write request Characteristic (request)
GATT server send response "status" OK for write Characteristic (request)
GATT server send indication (doesn't wait for acknowledge from client) with "data" from read Characteristic
Concretly, how to do that with FreeRTOS?
Create your GATT service as follow:
static const BTAttribute_t pxAttributeTable[] =
{
{
.xServiceUUID =
{
.uu.uu128 = GATT_SERVICE_UUID,
.ucType = eBTuuidType128
}
},
{
.xAttributeType = eBTDbCharacteristic,
.xCharacteristic =
{
.xUuid =
{
.uu.uu128 = GATT_CHARACTERISTIC_REQUEST_UUID,
.ucType = eBTuuidType128
},
.xPermissions = (IOT_BLE_CHAR_WRITE_PERM),
.xProperties = (eBTPropWrite)
}
},
{
.xAttributeType = eBTDbCharacteristic,
.xCharacteristic =
{
.xUuid =
{
.uu.uu128 = GATT_CHARACTERISTIC_RESPONSE_UUID,
.ucType = eBTuuidType128
},
.xPermissions = (IOT_BLE_CHAR_READ_PERM),
.xProperties = (eBTPropRead | eBTPropNotify)
}
},
{
.xAttributeType = eBTDbDescriptor,
.xCharacteristicDescr =
{
.xUuid =
{
.uu.uu16 = 0x2902,
.ucType = eBTuuidType16
},
.xPermissions = (IOT_BLE_CHAR_READ_PERM | IOT_BLE_CHAR_WRITE_PERM)
}
}
};

Categories

Resources