I want to write a command to a BluetoothGattCharacteristic with a parameter on it. I know how to write a command in bytes by setValue() method. But do not know how to write the parameter.
Here is an example of how to perform a BLE write:
public void writeValue(final String uuid, final byte[] value)
{
final BluetoothGattCharacteristic characteristic = getCharacteristic(bluetoothGatt, uuid);
characteristic.setValue(value);
bluetoothGatt.writeCharacteristic(characteristic);
}
The first argument of the method is a specified characteristic UUID of a service in your remote device
you want to access. The second argument is a byte Array you want to write to the characteristics of the remote device.
Related
I'm creating a custom BLE service on Android with a single characteristic that can be read/written. The code looks like this:
public static UUID MY_SERVICE = UUID.fromString("e0ec8d9c-5e4d-470a-b87f-64f433685301");
public static UUID MY_CHARACTERISTIC = UUID.fromString("e0ec8d9c-5e4d-470a-b87f-64f433685302");
/**
* Return a configured {#link BluetoothGattService} instance for the
* Custom Service.
*/
public static BluetoothGattService createCustomBleService() {
BluetoothGattService service = new BluetoothGattService(MY_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
// Current Configuration characteristic
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(MY_CHARACTERISTIC,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_READ |BluetoothGattCharacteristic.PERMISSION_WRITE);
boolean serviceAdded = service.addCharacteristic(characteristic);
Log.i(TAG, "Building BLE service addCharacteristic returned "+serviceAdded);
return service;
}
The call to addCharacteristic(...) returns true. The service itself is created, can be advertised, and the service and its characteristic are discoverable by clients. Somewhere else in client side code, subsequent to a BLE scan that locates said service, the discovery code that runs looks like this:
for (BluetoothGattService service : gatt.getServices()) {
serviceUuid = service.getUuid().toString();
if( MY_SERVICE.toString().equals(serviceUuid) ) {
List<BluetoothGattCharacteristic> gattCharacteristics = service.getCharacteristics();
for( BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics ) {
characteristicUuid = gattCharacteristic.getUuid().toString();
Log.d(TAG, "onServicesDiscovered() - found characteristic uuid="+characteristicUuid);
int cProps = gattCharacteristic.getProperties();
Log.d(TAG, "onServicesDiscovered() - found characteristic properties "+cProps);
if ((( MY_CHARACTERISTIC.toString().equals(characteristicUuid) ))&&((cProps & BluetoothGattCharacteristic.PROPERTY_WRITE)>0)) {
writeCharacteristic(gatt,gattCharacteristic,"configStringLiteral");
}
}
}
}
When this service discovery code runs, as I mentioned, it finds the custom service and the characteristic that were defined. Any values I set for characteristic properties show up properly at the time of discovery on client side. The characteristic shows as writeable.
The problem is that characteristic write fails always even though the properties say its writable.
Has anyone seen this?... or perhaps I'm doing something dumb and have been looking at it too long.
(BTW the device hosting the custom service at runtime is a Samsung Galaxy 7 and the client is a Galaxy 6 ...or vice versa, same behavior)
Permission info is not sent over BLE when the services are discovered. Therfore the permission property should not be used for remote characteristics.
A client should inspect the characteristic property only to decide what can be done. If it receives an error saying for example encryption needed, the Bluetooth stack shall start encryption and then retry the request.
So, characteristic properties are the correct way to declare to a client what can be done with it. Characteristic permissions only tell the local GATT Server implementation how it should react to incoming GATT requests.
I am trying to interface with an Red Bear Labs nRF8001 Arduino Shield via Android Studio. I am succesfully able to send a command via Android and recieve it on my Arduino.
However I wish to read the response from my Arduino, using "OnCharacteristicChange" - however research showed a descriptor is required.
This is my code sample:
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if(status != BluetoothGatt.GATT_SUCCESS){
Log.i("BtServiceCallback","OnServicesDiscovered Failed!");
}
BluetoothGattService service = gatt.getService(RBLService.UUID_BLE_SHIELD_SERVICE);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(RBLService.UUID_BLE_SHIELD_TX);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(RBLGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
byte[] val = readSetPoint.getBytes();
characteristic.setValue(val);
gatt.setCharacteristicNotification(characteristic,true);
gatt.writeCharacteristic(characteristic);
Log.i("Sent = ", characteristic.getStringValue(0));
}
Whenever I try to create a descriptor from the characteristic, I get the following error:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.bluetooth.BluetoothGattDescriptor.setValue(byte[])' on a null object reference
at uk.ac.as988brighton.bluecontroller.MainActivity$1.onServicesDiscovered(MainActivity.java:160)
at android.bluetooth.BluetoothGatt$1.onSearchComplete(BluetoothGatt.java:286)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:103)
at android.os.Binder.execTransact(Binder.java:573)
I am using the UUIDs from RBL's Github:
public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
public static String BLE_SHIELD_TX = "713d0003-503e-4c75-ba94-3148f18d941e";
public static String BLE_SHIELD_RX = "713d0002-503e-4c75-ba94-3148f18d941e";
public static String BLE_SHIELD_SERVICE = "713d0000-503e-4c75-ba94-3148f18d941e";
I am unsure as to what is causing the null descriptor.
Edit
I have used different combinations for the descriptor - such as using
BluetoothGattDescriptor characteristic.getDescriptor(RBLService.UUID_BLE_SHIELD_TX);
But still receiving the same error.
It looks like you need to make sure you're naming things the same. Both your service and characteristic uuid are declared as BLE_SHIELD_SERVICE and BLE_SHIELD_TX but you're trying to access them with UUID_BLE_SHIELD_SERVICE and UUID_BLE_SHIELD_TX.
So change
BluetoothGattService service = gatt.getService(RBLService.UUID_BLE_SHIELD_SERVICE); to
BluetoothGattService service = gatt.getService(RBLService.BLE_SHIELD_SERVICE);
and
BluetoothGattCharacteristic characteristic = service.getCharacteristic(RBLService.UUID_BLE_SHIELD_TX); to
BluetoothGattCharacteristic characteristic = service.getCharacteristic(RBLService.BLE_SHIELD_TX);
and I believe it should work. Also make sure you are broadcasting those exact service and characteristic uuid's from your peripheral.
Good luck!
I am implementing BLE in Android Studio. I have connected with the peripheral device ok. In my onServicesDiscovered method I want to analyze the services (and characteristics) and I get something like the following when I print out:
android.bluetooth.BluetoothGattService#41b6dd18
There is 4 services in the list and they all look similar except for the numbers at the end. How can I convert this to useful information. I have seen no reference to this format.
Thanks.
Try to read the uuid from the BluetoothGattService object.
You can find uuid of standard services on Bluetooth SIG website. If the uuid is not there (i.e. custom services), you should read the manual of the peripheral or reach out the peripheral maker.
That depends on what you consider "useful" information.
BLE works mostly like dictionary where you look up long numbers (characteristics) and get binary data, so without prior information about the device you're working on, there is not much you can see when you discover services.
That said, in the BLE docs, there is a method displayGattServices() which puts the discovered services info in an ExpandableListView, and here I changed it to print the UUIDs of services and characteristics to logcat instead.
Besides the UUIDs, you can use getProperties() to find out other characteristic properties such as the format of the characteristic data, or getPermissions() to see whether you can read or write the characteristic.
// Demonstrates how to iterate through the supported GATT
// Services/Characteristics.
private void displayGattServices(List<BluetoothGattService> gattServices) {
final String TAG = "BleServiceInfo";
if (gattServices == null) return;
String uuid;
String unknownServiceString = "Unknown service"
String unknownCharaString = "Unknown characteristic"
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
Log.d(TAG, "Service: " + gattService.getUuid().toString());
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
Log.d(TAG, "\tCharacteristic: " + gattCharacteristic.getUuid().toString());
}
}
}
Call this method from onServicesDisccovered() like this:
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
....
displayGattServices(gatt.getServices());
}
I'm new in the world of BLE, I am currently able to connect to the BLE module and I can communicate with it/receive Data by pressing a Button.
Now I want to use the Descriptor, but I don't exactly know how to define the required UUID. I only discovered that it is 0x2902.
I want to use the following Code:
BluetoothGattCharacteristic init_gatt=mConnectedGatt.getService(STATE_REQUEST_SERVICE_UUID).getCharacteristic(STATE_CHAR_UUID);
mConnectedGatt.setCharacteristicNotification(init_gatt,true);
BluetoothGattDescriptor descriptor=characteristic.getDescriptor(STATE_CHAR_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mConnectedGatt.writeDescriptor(descriptor);
Can anyone tell me how I can create the UUID which is necessary for this line?
BluetoothGattDescriptor descriptor=characteristic.getDescriptor(STATE_CHAR_UUID);
If you're looking for creating a random UUID, you can do that like this:
if(uniqueId == null) {
uniqueId = UUID.randomUUID().toString();
}
You can actually log the list of device supported using:
for (BluetoothGattDescriptor descriptor:characteristic.getDescriptors()){
Log.e(TAG, "BluetoothGattDescriptor: "+descriptor.getUuid().toString());
}
BluetoothGattCharacteristic init_gatt=mConnectedGatt.getService(STATE_REQUEST_SERVICE_UUID).getCharacteristic(STATE_CHAR_UUID);
for (BluetoothGattDescriptor descriptor:characteristic.getDescriptors()){
Log.e(TAG, "BluetoothGattDescriptor: "+descriptor.getUuid().toString());
}
That's all I'm testing atm.
For the other Characteristic I'm Using, I got the hex Values which I have to write to the Characteristic by a collegue. In this case not
When reading a characteristic of ble in android device or even subscribing to it, I receive 2 packets at the same time but I know that these data are not complete because when decoding them i find a part of the data sent by the characteristic so I think that the problem is that the ble write multiple packets to the characteristic at the same connection interval but the android recieve only 2 of them
I need to know how to receive all of them so I can have the full data at the end ?
This is the code of onCharacteristicChanged method
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (HEART_RATE_READING_CHAR.equals(characteristic.getUuid())){
byte[] char_float_value = characteristic.getValue();
String s="";
for(int i=0;i<char_float_value.length;i++){
s=s +String.format("%02x", char_float_value[i])+" ";
}
s = s.substring(0, s.length()-1);
Log.e("_____________", "_____________");
Log.d("TAG", s);
handler.sendMessage(Message.obtain(null,MSG_HEARTRATE,char_float_value[14]));
}
}
And these are the packets i receive at the same time
Packets