0

I'm using a Bluefruit Feather to send information from a temperature sensor to a Python script on my computer, over BLEUART.

I have a working version of this code for an accelerometer, the only difference being that the information sent is 4 ints (for the accelerometer: x, y, z, id), vs 2 floats for the temperature sensor (temp, float id). I have checked the values that the sensor sends over the Serial monitor and they are correct. The problem occurs when the floats are unpacked in Python: they are a factor of approximately 1.36*e-45 off.

In both cases, I store the variables in a struct and send this struct over BLE. The feather code looks like:

void sendData(){
  int numVals = 2;
  int vals[numVals];
  vals[0] = temp; 
  vals[1] = id; //1.0
  Serial.println(temp);
  Serial.println(id);  
  int cnt = numVals * sizeof(float) ;
  uint8_t buf[cnt];
  for (int _i=0; _i<numVals; _i++)
    memcpy(&buf[_i*sizeof(float)], &vals[_i], sizeof(float));
  bleuart.write( buf, cnt );
}

And on the Python end it looks like:

def received(data):
    floats = struct.unpack('ff', data)
    print('Received:', floats)

This is really mysterious! I have a feeling it's something to do with how floats are represented between C and Python, but have tried changing the values to doubles / unsigned longs and it's either crashed, or had similar issues.

1 Answers1

1

The problem you present is actually a broader one, regarding how objects are serialized and de-serialized under different operating systems, machine architectures, different compilers, etc.

Specifically you should pay attention to:

  • how structures are mapped to physical memory - it is not guaranteed that your struct occupies the minimal possible space in memory. In fact, it is likely the compiler introduces gaps between fields, to optimize memory access speed (see this SO question).
  • machine Endianess, which is the order in which multi-byte primitives (such as floats), are stored in memory

To solve your issue, you could either devise your own scheme (e.g. configure the the compiler to tightly pack the structures in-memory - see gcc's packed attribute), and always encode floats as big-endian. Better yet, you can use a framework such as Google's protocol buffers which is cross-language, cross-platform, cross-whatever to handle serialization for you.

Personally, I would go with option II, even though it introduces some overhead.

bavaza
  • 10,319
  • 10
  • 64
  • 103
  • Ah, I'm glad it's not just a simple bug! In the end, I only needed the first 4sf, so just multiplied by 1000 and sent as an int, but I'll look at protocol buffers for this project further down the line. Thanks! – Agnes Cameron Oct 12 '19 at 15:34