I'm putting together an application that is reading a custom BLE characteristic. I don't think the problem has anything to do with the Bluetooth, but for some reason, the formatting of the characteristic I'm reading doesn't seem to be coming out right. I'm sending a characteristic that has 2 float values - 1 and 2. With 4 bytes per float value, the actual characteristic values are is 0x3f800000 and 0x40000000 according to this website. Alright, so in my "onCharacteristicChanged" method, I have:
public void onCharacteristicChanged (BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic){
//Make sure it's the float characteristic through techniques that aren't relevant
Log.i("Float", characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, 0).toString());
Log.i("Float", characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, 4).toString());
So it will read the first 32 bit float and then the second 32 bit float. However, the values I get printed out are
I/Float: -Infinity
I/Float: 0.0
Ok, that's weird. Let's take a look at the raw bytes coming in and try to figure it out:
byte[] bytes = characteristic.getValue();
String value = toBinary(Arrays.copyOfRange(bytes, 0, 4));
String value2 = toBinary(Arrays.copyOfRange(bytes, 4, 8));
Log.i("Raw Val", value);
Log.i("Raw Val", value2);
Where "toBinary" is this method that takes the bytes and prints them out as a binary string:
String toBinary( byte[] bytes )
{
StringBuilder sb = new StringBuilder(bytes.length * Byte.SIZE);
for( int i = 0; i < Byte.SIZE * bytes.length; i++ )
sb.append((bytes[i / Byte.SIZE] << i % Byte.SIZE & 0x80) == 0 ? '0' : '1');
return sb.toString();
}
I see this output:
I/Raw Val: 00000000000000001000000000111111
I/Raw Val: 00000000000000000000000001000000
Ah, ok, for some reason, it looks like I'm getting 0x0000083f and 0x00000040, so for some reason the endianness of my bytes is opposite what it should be. I think it might be more of a pain to swap it on the microcontroller, so maybe I can do it through the app?
Log.i("Float Reversed", floatSwapEndianness32(characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, 0)).toString());
Log.i("Float Reversed", floatSwapEndianness32(characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, 4)).toString());
Where the method to swap the endianness is below:
private static Float floatSwapEndianness32(float test_float) {
// Keep only the significant bits of the UUID
int bits = Float.floatToIntBits(test_float);
Log.i("Original", Integer.toHexString(bits));
byte[] bytes = new byte[4];
bytes[0] = (byte)(bits & 0xff);
bytes[1] = (byte)((bits >> 8) & 0xff);
bytes[2] = (byte)((bits >> 16) & 0xff);
bytes[3] = (byte)((bits >> 24) & 0xff);
byte[] final_bytes = new byte[] { bytes[0], bytes[1], bytes[2], bytes[3]};
Log.i("New", Integer.toHexString(ByteBuffer.wrap(final_bytes).getInt()));
float myfloatvalue = ByteBuffer.wrap(final_bytes).getFloat();
return (myfloatvalue);
}
Now this is my output:
I/Original: ff800000
I/New: 80ff
I/Float Reversed: 4.6275E-41
I/Original: 0
I/New: 0
I/Float Reversed: 0.0
Ok now I'm really confused, that seemed to have wrong values coming from somewhere. As a last ditch effort, I'll read the two values as integers and print them as hex values:
Log.i("Float Int", Integer.toHexString(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0)));
Log.i("Float Int", Integer.toHexString(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 4)));
and it outputs:
I/Float Int: 3f800000
I/Float Int: 40000000
Wait, now when I receive the value as an integer it's in the correct endianness? At this point I started tearing my hair out and think I'm losing my grip on reality. What exactly is wrong here? Is BluetoothGattCharacteristic.FORMAT_FLOAT not swapping where it should be?
User contributions licensed under CC BY-SA 3.0