Android BLE readCharacteristic fails - android

I'm trying to read the initial state of a BLE device when I connect to it. Here's the code I have to try to do that:
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
if(status == BluetoothGatt.GATT_SUCCESS)
{
Log.i(TAG, gatt.getDevice().toString() + "Discovered Service Status: " + gattStatusToString(status));
for(BluetoothGattService service : gatt.getServices())
{
Log.i(TAG, "Discovered Service: " + service.getUuid().toString() + " with " + "characteristics:");
for(BluetoothGattCharacteristic characteristic : service.getCharacteristics())
{
// Set notifiable
if(!gatt.setCharacteristicNotification(characteristic, true))
{
Log.e(TAG, "Failed to set notification for: " + characteristic.toString());
}
// Enable notification descriptor
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCC_UUID);
if(descriptor != null)
{
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
// Read characteristic
if(!gatt.readCharacteristic(characteristic))
{
Log.e(TAG, "Failed to read characteristic: " + characteristic.toString());
}
}
}
}
else
{
Log.d(TAG, "Discover Services status: " + gattStatusToString(status));
}
}
But the read fails every time! Later if I initiate a read based on UI interaction it reads just fine! Any ideas about what's going on here?

In the Android BLE implementation, the gatt operation calls need to be queued so that only one operation (read, write, etc.) is in effect at a time. So for example, after gatt.readCharacteristic(characteristicX) is called, you need to wait for the gatt callbackBluetoothGattCallback.onCharacteristicRead() to indicate the read is finished. If you initiate a second gatt.readCharacteristic() operation before the previous one completes, the second one will fail (by returning false) This goes for all of the gatt.XXX() operations.
Its a little work, but I think the best solution is to create a command queue for all the gatt operations and run them one at a time. You can use the command pattern to accomplish this.

Related

Android 12 phone stuck seeing same BLE peripheral Service and Characteristic UUIDs

first post here!
I'm trying to use an Arduino Nano RP2040 Connect as a Bluetooth Low Energy peripheral, currently having 1 service with 3 characteristic exposing:
a Int representing the value off of a potentiometer;
a Int representing the RSSI (did it as for visual debug, there's a LED representing it);
a Bool representing the status of a LED;
Service and characteristics UUIDs are randomly generated.
The code is this:
#include <ArduinoBLE.h>
#define LED_RSSI 2
#define LED_STATUS 4
int oldSensorValue = 0;
int oldRssiValue = 0;
bool ledState = false;
long previousMillis = 0;
BLEService sensorService("18902a9a-1f4a-44fe-936f-14c8eea41800");
BLEIntCharacteristic sensorChar("18902a9a-1f4a-44fe-936f-14c8eea41801", BLERead | BLENotify);
BLEIntCharacteristic rssiChar("18902a9a-1f4a-44fe-936f-14c8eea41802", BLERead | BLENotify);
BLEBoolCharacteristic ledChar("18902a9a-1f4a-44fe-936f-14c8eea41803", BLERead | BLEWrite | BLENotify);
void setup() {
Serial.begin(9600);
while (!Serial) {
;
}
pinMode(LED_BUILTIN, OUTPUT);
pinMode(LED_RSSI, OUTPUT);
pinMode(LED_STATUS, OUTPUT);
digitalWrite(LED_STATUS, ledState);
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1)
;
}
BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
BLE.setLocalName("Sensor Monitor");
//BLE.setDeviceName(name); //defaults “Arduino”
BLE.setAppearance(0x015); //set appearance as "Sensor" 0x015
sensorService.addCharacteristic(sensorChar);
sensorService.addCharacteristic(rssiChar);
sensorService.addCharacteristic(ledChar);
BLE.addService(sensorService);
BLE.setAdvertisedService(sensorService); // add the service UUID
sensorChar.writeValue(oldSensorValue);
rssiChar.writeValue(oldRssiValue);
ledChar.writeValue(ledState);
ledChar.setEventHandler(BLEWritten, onUpdateLed);
sensorChar.setEventHandler(BLESubscribed, onSubscribedChar);
rssiChar.setEventHandler(BLESubscribed, onSubscribedChar);
ledChar.setEventHandler(BLESubscribed, onSubscribedChar);
sensorChar.setEventHandler(BLEUnsubscribed, onUnsubscribedChar);
rssiChar.setEventHandler(BLEUnsubscribed, onUnsubscribedChar);
ledChar.setEventHandler(BLEUnsubscribed, onUnsubscribedChar);
startAdvertise();
}
void loop() {
BLEDevice central = BLE.central();
if (central) {
while (central.connected()) {
long currentMillis = millis();
long timer2 = millis();
if (currentMillis - previousMillis >= 200) {
previousMillis = currentMillis;
updateSensorValue();
updateRSSI();
}
}
}
}
void startAdvertise() {
if (!BLE.advertise()) {
Serial.println("Bluetooth® device failed to advertise.");
while (1) {
;
}
} else {
Serial.println("Bluetooth® device active, waiting for connections...");
}
}
void updateSensorValue() {
int sensorValue = analogRead(A0);
if (sensorValue != oldSensorValue) {
// Serial.print("sensor Level % is now: ");
// Serial.println(sensorValue);
sensorChar.writeValue(sensorValue);
oldSensorValue = sensorValue;
}
}
void updateRSSI() {
int newRssiValue = BLE.rssi();
if (newRssiValue != 127)
analogWrite(LED_RSSI, map(abs(newRssiValue), 0, 128, 255, 0));
else
analogWrite(LED_RSSI, 0);
if (newRssiValue != oldRssiValue) {
// Serial.print("RSSI is now: ");
// Serial.println(newRssiValue);
rssiChar.writeValue(newRssiValue);
oldRssiValue = newRssiValue;
}
}
void onUpdateLed(BLEDevice central, BLECharacteristic characteristic) {
Serial.println("Central '" + central.address() + "' wrote '" + ledChar.value() + "' to characteristic '" + characteristic.uuid() + "'");
ledState = ledChar.value();
digitalWrite(LED_STATUS, ledState);
}
void onSubscribedChar(BLEDevice central, BLECharacteristic characteristic) {
Serial.println("Central '" + central.address() + "' subscribed to characteristic '" + characteristic.uuid() + "'");
}
void onUnsubscribedChar(BLEDevice central, BLECharacteristic characteristic) {
Serial.println("Central '" + central.address() + "' unsubscribed to characteristic '" + characteristic.uuid() + "'");
}
void blePeripheralConnectHandler(BLEDevice central) {
// central connected event handler
Serial.println("Central '" + central.address() + "' connected");
digitalWrite(LED_BUILTIN, HIGH);
BLE.stopAdvertise();
}
void blePeripheralDisconnectHandler(BLEDevice central) {
// central disconnected event handler
Serial.println("Central '" + central.address() + "' disconnected");
digitalWrite(LED_BUILTIN, LOW);
analogWrite(LED_RSSI, 0);
startAdvertise();
}
I have tried its functionality with nRF Connect and LightBlue on Android, trying to read, subscribe and write characteristics, everything seems to work as intended.
The problem I'm facing is that whatever I do with the UUIDs on the Arduino side I keep seeing the same ones on the apps mentioned above.
On nRF Connect there's the possibility to "Refresh services" and that does fixes temporarely the issue but if I disconnect from the peripheral and reconnect the issue appears again.
Tried cleaning data and cache of both apps, of bluetooth system app, without success.
Its not only those 2 apps that do this behaviour:I'm trying to interface the Arduino with a Unity app ran on Android and it fails to subscribe to the characteristics with the "new" UUIDs but somehow succeed if I set the Arduino with the UUIDs my phone is stuck seeing.
A completely different phone sees updates UUIDs, everytime, but I need to use my phone for developing.
Is there something wrong with my phone behaviour? It's a Motorola Moto G52.
How can I fix this? What am I doing wrong? Any help appreciated
Apparently the client uses GATT caching. This way it saves some round trips for service discovery for every connection start.
A client can cache the database structure (only) in one of the following cases:
The devices are bonded. The server uses the Service Changed characteristic to inform the client when something changes with the db structure, by sending an indication containing the range of the handles that have changed.
Both the client and server support the relatively new Database Hash characteristic, which contains a hash of the db structure which the server stores in a characteristic which the client reads on every reconnection. If it has changed since the last time, the client must rediscover.
The server never changes the db structure during its lifetime. This is indicated to the client by not having the Service Changed characteristic.
If none of the above cases apply, then the client is buggy and behaves incorrectly.

