How to send float over serial

2019-02-13 11:32发布

问题:

What's the best way to send float, double, and int16 over serial on Arduino?

The Serial.print() only sends values as ASCII encoded. But I want to send the values as bytes. Serial.write() accepts byte and bytearrays, but what's the best way to convert the values to bytes?

I tried to cast an int16 to an byte*, without luck. I also used memcpy, but that uses to many CPU cycles. Arduino uses plain C/C++. It's an ATmega328 microcontroller.

回答1:

Yes, to send these numbers you have to first convert them to ASCII strings. If you are working with C, sprintf() is, IMO, the handiest way to do this conversion:

[Added later: AAAGHH! I forgot that for ints/longs, the function's input argument wants to be unsigned. Likewise for the format string handed to sprintf(). So I changed it below. Sorry about my terrible oversight, which would have been a hard-to-find bug. Also, ulong makes it a little more general.]

char *
int2str( unsigned long num ) {
    static char retnum[21];       // Enough for 20 digits plus NUL from a 64-bit uint.
    sprintf( retnum, "%ul", num );
    return retnum;
}

And similar for floats and doubles. The code doing the conversion has be known in advance. It has to be told - what kind of an entity it's converting, so you might end up with functions char *float2str( float float_num) and char *dbl2str( double dblnum).

You'll get a NUL-terminated left-adjusted (no leading blanks or zeroes) character string out of the conversion.

You can do the conversion anywhere/anyhow you like; these functions are just illustrations.



回答2:

hm. How about this:

void send_float (float arg)
{
  // get access to the float as a byte-array:
  byte * data = (byte *) &arg; 

  // write the data to the serial
  Serial.write (data, sizeof (arg));
}


回答3:

Use the Firmata protocol. Quote:

Firmata is a generic protocol for communicating with microcontrollers from software on a host computer. It is intended to work with any host computer software package. Right now there is a matching object in a number of languages. It is easy to add objects for other software to use this protocol. Basically, this firmware establishes a protocol for talking to the Arduino from the host software. The aim is to allow people to completely control the Arduino from software on the host computer.



回答4:

The jargon word you need to look up is "serialization".

It is an interesting problem over a serial connection which might have restrictions on what characters can go end to end, and might not be able to pass eight bits per character either.

Restrictions on certain character codes are fairly common. Here's a few off the cuff:

  • If software flow control is in use, then conventionally the control characters DC1 and DC3 (Ctrl-Q and Ctrl-S, also sometimes called XON and XOFF) cannot be transmitted as data because they are sent to start and stop the sender at the other end of the cable.

  • On some devices, NUL and/or DEL characters (0x00 and 0x7F) may simply vanish from the receiver's FIFO.

  • If the receiver is a Unix tty, and the termio modes are not set correctly, then the character Ctrl-D (EOT or 0x04) can cause the tty driver to signal an end-of-file to the process that has the tty open.

A serial connection is usually configurable for byte width and possible inclusion of a parity bit. Some connections will require that a 7-bit byte with a parity are used, rather than an 8-bit byte. It is even possible for connection to (seriously old) legacy hardware to configure many serial ports for 5-bit and 6-bit bytes. If less than 8-bits are available per byte, then a more complicated protocol is required to handle binary data.

ASCII85 is a popular technique for working around both 7-bit data and restrictions on control characters. It is a convention for re-writing binary data using only 85 carefully chosen ASCII character codes.

In addition, you certainly have to worry about byte order between sender and receiver. You might also have to worry about floating point format, since not every system uses IEEE-754 floating point.

The bottom line is that often enough choosing a pure ASCII protocol is the better answer. It has the advantage that it can be understood by a human, and is much more resistant to issues with the serial connection. Unless you are sending gobs of floating point data, then inefficiency of representation may be outweighed by ease of implementation.

Just be liberal in what you accept, and conservative about what you emit.



回答5:

Does size matter? If it does, you can encode each 32 bit group into 5 ASCII characters using ASCII85, see http://en.wikipedia.org/wiki/Ascii85.



回答6:

Perhaps that is best Way to convert Float to Byte and Byte to Float,-Hamid Reza.

int breakDown(int index, unsigned char outbox[], float member)
{
  unsigned long d = *(unsigned long *)&member;

  outbox[index] = d & 0x00FF;
  index++;

  outbox[index] = (d & 0xFF00) >> 8;
  index++;

  outbox[index] = (d & 0xFF0000) >> 16;
  index++;

  outbox[index] = (d & 0xFF000000) >> 24;
  index++;
  return index;
}


float buildUp(int index, unsigned char outbox[])
{
  unsigned long d;

  d =  (outbox[index+3] << 24) | (outbox[index+2] << 16)
    | (outbox[index+1] << 8) | (outbox[index]);
  float member = *(float *)&d;
  return member;
}

regards. `



回答7:

Structures and unions solve that issue. Use a packed structure with a byte sized union matching the structure. Overlap the pointers to the structure and union (or add the union in the structure). Use Serial.write to send the stream. Have a matching structure/union on receiving end. As long as byte order matches no issue otherwise you can unpack using the "C" hto(s..l) functions. Add "header" info to decode different structures/unions.