Android Bluetooth Low Energy Getting response to specific request - android

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

Related

Bluetooth Low Energy notifications

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.

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

How to read available text (not ending with new line) from TCP socket using BufferedReader

I'm communication with a server through a tcp socket connection, i'm able to read lines that ends with \n fine, however when the line is not terminated (ends in \n) i'm not able to read it. I tried the following but it didn't work and caused my app to freeze at startup:
private Socket socket;
private BufferedReader input;
public boolean isConnected;
#Override
public void onCreate()
{
try
{
socket = new Socket ("server.ip.add.ress", 23456);
input = new BufferedReader (new InputStreamReader (socket.getInputStream());
handshake();
isConnected = true;
}
catch // Handle IOException and UnknownHostException
}
// custom runnable to read availabe input from the server
private class MyRunnable implements Runnable
{
private volativle String value;
public String getValue()
{
return value;
}
#Override
public void run()
{
int count;
char[] buffer = new char[10]; // expected message 'username: '
try
{
count = input.read (buffer, 0, 10);
if (count > 0) value = new String (buffer);
}
catch // IOException
}
}
// when connection is established with server expect 'username: ' from
// the server and send the user name back to it
public void handshake()
{
MyRunnable runnable = new MyRunnable();
try
{
Thread thread = new Thread (runnable);
thread.start();
thread.join();
String greeting = runnable.getValue();
if (greeting.equals ("username: ")) // Send username back
}
catch // InterruptedException
}
why is it hanging? and how can i read a non terminated line?
Edit:
To clarify: The server sends the greeting message username: immediately after the connection is established with a client, the client wait for the greeting and send back it's username when received (that's what handshake() does), if no handshake the client disconnects otherwise it start listening for incoming messages. Because i need to know if handshake is complete before starting the listener i had to use Thread.join().
The problem: Thanks for the comments and answers below, it turned out that BufferedReader.read() blocks the thread and waits until something is sent from the server and if nothing is being sent it causes the app to hang, Therefor there's no way to find out if the line has ended.
The solution: In my specific situation i just wanted to know if a specific message is sent "username: " so i used read (buffer, 0, 10) to read exactly 10 characters (the length of "username: "), and because it blocks if nothing is sent i used Thread.join (1000) which waits only one second and then if nothing received i disconnect the client.
Why is it hanging?
This is what it is suppose to be. It will block the thread if no data is available to read. This is also why you want to put it in a background thread.
Can it not just return if nothing is available?
What you are looking for is ready(), which will tell you whether there is available data or not.
Indicates whether this reader is ready to be read without blocking.
Returns
true if this reader will not block when read is called, false if unknown or blocking will occur.
But you should be very careful when using this function. Because networking is a lot about timing. The fact that you don't have any data to read at this second doesn't necessary mean that it won't be any data in the next second.
So a better design of the server should be more or less as the following:
If the username is found, return the username
If the username is not found, return an error message to let the client side know that the username is not found
There's no need for the thread. Your goal is to wait until you've read what you've been waiting for. Why not just let read() perform the wait for you?
What you're struggling with is the classic problem of TCP communication: "when do I know that I've got everything the server sent?"
In your case, you're expecting to read bytes until the collection of bytes ends with "username: ". So, change your algorithm to perform 1 byte reads (filling a buffer as you go) until that buffer ends with "username: ".
You can make a more complicated algorithm -- which would be more efficient -- that would attempt to read multiple bytes at a time and append them to a buffer -- performing your check each time. But either strategy is logically equivalent.
I also recommend just using the InputStreamReader. It has various read() methods. I am a bit suspicious about the BufferedInputReader, especially when dealing with data that isn't newline terminated. I'm probably just paranoid. I've just never used it when writing TCP client/server programs, so I'm not sure.

Finding out Android Bluetooth LE GATT Profiles

I've implemented the Android LE bluetooth example that find a heart rate monitor and connects to it. However, this example has a class that defines the GATT profile like this:
private static HashMap<String, String> attributes = new HashMap();
public static String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb";
public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
static {
// Sample Services.
attributes.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate Service");
attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service");
// Sample Characteristics.
attributes.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement");
attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String");
}
public static String lookup(String uuid, String defaultName) {
String name = attributes.get(uuid);
return name == null ? defaultName : name;
}
Now, what I'm wanting to do is change it so that this program finds any device with bluetooth le but I have no idea how Google have gotten the information for the heart rate measurement of the client characteristic config.
The Bluetooth SIG maintains a list of "Assigned Numbers" that includes those UUIDs found in the sample app:
https://www.bluetooth.com/specifications/assigned-numbers/
Although UUIDs are 128 bits in length, the assigned numbers for Bluetooth LE are listed as 16 bit hex values because the lower 96 bits are consistent across a class of attributes.
For example, all BLE characteristic UUIDs are of the form:
0000XXXX-0000-1000-8000-00805f9b34fb
The assigned number for the Heart Rate Measurement characteristic UUID is listed as 0x2A37, which is how the developer of the sample code could arrive at:
00002a37-0000-1000-8000-00805f9b34fb
In addition to #Godfrey Duke's answer, here's a method I use to extract the significant bits of the UUID:
private static int getAssignedNumber(UUID uuid) {
// Keep only the significant bits of the UUID
return (int) ((uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >> 32);
}
Example of usage:
// See https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.heart_rate.xml
private static final int GATT_SERVICE_HEART_RATE = 0x180D;
(...)
for (BluetoothGattService service : services) {
if (getAssignedNumber(service.getUuid()) == GATT_SERVICE_HEART_RATE) {
// Found heart rate service
onHeartRateServiceFound(service);
found = true;
break;
}
}
I just wanted to add a few things :
0000-1000-8000-00805f9b34fb isn't the sole root for bluetooth UUIDs out there.
*-0002a5d5c51b is commonly seen as well (see https://uuid.pirate-server.com/search?q=0002a5d5c51b)
anything can be used, and has been used :
see https://uuid.pirate-server.com/search?q=gatt
see around https://github.com/edrosten/bluez/blob/master/monitor/uuid.c#L543 (includes uint16 & uint128 integers)
feel free to use whatever you want to use, but beware of using UUIDv1 UUIDs if you care significantly about security, because they embed a timestamp and a mac address which can be used to track you, to some extent, even if you use an online generator, see https://uuid.pirate-server.com/search?q=0800200c9a66 for all the examples of UUIDs using https://www.famkruithof.net/uuid/uuidgen?typeReq=-1 as a generator
Here's the page on the gatt callback: https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html
You need to use the BluetoothGatt.discoverServices();
and then in the callback onServicesDiscovered(...) i think you need to useBluetoothGatt.getServices();

Categories

Resources