After successful ble connection, I am writing a Characteristic with DefaultWriteType.
Just after that onCharacteristicChanged triggers and after that onCharacteristicWrite triggers with same Characteristics UUID which was write but with values came as response in that Characteristics are same as response of onCharacteristicChanged.
Summary:
How onCharacteristicChanged triggers before onCharacteristicWrite ?
As I believe onCharacteristicWrite indicates that your write operation was successful or not.
And onCharacteristicChanged responds/notify to the command for which we write to ble device.
Is this possible or is it going out of Ble cycle?
I think this is expected.
Because onCharacteristicWrite only indicates, that your write operation was successful or not.
A BluetoothGattCharacteristic can hold only one value, which is the last one send or received.
Both operations are using the same BluetoothGattCharacteristic instance.
So regarding to your described sequence, it is "normal" that you have the same value in both callbacks. Since the last operation, which manipulates the value in your characteristic was a BLE notification.
I think that was not the best idea, how it was implemented in the BLE stack. It would be better if the operations would not interfere each other and would be more idempotent/immutable.
Related
After writing, onCharacteristicWrite() gets called with the status BluetoothGatt.GATT_SUCCESS, but I don't actually see it on my peripheral.
I know the issue isn't on the peripheral because it works when I use a generic BLE Scanner app.
This is what my writeCharacteristic function looks like:
fun writeCharacteristic(characteristic: BluetoothGattCharacteristic, value: String) {
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
characteristic.setValue(value)
bluetoothGatt?.writeCharacteristic(characteristic)
?: run {
Log.w(TAG, "BluetoothGatt not initialized")
}
}
I've tried using a String or a ByteArray as the payload.
I've also tried adding the operation to a queue.
The issue remains the same.
Well the success status is only reported after the peripheral has actually sent back a Write Response.
So either you write to the wrong characteristic, the wrong device, or you are not correctly detecting at the peripheral when an incoming write has been received.
One way to debug this is to use the hci snoop log in Android, or using a BLE sniffer.
After connecting to BLE device and discovering its services, I check that a certain characteristic is readable, then, I call readCharacteristic. This call returns true, but the callback onCharacteristicRead is not called.
After merely 30 seconds, the onCharacteristicRead is called with characteristic == null and the onDeviceDisconnected is called with status code equal to 22.
I have tested nRF Connecte to read the same characteristic and it worked fine.
What could possibly be wrong ?
The fact that it timeouts after 30 seconds and disconnects with error 22 (local device terminated the connection) indicates that the peripheral did not respond within 30 seconds, as required by the GATT standard. You should debug the peripheral to find the cause.
The reason it works in nRF Connect might be that the previous GATT sent / received put the peripheral in a different state.
You could check out the HCI log in Android to see all raw packets to maybe figure out what's going on.
I'm new to BLE development on Android, and I'm looking at the API docs and don't see a way to cancel a read/write characteristic/descriptor operation that has been "queued" to the remote device. How can I achieve that?
Specifically, after calling the following, how to I cancel the write after a timeout (handled elsewhere using an AsyncTask)?
private void writeCharacteristic(BluetoothGatt gatt) {
Log.i(TAG, "Writing to " + mCharacteristic);
characteristic.setValue(mPayload);
gatt.writeCharacteristic(mCharacteristic);
}
You can't. A Write Request is sent over to the remote device and it answers with a Write Response. When the Write Response is received, the onCharacteristicWrite callback is called. There is no "cancellation" specified in the BLE protocol. Instead a 30 second timeout is specified. If the remote device does not send a Write Response within 30 seconds, the link is dropped. This is implemented for you in Android's Bluetooth stack. Since there may also only be one outstanding request at a time with the GATT protocol, there is no way to "retry" the operation.
I would advice against handling operations this way as it is not guaranteed that any Bluetooth LE operation will succeed or fail in X amount of time. Factors such as distance, interference etc will all affect your operation.
You will get a definite result of your operation on your BluetoothGattCallback methods. For example:
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status==BluetoothGatt.SUCCESS)
{
//operation completed successfully
}
else
{
//operation failed
}
}
All write/read operations for characteristics and descriptors deliver a result like that.
I'm working on an android BLE app. I have a BLE device which storing the coordinates of where the device went.
In order to get the data from the device after connected, we will need to send several commands and read its response. The steps are as follow:
Send first command to device
Read response (some data..)
Send second command to device
Read response (coordinates)
And this is how I did
After connected, sending a command to the device by using writeCharacteristic
When receiving GATT_SUCCESS in onCharacteristicWrite callback, call readCharacteristic to read its response
When receiving GATT_SUCCESS in onCharacteristicRead callback, continue to send second command to the device by using writeCharacteristic
When receiving GATT_SUCCESS in onCharacteristicWrite callback, call readCharacteristic to read its response
So here's the problem, when I issued readCharacteristic after write operation succeeded, the response is not expected as always and sometime it return the same command that I wrote. However, if I put some delay (e.g. 1 second) after I write, the response is correct when I read but the delay may different every time.
I am wondering if anyone having the same issue like this or anyone can explain to me why is this happening? Is that possible that we can know when is the correct time to read the characteristic?
Note: The device does not provide any notification/indication.
I am implement the connection between Android and BLE? like Anti-lost or finder , After android phone has connected to the BLE device , phone read the RSSI of BLE device every second.
If the RSSI of BLE device is lower than RSSI threshold , it deem Out of Range. For example: Threshold is -70 , and the current RSSI of device is -80.
When app is deem Out of Range. it send the message to the BLE every 5 second. But it always disconnect after few times. I uses the following code to send the message to the BLE device.
BluetoothGattService HelloService = Gatt.getService(HELLO_SERVICE_UUID);
if(HelloService == null) {
Log.d(TAG, "HelloService not found!");
return;
}
//If the Service is not null , try to get the characteristic.
BluetoothGattCharacteristic Characteristic = HelloService.getCharacteristic(UUID_HELLO_CHARACTERISTIC);
if(Characteristic == null) {
Log.d(TAG, "Characteristic not found!");
return;
}
Gatt.setCharacteristicNotification(Characteristic, true);
Characteristic.setValue(text, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
Gatt.writeCharacteristic(Characteristic);
Log.d(TAG, "StepCount Characteristic End!");
The above code is correct , the BLE can receive the message. But the BLE device will disconnect after few second. It seems do more than one thing in a short time is burden to BLE device.
The question is: How to make the connection more stable between Android and BLE ?.
Some suggestions:
Don't use notifications if you can avoid it. Based on personal experience with some phones in some environments notifications can stop working and appear to cause general instability. Try to do periodic reads instead.
Only do a read or write once you have received a callback to BluetoothGattCallback.onCharacteristicWrite() or BluetoothGattCallback.onCharacteristicRead() for the previous read or write.
More generally, never do two things at once, whether that be scanning, connecting, reading, writing, whatever. You should serialize all operations using a job queue, only popping from that queue when the previous job completes (or fails).
In almost-out-of-range scenarios like you're talking about, operations can take a long time to complete, longer than 5 seconds sometimes. So doing another operation in 5 seconds or less you're effectively "stomping" the previous operation. However, operations can also never return with a callback in these cases, so you do have to implement a timeout. I use 10 seconds. Beyond that, the operation failed.