簡體   English   中英

如何最大程度地減少來自Arduino的串行通信讀取錯誤

[英]How to minimize serial communication read errors from a arduino

我試圖建立計算機和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:以上是我能得到的最漂亮的輸出。

除了未終止的字符串緩沖區存在的問題外,您也不能說您將在一次read調用中收到完整的消息。 相反,您必須循環閱讀,直到消息末尾(發送的換行符)為止。

當然,這會給您帶來另一個問題,即您可以在同一read調用中收到一條消息的末尾和下一條消息的末尾。 這意味着您必須保存下一條消息的開頭,並在下一次調用read之前將其放入緩沖區。

我認為這是您的錯誤:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM