我正试图在我的计算机和我的arduino之间建立一个串行通信通道。当我看到ArduinoIDE时,我从arduino发送了一个完美的消息 - 3个相同的数字。现在,我正在尝试创建一个c ++应用程序来在运行Ubuntu的计算机上读取该数据,但是我在字符串上得到了很多垃圾。我一直在阅读和搜索,但没有成功。任何人都可以帮我找到问题的根源吗?
代码:
SerialComm.h:
#ifndef SERIALCOMM_HPP
#define SERIALCOMM_HPP
#include <fstream>
#include <string>
#include <stdio.h> // standard input / output functions
#include <string.h> // string function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitionss
class SerialComm {
public:
SerialComm() noexcept {
}
virtual ~SerialComm() noexcept {
tcsetattr(fd, TCSANOW, &port_settings);
close(fd);
}
void begin(std::string port, speed_t baudrate);
std::string read_data();
private:
int fd;
speed_t _baudrate;
std::string _port;
static constexpr int BUFFER_SIZE = 256;
char buffer[BUFFER_SIZE];
termios port_settings;
};
SerialComm.cpp
#include "SerialComm.hpp"
#include <iostream>
using namespace std;
void SerialComm::begin(string porta, speed_t baudrate) {
_port = porta;
_baudrate = baudrate;
// abre a porta
fd = open(_port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
printf((string("Unable to open port ") + _port).c_str());
} else {
fcntl(fd, F_SETFL, 0);
printf("port is open.\n");
}
cfsetispeed(&port_settings, _baudrate); // set baud rates
cfsetospeed(&port_settings, _baudrate);
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB;
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
}
string SerialComm::read_data() {
int state = 1;
while (true) {
state = read(fd, buffer, BUFFER_SIZE);
if (state > 0)
{
return string( buffer );
}
}
}
main.ccp
int main(int argc, char* argv[])
{
SerialComm serial;
serial.begin("/dev/ttyACM0", B115200);
for(auto i = 0; i < 100; ++i)
{
cout << serial.read_data() << endl;
}
}
serial.ino:
double sinal = 0;
void setup()
{
Serial.begin( 115200 );
}
void loop()
{
sinal = analogRead( A0 ) * ( 5.0 / 1024.0 );
Serial.print( "$" );
Serial.print( sinal, 5 );
Serial.print( "," );
Serial.print( sinal, 5 );
Serial.print( "," );
Serial.print( sinal, 5 );
Serial.print( "#\n" );
}
Arduino IDE输出:
$2.24121,2.24121,2.24121#
$2.24609,2.24609,2.24609#
$2.24121,2.24121,2.24121#
$2.24121,2.24121,2.24121#
$2.24609,2.24609,2.24609#
电脑输出:
$2.24609,2.24?�̯m#
$2.
09375#
$2.2412109375,2.2412109937500#
$2.2460937500,2.2460937500,2.2460937500#
375#
$2.2460937500,2.2460937500,2.2460937500#
$2.
375,2.2412109375#
$2.241210937937500#
$2.2460937500,2.2460937500,2.2460937500#
PS:以上是我能得到的最漂亮的输出。
答案 0 :(得分:4)
除了未终止字符串缓冲区的问题之外,您也不能说只需一次调用read
就会收到完整的消息。相反,你必须在循环中读取,直到你得到消息的结尾(你发送的换行符)。
这当然会给您带来另一个问题,即您可以在同一read
次呼叫中收到一条消息的结尾和下一条消息的开头。这意味着您必须保存下一条消息的开头,并在下次调用read
之前将其放入缓冲区。
答案 1 :(得分:2)
我认为这是你的错误:
string SerialComm::read_data() {
int state = 1;
int receivedbyte = 0; // never used!
while (true) {
state = read(fd, buffer, BUFFER_SIZE);
if (state > 0)
{
return string( buffer );
}
}
buffer[receivedbyte + 1] = '\0'; // never reached! And "off-by-one" if it were...
}
这可能会更好:
string SerialComm::read_data() {
int receivedbyte = 0;
while (true) {
receivedbyte = read(fd, buffer, BUFFER_SIZE - 1);
if (receivedbyte > 0)
{
buffer[receivedbyte] = '\0';
return string( buffer );
}
}
}
这应该消除由于未终止的字符串而导致的任何垃圾。也就是说,要获得漂亮的以换行符结尾的字符串,您可能需要一个外部循环来查找这些边界并以正确的方式划分流。
这样做的一种方法可能是:在类中声明string received
以保存尚未返回给调用者的所有缓冲输入。然后,重写read_data(),如下所示:
string SerialComm::read_data()
{
while (true)
{
size_t pos = received.find_first_of('\n', 0);
if (pos != string::npos)
{
string result = received.substr(0, pos);
received.erase(0, pos);
return result;
}
int receivedbytes;
do
{
receivedbytes = read(fd, buffer, BUFFER_SIZE - 1);
} while (receivedbytes == 0);
if (receivedbytes < 0)
abort(); // error... you might want to handle it more cleanly, though
buffer[receivedbytes] = 0;
received += string( buffer );
}
}
如果你想要一个没有完整行可以看到的空字符串的版本,而不是永远等待数据,你可以使用这个版本的代码。注意:如果缓存数据没有终止换行符,它将保留在它上面,直到它看到终止换行符。您可能希望添加单独的flush
方法以使该数据可见。
string SerialComm::read_data()
{
while (true)
{
size_t pos = received.find_first_of('\n', 0);
if (pos != string::npos)
{
string result = received.substr(0, pos);
received.erase(0, pos);
return result;
}
int receivedbytes = read(fd, buffer, BUFFER_SIZE - 1);
if (receivedbytes < 0)
abort(); // error... you might want to handle it more cleanly, though
if (receivedbytes == 0)
return string(); // nothing to see yet
// Add received data to buffer and loop to see if we have a newline yet.
buffer[receivedbytes] = 0;
received += string( buffer );
}
}