c++ - 串行通信 - 我无法将传入的 char 数组转换为 int

标签 c++ arrays serialization int type-conversion

我正在尝试从 Arduino 接收一个数字作为 C++ 中的整数。完整代码如下:

#define STRICT
#include <tchar.h>
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "Serial.h"
#include <boost\lexical_cast.hpp>
enum { EOF_Char = 27 };

int __cdecl _tmain(int /*argc*/, char** /*argv*/)
{
    CSerial serial;
    LONG    lLastError = ERROR_SUCCESS;

    // Attempt to open the serial port (COM4)
    lLastError = serial.Open(_T("COM4"), 0, 0, false);

    // Setup the serial port (9600,8N1, which is the default setting)
    lLastError = serial.Setup(CSerial::EBaud9600, CSerial::EData8, CSerial::EParNone, CSerial::EStop1);

    // Register only for the receive event
    lLastError = serial.SetMask(CSerial::EEventBreak |
        CSerial::EEventCTS |
        CSerial::EEventDSR |
        CSerial::EEventError |
        CSerial::EEventRing |
        CSerial::EEventRLSD |
        CSerial::EEventRecv);

    // Use 'non-blocking' reads, because we don't know how many bytes
    // will be received. This is normally the most convenient mode
    // (and also the default mode for reading data).
    lLastError = serial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking);

    // Keep reading data, until an EOF (CTRL-Z) has been received
    bool fContinue = true;
    do
    {
        // Wait for an event
        lLastError = serial.WaitEvent();

        // Save event
        const CSerial::EEvent eEvent = serial.GetEventType();


        // Handle data receive event
        if (eEvent & CSerial::EEventRecv)
        {
            // Read data, until there is nothing left
            DWORD dwBytesRead = 0;
            char szBuffer[101];
            do
            {
                // Read data from the COM-port
                lLastError = serial.Read(szBuffer, sizeof(szBuffer) - 1, &dwBytesRead);

                if (dwBytesRead > 0)
                {
                    // Finalize the data, so it is a valid string
                    szBuffer[dwBytesRead] = '\0';

                    // Display the data
                    printf("%s", szBuffer);


                    // Check if EOF (CTRL+'[') has been specified
                    if (strchr(szBuffer, EOF_Char))
                        fContinue = false;
                }
            } while (dwBytesRead == sizeof(szBuffer) - 1);
        }
    } while (fContinue);

    // Close the port again
    serial.Close();
    return 0;
}

我让我的 Arduino 不断发送数字 51。此代码工作正常并始终显示“51”。但是,我想要一个 int 在 C++ 中进行操作。

首先我添加了

std::stringstream str(szBuffer);
int tester;
str >> tester;
printf("My number is: %d\n", tester+1);

紧接着

printf("%s", szBuffer);

典型的结果如下:

51My number is: 52
51My number is: 52
51My number is: 52
51My number is: 52
51My number is: 52
5My number is: 6
1My number is: 2

完美地做了 5 或 6 次后,输出总是将传入的数字连续分隔一两次(我还没有找到具体的模式,但它总是 5-6 和 1-2) .

我的另一个尝试是使用 boost 库:

int tester = boost::lexical_cast<int>(szBuffer);
printf("My number is: %d\n", tester);

紧接着

printf("%s", szBuffer);

我得到了相同的结果(5-6 次正确后出现 1-2 次错误)。我不认为 Arduino 正在发送错误数据,因为只是一个

printf("%s", szBuffer);

永远不会偏离它应该是的数字。转换是否会扰乱数据的接收?谢谢。

编辑:Arduino 代码是:

void setup() {
    Serial.begin(9600); // same as in your c++ script
}

void loop() {
      Serial.print(51);
      delay(1000);
}

最佳答案

对于串行端口,没有一种机制可以让发送器通知接收器作为一个 block 传输了多少字节。 IE。 Serial.print(51); 告诉接收方它发送两个字符作为一个数字时没有“隐藏”标记。您必须向串行协议(protocol)添加某种指示(空格、逗号、行尾、初始字节数等)。

正因为如此,你从serial.Read获取的字符数取决于你要求它读取的字符数(第二个参数)和串口接收的字符数缓冲区,以较小者为准。大多数时候,Arduino 似乎在您调用 serial.Read 之前发送了两个数字,但有时它只能及时输出一个数字……下一次通过循环读取第二个数字。

因此,假设您决定使用换行符来分隔数字。您在 Arduino 端只需更改为 Serial.println(51);。接收端稍微复杂一些。

不知道你的串口库里面有什么。大多数都有某种“读取行”功能,您只需将 serial.Read 调用替换为类似以下内容:

serial.Readline(szBuffer, sizeof(szBuffer) - 1);

它会处理空终止输出。如果它不处理空终止,您需要找到行尾并将其更改为 \0 自己。从现在开始,您的代码将正常工作,因为 serial.Readline 函数将阻塞直到它获取整行。

如果您没有“读取行”或至少没有“读取到此字符”功能,那就有点难了。您必须重复调用 serial.Read,在缓冲区中移动,直到看到行结束符。此外,您还冒着读取下一行的部分或全部的风险,因此您不能在读取完数字后就丢弃读取的所有数据;您必须移动缓冲区中的数据,以便下一行(以及更多)的数据位于缓冲区的开头。


如果您使用的是 Boost(是吗?我看到它没有 CSerial),看起来它有一个 read_until功能。这需要三个参数:您正在读取的流、用于存储数据的流缓冲区以及要停止的内容。在这种情况下,用于存储的流缓冲区是 std::stringstream 中的缓冲区:

std::stringstream buffer;
size_t chars = boost::asio::read_until(serial, buffer.rdbuf(), '\n');
if(chars == 0) return;
int tester;
buffer >> tester;
printf("My number is: %d\n", tester+1);

关于c++ - 串行通信 - 我无法将传入的 char 数组转换为 int,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29955981/

相关文章:

ruby-on-rails - 将 Rails Form Helpers 与序列化的自定义类一起使用

c++ - 将重叠的应用程序从我的应用程序中移到 x 轴上。多功能 Controller

C++ createObject() 工厂

C `Abort trap: 6` 仅在某些条件下

java - Java 填充二维数组中的特定行

c++ - 序列化包含 char* 的结构

c++ - 使用 Visual Studio 2012 并使用旧平台工具集进行编译?

c++ - 在多 GPU 上启动异步内存复制操作

c - 无重复创建二维数组的最佳方法

java - 阻止 GSON 序列化 JSON 字符串