Android BLE - Device keep disconnecting after a succesfull characteristic write

I'm working on a mobile app that implements Bluetooth LE and communicates with a HC-08 device.
While everything is ok on iOS with CoreBluetooth, i encounter some problems on my Android version.
I start by getting a Bluetooth Device
BluetoothDevice bluetoothDevice;
After that, i connect to it:
bluetoothDevice.connectGatt(context, false, callback);
Which is calling a callback function inside which i wait for STATE_CONNECTED and discover services, then write to characteristic:
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "onConnectionStateChange with success");
if(newState == BluetoothProfile.STATE_CONNECTED){
Log.e("BLE_LOG", "sucessfully connected to device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress());
bluetoothGatt = gatt;
gatt.discoverServices();
}
else if(newState == BluetoothProfile.STATE_DISCONNECTED){
Log.e("BLE_LOG", "succesfully Disconnected from device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress());
gatt.close();
}
}
else{
Log.e("BLE_LOG", "onConnectionStateChange with status: " + status + " and newState: " + newState);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "sucessfully discovered services for device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress());
service = bluetoothGatt.getService(UUID.fromString(BLEFunctions.PROPRIETARY_SERVICE));
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(BLEFunctions.PROPRIETARY_CHARACTERISTIC));
//Souscription notifications
if(bluetoothGatt.setCharacteristicNotification(characteristic, true)){
Log.e("BLE_LOG", "subscribed to notifications from characteristic");
}
characteristic.setValue(SET_ON);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
bluetoothGatt.writeCharacteristic(characteristic);
}
else{
Log.e("BLE_LOG", "Error discovering services for device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress() + " with status: " + status);
}
}
When services are discovered, i'm writting to characteristic, which actually work because my bluetooth device is acting as expected (it switches on a relay), and onCharacteristicWrite is called:
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "sucessfully written characteristic for device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress());
}
else{
Log.e("BLE_LOG", "Error writing characteristic for device: " + bluetoothDevice.getName() + "/" + bluetoothDevice.getAddress() + " with status: " + status);
}
}
The problem is that, as soon as the characteristic is written, the device disconnect and i can't continue my process.
At a point in the program, i've got connectionStateChange with status 8 (which seems to be : GATT_INSUFFICIENT_AUTHORIZATION)
I investigated several potential causes, as :
-Maybe the characteristic needs bonding before writing? I know it is handled automatically on iOS for example.
-Maybe the device i use has some incompatibilities with Android (but it works well with software like 'LightBlue', 'BLE Scanner', etc...
Can't figure out what is wrong.
Please help me solve this problem
Adding the log to help:
2022-05-05 09:58:25.600 22204-22204/com.******.******E/BLE_LOG: clicked on device: Hall/B0:B1:13:76:0B:1E
2022-05-05 09:58:26.065 22204-22467/com.******.******E/BLE_LOG: onConnectionStateChange with success
2022-05-05 09:58:26.068 22204-22467/com.******.******E/BLE_LOG: sucessfully connected to device: AAA-000000000001001/B0:B1:13:76:0B:1E
2022-05-05 09:58:26.727 22204-22467/com.******.******E/BLE_LOG: sucessfully discovered services for device: AAA-000000000001001/B0:B1:13:76:0B:1E
2022-05-05 09:58:26.732 22204-22467/com.******.******E/BLE_LOG: subscribed to notifications from characteristic
2022-05-05 09:58:26.754 22204-22467/com.******.******E/BLE_LOG: sucessfully written characteristic for device: AAA-000000000001001/B0:B1:13:76:0B:1E
2022-05-05 09:58:31.854 22204-22467/com.******.******E/BLE_LOG: onConnectionStateChange with status: 8 and newState: 0
Using my device with software like nRF Connect or LightBlue works well. (no disconnection)
I made progress and managed to point out a reason why the problem occures.
Actually, i'm discovering services via
gatt.discoverServices();
and as soon as i get the callback method, i get the service and characteristic i need and write to it.
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "successfully discovered services");
}
BluetoothGattService service = gatt.getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"));
Log.e("BLE_LOG", "got serivce with uuid: " + service.getUuid().toString());
bluetoothGatt = gatt;
characteristic = service.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
Log.e("BLE_LOG", "got characteristic with uuid: " + characteristic.getUuid().toString());
byte[] data = {
0x24, 0x6F, 0x6E, 0x25
};
characteristic.setValue(data);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(characteristic);
}
When i deport the write part in another function, like a button function, it works well and doesn't disconnect.
public void clicked_open(View v){
byte[] data = {
0x24, 0x6F, 0x6E, 0x25
};
characteristic.setValue(data);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(characteristic);
}
It's as if something wasn't totally finished when i call my write as soon as i get the service discovered callback.
Maybe there is a way to ensure everything is ok before writing...
As you realized in your last edit, you are writing to the characteristic too soon. onServicesDiscovered is called multiple times, once for each found characteristic. You are already checking for the finished discovery and just need to move your writing operation there.
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
BluetoothGattService service = gatt.getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"));
Log.e("BLE_LOG", "got serivce with uuid: " + service.getUuid().toString());
if(status == BluetoothGatt.GATT_SUCCESS){
Log.e("BLE_LOG", "successfully discovered services");
bluetoothGatt = gatt;
characteristic = service.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
Log.e("BLE_LOG", "got characteristic with uuid: " + characteristic.getUuid().toString());
byte[] data = {
0x24, 0x6F, 0x6E, 0x25
};
characteristic.setValue(data);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(characteristic);
}
}
The parameter status will only be GATT_SUCCESS after the discovery finished, see the documentation for more information.

