BLE: How to send Write Request instead of Write Command? - android

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.

Related

Bluetooth BLE reliable write is not working as expected

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.

Android device as BLE central and read/write operations without any trigger from peripheral

My Android app is made as a BLE central device. I have a device which is peripheral. I want to send some data from Android app to peripheral device without any request from the peripheral. Can I achieve this ?
In my peripheral device to write data i am using below code :
BluetoothGattCharacteristic charac = Service
.getCharacteristic(UUID.fromString(SampleGattAttributes.LOCAL_TIME));
byte[] value = new byte[1];
String valuetosend = "data from client to server";
value = valuetosend.getBytes();
charac.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
charac.setValue(value);
Log.d("BluetoothLeservice::","Write Status:" + charac.getValue());
boolean status = mBluetoothGatt.writeCharacteristic(charac);
Do I have to do in the same way for central also? Any reference links would be helpful.
I think the process is same, you have to write data to the particular characteristic and service. You should know the value of the characteristic and service. In your case it is SampleGattAttributes.LOCAL_TIME.

Cancelling BluetoothGatt read/write operation?

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.

BLE 4.0 getting the broadcast data from device to phone

I have two devices. one Android phone with API level more than 18 and other is blue-tooth device 4.0.
Devices are successfully connected to each other.
Now flow of command is as follow:
a. Send the "hello" text to blue-tooth device.
UUID uuid = UUID.fromString("18cda784-4bd3-4370-85bb-bfed91ec86af");
BluetoothGattCharacteristic selectedChar = selectedGattService.getCharacteristic(uuid);
mBluetoothLeService.setCharacteristicNotification(selectedChar, true);
boolean flag = selectedChar.setValue("");
mBluetoothLeService.writeCharacteristic(selectedChar);
In this case I am getting hello through the GATT reciver. what is the meaning of this.
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
intentFilter.addAction(BluetoothLeService.EXTRA_DATA);
return intentFilter;
}
b. bluetooth device will perform some operations
auto done by bluetooth device
c. Result of operation is sent to android phone
Brodcated by device.
For this I used notification.
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
return;
}
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID
.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
System.out.println("nov7 descriptordescriptor " + descriptor);
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
I am not getting any data. Any idea please.
saw your email, I think you are somehow connected via Bluetooth classic, but are then attempting to 'chat' on BTLE protocol.
That's the problem.
There is almost no way an Android 4.0 device has BTLE.
Even if it has an BTLE chip(there was some early Motorola phones with BTLE - you had to import a .jar from Motorola Inc.), it wouldn't use the Android BTLE API you seem to use.
So to make a long story short, you should either be using Bluetooth Classic (SPP) with the normal BluetoothSocket, or be using two Android BTLE devices.
Here is how to check if the devices have BTLE:\
If you want to declare that your app is available to BLE-capable devices only, include the following in your app's manifest:
However, if you want to make your app available to devices that don't support BLE, you should still > include this element in your app's manifest, but set required="false". Then at run-time you can determine BLE availability by using PackageManager.hasSystemFeature():
// Use this check to determine whether BLE is supported on the device.
// Then you can selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
Two things I see that you should be doing.
BluetoothGatt has its own setCharacteristicNotification method. In addition to writing the characteristic descriptor to enable notifications, you need to call that method to enable notifications. Think of it as writing the descriptor enables notifications on the BLE device and setCharacteristicNotification enables it on the Android device.
So in your setCharacteristicNotification method above I would add the following:
// I'm assuming you have access to the BluetoothGatt object in your BluetoothGattService object
gatt.setCharacteristicNotification(characteristic, true);
You shouldn't be trying to write any data to the characteristic until you have received confirmation that the descriptor was written. That means you need to wait until you get the callback to onDescriptorWrite in your implementation of BluetoothGattCallback.

Android does not make the BLE findme device beep

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.

Categories

Resources