Я пытаюсь установить последовательный канал связи между моим компьютером и моим 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: вышеупомянутое было самым симпатичным выводом, который я мог получить.
Я думаю, что это ваша ошибка:
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 );
}
}
Помимо проблемы с неопределенным строковым буфером, вы также не можете сказать, что вы получите полное сообщение за один вызов read
, Вместо этого вы должны читать в цикле, пока не получите конец сообщения (новую строку, которую вы отправляете).
Это, конечно, создает вам еще одну проблему: вы можете получить конец одного сообщения и начало следующего сообщения в том же read
вызов. Это означает, что вы должны сохранить начало следующего сообщения и поместить его в буфер перед следующим вызовом read
,