0
votes

I am trying to work with read() function with serial connection.

I initialize serial port with the following settings:

bool HardwareSerial::begin(speed_t speed) {


    int USB = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);

    if (USB == 1) {

        std::cout << "\n  Error! in Opening ttyUSB0\n" << std::endl;
    } else {

        std::cout << "\n  ttyUSB0 Opened Successfully\n" << std::endl;
    }

    struct termios tty;
    struct termios tty_old;
    memset(&tty, 0, sizeof tty);

    // Error Handling
    if (tcgetattr(USB, &tty) != 0) {
        std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;
    }

    //Save old tty parameters
    tty_old = tty;

    // Set Baud Rate 
    cfsetospeed(&tty, (speed_t) speed);
    cfsetispeed(&tty, (speed_t) speed);

    // Setting other Port Stuff 
    tty.c_cflag &= ~PARENB; // Make 8n1
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;

    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    tty.c_cflag &= ~CRTSCTS; // no flow control
    tty.c_cc[VMIN] = 1; // read doesn't block
    tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
    tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines

    // Make raw
    cfmakeraw(&tty);

    //Flush Port, then applies attributes
    tcflush(USB, TCIFLUSH);
    if (tcsetattr(USB, TCSANOW, &tty) != 0) {
        std::cout << "Error " << errno << " from tcsetattr" << std::endl;
    }

    _USB = USB;

    return true;

}

Then I periodically call class member read() function that calls stream read:

int HardwareSerial::read() {

    int n = 0;
    char buf;

    n = ::read(_USB, &buf, 1);

    std::cout << std::hex << static_cast<int> (buf) << " n:";
    std::cout << n << std::endl;

}

While the port receives data read() works as expected and prints incoming bytes. But if I stop sending bytes the program hangs until some bytes will have not received. I expect that ::read will return 0, but it does not return anything and waits for incoming data. After new data is received the program continues work and ::read returns 1;

So what I missed in the configuration? I tried different VMIN and VTIME but the result is the same.

1

1 Answers

5
votes

You are reading from the USB in blocking manner, e.g. if no data available the call is blocked and process will not make any progress until data will arrive.

Than can be done, you can set descriptor to read in NON-BLOCKING mode, something along these lines:

int flags = fcntl(_USB, F_GETFL, 0);
fcntl(_USB, F_SETFL, flags | O_NONBLOCK)

Now than you will try to read you can do:

int count;
char buffer;
count = read(_USD, buf, 1);
// Check whenever you succeeded to read something
if(count >=0) {
    // Data is arrived
} else if(count < 0 && errno == EAGAIN) {
    // No Data, need to wait, continue, or something else.
}

You can also probably use select function to check whenever device descriptor is ready to read from.