Serial Port Prototypting

Lots of small embedded/robotic systems make extensive use of serial ports for communication between sensors, actuators, and a central processor of some sort. Despite this being one of the simplest interfaces one can work with, it can still be a serious headache to initially set up, debug, and prototype with. This tutorial is going to look at some handy tools you can use to make prototyping, development, and hardware configuration easier when using a serial port on a Linux box.

Port Configuration

Most of the default behaviors for Linux’s serial port functionality were chosen based on the assumption that the port would be used for communication with some kind of external modem or terminal application. While the implementation is flexible enough to give you near complete control of the port, these defaults can be disorienting for first timers.

One of the more annoying behaviors is line termination – depending on your particular OS the serial port might be sticking new line or carriage return characters on the end of your packets without you realizing it. Weird things might also happen when your control program opens or closes the port.

Ideally what you’re after is a ‘raw port’, one which sends only the exact character sequences specified. There’s a few different ways to go about getting this behavior:

Via C Program

From within a C program a serial port’s properties are set through the termios interface.

#include 
#include  

First, obtain a file handle to the serial device based on the IO characteristic desired:

int fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NONBLOCK); //non-blocking
int f d = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_SYNC); //blocking

Then, set up the control data structure and use it to set the baud rate for the port:

struct termios attrs;
cfsetispeed(&attrs, B115200);
cfsetospeed(&attrs, B115200);

Set the control flags associated with desired port behavior:

No Parity
attrs.c_cflag &= ~PARENB;
Odd Parity
attrs.c_cflag |= PARENB;
attrs.c_cflag |= PARODD;
Even Parity
attrs.c_cflag |= PARENB;
attrs.c_cflag &= ~PARODD;
One Stop Bit
attrs.c_cflag &= ~CSTOPB;
Two Stop Bits
attrs.c_cflag |= CSTOPB;
Set Character/Word Size to N=[5,8] bits
attrs.c_cflag &= ~CSIZE;
attrs.c_cflag |= CSN;

Finally, apply the settings structure to the port to commit the changes.

tcsetattr(fd, TCSANOW, &attrs);

Via Command Line

You can also deal with this settings completely outside of your program, which cuts down on added logic that doesn’t actually add to your controller. Be aware that each time you reboot the machine (or add/remove the USB device when applicable) the settings are going to reset.

To set raw mode and a baud rate, use the following command:

 stty -F /dev/ttyS0 raw 

Other options:

No Parity -parenb
Odd Parity parenb parodd
Even Parity parenb -parodd
One Stop Bit -cstopb
Two Stop Bits cstopb
Set Character/Word Size to N=[5,8] bits csN

To set a port to 115200 baud with even parity, a 8 bit character size, and two stop bits:

stty -F /dev/ttyS0 raw 115200
stty -F /dev/ttyS0 parenb -parodd cstopb cs8

Check the stty man page for more information.

Command Line I/O

echo

When you’re first testing a new peripheral, it’s a pain to have to write up a full program in order to send out a simple test string to see if the device works. Fortunately, unix has a solution – echo. echo is used to copy a string specified by the user to stdout (or to some node like /dev/ttyS0 that the user specifies using a pipe). Let’s say I wanted to print “hello world” to a file, I would do so by:

echo "hello world" > somefile

And now somefile will contain the string “hello worldn”. Echo can also be used to push arbitrary binary strings to a file. (Don’t forget, in unix my serial port is also a file.) Let’s say I want to send a byte string of:

[ 0xFF, 0xFF, 0x10, 0x11, 0x12, 0x2D ]

out of /dev/ttyS0. I can do this by writing some C code to push the characters, or I can avoid reinventing the wheel and use echo:

echo -n -e "xFFxFFx10x11x12x2D" > /dev/ttyS0

You’ll notice that two new command line switches were added to my echo call. The first ‘-n’ supresses the automatic newline that echo adds by default to the end of a string. If I’m printing out to a serial port I almost certainly don’t want this behavior, so I use ‘-n’ to turn it off. The second switch ‘-e’ enables the intepretation of “backslash escapes”, these are the format codes like “xFF” that allow us to print non-ascii characters that aren’t available on the keyboard. You can read about all of them in the man page, the one’s you’re most likely to use are:

  • xHH – specify a byte with hexadecimal value HH
  • NNN – specify a byte with octal value NNN
  • n – newline character (equivalent to x0A or 012)

