Linux Serial Port: Blocking Read with Timeout

18,610

From man 3 termios:

MIN > 0; TIME > 0: TIME specifies the limit for a timer in tenths of a second. Once an initial byte of input becomes available, the timer is restarted after each further byte is received. read(2) returns either when the lesser of the number of bytes requested or MIN byte have been read, or when the inter-byte timeout expires. Because the timer is only started after the initial byte becomes available, at least one byte will be read.

Note that the timer does not start until at least one byte of data is received. After receiving that first data byte, the read will timeout if there is ever a gap of TIME tenths of a second between receiving consecutive data bytes.

Share:
18,610
It'sPete
Author by

It'sPete

if (pete.had_coffee()) { pete.mood++; } else if (pete.in_staff_meeting()) { pete.mood = SAD_PANDA; } C/C++ code monkey. Interested in everything from embedded programming to high performance stuff on GPUs. Networking is also pretty cool, I guess... "Job satisfaction is the feeling you get right before you die of a heart attack." - Dilbert It's not a real code review until someone gets punched in the face. I think woodchucks are adorable. SOreadytohelp

Updated on June 24, 2022

Comments

  • It'sPete
    It'sPete almost 2 years

    I have studied many useful threads and some tutorials, but I'm still having some issues with something that should be very simple. For reference here are some threads that I've perused:

    How to implement a timeout in read function call?

    how to open, read, and write from serial port in C

    At any rate, I have a bit of a problem. My code works fine if I receive data. If I don't, the read() function stalls and the only way to get out of my program is to use kill -9 (NOTE: I use signal handling to signal to the thread reading the serial data to terminate. This is not the culprit, the read() call still stalls even if I have removed my signal handling). What I'm trying to do is to have a read that blocks and reads a chunk at a time (therefore saving CPU usage), however if the read receives no data, I wan't it to timeout.

    Here are the settings that I'm applying to the port:

    struct termios serial_struct;
    serial_struct.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
    serial_struct.c_iflag = IGNPAR;
    serial_struct.c_oflag = 0;
    serial_struct.c_lflag = 0;
    serial_struct.c_cc[VTIME] = 1;  // timeout after .1s that isn't working
    serial_struct.c_cc[VMIN] = 64;  // want to read a chunk of 64 bytes at a given time
    

    I then set these settings with tcsetattr() and confirm that the port received the settings via tcgetattr(). I'm thinking that my settings may be conflicting, because my reads appear to be blocking and wait until 64 bytes are received, but do not do anything with regards to the timeout. I understand that I can use select() to deal with a timeout, but I'm hoping to avoid the multiple system calls.

    As always, thanks in advance for the help.

  • It'sPete
    It'sPete over 10 years
    AHA! So termios has no way to set a "real" timeout in the event that no data is being passed? How do I solve this then? With select()?
  • paulsm4
    paulsm4 over 10 years
    Yes: The link I cited above also discusses "select()". Q: What is the device?
  • Casey
    Casey over 10 years
    @It'sPete I think almost every program eventually grows to the point that you end up either (a) using select or poll to multiplex I/O or (b) handle separate streams with separate threads. May as well get it over with.
  • It'sPete
    It'sPete over 10 years
    @Casey I'm only hoping to accomplish reading from one device, a single serial port that is configurable. One thread handles reading from the port and putting the data in a location where it can be accessed by consumers. I was hoping to avoid multiple system calls, because the read happens in a loop. However, if there is no other way to find out if I'm getting data, then it looks like I'll have to...
  • Casey
    Casey over 10 years
    @It'sPete You can minimize the number of system calls by setting the descriptor to non-blocking mode and then only calling select when read returns EAGAIN or EWOULDBLOCK.
  • It'sPete
    It'sPete over 10 years
    @Casey Yea, I was thinking about that to. My problem is that my block size is large enough that I would be exiting out of read() alot in a non-blocking mode. It looks like I'll have to use select() in the outer conditional to see if data is present and then use read() as I am. The performance hit shouldn't be too much of a detriment. Thanks for your answer and help!
  • sawdust
    sawdust about 2 years
    The above answer is incomplete. VMIN and VTIME have no effect unless the serial teminal is in non-blocking mode and non-canonical mode is used. "So termios has no way to set a "real" timeout in the event that no data is being passed?" -- Use VMIN = 0 and VTIME = <timeout in deciseconds> (and non-blocking, non-canonical mode). But don't expect to receive whole messages per syscall.