Android BLE - Device keep disconnecting after a succesfull characteristic write - android

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.

Related

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.

Android, very Simple BLE Example, unable to connect to device

At work we have a device that updates a custom characteristic in a custom service every few seconds and uses BLE. All I want to do is monitor those updates. My objective here is to write the bare minimum of code in order to connect to that device, then to the service and then monitor its characteristic. However I can't connect to the device.
Every time a bluetooth device is found by scanning this function is called:
void checkDevice(BluetoothDevice device){
if (targetFound) return;
if (device.getAddress().equals(DEVICE_ADDRESS)){
logger.append("-> Target device found with name: " + device.getName() + "\n");
targetFound = true;
targetDevice = device;
bleAdapter.stopLeScan(new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
logger.append("-> Scan has stopped\n");
}
});
logger.append("-> Attempting to connect to target device\n");
gattConnection = targetDevice.connectGatt(this,false,gattCallBackFuntion);
}
}
This is my implementation of the gattCallBackFunction:
private BluetoothGattCallback gattCallBackFuntion = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
Boolean gattRes = gattConnection.discoverServices();
logger.append("-> Connected to GATT Server. Starting service discovery. Result: " + gattRes.toString() + "\n");
}
else{
logger.append("-> Connection state has changed but is " + newState);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
List<BluetoothGattService> services = gatt.getServices();
String list = "";
String tab = " ";
for (int i = 0; i < services.size(); i++){
BluetoothGattService service = services.get(i);
list = list + tab + service.toString() + "\n";
}
logger.append("-> Services Discovered: \n");
logger.append(list);
}
};
Both of these functions are implemented in the same class which is the only activity.
As I understand it at some point the function onConnectionStateChange must be invoked by Android indicating a connection to the Device. However this never happens. The app prints "Attempting to connect to target device" and nothign else happens
Any ideas?
Most of the Bluetooth gatt callbacks run inside a background thread hence you should not perform any UI operations inside the callback. You need to run all the operations on UI thread as per the Android UI guidelines

Receiving multiple BLE Packets per one connection interval in Android

My device information
Nordic board :
MTU size : 247.
This board send notification multiple packets in one connection interval (Just counter value like 1,2,3,4...) at "Heart Rate Measurement".
Android device :
Version 5.0 and 6.0. (Using two device).
communicate Bluetooth Low Energy(BLE) with board.
board connect with my Android app MTU is setting 244.
Application source is google sample project BluetoothLeGatt
App send notification value at "Heart Rate Service" characteristic.
Receive notification value at Gattcallback "onCharacteristicChanged()"
Problem
My Android app lost some packet.
I read this post. Maximizing BLE Throughput on iOS and Android. So I send E-mail this post author and I search another information for Android.
I found some similarly question. but that question answer was not work. Then I found one question what I want exactly. but this question have no answers. Android receiving multiple BLE packets per connection interval. Unfortunately I don't have any reply E-mail answer.
My question is how do I set Android BLE notification. (Not Nordic board setting)
(My question is same Android receiving multiple BLE packets per connection interval)
Under line is my sample code. at notification.
#Connect
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) {
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w(TAG, "Device not found. Unable to connect.");
return false;
}
// We want to directly connect to the device, so we are setting the autoConnect
// parameter to false.
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
Log.d(TAG, "Trying to create a new connection.");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
#GattCallback
/*broadcastUpdate method is display value*/
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mConnectionState = STATE_CONNECTED;
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:");
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
mConnectionState = STATE_DISCONNECTED;
broadcastUpdate(ACTION_GATT_DISCONNECTED);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onServicesDiscovered success: (status)" + status);
//findServiceOther(gatt);
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
Log.d(TAG, "Request MTU");
gatt.requestMtu(SET_MTU);
} else {
Log.w(TAG, "onServicesDiscovered failed: (status)" + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onCharRead Success");
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
} else {
Log.d(TAG, "OnCharRead Error: " + status);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (characteristic.getUuid().equals(SampleGattAttributes.UUID_HEART_RATE_MEASUREMENT))
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic, FLAG_HEART_RATE);
else
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
#Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
boolean priority = gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
Log.d(TAG, "MTU changed (mtu/status) / Priority : (" + mtu + "/" + status + ") / " + priority);
changed_MTU_Size = mtu;
broadcastUpdate(ACTION_DATA_AVAILABLE, changed_MTU_Size, FLAG_MTU);
broadcastUpdate(ACTION_GATT_CONNECTED);
}
#set notification
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
if (SampleGattAttributes.UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
// This is specific to Heart Rate Measurement.
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(SampleGattAttributes.UUID_CLIENT_CHARACTERISTIC_CONFIG);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
Edit_1
I test Nordic offical app in Google play store.
nRF Connect for Mobile
but this app miss packet too.
Edit_2
I found some problem too.
< Nordic board constant setting>
HEART_RATE_MEAS_INTERVAL : 10
MIN_CONN_INTERVAL : MSEC_TO_UNITS(40, UNIT_1_25_MS)
MAX_CONN_INTERVAL : MSEC_TO_UNITS(40, UNIT_1_25_MS)
SLAVE_LATENCY 0
CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
< Run in my Android app >
skip requestMTU : receive 20byte packet successful. (Data rate average is 2200byte/second in my app)
requestMTU : I try so many MTU size (ex: 23(can small size), 40, 100, 255(target) ...) but lost some packet (Data rate 8500 ~ 9500 byte/second in my app)
I wonder requestMTU and notification receive interrelation.
i have implemented a file transfer over LE on a nrf51422 and SD310 by using the Nordic UART Service. The android App is written in CPP by using Qt and the QtBluetooth library. Therefore, this answer my not really help you. But, i have taken some afford to achive a usable data rate. 1st, set the connection parameters to 7.5ms (min) and 15ms (max). 2nd, on the peripheral side, i send up to 7 packets (til the buffer is full). So the peripheral send up to 7 packets per connection event.
On the Android side, the charachteritic changed event arrive me frequently with a data size of 20 bytes (due to the maximum MTU size of 23, 3 bytes used by nordic uart service) and i read the data instantly.
Perhaps your mistake is the MTU size of 244. The default BLE MTU size is 23 (Core spec 4.1).
Regards

Android BLE readCharacteristic fails

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.

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