Sending Array of Floats over Serial?

Hello,

I have been using 2 IMUs on the Arduino, and sending the orientation data to a python script. However, when I am writing the data using Serial.print or println, I get errors sometimes when the python code recieves a weird character or something, and returns something like "/xfe" instead of the float value I am trying to send. For this reason, I am trying to send my data with Serial.write(), and I have seen people sending single floats with a byte pointer, but how would you send an array of 6 floats, for instance? Would it be the same process using an array of bye pointers?

Thanks a lot,

Vik

There not sending byte pointers, they use byte pointers to access the 4 bytes of which a float consists. To sends a float array just treat every array entry as another array so it becomes a 2D array. Or just cast the whole array to a byte and just send them all aka 6 x 4 = 24 bytes

  float myFloats[6];
  byte *p = (byte*)myFloats;
  for(byte i = 0; i < sizeof(myFloats); i++){
    Serial.write(p[i]);
  }

But remember, float != decimal point! Floats are "misused" a lot on Arduino and are slow and bulky because a Arduino Uno (or other standard Arduino's) have no hardware support for float calculations. Also, they have around 6 digits of accuracy. In like 99,9% of the cases I see here you're better of using a fixed point notation. So instead of trying to save 12,34, just save 1234 to an int and remember there is a decimal point two places from the right. That's faster, smaller and more accurate :slight_smile:

Thanks for replying so fast! I will try this out tomorrow and keep you posted!

Thanks again,
Vik

I see what you are saying about the decimal point. Would it be a similar method to send an unsigned int instead of a float using serial.write? also, I am using python on the recieving end of the serial, but I cannot seem to get the correct values out of the serial, python prints some weird character that looks like an missing character error or something - �

Yeah, it's the same for an int.

But remember, you're now really sending the bits of the value. In order to recover it in the other side you have to treat it as bits as well, the ASCII representation is useless. And you have to glue it together again to get the int back. Take in account the endianness and sign type.

Also, Python might be doing some processing before passing the characters on to you, like changing \n to \n\r.

Ah, I see. I have tried to "reassemble" the sent values from the arduino in python, but when I use Python's build in "struct.unpack()" function, It reassembles the values into some large 8-10 digit values that I have no clue where they are coming from.

Arduino Code:

int myInts[6]= {1455, 1446, 6766, 974, 365, 455};

void setup()
{
  Serial.begin(9600);
}
void loop()
{  
  
  byte *p = (byte)myInts;
  for(byte i = 0; i < sizeof(myInts); i++){
    Serial.write(p[i]);
  }
}

Python's Output:

(-1090650113,)
(285341695,)
(10683141,)
(-1090650177,)
(285341695,)
(10683141,)

and it repeats the same after, since I am sending the same 6 digits over and over. But I still don't understand where these large numbers are coming from.

Thanks,

Vik

You didn't show the most important part (although not Arduino related) and that's the Python code. My Python is a bit limited/rusty but we can have a look. But I think the problem is in the different size of a int (16-bit on Arduino, 32-bit on PC) and maybe the endianness.

Here is the python Code:

import argparse
import numpy
import time
import serial 
import struct


ser = serial.Serial('/dev/ttyACM0', 9600)
while True:
    b = ser.read(4)

    i = struct.unpack('i' ,b)
    print(i)

I am not sure what endianness is, though. Maybe that is the problem.
Thanks

And just to check in between. Can you print the individual bytes in Python (print them as HEX).

VikramBhat:
Here is the python Code:

If you are sending a float why are you trying to unpack it as an integer? You should probably be using 'f'

However if this was my project I would just use Serial.print() unless I needed to send so much data that I needed the extra performance of shorter messages. Have a look at this Python - Arduino demo

IIRC on this linux laptop the endianess is the same as the Arduino.

...R

I'm not that great with Python datatypes but can you try:

i = struct.unpack('<s', b)

@Robin2, see post #6 :wink: No floats :slight_smile:

(b'\x01',)
(b'\xbe',)
(b'\xff',)
(b'\xf7',)
(b'\x01',)
(b'\x11',)
(b'\x05',)
(b'\x03',)
(b'\xa3',)
(b'\x00',)
(b'\xbf',)
(b'\xff',)
(b'\xfd',)
(b'\xbe',)
(b'\xff',)
(b'\xf7',)
(b'\x01',)
(b'\x11',)
(b'\x05',)
(b'\x03',)
(b'\xa3',)
(b'\x00',)
(b'\xbf',)
(b'\xff',)
(b'\xfd',)
(b'\xbe',)
(b'\xff',)

Here's the Pasted output from using i = struct.unpack('<s', b). I think I mightve copied too much though..

void loop()
{  
  byte *p = (byte)myInts;
  for(byte i = 0; i < sizeof(myInts); i++){
    Serial.write(p[i]);
  }
}

I don't understand. That code just spits out an unending stream of bytes with no delimiting, start or end markers, How does the Python program sort the bytes into ints, reliably?

That's indeed true. A assumed syncing of it otherwise :slight_smile:

@VikramBhat My Python is to rusty to interpret that output...

This line is surely wrong:

byte *p = (byte)myInts;

A pointer's value can be bigger than 8 bits wide, but you're losing everything but the lowest 8 bits by casting myInts to a byte.

septillion:
@Robin2, see post #6 :wink: No floats :slight_smile:

Silly me to take notice of the Thread title.

...R

Is there some better way to send the bytes then? with starting markers?

The serial input basics thread by Robin2 shows how.

here's some ideas for python floats types
getting from and to byte arrays and lists
in a C type way
https://www.raspberrypi.org/forums/viewtopic.php?f=32&t=186737&p=1180321&hilit=union#p1180321

should be easy then to send / receive fixed length packets of data

edit link fixed