1

i'm losing my mind with this issue.

The fact is, one android device which is advertising a string value: "78d89537-4309-4728-87f6-3ab2bbe231d8" (36 bytes). I'm using a characteristic defined as

 anonIdCharacteristic = new BluetoothGattCharacteristic(TippeeBluetoothManager.UUID_READ_CHARACTERISTIC,
            BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_BROADCAST,
            BluetoothGattCharacteristic.PERMISSION_READ  );
    anonIdCharacteristic.setValue(idToAdvertise);

as you can see i'm advertising in "READ" mode, not notify.

When another android device connect and try to read the characteristic, the onCharacteristicRead method is called but the value passed is wrong. More specifically is:

"78d89537-4309-4728-87f678d89537-4309-4728-87f678d89537-4309-4728-87f6..." (600 bytes)

which is part of the value expected, but repeated.

If i put myself on debug "server side" is see that the number of bytes sent are correct. On debug "client side" the byte are 600

What am i doing wrong ?

Thanks in advance

---- EDIT ---

i found some more information.

onCharacteristicReadRequest is called repeatedly with crescent offset that is causing the "dirty" buffer now i'm responding this way:

if (BluetoothManager.UUID_READ_CHARACTERISTIC.equals(characteristic.getUuid())) { mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, getStoredValue()); return; }

using offset values. Not working yet but it's something.

I wonder what is telling the app how long is response..

2 Answers2

4

Ok, i got it so i'll leave my response to help someone else will be in my situation.

The correct solution is

@Override
        public void onCharacteristicReadRequest(BluetoothDevice device,
                                                int requestId,
                                                int offset,
                                                BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
            Log.i(TAG, "onCharacteristicReadRequest " + characteristic.getUuid().toString());

            byte[] fullValue = getStoredValue();

            //check
            if (offset > fullValue.length) {
                mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, new byte[]{0} );
                return;

            }


            int size = fullValue.length - offset;
            byte[] response = new byte[size];

            for (int i = offset; i < fullValue.length; i++) {
                response[i - offset] = fullValue[i];
            }



            if (MYBluetoothManager.UUID_READ_CHARACTERISTIC.equals(characteristic.getUuid())) {
                mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, response);
                return;
            }



            mGattServer.sendResponse(device,
                    requestId,
                    BluetoothGatt.GATT_FAILURE,
                    0,
                    null);
        }

    };

The callback is called repeatedly with an offset, and this was clear. What was not clear it's i am supposed to respond with an array that contains all the data starting from that offset.

So i start with preparing an array with all the data. If the offset requested exceed the length of the data, i just return an array of 0 byte.

If it is not so, i prepare a portion of the original array starting from the offset requested by the callback till i have some information. So is not important if the array contains to much informations, on the second, third callback i know where to start to return the data.

sorry if it's not clear, but enable logging and you will understand what i mean.

good luck to everyone

1

hope you are still in for this one, because I havn't found any answers on the web on this and had to 'dig the dirt' myself.

So i`ll try to break it down:

Assuming we have made connection between two android BLE devices(central and peripheral),

  1. When BLE central device, issue characterRead(...) request for a given characteristic, the onReadCharacteristic(...) callback is called in the peripheral device with the relevant characteristic as parameter. The right way is to issue sendResponse(...) method with the value as the wanted value for the central to read(what you as peripheral want to answer actually). So as I see you quite right on that approach and all is well.

  2. The MTU size for the give link, is the default - 20 bytes.(Actually 23, but for the GATT protocol we need 3 bytes) Therefor, any buffer larger than 20 bytes will be split to chunks(fragmented) by android. You have couple of options here:

    2.1. Learn to work with the given android mechanism and send your chunks accordingly to the offset.

    2.2. Change the MTU size of the link using requestMtu(...) API from the central so it can support bigger messages.

    2.3. Know the MTU of the link, and build a fragmentation mechanism yourself(will leave the offset at 0 constantly with each call).

So to conclude, android has a fragmentation mechanism you were not aware of.

Sielar
  • 292
  • 2
  • 10