如何通过串口发送浮点数

10

如何在Arduino上通过串口发送floatdoubleint16的最佳方法是什么?

Serial.print()只能以ASCII编码格式发送值。但我想以字节为单位发送值。Serial.write()接受字节和字节数组,但将值转换为字节的最佳方法是什么?

我尝试将int16强制转换为byte*,但没有成功。我还使用了memcpy,但那会使用太多的CPU周期。Arduino使用的是纯C/C++。它是一个ATmega328微控制器。


可能是重复的问题:如何以二进制输出整数? - Martin York
9个回答

10

嗯,这个怎么样:

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
如果将它们作为字节发送 - 按定义是二进制的 - 那么可能无法正常工作:一些字节看起来像控制字符,接收者可能会对其做出反应。因此,这些数字必须转换成 ASCII 码。此外,在串行线上发送字符时,几乎总是以七位形式发送,因此尝试将它们作为八位字节发送将使高位丢失。 - Pete Wilson
1
如果发射器和接收器具有不同的字节序,那么这种数据传输方式将无法工作。 - Étienne
1
没错,但还有多少大端机器存在呢?此外:那些仍在使用大端机器的人(例如那些为上一代游戏主机编写代码的可怜人)知道这种差异,并不会在Stackoverflow上寻找解决方案。 - Nils Pipenbrinck

9

是的,要发送这些数字,您必须首先将它们转换为ASCII字符串。如果您正在使用C语言,sprintf()是我认为最方便的方法来进行此转换:

[稍后添加:啊啊啊!我忘记了对于ints/longs,函数的输入参数应该是无符号的。同样适用于传递给sprintf()的格式字符串。因此,我在下面进行了更改。抱歉我的糟糕疏忽,这可能会成为一个难以发现的错误。此外,ulong使其更加通用。]

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;
}

类似的,对于浮点数和双精度浮点数也是如此。进行转换的代码必须事先知道。它必须被告知 - 它正在转换什么样的实体,因此您可能会得到函数 char *float2str( float float_num)char *dbl2str( double dblnum)
您将从转换中获得一个以NUL结尾的左对齐(没有前导空格或零)的字符串。
您可以在任何地方/任何方式进行转换;这些函数只是示例。

3
使用Firmata协议。引用:
"Firmata是一种通用的协议,用于从主机计算机上的软件与微控制器进行通信。它旨在与任何主机计算机软件包配合使用。目前,有许多语言中的匹配对象。很容易为其他软件添加对象以使用此协议。基本上,该固件建立了一个协议,用于从主机软件与Arduino进行通信。目标是允许人们从主机计算机上的软件完全控制Arduino。"

2
您需要查找的行话词是“序列化”。
这是在串行连接上一个有趣的问题,因为可能会有限制,例如哪些字符可以端对端传输,以及每个字符可能无法传递八个位。
某些字符代码的限制相当普遍。以下是一些举例:
- 如果使用软件流控制,则传统上不能将控制字符DC1和DC3(Ctrl-Q和Ctrl-S,有时也称为XON和XOFF)作为数据传输,因为它们被发送到电缆另一端的发送器开始和停止。 - 在某些设备上,NUL和/或DEL字符(0x00和0x7F)可能会从接收器的FIFO中消失。 - 如果接收器是Unix tty,并且termio模式未正确设置,则字符Ctrl-D(EOT或0x04)可能会导致tty驱动程序向具有tty打开的进程发出文件结束信号。
串行连接通常可配置字节宽度和可能包括奇偶校验位。一些连接将要求使用带有奇偶校验的7位字节,而不是8位字节。甚至可以将许多串行端口配置为5位和6位字节以连接(旧)遗留硬件。如果每个字节少于8位,则需要更复杂的协议来处理二进制数据。
ASCII85是一种流行的技术,用于解决7位数据和控制字符限制的问题。这是一种重新编写二进制数据的约定,仅使用85个精心选择的ASCII字符代码。
此外,您必须确保发送方和接收方之间的字节顺序正确。您还可能需要担心浮点格式,因为并非每个系统都使用IEEE-754浮点。
总之,经常选择纯ASCII协议是更好的答案。它的优点是可以被人类理解,并且更能抵御串行连接问题。除非您发送了大量浮点数据,否则表示效率的低下可能会被实现的简便性所抵消。
只要在接受时开明,在发出时保守。

1

1
这很简单。使用 Serial.println() 函数。
void setup() {
  Serial.begin(9600);

}

void loop() {
  float x = 23.45585888;
  Serial.println(x, 10);
  delay(1000);
}

这是输出内容:

enter image description here


0

也许这是将浮点数转换为字节和将字节转换为浮点数的最佳方法,-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;
}

致敬。


0
结构体和联合体可以解决这个问题。使用一个紧凑的结构体,其中包含一个字节大小的与结构体匹配的联合体。重叠结构体和联合体的指针(或将联合体添加到结构体中)。使用Serial.write发送流。在接收端具有匹配的结构/联合体。只要字节顺序匹配,就不会出现问题;否则,可以使用“C” hto(s..l)函数进行解压缩。添加“头”信息以解码不同的结构体/联合体。

0

对于Arduino IDE:

float buildUp(int index, unsigned char outbox[])
{
  unsigned long d;
 d = (long(outbox[index +3]) << 24)  |   \
     (long(outbox[index +2]) << 16) |   \
     (long(outbox[index +1]) << 8)  |   \
     (long(outbox[index]));
 float member = *(float *)&d;
 return member;
}

否则不起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接