BluetoothGattCharacteristic.value returns empty value

I am trying to parse a value of a BluetoothGattCharacteristic to another activity. I can read the characteristic successfully, but then the value it gives me is empty...
I am trying to read the device name. In another BLE app (nRF connect) can I see the characteristic and its value. Can someone help me, what am I doing wrong?
This method loops and logs the services found with its characteristics:
private void displayGattServices(List<BluetoothGattService> gattServices) {
//Check if there is any gatt services. If not, return.
if (gattServices == null) return;
// Loop through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
Log.i(TAG, "SERVICE FOUND: " + gattService.getUuid().toString());
//Loop through available characteristics for each service
for (BluetoothGattCharacteristic gattCharacteristic : gattService.getCharacteristics()) {
Log.i(TAG, " CHAR. FOUND: " + gattCharacteristic.getUuid().toString());
}
}
//****************************************
// CONNECTION PROCESS FINISHED!
//****************************************
Log.i(TAG, "*************************************");
Log.i(TAG, "CONNECTION COMPLETED SUCCESFULLY");
Log.i(TAG, "*************************************");
goToDisplayBleServicesActivityOnListItemClick();
}
goToDisplayBleServiceActivityOnListItemClick():
private void goToDisplayBleServicesActivityOnListItemClick() {
Intent intent = new Intent(this, displayBleServicesActivity.class);
BluetoothGattService selectedService = mBluetoothGatt.getService(UUID.fromString("00001800-0000-1000-8000-00805f9b34fb"));
BluetoothGattCharacteristic selectedCharacteristic = selectedService.getCharacteristic(UUID.fromString("00002a00-0000-1000-8000-00805f9b34fb"));
if (mBluetoothGatt.readCharacteristic(selectedCharacteristic)) {
intent.putExtra("READ_CHAR", selectedCharacteristic.getValue().toString());
} else {
intent.putExtra("READ_CHAR", "It did not work out");
}
startActivity(intent);
}
What am I doing wrong here?
If you read the documentation at https://developer.android.com/reference/android/bluetooth/BluetoothGatt#readCharacteristic(android.bluetooth.BluetoothGattCharacteristic) it says:
This is an asynchronous operation. The result of the read operation is reported by the BluetoothGattCallback#onCharacteristicRead callback.
So you must override the onCharacteristicRead callback. Inside that one you can get the value.

