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.
Related
I'm trying to send packets to bluetooth low energy (BLE) device from smartphone (Android App)
I know how to send Write Command:
something like this:
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
for (BluetoothGattService service : services) {
for (final BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
if (characteristic.getUuid().toString().equals(CONTROL_UUID)) {
boolean setValue = characteristic.setValue(new byte[]{/*..BYTES.*/});
boolean writeCharacteristic = gatt.writeCharacteristic(characteristic);
}
}
}
}
when I see this sent command in Wireshark (sniffing app), I see something like this
but I also need to send something like this (not write command, but write request)
official app which controls that devices sends this write request only once after connecting
seems without it I would not be able to control that device
so before I would send any write command I need to send this write request first
See https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setWriteType(int)
You need to use the WRITE_TYPE_DEFAULT.
Following is my reliable gatt characteristic reliable write function and byte array byte1 is having value more than 20 bytes.
private void beginReliableWriteToGattServer(BluetoothDevice device, UUID serviceUUID,UUID charUUID, byte[] byte1){
if(mGatt != null){
BluetoothGattService service = mGatt.getService(serviceUUID);
if(service != null){
BluetoothGattCharacteristic gattCharacteristic = service.getCharacteristic(charUUID);
if(gattCharacteristic != null){
Logger.d(TAG, "BeginReliable Write="+mGatt.beginReliableWrite());
gattCharacteristic.setValue(byte1);
mGatt.writeCharacteristic(gattCharacteristic);
Logger.d(TAG, "ExecuteReliable Write="+mGatt.executeReliableWrite());
}
}
}
}
Below are write Gatt characteristic logs
BeginReliable Write=true
ExecuteReliable Write=false
D/Bluetooth_GATTCallBack: onCharacteristicWrite 17
First, you can't have multiple GATT requests outstanding at the same time. Since both writeCharacteristic and executeReliableWrite are requests to the peer device (beginReliableWrite is not a request but only sets a flag in Android's BLE stack that the following writes are "reliable writes"), you need to first wait for the onCharacteristicWrite until you are allowed to send executeReliableWrite.
Now, regarding error code 17, I assume this corresponds to the ATT error code Insufficient Resources 0x11. To handle that you need to check why the peripheral sends that error code.
You should also know that Android has a design bug that reliable writes aren't really reliable. The protocol is that the data is first sent to the server, then the server sends the same data back to the client. According to the GATT specification, the client must then verify that the received data is equal to the sent data, otherwise it must abort. Unfortunately that info gets lost between the C and Java layer in Android's Bluetooth stack so there is no way to verify this.
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.
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.
I'm trying to implement the application which will communicate with BLE findme device. I have the one of these devices, but have some problem with it. Using iPhone I have tested this device with bleTools application and this app works correctly, i.e. I have managed to read all device's characterictics and send the characteristics to make the device beep. But using Android (Nexus 5) I could only read the device's characteristics, but cannot make the device beep.
My code is:
private static final UUID IMMEDIATE_ALERT_SERVICE =
UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
private static final UUID IMMEDIATE_ALERT_LEVEL =
UUID.fromString("00002a06-0000-1000-8000-00805f9b34fb");
...
public void beep(DeviceData device) {
BluetoothGatt gatt = mConnectedDevices.get(device.getDeviceAddress());
BluetoothGattService bluetoothGattService = gatt.getService(IMMEDIATE_ALERT_SERVICE);
if (bluetoothGattService == null) {
return;
}
BluetoothGattCharacteristic characteristic =
bluetoothGattService.getCharacteristic(IMMEDIATE_ALERT_LEVEL);
if (characteristic == null) {
return;
}
byte[] arrayOfByte = new byte[1];
arrayOfByte[0] = (byte) 0x01;
characteristic.setValue(arrayOfByte);
gatt.writeCharacteristic(characteristic);
}
The callback method returns Ok:
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (characteristic.getUuid().toString().equals(IMMEDIATE_ALERT_LEVEL.toString())) {
//TODO: use device address to identify the device-receiver
Message msg = new Message();
msg.what = MSG_PARAM_WRITTEN;
msg.obj = (status == BluetoothGatt.GATT_SUCCESS);
mHandler.sendMessage(msg);
}
}
but nothing happens on the device side.
Can anyone explain me what I'm doing wrong or maybe give some advice what should I do?
And again, I can read the device characteristics, but cannot write them to the device.
Unlike iOS, Android has quite a few undocumented tricks with Bluetooth. I'm assuming you are using the standard Bluetooth library included in Android 4.3 and later. If you using other libraries like Samsung or Broadcom, the results could be different.
Because I do not have a findeme device I cannot confirm anything. But I have worked with both classic and low energy Bluetooth energy on Android for a while now. My advice is to go through the complete process of scan, discover services and read/write characteristics.
startLeScan
onLeScanCallBack connect to the device
onConnect discoverServices
onServicesDiscovered get all characteristics
check the properties on each characteristic
if you can read it, go ahead and do that
after that is done, you can then write the characteristic and listen for the onCharacteristicWrite event. You may get the beep then. If not, you'll need to go back to the iOS project and trace every bit that is sent to and received from the findme device. That sounds difficult but it's really just a matter of reading bytes inside of the right delegates. Document that. Then go back and recreate the bit sequence on the Android side. Again, capture all the traffic to and from the findme device. If you can get the bit sequence to match, you'll have success.
There's one more very important thing to know about Android BLE. The writes must be sequential. By that I mean, if you write a characteristic, you must wait for the onCharacteristic event to fire before you can write another one. This is not documented on the Android developer site. The best way to implement this functionality is to use a LinkedList as a work queue.
Good luck and ping me if you have questions.