cat

The flip side of echo, an input handling program, is cat. cat will copy the contents of its input files to stdout. You can use this with a serial port to view the activity on the line. Running the command:
cat /dev/ttyS0
will print any activity on the serial port to your terminal screen. Unfortunately, your terminal isn’t equipped to handle the non-printable bytes that your serial device is probably sending to your machine. To get a better look at things, you can redirect cat’s output to a program that does hex formatting, like so:
cat /dev/ttyS0 | hexdump
Now you’ll get a hexadecimal printed version of your data instead, a much more intuitive format for peripheral communication.
If you’d also like to save a copy of the data somewhere as it comes stream in, you can use a neat little program called tee. Change your current cat command to:
cat /dev/ttyS0 | hexdump | tee somefile
and the file somefile will contain a copy of whatever cat processed, in hex formatting. If you remove the hexdump command from this chain, the file will contain the raw data.

socat

Another painful part of developing for serial devices is unit testing the code. Most times the best way to really exercise code is with other bits of code – before you hook things up to your often expensive hardware. It’s possible to do code testing using either files or pipes as an endpoint (instead of your serial device), but these mechanisms aren’t entirely symmetric in terms of their access mechanisms. Character files don’t track well – input and output tend to step on each other’s toes. Pipes, on the other hand, are unidirectional where a terminal endpoint is bidirectional, forcing the I/O logic to fork at the front end and creating needless headaches. How, then, does one achieve a reasonable simulation of the device from a file access standpoint? Then answer is TTY emulation through socat.

Socat (SOcket CAT) is (quoting the man page) “a command line based utility that establishes two bidirectional byte streams and transfers data between them.” Basically it lets you create all types of pipes, sockets, and other transport mechanisms. For embedded systems work it incredibly useful for its ability to credibly fake a serial port (pty). The following command will create a pair of virtual ptys:

socat -d -d pty,echo=0,raw pty,echo=0,raw

The program will print out the two end points it has created (probably in /dev/pts/, but that depends on your particular system). To one of the end points you can hook your control program. To the other you’ll have to hook up to something that can somewhat credibly fake the I/O characteristic of your test device – or you could just use the cat/echo combination from above. The one hang up with this approach is that the actual ptys allocated are not always consistent – sometimes /dev/pts/1 will be allocated and sometimes /dev/pts/5 will be allocated. This is a function of the state of the OS as a whole and there’s not a lot you can do about it. Socat can, however, create links to the unreliably named pts nodes. The links themselves will be in whatever location is specifed, with the names requested. To do this:

socat pty,echo=0,raw,link=link1 pty,echo=0,raw,link=link2

This will create link1 and link2 in the current directory that will point back to the underlying ptys.

If you’ve written your controller to use stdio to do its communication, you can have socat invoke the program directly and connect the endpoint automatically. To get this behavior do the following:

socat -d -d pty,echo=0,raw "exec:progname,pty,echo=0,raw"

Socat’s command line can also be used to specify whatever configuration parameters the virtual ports might require (the same type of behavior covered in the “Port Configuration” section above.

Baud Rate bRATE (ex: b115200)
No Parity parenb=0
Odd Parity* parenb=1,parodd=1
Even Parity* parenb=1,parodd=0
One Stop Bit cstopb=0
Two Stop Bits cstopb=1
Set Character/Word Size to N=[5,8] bits csN (ex: cs8)

*(The man page indicates that these options should work, but the version of socat on my system would not accept them – your mileage may vary.)

To create a port pair with 115200 baud with no parity, a 8 bit character size, and two stop bits:

socat pty,echo=0,raw,link=link1,b115200,parenb=0,cs8,cstopb=1 pty,echo=0,raw,link=link2,b115200,parenb=0,cs8,cstopb=1

 

Post a Comment

Your email address will not be published. Required fields are marked *

*

Please type the characters of this captcha image in the input box

Please type the characters of this captcha image in the input box