How to open, read, and write from serial port in C?
I am a little bit confused about reading and writing to a serial port. I have a USB device in Linux that uses the FTDI USB serial device converter driver. When I plug it in, it creates: /dev/ttyUSB1. I thought itd be simple to open and read/write from it in C. I know the baud rate and parity information, but it seems like there is no standard for this? Am I missing something, or can someone point me in the right direction?
EDIT: I’d look at ribram’s link. However, the point remains that while a serial device is represented as a file, devices often have more specific interfaces implemented via system calls like ioctl and fcntl .
Understanding UNIX termios VMIN and VTIME is a great resource to understand VTIME and VMIN which are used to handle the blocking characteristics of a read() on a serial port.
Do not use code from Frerking’s «Serial Programming HOWTO» as mentioned in the first comment. They are not written to be POSIX compliant, so the code examples are not portable and may not work reliably for you.
2 Answers 2
I wrote this a long time ago (from years 1985-1992, with just a few tweaks since then), and just copy and paste the bits needed into each project.
You must call cfmakeraw on a tty obtained from tcgetattr . You cannot zero-out a struct termios , configure it, and then set the tty with tcsetattr . If you use the zero-out method, then you will experience unexplained intermittent failures, especially on the BSDs and OS X. «Unexplained intermittent failures» include hanging in read(3) .
#include #include #include #include #include int set_interface_attribs (int fd, int speed, int parity) < struct termios tty; if (tcgetattr (fd, &tty) != 0) < error_message ("error %d from tcgetattr", errno); return -1; >cfsetospeed (&tty, speed); cfsetispeed (&tty, speed); tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars // disable IGNBRK for mismatched speed tests; otherwise receive break // as \000 chars tty.c_iflag &= ~IGNBRK; // disable break processing tty.c_lflag = 0; // no signaling chars, no echo, // no canonical processing tty.c_oflag = 0; // no remapping, no delays tty.c_cc[VMIN] = 0; // read doesn't block tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls, // enable reading tty.c_cflag &= ~(PARENB | PARODD); // shut off parity tty.c_cflag |= parity; tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CRTSCTS; if (tcsetattr (fd, TCSANOW, &tty) != 0) < error_message ("error %d from tcsetattr", errno); return -1; >return 0; > void set_blocking (int fd, int should_block) < struct termios tty; memset (&tty, 0, sizeof tty); if (tcgetattr (fd, &tty) != 0) < error_message ("error %d from tggetattr", errno); return; >tty.c_cc[VMIN] = should_block ? 1 : 0; tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout if (tcsetattr (fd, TCSANOW, &tty) != 0) error_message ("error %d setting term attributes", errno); > . char *portname = "/dev/ttyUSB1" . int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC); if (fd < 0) < error_message ("error %d opening %s: %s", errno, portname, strerror (errno)); return; >set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity) set_blocking (fd, 0); // set no blocking write (fd, "hello!\n", 7); // send 7 character greeting usleep ((7 + 25) * 100); // sleep enough to transmit the 7 plus // receive 25: approx 100 uS per char transmit char buf [100]; int n = read (fd, buf, sizeof buf); // read up to 100 characters if ready to read
The values for speed are B115200 , B230400 , B9600 , B19200 , B38400 , B57600 , B1200 , B2400 , B4800 , etc. The values for parity are 0 (meaning no parity), PARENB|PARODD (enable parity and use odd), PARENB (enable parity and use even), PARENB|PARODD|CMSPAR (mark parity), and PARENB|CMSPAR (space parity).
«Blocking» sets whether a read() on the port waits for the specified number of characters to arrive. Setting no blocking means that a read() returns however many characters are available without waiting for more, up to the buffer limit.
CMSPAR is needed only for choosing mark and space parity, which is uncommon. For most applications, it can be omitted. My header file /usr/include/bits/termios.h enables definition of CMSPAR only if the preprocessor symbol __USE_MISC is defined. That definition occurs (in features.h ) with
#if defined _BSD_SOURCE || defined _SVID_SOURCE #define __USE_MISC 1 #endif
The introductory comments of says:
/* These are defined by the user (or the compiler) to specify the desired environment: . _BSD_SOURCE ISO C, POSIX, and 4.3BSD things. _SVID_SOURCE ISO C, POSIX, and SVID things. . */