88950

Linux serial read blocks minicom

Question:

I'm attempting to read from a serial port (/dev/ttyS4) on a BeagleBone Black, but I think(?) this should apply to all Linux devices in general.

Currently, I can set up minicom with a baud rate of 9600 and 8N1 data to read from the serial port correctly. However, if I attempt to directly cat /dev/ttyS4, nothing shows up in my terminal. My code also does this, and returns a Resource temporarily unavailable error, which I suspect is what is happening with the cat command.

If I run stty -F /dev/ttyS4, I get the following output (which, as far as I can tell, is consistent with my minicom settings):

<pre class="lang-sh prettyprint-override">speed 9600 baud; line = 0; intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>; start = <undef>; stop = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; flush = <undef>; -brkint -imaxbel -opost -onclr -isig -iexten -echo -echoe -echok -echoctl -echoke

An interesting note is that when I have minicom open, if I start my program, minicom will stop printing anything, and stay that way even if I stop my program. I need to open the serial settings again (Ctrl-A, P) and close it for minicom to resume working (it appears that nothing was changed).

My code is as follows:

<pre class="lang-cpp prettyprint-override">int main() { std::cout << "Starting..." << std::endl; std::cout << "Connecting..." << std::endl; int tty4 = open("/dev/ttyS4", O_RDWR | O_NOCTTY | O_NDELAY); if (tty4 < 0) { std::cout << "Error opening serial terminal." << std::endl; } std::cout << "Configuring..." << std::endl; struct termios oldtio, newtio; tcgetattr(tty4, &oldtio); // save current serial port settings bzero(&newtio, sizeof(newtio)); // clear struct for new settings newtio.c_cflag = B9600 | CS8 | CREAD | CLOCAL; newtio.c_iflag = IGNPAR | ICRNL; newtio.c_oflag = 0; newtio.c_lflag = ICANON; tcflush(tty4, TCIFLUSH); tcsetattr(tty4, TCSANOW, &newtio); std::cout << "Reading..." << std::endl; while (true) { uint8_t byte; int status = read(tty4, &byte, 1); if (status > 0) { std::cout << (char)byte; } else if (status == -1) { std::cout << "\tERROR: " << strerror(errno) << std::endl; } } tcsetattr(tty4, TCSANOW, &oldtio); close(tty4); } <hr />

<strong>Edit:</strong> I've gotten the serial port to work correctly (in python) by following Adafruit's tutorial for using python with the BeagleBone. At this point I'm certain that <em>I'm</em> doing something wrong; the question is what. I would much prefer using C++ over python, so it'd be great to get that working.

Answer1:

Your program opens the serial terminal in nonblocking mode.

<blockquote> int tty4 = open("/dev/ttyS4", O_RDWR | O_NOCTTY | O_NDELAY); </blockquote>

Nonblocking I/O, especially read operations, requires additional, special handling in a program. Since you neglect to mention this mode, and your program has no capability to properly process this mode, this could be considered a bug.

Either remove <strong>O_NDELAY</strong> option from the <strong>open()</strong> call, or insert a fcntl(tty4, F_SETFL, 0) statement to revert back to blocking mode.

<hr /><blockquote>

My code also does this, and returns a Resource temporarily unavailable error,

</blockquote>

That's an EAGAIN error, which is consistent with a nonblocking <strong>read()</strong>.<br /> The man page describes this error will occur when "the file descriptor ... has been marked nonblocking (O_NONBLOCK), and the read would block".<br /> The <strong>read()</strong> syscall "would block" because there is no data to satisfy the read request.

If you insist on using nonblocking mode, then your program must be able to cope with this condition, which is not an error but a temporary/transient status.<br /> But blocking mode is the simpler and preferred mode of operation for typical programs in a multitasking system.<br /> Your program should be modified as previously mentioned.

<hr />

There are numerous issues with the initialization of the serial terminal.

<hr /><blockquote> tcgetattr(tty4, &oldtio); // save current serial port settings </blockquote>

The return values from the <strong>tcgetattr()</strong> and <strong>tcsetattr()</strong> syscalls are never checked for errors.

<hr /><blockquote> bzero(&newtio, sizeof(newtio)); // clear struct for new settings </blockquote>

Starting with an empty termios structure is almost always a bad idea. It may appear to work on some systems, but it is not portable code.<br /> The proper method for initializing a termios structure is to use values from <strong>tcgetattr()</strong>.<br /> See <a href="http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_12.html#SEC237" rel="nofollow">Setting Terminal Modes Properly</a>.<br /> Since it is already called, all you need is newtio = oldtio to copy the structure.

<hr /><blockquote> newtio.c_cflag = B9600 | CS8 | CREAD | CLOCAL; newtio.c_iflag = IGNPAR | ICRNL; newtio.c_oflag = 0; newtio.c_lflag = ICANON; </blockquote>

Rather than assignment of constants, the proper method of changing these flags is to enable or disable the individual attributes.<br /> The following should suffice for canonical mode:

newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~CSIZE; newtio.c_cflag |= CS8; /* 8-bit characters */ newtio.c_cflag &= ~PARENB; /* no parity bit */ newtio.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ newtio.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */ newtio.c_lflag |= ICANON | ISIG; /* canonical input */ newtio.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN); newtio.c_iflag &= ~INPCK; newtio.c_iflag |= ICRNL; newtio.c_iflag &= ~(INLCR | IGNCR | IUCLC | IMAXBEL); newtio.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */ newtio.c_oflag &= ~OPOST; <hr />

