7

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 (my implementation is based off the 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:

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");

    /*... Arithmetic 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 BroadcastReceiver in MainActivity then calls a method in my Service, verifying an asking for a writeCharacteristic(), as 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.

Community
  • 1
  • 1
clb9355
  • 289
  • 4
  • 16

2 Answers2

0

I am not an expert, but in my BLE app

  • I first connect to peripheral
  • then pair to it (by calling createBond() method),
  • then discover services,
  • then discover characteristics (and store them in app variables) and
  • finally write values to characteristics (by using the app variables)

Have you tried that?

Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
  • 1
    I have not tried to implement any kind of pairing yet - the rest of the process is there though. I'm letting the user select a device and connect with device.connectGatt, reading/storing all the characteristics and setting up notifiers. I'm able to do both of those fine, I can read on command, set descriptors for notifiers and have the characteristics be received in `onCharacteristicChanged()`. I don't believe Pairing would be necessary to write, but since it's something I intend to implement later on, it can't hurt me to try now. I'll let you know if it works. – clb9355 May 26 '15 at 10:09
0

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.

stkent
  • 19,772
  • 14
  • 85
  • 111
clb9355
  • 289
  • 4
  • 16