我正在尝试从 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/