The following is the preferred method for setting the baudrate:

cfsetospeed(&newtio, B9600); cfsetispeed(&newtio, B9600);

If any salient attributes are left unspecified, the existing settings are used.<br /> This can lead to erratic program behavior, e.g. sometimes it works, sometimes it doesn't.

<hr /><blockquote>

An interesting note is that when I have minicom open, if I start my program, minicom will stop printing anything, and stay that way even if I stop my program. I need to open the serial settings again (Ctrl-A, P) and close it for minicom to resume working (it appears that nothing was changed).

</blockquote>

The serial terminal is not intended for sharing among more than one process.<br /> Some of the termios attributes have to be implemented in the serial device driver, which has no concept of sharing the port. The most recent termios attributes are in effect for the device.<br /> When you execute your program after <strong>minicom</strong> has started, you are clobbering the termios attributes that <strong>minicom</strong> expects to execute with.<br /> You are restoring the termios attributes to <strong>minicom</strong>'s requirements by using its menu.

Recommend

  • Serial port read is not complete
  • RS-232 communication with RPi
  • When pty [Pseudo terminal] slave fd settings are changed by “tcsetattr” , how can the master end cap
  • How to change Linux interrupt timer for tcsetattr()
  • How to read RTS/CTS pins' values on Linux?
  • Writing AT commands embedded linux
  • Is the size of std::array guaranteed to be equal to the size of T[N]? [duplicate]
  • malloc error C++ [duplicate]
  • how to pass array of struct to GPU?
  • qsort of struct array not working
  • Modifying a array in a function in C
  • How can I fill out void* C pointer in Go?
  • sizeof(array) / sizeof(int) [duplicate]
  • How do I configure log4j to send log events to java.util.logging using JULAppender?
  • How to best show progress info when using ADO.NET?
  • C Pointer confusion
  • Difference between 2D char Array and char** (OR, 3D char Array and char*** etc)
  • wcstombs() has invalid output on Android
  • Conflicting Types Error
  • Custom Keybindings for Ipython terminal
  • Pipe for multiple processes
  • Can we reuse allocated memory
  • maven and jboss modules
  • Why must we Forward Declare a class and include the corresponding header file in a header file
  • Getting segmentation fault while using malloc
  • Opening links in a new tab and only the new tab
  • How to record a JNLP/ Java Web Start application with JMeter
  • OSStatus error -50 (invalid parameters) AudioQueueNewInput recording audio on iOS
  • Complex trait requirements on struct
  • How to work with AMMediaType for video filters
  • XBee Linux Serial Port on Rasberry Pi
  • Opening two instances of InAppBrowser (_system and _blank) prevents events from triggering
  • Monotouch crashes with NullReferenceException on non nullable object
  • sweetalert2 inputoptions from file in select example
  • How to unpack 32bit integer packed in a QByteArray?
  • Simple linked list-C
  • Why is the size of this struct 32?
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • Linking SubReports Without LinkChild/LinkMaster
  • XCode 8, some methods disappeared ? ex: layoutAttributesClass() -> AnyClass