Android does not make the BLE findme device beep - android

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.

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 BLE reconnection very slow

Background:
I have a BLE peripheral with two modes: "Application" and "Bootloader". In both modes, the device advertises with the same MAC address.
To switch from one mode to the other, the BLE peripheral must reboot itself. In doing so, it has to disconnect any active BLE connection.
The BLE peripheral only stays in Bootloader mode for about 5 seconds. If nobody connects to it within that window, it switches to Application mode.
The Problem:
Android takes a very long time to reconnect to the BLE device, long enough that I'm missing the 5 second window. The raw code has a few layers down to the BluetoothGATT and BluetoothAdapter layers, but the sequence of calls boils down to:
BluetoothGattCharacteristic c = mCharacteristics.get(POWER_STATE_UUID);
c.setValue(SHUTDOWN_VALUE);
mBluetoothGatt.writeCharacteristic(c);
// Signalled by BluetoothGattCallback.onCharacteristicWrite
bleWriteCondition.await();
mBluetoothGatt.disconnect();
// Wait for the underlying layer to confirm we're disconnected
while( mConnectionState != BluetoothProfile.STATE_DISCONNECTED ) {
// Signalled by BluetoothGattCallback.onConnectionStateChange
bleStateCondition.await();
}
mBluetoothGatt.connect();
while (mConnectionState != BluetoothProfile.STATE_CONNECTED) {
// Signalled by BluetoothGattCallback.onConnectionStateChange
bleStateCondition.await();
if (bleStateCondition.stat != 0) {
break;
}
}
Am I going about this entirely the wrong way? I've tried calling close() on the BluetoothGatt instance, then generating a new one with BluetoothDevice.connectGatt, but I get the same extremely slow behavior.
I'm testing on a Samsung Galaxy S4, API level 21.
The problem here is that the gatt connect call issues a background connection request. It can take quite a long time for this call to result in a connection. A description of the two types of connection request is here : Direct vs Background connections
The absolute fastest way to get a connection is to do a scan and upon finding your device issue a direct connection request to it. As the scan has just found it, you know it is there and the connection will complete quickly. This is more complicated than your example code, but will be most effective given your small window. A scan is the most aggressive way to find a device. However, if you already have the device object, you could just call a direct connection request on the device.
Scans are issued using code like this :
scanner = bluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters = new ArrayList<ScanFilter>();
ScanFilter uuidFilter = new ScanFilter.Builder()
.setServiceUuid(YOUR_SERVICE_UUID).build();
filters.add(uuidFilter);
scanner.startScan(filters, settings, myScanCallback);
Upon finding your device (using the scan callback), issue a direct connection request via this method call :
myGatt = myDevice.connectGatt(this, false, myGattCallback);
The key part being the parameter of false. The connection request will time out in around 30s if the device is not found.

Bond a BLE device without createBond()

I am creating an Android app - one feature of this app is that if the user connects to a BLE device and clicks a button, the app will remember the device and automatically connect in future. From what I understand, this means I need bonding.
I came across the createBond() method, but realised that it is only usable in API 19 (Android 4.4) and onwards. The person I am making this for would much prefer it be available in API 18 (Android 4.3) as the product has already been advertised as such.
Assuming that I am correct in my approach (meaning, through active use of Bonding - I'm self-teaching Bluetooth, so please correct me if this isn't the way to go or if I am misunderstanding), is there any way that I can achieve what I want to without createBond()? If so, how? Or is API19 something we will have to live with?
One option is to use a preference in conjunction with your callback for device scanning.
Assuming you're using an LeScanCallback somewhere in your app:
BluetoothAdapter.LeScanCallback mCallBack =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
// You can place this elsewhere so this isn't called for every device
String storedAddr = myContext.getSharedPreferences("global", Context.MODE_PRIVATE).getString("storedAddr","");
if(storedAddr.equals("")) {
// Do your normal process to list / connect to devices.
// Store device.getAddress() in preferences once connected.
} else if(device.getAddress().equals(storedAddr) {
// Do whatever you want with the matching device
}
}
}
Of course, you'll have to deal with an option to remove that stored address so that they can choose a new device to save, if needed.

Pairing to a Bluetooth Low Energy device in Android

Is it possible to automatically connect to Bluetooth Low Energy (BLE) devices?
The Android documentation indicates that the [BluetoothDevice.connectGatt()](https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback)) has a autoConnect parameter:
boolean indicating whether to automatically connect to the BLE device
as soon as it becomes available
However, to call this, you need a BluetoothDevice first. AFAIK the only way to get this is by scanning available devices. Setting up a scan every time to connect to a device doesn't seem like a desirable way. Also, I tried using nRF Control Master Panel to connect to my peripheral using the autoConnect = true, but this does not connect to the device. Connecting without the autoConnect however does make it connect, and I've managed to read and write data from and to my peripheral this way with success.
The general way in Bluetooth to have two devices paired. However, searching for my BLE device and using BluetoothDevice.createBond() does not seem to work. In my ACTION_BOND_STATE_CHANGED-callback, the EXTRA_BOND_STATE and EXTRA_PREVIOUS_BOND_STATE just go from BOND_BONDING to BOND_NONE and back. I don't read out an error or anything - so maybe I'm missing something here. Here's the callback:
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
Log.e(TAG, "prevState " + prevState + ", state " + state);
}
}
};
So this type of bonding does not seem to work.
My question is: am I doing something wrong for pairing or the autoConnect? Or is how I currently have it working the only correct way? It seems like a real pain (and battery-drain) to have to scan for devices every time, see if the device is there, if so read data and check back tomorrow, otherwise check back in an hour or so. The point of Bluetooth is that it should pair directly whenever it is near, isn't it?
It does work without rescan. You do not need pairing at all. Just call BluetoothGatt.connect() again for gatt object you aquired from first connection.
You will receive onConnectionStateChange event in your BluetoothGattCallback as soon as ble device will be available again. If you use autoconnect option, you don't even need to call BluetoothGatt.connect() method. Just monitor your cllback, and don't forget to close BluetoothGatt with close() if you don't see any connection for too long.
And yes, to obtain first connection you should scan for ble devices with BluetoothAdapter.startLeScan, not the common bluetooth devices scan.

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.

Categories

Resources