clb9355 clb9355 - 2 months ago 47
Android Question

writeCharacteristic() returns true, but does not call onCharacteristicWrite()

I wish to read a characteristic value stored in a device, modify the value, and then write it to the device. For some reason,

writeCharacteristic()
return true, but the internal device value does not change and
onCharacteristicWrite()
is not called. In fact, for some reason it is only called if I attempt to write something, and then is called shortly after I close or reopen the app - and doesn't change the device value. I've been looking into this for a few days and it's driving me crazy. It may be worth noting that reading characteristics both manually and via a notification both work fine.

Why is the writeCharacteristic() not going through, and why is onCharacteristicWrite() being called at such an odd time?

I am not sure where the issue stems from. It could be something dumb and simple like calling writeCharacteristic() incorrectly (It is based off this question - Working with BLE Android 4.3 how to write characteristics?). Would it be possible that the request is being ignored because onCharacteristicRead() is somehow unfinished?

I believe these links appear most helpful for indicating the issue but I haven't been able to pull anything from them myself.
- Android BLE API: GATT Notification not received
- https://code.google.com/p/android-developer-preview/issues/detail?id=1714

As a walkthrough of my code, an
onItemClick()
event is hit, which initiates the sequence.

int flag = 1;
((MainActivity)getActivity()).BtService.readFlag(flag);


It calls a function (in a Service) which verifies the Bluetooth connection and reads the characteristic.

public boolean readFlag(int flag){
/*... removed code here verifies that the Bluetooth Gatt is available,
that the Service exists...*/

BluetoothGattCharacteristic characteristic = Service
.getCharacteristic(SEND_FLAGS_CHAR);
if (characteristic == null) {
Log.e(TAG, "char not found!");
return false;
}

try {
// Store intended flag value in SharedPreferences, then read the current one.
Editor fEditor = sPrefs.edit();
fEditor.putInt(calibrationSetFlagToSendKey, flag);
fEditor.commit();

mConnectedGatt.readCharacteristic(characteristic);

//Catch response in onCharacteristicRead() callback.
return true;
}
catch (NullPointerException e) {
Log.w("readCharacteristic", "The characteristic could not be read.");
return false;
}
}


onCharacteristicRead is called, which only calls a Handler to modify and broadcast the result back to MainActivity. "trueFlagValue" is a byte that is stored globally within the Service (bad practise I know, but I intend to modify this later.)

case MSG_SEND_FLAG:
characteristic = (BluetoothGattCharacteristic) msg.obj;
if (characteristic.getValue() == null) {
Log.w(TAG, "Error obtaining current flags");
return;
}

int recentReadDeviceFlag = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
int initialFlag = sPrefs.getInt(calibrationSetFlagToSendKey, 0);
if (initialFlag == 0) Log.e("Write Debug", "Flag to send is apparently 0");

/*... Aritmetic modifying flag integer value...*/
//Desired value is the OR'd value of the value read and value desired
trueFlagValue = (byte) ((byte) initialFlag | (byte) recentReadDeviceFlag);
Log.d("Read Debug", "OR'd value is " + trueFlagValue +", sending broadcast");
Intent fIntent = new Intent("com.example.appwithble.SEND_FLAGS");
sendBroadcast(fIntent);
break;


The Broadcast Receiver in MainActivity then calls a method in my Service, verifying an asking for a writeCharacteristic(), like it was done for readCharacteristic. Accesses the trueFlagValue set earlier.

else if("com.example.appwithble.SEND_FLAGS".equals(intent.getAction())) {
BtService.performWriteFlag();
}

public boolean performWriteFlag () {
/*... check Gatt is available and that Service exists...*/
BluetoothGattCharacteristic characteristic = Service
.getCharacteristic(SEND_FLAGS_CHAR);
if (characteristic == null) {
Log.e(TAG, "char not found!");
return false;
}

byte[] byteValue = new byte[1];
byteValue[0] = trueFlagValue;
characteristic.setValue(byteValue);

boolean status = mConnectedGatt.writeCharacteristic(characteristic);
return status;
}


My
onCharacteristicWrite()
callback should then be called, and currently contains only a line of code to log a message. In reality, this is only ever called sometimes, as the app is closed or reopened. The value stored in my peripheral's characteristic never changes.

Answer

Such a rookie mistake.

As I've not experimented with writing data before I assumed something was wrong with the writing process or the device. After looking at the kinds of data coming from by readCharacteristic() and by asking around about the device I am sending to, I started to realise that perhaps the data I am sending is in the wrong format.

It turns out that even though only four different flags need to be sent through (could be done with two bits), the characteristic needs to be formatted at two bytes for my particular device, not one. So if I had made my byte array something like this, it would've worked.

byte[] byteValue = new byte[2];
byteValue[0] = trueFlagValue;
byteValue[1] = (byte)0;
characteristic.setValue(byteValue);

I expect if anyone else had an odd problem where the write is initiated but not completed, then it's likely an issue like this - at least, something in the device is rejecting the write.

Comments