I am currently working on a python-based Bluetooth Low Energy client for OS X and I got stuck while trying to write a remote float characteristic value.
First of all: my setup is an Arduino 101 with integrated BLE module and a Macbook. On the Arduino side the code is working well, I can read and write BLE characteristic's from a BLE explorer app on my phone.
Also I did achieve to read out the characteristics float values in my python script on the Macbook. Yet I did not fully understand what exactly I am doing. After requesting the characteristic's value from the peripheral, the peripheral answers and I get what I think is binary data? I could manage to unpack the data to a float by using.
def characteristic_to_float(data):
float_value = struct.unpack('f', data)[0]
return float_value
The received floats are correct. But I couldn't get my python code to properly write a float value to the peripheral. I tried this:
a_number = 5.3
def float_to_characteristic(float_value):
val = struct.pack('<f', float_value)[0]
return val
characteristic_1.write_value(float_to_characteristic(a_number))
The python code itself will run through without an error, but the Arduino doesn't receive the data. I am sure of that, because I implemented a function on it that sends out a Serial print when a characteristic has been written.
Remember the characteristics are read & write and I could verify this by using my phone. I think I got something wrong with the struct.pack thing, since I can't find any information about what data format is needed...
Does someone know how I can pack or convert the float to the right data format? Unfortunately the lib I use (Adafruit BluefruitLE) does not have a documentation.
Thanks!
-
Try packing and then unpacking the value entirely in python as a test. Try sending the packed value from the arduino back to it directly as a test. Figure out how to dump the bytes of the packed value as hex from python for verification, etc...Chris Stratton– Chris Stratton2016年10月24日 19:35:05 +00:00Commented Oct 24, 2016 at 19:35
3 Answers 3
Two solutions came to my mind if am gonna do your thing, 1) convert the number to a string char by char and send it as it is then unpack it as char by char , for example 5.3 --> "5.3" it will be received then you have to decompose it 5 and . and 3 (inefficient solution because of conversion of types will be alot)
2) calculate the number of decimal digits and send it with the number something like: 5.3 ---> 53 and one decimal point, you would send it as 53:1 and then unpack it and reconstruct it
the second solution would be better for me since i have to send it as 2 things number: decimal point and then reconstruct it, this library will ease the reconstruction of decimal point sent by 53:1 (if you want to send floats in two directions) SimpleSerialExtractor
I could solve the issue by taking another look in the documentation of the struct lib in python. I tested different pack and unpack methods and wondered where the [0] in my code came from (I did copy it from somewhere). So what I found working for my setup simply is:
def float_to_characteristic(float_value):
val=struct.pack('<f',float_value)
return val
When I send this packed value through BLE the Arduino will accept it.
I used this way: define a BLE format descriptor (0x2904) Attach the descriptor to the characteristic
struct BLE2904_Data {
uint8_t m_format;
int8_t m_exponent;
uint16_t m_unit;
uint8_t m_namespace;
uint16_t m_description;
} __attribute__((packed));
struct BLE2904_Data desc1; // must remain valid memory for ArduinoBLE
desc1.m_format = 0x14; // float
desc1.m_exponent = 0x0;
desc1.m_unit = 0x2728; // voltage
desc1.m_namespace = 1;
desc1.m_description = 0x10f;
BLEDescriptor voltageLabelDescriptor("2904", (uint8_t*)&desc1, sizeof(desc1));
batteryLevelCharacteristic.addDescriptor(voltageLabelDescriptor);
The 2904 descriptor sets the data type see here for more details: