bluetooth ble writecharacteristics return write_empty ondeviceresult - android

I have just connected to ble device successfully, and enabled the notifications for read and write. but after i set value true and call writecharacteristics method i can write to device and get write empty response from bledevice..
What should be the flow of a write request to ble device?
public void onServicesDiscovered(BluetoothGatt bluetoothGatt, int i) {
BLEService.this.f8468j = BLEService.this.f8466h.getCharacteristic(BLEService.this.f8462d);//readchar.
BLEService.this.f8467i = BLEService.this.f8466h.getCharacteristic(BLEService.this.f8460b);//writechar.
setcharacteristicnotification(true, bluetoothgat, BLEService.this.f8467i);
// i need to writechar right now in here. but ondeviceresult returns "write_empty"... what is wrong?
bArr = new byte[]{97,48,24,69,96,27,79,91,55,7,30,63,87,84,7,87}
this.BluetoothGattCharacteristicwrite.setValue(bArr);
this.BluetoothGatt.writeCharacteristic(this.BluetoothGattCharacteristicwrite);
super.onServicesDiscovered(bluetoothGatt, i);
}
};
-
private void setcharacteristicnotification(boolean z, BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic bluetoothGattCharacteristic) {
if (bluetoothGatt != null && bluetoothGattCharacteristic != null) {
bluetoothGatt.setCharacteristicNotification(bluetoothGattCharacteristic, z);
BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptor(BLEService.this.f8463e);
if (descriptor != null) {
descriptor.setValue(z ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
BLEService.this.f8465g.writeDescriptor(descriptor);
}
//("BLEService", "=== enableNotification isWriting = true ===");
BLEService.this.m11448a(7);
}
}

You can't have multiple outstanding GATT requests at a time. Now you call writeDescriptor followed by writeCharacteristic without waiting for onDescriptorWrite.

Related

Android BluetoothGattServer hangs - client cannot read/write characteristics

I'm developing a project that consists of a BLE GATT server ran on Android phone (using BluetoothGattServer Java class) and a BLE client on an IoT board. The concept of using the phone as the server is to somehow protect the IoT board from attacking clients. When my Android application wants to talk to the external device it starts advertising a special set of data and if the external device recognizes the advertisment it connects to the Android BLE GATT server. Then the external device reads the presented services and chareacteristics and registers for notifications on some of the chars.
By far it all happens well.
After that the external device tries to write authentication data to one of the chars. If everything was clean and perfect, the process goes well. But if for a reason the last connection broke in the middle of some operation and was not properly closed, the external device cannot read/write the characteristic. If I restart the phone or clear Bluetooth cache (Settings > Apps > Bluetooth > Clear Data) all the operations proceed fine, but I cannot force users to do this in normal operation and I haven't found how and if I could clear this cache programmatically from inside the app.
I read over the web for GATT client cache undocumented "Refresh" method, but in GATT server there is no such.
I read about and tried the "Service changed" characteristic (0x2A05) but it doesn't help me much.
Having doubts about which of the devices is causing the problem I have tried with another phone as a client. I ran "BLE Scanner" App on it and tried to connect to the server phone - the problem persists. I can connect, all the characteristics are discovered, but when I try to read/write some char the connection brakes after a 30 sec timeout - exactly the same behavior like in the original situation. The conclusion is I have a problem with Android BluetoothGattServer.
During the development the problem occurs mostly when connection is broken by some error in communication. In real life usage after I have all errors fixed that will not happen, but having in mind tha it is wireless radio connection, it can be disconnected by literally everything and I shall have a reliable mechanism to reconnect.
I open the server with this code:
private void startServer() {
BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter();
mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
if (mBluetoothLeAdvertiser == null) {
Log.w("BLE", "Failed to create advertiser");
return;
}
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setConnectable(true)
.setTimeout(0)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.build();
byte bData[] = new byte[24];
bData = ...... // some proprietary advertising data
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName(false)
.setIncludeTxPowerLevel(false)
.addManufacturerData(iManufID, bData)
.build();
mBluetoothLeAdvertiser
.startAdvertising(settings, data, mAdvertiseCallback);
mBluetoothGattServer = mBluetoothManager.openGattServer(ctxActivity, mGattServerCallback);
mBluetoothGattServer.clearServices();
mBluetoothGattServer.addService(BLEProfile.createBLEService());
/* Static method, which builds Service->Chars->Descriptors.
I assign the Client Config descriptor (0x2902) to each characteristic. */
}
For stopping the sertver I use this code:
private void stopServer() {
if (mBluetoothGattServer == null) return;
mBluetoothGattServer.clearServices();
mBluetoothGattServer.close();
if (mBluetoothLeAdvertiser == null) return;
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
}
I stop and start the server each time a connection was broken.
Does anyone have an idea what am I doing wrong?
Also it is important to mention that in real life IoT devices will be many in a room, phones may be many in a room. One phone should be able to be connected by any of the IoT devices it requests sequentially and one IoT device should be able to connect to more than one phone sequentially. The advertising data of the phone's GATT Server defines which of the IoT devices is requested to connect and it will change each time the phone requests connection with a different device.
Update:
Here is the code for the Server Callback:
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onMtuChanged(BluetoothDevice device, int mtu) {
super.onMtuChanged(device, mtu);
Log.i("BLE", "MTU changed: "+mtu);
}
#Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
if(BLEProfile.XXXXXX.equals(characteristic.getUuid()))
{
// ... some data checks ...
mBluetoothGattServer.sendResponse(device,
requestId,
iValid,
0,
bEncrypted);
characteristic.setValue(bEncrypted);
mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, true);
} else if (...) /* similar operations for all other characteristics */
{
...
}
else
{
Log.i("BLE", "Write not mine characteristic");
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}
}
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("BLE", "BluetoothDevice ... CONNECTED: " + device);
if(device != null)
{
BluetoothGattService mServ = mBluetoothGattServer.getService(BLEProfile.GATT_SERVICE);
if(mServ != null)
{
BluetoothGattCharacteristic mChar = mServ.getCharacteristic(BLEProfile.SERVICE_CHANGED);
if(mChar != null)
mBluetoothGattServer.notifyCharacteristicChanged(device, mChar, false);
}
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mRegisteredDevices.remove(device);
stopAdvertising();
startAdvertising();
}
}
#Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
BluetoothGattCharacteristic characteristic) {
/* Not used currently. Just some testing code below. */
if(BLEProfile.XXXXXX.equals(characteristic.getUuid()))
{
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_SUCCESS,
0,
new byte[] {0x01, 0x02, 0x03, 0x04, 0x05});
} else if(...) /* similar operations for all other characteristics */
{
...
}
else
{
Log.i("BLE", "Read not mine characteristic");
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}
}
#Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset,
BluetoothGattDescriptor descriptor) {
if (BLEProfile.CLIENT_CONFIG.equals(descriptor.getUuid())) {
Log.d("BLE", "Config descriptor read");
byte[] returnValue;
if (mRegisteredDevices.contains(device)) {
returnValue = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else {
returnValue = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
}
/* Not sure why I am responding with GATT_FAILURE here insted of GAT_SUCCESS !?!? May be some copy/paste mistake. */
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
returnValue);
} else {
Log.w("BLE", "Unknown descriptor read request");
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}
}
#Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
BluetoothGattDescriptor descriptor,
boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
if (BLEProfile.CLIENT_CONFIG.equals(descriptor.getUuid())) {
if (Arrays.equals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE, value)) {
Log.i("BLE", "Subscribe device to notifications: " + device);
mRegisteredDevices.add(device);
} else if (Arrays.equals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE, value)) {
Log.i("BLE", "Unsubscribe device from notifications: " + device);
mRegisteredDevices.remove(device);
}
if (responseNeeded) {
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_SUCCESS,
0,
null);
}
} else {
Log.w("BLE", "Unknown descriptor write request");
if (responseNeeded) {
mBluetoothGattServer.sendResponse(device,
requestId,
BluetoothGatt.GATT_FAILURE,
0,
null);
}
}
}
};