BLE Gatt onConnectionStateChanged failed with status 257 in Android

I am developping a Android app that connects to multiple BLE devices at the same time, after that i read characteristic permanently from those devices but after a while, I am getting a status 257 in the onConnectionStateChanged() function, the android documentation doesn't explain what is the reason of the error, or how to fix it.
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i("TAG","onConnectionStateChange, status : "+status+" parseConnection : "+ GattStatusParse.parseConnection(status)+" or "+ GattStatusParse.parseConnection(status));
isConnected = BluetoothProfile.STATE_CONNECTED == newState;
if (status == BluetoothGatt.GATT_SUCCESS) {
if (isConnected) {
Log.i(TAG, "GATT connected." + connectedBluethoothDevice.toString());
gatt.discoverServices();
} else {
Log.i("TAG"," GATT disconnected " + device.getAddress() + " state of the opération : " + status + " connexion state : " + newState);
if (connectedBluethoothDevice.contains(device)) {
connectedBluethoothDevice.remove(device);
}
}else{
if (connectedBluethoothDevice.contains(device)) {
int mConnectionState = mBluetoothManager.getConnectionState(device, BluetoothProfile.GATT);
if(mConnectionState==BluetoothProfile.STATE_DISCONNECTED || mConnectionState==BluetoothProfile.STATE_DISCONNECTING){
connectedBluethoothDevice.remove(device);
}
}
}
}
could anyone help me to fix this problem, thanks.
I also didn't find error code 257 in the documentation, but my observation is that this code means that you are trying to connect to a Bluetooth device too many times in too short time. Reconnecting bluetooth on your phone should resolve this issue.

mBluetoothGatt.getService(uuid) returns null

In my app , i am passng the UUID number of the hearing aid service as in the BLE sample from google i.e. 0000a00-0000-1000-8000-00805f9b34fb
But the getservice returns null means that the service is not supported by the BluetoothGatt .
Why is this happening , can anybody please help me .
You have to first discover all services for the given device per documentation.
This function requires that service discovery has been completed for the given device.
http://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getService(java.util.UUID)
#Override
// New services discovered
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
BluetoothGattService mBluetoothGattService = mBluetoothGatt.getService(UUID.fromString(serviceUUID));
if (mBluetoothGattService != null) {
Log.i(TAG, "Service characteristic UUID found: " + mBluetoothGattService.getUuid().toString());
} else {
Log.i(TAG, "Service characteristic not found for UUID: " + serviceUUID);
}
}
Or you can just run a search
for (BluetoothGattService gattService : gattServices) {
Log.i(TAG, "Service UUID Found: " + gattService.getUuid().toString());
}
Try doing a full discover[1] of the remote database and then iterate through the services. Maybe you've got the UUID wrong.
[1] http://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#discoverServices()
Your UUID is also wrong. It should be 0000a00X-0000-1000-8000-00805f9b34fb, not 0000a00-0000-1000-8000-00805f9b34fb

Categories

Resources