如何通过串口发送浮点数

发布于 2024-09-10 10:31:05 字数 486 浏览 5 评论 0原文

在 Arduino 上通过串行发送 floatdoubleint16 的最佳方式是什么?

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

骷髅 2024-09-17 10:31:05

嗯。这个怎么样:

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

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));
}
画尸师 2024-09-17 10:31:05

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

[稍后添加:AAAGHH!我忘记了 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 结尾的左调整(无前导空格或零)字符串。

您可以在任何地方/以您喜欢的方式进行转换;这些功能只是示例。

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.

丢了幸福的猪 2024-09-17 10:31:05

使用 Firmata 协议。引用:

Firmata 是一种用于与微控制器通信的通用协议
从主机上的软件。它旨在与任何主机一起工作
计算机软件包。现在 a 中有一个匹配的对象
语言数量。很容易为其他软件添加对象
使用此协议。基本上,该固件建立了一个协议
从主机软件与 Arduino 对话。目的是让
人们可以通过主机上的软件完全控制 Arduino
计算机。

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.

云柯 2024-09-17 10:31:05

您需要查找的行话是“序列化”。

这是串行连接上的一个有趣问题,串行连接可能对哪些字符可以端到端进行限制,并且可能无法每个字符传递八位。

对某些字符代码的限制相当常见。以下是一些即兴的内容:

  • 如果使用软件流控制,则通常控制字符 DC1 和 DC3(Ctrl-Q 和 Ctrl-S,有时也称为 XON 和 XOFF)不能作为数据传输,因为它们是发送以启动和停止电缆另一端的发送器。

  • 在某些设备上,NUL 和/或 DEL 字符(0x00 和 0x7F)可能会从接收器的 FIFO 中消失。

  • 如果接收方是 Unix tty,并且 termio 模式设置不正确,则字符 Ctrl-D(EOT 或 0x04)可能会导致 tty 驱动程序向具有文件结束符的进程发出文件结束信号。 tty open。

串行连接通常可配置字节宽度并可能包含奇偶校验位。某些连接需要使用带有奇偶校验的 7 位字节,而不是 8 位字节。甚至可以连接到(非常旧的)传统硬件来配置许多 5 位和 6 位字节的串行端口。如果每个字节可用的位数少于 8 位,则需要更复杂的协议来处理二进制数据。

ASCII85 是一种流行的技术,用于解决 7 位数据和控制字符的限制。这是仅使用 85 个精心挑选的 ASCII 字符代码重写二进制数据的约定。

此外,您当然还必须担心发送方和接收方之间的字节顺序。您可能还需要担心浮点格式,因为并非每个系统都使用 IEEE-754 浮点。

最重要的是,选择纯 ASCII 协议通常是更好的答案。它的优点是人类可以理解,并且更能抵抗串行连接的问题。除非您发送大量浮点数据,否则实现的简便性可能会抵消表示效率低下的问题。

只要对你接受的东西保持开放的态度,对你发出的东西保持保守即可。

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.

情深如许 2024-09-17 10:31:05

尺寸重要吗?如果是这样,您可以使用 ASCII85 将每个 32 位组编码为 5 个 ASCII 字符,请参阅 http://en .wikipedia.org/wiki/Ascii85

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.

不打扰别人 2024-09-17 10:31:05

这很简单。使用 Serial.println() 函数

void setup() {
  Serial.begin(9600);

}

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

这是输出:

在此处输入图像描述

This simply works. Use Serial.println() function

void setup() {
  Serial.begin(9600);

}

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

And this is the output:

enter image description here

猫弦 2024-09-17 10:31:05

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

问候。
`

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.
`

骷髅 2024-09-17 10:31:05

结构和工会解决了这个问题。使用带有与该结构匹配的字节大小联合的打包结构。重叠指向结构和联合的指针(或在结构中添加联合)。使用 Serial.write 发送流。在接收端有匹配的结构/联合。只要字节顺序匹配就没有问题,否则您可以使用“C” hto(s..l) 函数解包。添加“标头”信息以解码不同的结构/联合。

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.

挽清梦 2024-09-17 10:31:05

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

否则无法工作。

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

otherwise not working.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文