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.
Related
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.
A BLE Tracker Device tries to connect to my BLE Android app. I would like to know about the event when the device tries to connect to me (means before it actually gets connected to me) and in case the device fails to connect to me i would like to know the reason why device could not connect to me.
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
Log.d(TAG, "onConnectionStateChange Status: " + status);
// logs += "\n\nonConnectionStateChange Status: " + status;
// updateHomeUI();
if (newState == BluetoothProfile.STATE_CONNECTED) {
connectedDeviceAddress = device.getAddress().toString();
Log.i(TAG, "BluetoothDevice CONNECTED:\n" + device);
FragmentManager fragmentManager = getSupportFragmentManager();
try {
logs += "\n\nBluetoothDevice CONNECTED: \n" + device;
updateHomeUI();
} catch (Exception e) {
e.printStackTrace();
}
mRegisteredDevices.add(device);
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Device CONNECTED: " + connectedDeviceAddress, Toast.LENGTH_SHORT).show();
}
});
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "BluetoothDevice DISCONNECTED: " + device);
//Remove device from any active subscriptions
logs += "\n\nBluetooth Device DISCONNECTED:\n" + device;
updateHomeUI();
mRegisteredDevices.remove(device);
//restarting everything -- test
// bluetoothAdapter.disable();
// bluetoothAdapter.enable();
// startAdvertising();
// startServer();
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Device DISCONNECTED", Toast.LENGTH_SHORT).show();
}
});
}
if (newState == BluetoothProfile.STATE_CONNECTING) {
Log.d(TAG, "Connecting...Status: " + status);
logs += "\n\nConnecting...Status: " + status;
updateHomeUI();
}
}
};
Above is the piece of code that i am using. You can see i have implemented the callback method onConnectionStateChange. I am also checking for the following values:
STATE_CONNECTED
STATE_DISCONNECTED
STATE_CONNECTING
Even though i am checking for all these, i realised that my code never gets inside STATE_CONNECTING check, thus i am not able to know when the device tries to connect to me and i cant find a way to know the error if in case the device fails to connect to my app.
Any help would be apprecicated.
For a BLE peripheral's point of view, there is no such state as the remote central is "connecting". There is just disconnected and suddenly connected. On the link layer, there is just one packet the central sends to the peripheral: "hey you're now connected", in response to an advertisement sent by the peripheral.
If you read Android's documentation at https://developer.android.com/reference/android/bluetooth/BluetoothGattServerCallback#onConnectionStateChange(android.bluetooth.BluetoothDevice,%20int,%20int), you will see that CONNECTING is not one of the possible states for this callback.
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
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.
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