Android BLE characteristics getValue is no any response?

I am trying to configure READ characteristic with notification attribute. At the moment I can see this characteristic on android app with properties READ and NOTIFY.
here is my device's uuid info :
UUID info
YES, I need to notification attribute first , then second I send command , that will be receive value, but ,
in fact , I don't receive any value , I don't even know what's wrong, here is my code :
public void onServicesDiscovered(BluetoothGatt gatt, int status){
if (status == gatt.GATT_SUCCESS){
runOnUiThread(new Runnable(){
public void run() {
BluetoothGattService gattService = gatt.getService(SERVICE_UUID);
if(gattService != null){
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(NOTIF_UUID);
if (gattCharacteristic != null ){
for (BluetoothGattDescriptor descriptor : gattCharacteristic.getDescriptors()){
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
boolean sucess = gatt.writeDescriptor(descriptor);
}
gatt.setCharacteristicNotification(gattCharacteristic, true);
}
characteristic = gattService.getCharacteristic(WRITE_DATA);
if (characteristic != null){
sendCommand(characteristic, gatt); //send command
}
now , I have already send command , and it's return success , because onCharacteristicWrite() has response,
but onCharacteristicRead() and onCharacteristicChanged() is not any response? Why ?
which part I missing?
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
instead of
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

Android NordicSemiconductor/Android-BLE-Library onCharacteristicChanged is not called

I'm using this library
NordicSemiconductor/Android-BLE-Library for BLE communication and successfully able to connect the peripheral device. I'm getting the services and characteristics and able to write the characteristics.
But for somehow after writing the characteristics the onCharacteristicChanged is not called. Though I enable the notifications first and than write.
This is how I'm enabling it with new version of library.
setNotificationCallback(bluetoothGattCharacteristic).with(object : ProfileDataCallback {
override fun onDataReceived(device: BluetoothDevice, data: Data) {
BluetoothNotificationBus.getBus().post(BluetoothNotificationEvents.PostPeripheralValueForCharacteristicChangedNotification(device,bluetoothGattCharacteristic.service,bluetoothGattCharacteristic))
}
override fun onInvalidDataReceived(device: BluetoothDevice, data: Data) {
BluetoothNotificationBus.getBus().post(BluetoothNotificationEvents.PostPeripheralValueForCharacteristicChangedNotification(device,bluetoothGattCharacteristic.service,bluetoothGattCharacteristic))
}
})
enableNotifications(bluetoothGattCharacteristic).enqueue()
Also in logcat:
2019-05-20 14:03:24.110 6148-6148/com.nextmunich.trumaandroidpoc D/BluetoothGatt: setCharacteristicNotification() - uuid: f47b0100-f3b2-11e8-8eb2-f2801f1b9fd1 enable: true
Also from BleManager.java, below method returns true
#MainThread
private boolean internalEnableNotifications(final BluetoothGattCharacteristic characteristic) {
final BluetoothGatt gatt = mBluetoothGatt;
if (gatt == null || characteristic == null || !mConnected)
return false;
final BluetoothGattDescriptor descriptor = getCccd(characteristic, BluetoothGattCharacteristic.PROPERTY_NOTIFY);
if (descriptor != null) {
log(Log.DEBUG, "gatt.setCharacteristicNotification(" + characteristic.getUuid() + ", true)");
gatt.setCharacteristicNotification(characteristic, true);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
log(Log.VERBOSE, "Enabling notifications for " + characteristic.getUuid());
log(Log.DEBUG, "gatt.writeDescriptor(" + CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID + ", value=0x01-00)");
return internalWriteDescriptorWorkaround(descriptor);
}
return false;
}
Please guide to overcome this issue. As I know there are different threads on stackoverflow but none of them worked for me.
I've also test my code with nRF Connect App and it seems notification works fine.

BLE - i am not able to read 2 characteristic one of temperature service and another of Battery service at same time

This is my code where i am discovering services and then get characteristic and setting descriptor for both temperature and battery characteristic.
In starting i am discovering services for Temperature and Battery.
then,I discover characterstic for each Temperature and Battery service
and write descriptor for both.
When i run the code,the call is going to onCharactersticChanged and i got the temperature result.
But,call is not going on OnCharactersticRead for battery
for (BluetoothGattService service : services) {
Log.e("asd service discoverd", service.getUuid().toString());
// check for service should be temperature service or Battery service
if (service.getUuid().equals(BT_THERMO_SERVICE) || service.getUuid().equals(BT_BATTERY_SERVICE)) {
Log.e("asd service discoverd", service.getUuid().toString());
List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
//
// Create a compartor
// sort list with cpmparor
// addd in queue
// read same
for (BluetoothGattCharacteristic characteristic : characteristics) {
Log.e("asd charac discoverd:", characteristic.getUuid().toString());
if (characteristic.getUuid().equals(BT_REAL_TIME_TEMPERATURE_CHARTERISTICS) || characteristic.getUuid().equals(BT_BATERY_LEVEL_CHARACTERISTICS)) {
Log.e("asd charac discoverd:", characteristic.getUuid().toString());
arrayList.add(characteristic);
// check if characterstic is RealTime Temperature Measurement characterstic or Battery Level characterstic
if (characteristic.getUuid().equals(BT_REAL_TIME_TEMPERATURE_CHARTERISTICS)) {
//indicate ble to send temperature data each time when new data value found
//notify ble device to send data
gatt.setCharacteristicNotification(characteristic, true);
for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
} else
if (characteristic.getUuid().equals(BT_BATERY_LEVEL_CHARACTERISTICS)) {
//notify ble device to send data
gatt.readCharacteristic(characteristic);
for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}
}
}
These are my gattcallback methods.
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.e("charvalu", "" + characteristic);
if (characteristic.getUuid().equals(BT_BATERY_LEVEL_CHARACTERISTICS)) {
byte b[] = characteristic.getValue();
if (b.length != 0) {
Log.e("battery", Integer.toString(b.length));
// ByteBuffer batterybuffer = ByteBuffer.wrap(b);
// Long batteryStatus = batterybuffer.getLong();
// Log.e("battery","" + batteryStatus);
}
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.e("inside char", "" + characteristic);
if (characteristic != null) {
Log.e("on change char", characteristic.getUuid().toString());
if (characteristic.getUuid().equals(BT_REAL_TIME_TEMPERATURE_CHARTERISTICS)) {
Log.e("on change inside", characteristic.getUuid().toString());
//temperature data comes in byte array of size 12
byte b[] = characteristic.getValue();
if (b.length != 0) {
//check header and tail of data packet
if (b[0] == 0x7C && b[11] == 0x7D) {
//Temp reading is stored in 7 and 8 byte
ByteBuffer tempBuffer = ByteBuffer.wrap(b, 7, 2);
tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
Short temp = tempBuffer.getShort();
final Float fTemp = (float) (temp / 100.0);
Log.e("sunittemp", Float.toString(fTemp));
runOnUiThread(new Thread() {
#Override
public void run() {
super.run();
workingListener.unstableReading(new IvyThermoReading(fTemp));
}
});
}
}
}
}
}
Unfortunately sending all read and write requests at once synchronously does not work since android only allows one pending GATT operation at a time. You must somehow enqueue the work and continue sending another request once the callback of the previous request arrives.

Android BLE: onCharacteristicChanged never fires

I'm trying to write an Android app that mimics functionality already present in an iOS app I wrote. I am interfacing with 2 different BLE devices:
Blood Pressure Cuff
Weight Scale
On iOS, I have both devices working well and reporting data. On Android, I can't get it to work. After hours of research and testing, I think the basic issue I'm trying to solve is this:
On iOS, I call the following code to enable the BLE device to notify my iOS device when it has data to report:
#pragma mark - CBPeripheralDelegate Protocol methods
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
for (CBCharacteristic *characteristic in [service characteristics]) {
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
That's it. The notes for this method in iOS say the following:
If the specified characteristic is configured to allow both notifications and indications, calling this method enables notifications only.
Based on that (and the fact that it works in iOS), I'm figuring that the configuration descriptor for the characteristic for which I want notifications should be configured like this:
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
With that in mind, my BLEDevice class looks like this:
public abstract class BLEDevice {
protected BluetoothAdapter.LeScanCallback mLeScanCallback;
protected BluetoothGattCallback mBluetoothGattCallback;
protected byte[] mBytes;
protected Context mContext;
protected GotReadingCallback mGotReadingCallback;
protected String mDeviceName;
public final static UUID UUID_WEIGHT_SCALE_SERVICE
= UUID.fromString(GattAttributes.WEIGHT_SCALE_SERVICE);
public final static UUID UUID_WEIGHT_SCALE_READING_CHARACTERISTIC
= UUID.fromString(GattAttributes.WEIGHT_SCALE_READING_CHARACTERISTIC);
public final static UUID UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC
= UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
public final static UUID UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR
= UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
abstract void processReading();
interface GotReadingCallback {
void gotReading(Object reading);
}
public BLEDevice(Context context, String deviceName, GotReadingCallback gotReadingCallback) {
mContext = context;
BluetoothManager btManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
final BluetoothAdapter btAdapter = btManager.getAdapter();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
mContext.startActivity(enableIntent);
}
mDeviceName = deviceName;
mBluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue();
mBytes = data;
Log.d("BluetoothGattCallback.onCharacteristicChanged", "data: " + data.toString());
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
// this will get called when a device connects or disconnects
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
if (mBytes != null) {
processReading();
}
}
}
#Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.d("onDescriptorWrite", "descriptor: " + descriptor.getUuid() + ". characteristic: " + descriptor.getCharacteristic().getUuid() + ". status: " + status);
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
// this will get called after the client initiates a BluetoothGatt.discoverServices() call
BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
if (service != null) {
BluetoothGattCharacteristic characteristic;
characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
if (characteristic != null) {
gatt.setCharacteristicNotification(characteristic, true);
}
characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
if (characteristic != null) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}
}
};
mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
Log.d("LeScanCallback", device.toString());
if (device.getName().contains("{Device Name}")) {
BluetoothGatt bluetoothGatt = device.connectGatt(mContext, false, mBluetoothGattCallback);
btAdapter.stopLeScan(mLeScanCallback);
}
}
};
btAdapter.startLeScan(mLeScanCallback);
}
}
NOTE: It might be important to know that these 2 devices function in the following way:
The BLE device is turned on an a measurement is initiated on the device.
Once the measurement has been taken, the BLE device attempts to initiate a BLE connection.
Once the BLE connection is made, the device pretty much immediately sends the data, sometimes sending a couple of data packets. (If previous data measurements haven't been successfully sent over BLE, it keeps them in memory and sends all of them, so I only really care about the final data packet.)
Once the final data packet is sent, the BLE device disconnects rapidly.
If the BLE device fails to send data (as is currently happening on the Android app), the BLE device disconnects pretty rapidly.
In my LogCat, I see a lot of output that's exactly like I'd expect.
I see a list of services like I expect, including the data service I want.
I see a list of characteristics like I expect, including the data characteristic I want.
I see a list of descriptors like I expect, including the "configuration" (0x2902) descriptor.
The most recent failure I'm experiencing is a status of "128" being reported in onCharacteristicWrite. The comments to question #3 (below) seem to indicate this is a resource issue.
I've looked at the following questions:
Android BLE onCharacteristicChanged not called
Android BLE, read and write characteristics
Android 4.3 onDescriptorWrite returns status 128
Here's why they don't give me what I need:
This question's answer was not to read the descriptor's value. I'm not doing that, so that can't be what's getting in the way.
This is basically an overview of the various methods that are available, which I think I now understand. The big key in this question/answer is not to write multiple times to different descriptors, but I'm also not doing that. I only care about the one characteristic.
This question/answer seems to be related to BLE resource limitations, but I don't think this applies. I'm only connecting this one device and I'm trying to do a very, very simple data transfer. I don't think I'm hitting resource ceilings.
I've tried a bunch of examples and tutorials, including Google's Android sample code. None of them seem to enable the BLE device to notify my Android device of data updates. It's obviously not the device, since the iOS version works. So, what is the iOS code doing in the background to get the notifications to work and what code on the Android side will mimic that functionality?
EDIT/UPDATE
Based on #yonran's comments, I updated my code by changing the onServicesDiscovered implementation to this:
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
// this will get called after the client initiates a BluetoothGatt.discoverServices() call
BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
if (service != null) {
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
if (characteristic != null) {
if (gatt.setCharacteristicNotification(characteristic, true) == true) {
Log.d("gatt.setCharacteristicNotification", "SUCCESS!");
} else {
Log.d("gatt.setCharacteristicNotification", "FAILURE!");
}
BluetoothGattDescriptor descriptor = characteristic.getDescriptors().get(0);
if (0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) {
// It's an indicate characteristic
Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is INDICATE");
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
} else {
// It's a notify characteristic
Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is NOTIFY");
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
}
}
}
That does seem to have changed some things a little bit. Here's the current Logcat, following that code change:
D/BluetoothGatt﹕ setCharacteristicNotification() - uuid: <UUID> enable: true
D/gatt.setCharacteristicNotification﹕ SUCCESS!
D/onServicesDiscovered﹕ Characteristic (<UUID>) is INDICATE
D/BluetoothGatt﹕ writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
D/BluetoothGatt﹕ onDescriptorWrite() - Device=D0:5F:B8:01:6C:9E UUID=<UUID>
D/onDescriptorWrite﹕ descriptor: 00002902-0000-1000-8000-00805f9b34fb. characteristic: <UUID>. status: 0
D/BluetoothGatt﹕ onClientConnectionState() - status=0 clientIf=6 device=D0:5F:B8:01:6C:9E
So, it would appear that I'm now setting everything up properly (since setCharacteristicNotification returns true and the onDescriptorWrite status is 0). However, onCharacteristicChanged still never fires.
I've been able to successfully catch onCharacteristicChanged() with multiple services and characteristics by:
Writing descriptor values in the broadcastReceiver() in the main loop after service discovery is finished.
private final BroadcastReceiver UARTStatusChangeReceiver = new BroadcastReceiver() {
//more code...
if (action.equals(uartservice.ACTION_GATT_SERVICES_DISCOVERED)) {
mService.enableTXNotification();
}
and
By adding a delay between descriptor value settings
public void enableTXNotification(){
/*
if (mBluetoothGatt == null) {
showMessage("mBluetoothGatt null" + mBluetoothGatt);
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
*/
/**
* Enable Notifications for the IO service and characteristic
*
*/
BluetoothGattService IOService = mBluetoothGatt.getService(IO_SERVICE_UUID);
if (IOService == null) {
showMessage("IO service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_IO);
return;
}
BluetoothGattCharacteristic IOChar = IOService.getCharacteristic(IO_CHAR_UUID);
if (IOChar == null) {
showMessage("IO charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_IO);
return;
}
mBluetoothGatt.setCharacteristicNotification(IOChar,true);
BluetoothGattDescriptor descriptorIO = IOChar.getDescriptor(CCCD);
descriptorIO.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptorIO);
/**
* For some reason android (or the device) can't handle
* writing one descriptor after another properly. Without
* the delay only the first characteristic can be caught in
* onCharacteristicChanged() method.
*/
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* Enable Indications for the RXTX service and characteristic
*/
BluetoothGattService RxService = mBluetoothGatt.getService(RXTX_SERVICE_UUID);
if (RxService == null) {
showMessage("Rx service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
BluetoothGattCharacteristic RxChar = RxService.getCharacteristic(RXTX_CHAR_UUID);
if (RxChar == null) {
showMessage("Tx charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
mBluetoothGatt.setCharacteristicNotification(RxChar,true);
BluetoothGattDescriptor descriptor = RxChar.getDescriptor(CCCD);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE );
mBluetoothGatt.writeDescriptor(descriptor);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* Enable Notifications for the Battery service and Characteristic?
*/
BluetoothGattService batteryService = mBluetoothGatt.getService(BATTERY_SERVICE_UUID);
if (batteryService == null) {
showMessage("Battery service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_BATTERY);
return;
}
BluetoothGattCharacteristic batteryChar = batteryService.getCharacteristic(BATTERY_CHAR_UUID);
if (batteryChar == null) {
showMessage("Battery charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_BATTERY);
return;
}
}
I was facing the same problem.
that's because when the device is sending the indicate value, your application is charged in another process and that's why you never get the indicate value which make the onCharacteristicChanged never fires.
to resolve your problem try to put all traitement in a service. and just call functions from your activity.

Categories

Resources