Port of Call

A few weeks ago I mentioned how I’ve been looking for ways to incorporate USB into more of my projects. I’ve been looking at several “native” options. I also mentioned that there are several techniques for getting a virtual serial port either through a stock cable or building any of a variety of USB ports on the board (including one that fits in a 9-pin serial port socket).

If your application doesn’t demand anything more sophisticated than a serial port, a USB to serial converter really isn’t a bad option. Operating system support is widespread. The 9-pin adapter is a little pricey, but other chips and cables are inexpensive and readily available. You can also preserve any investment you already have in serial port-based software.

I had a project a few weeks ago that needed a large number of PWM channels controlled by some PC software. Since it is getting hard to find PCs with serial ports, I built a stock PIC board but replaced the 9-pin RS-232 connector with a USB adapter (the USB-DB9 from FTDI).

[Click image to view at full size]

Port of Call

That was easy, but then I needed software to communicate with the board itself. Lately I’ve been experimenting with the Qt framework (Qt now has preliminary support for Android, but that’s a topic for another blog). Qt provides a lot of abstractions, but serial ports aren’t among them.

On the other hand, I had some code from a serial-controlled I/O interface handy that has parallel implementations for Linux and Windows, so I thought that would make a good fit for Qt, which is adept at handling multiple platforms. I’ve never been one to pass up a chance to steal code (especially my own), so I pulled a copy from the existing project and set about converting to work for this one. On the other hand, maybe I should have borrowed the code at http://is.gd/l9UCzR or http://is.gd/991kuP, but I figured it was better to use code I’d used before.

The original project was a C program, but it was simple enough to convert to C++ in case I wanted to use a Qt facilities. The code was simple enough that renaming the source files did the trick. The code has three files: a header, a Linux source file, and a Windows source file. Obviously, you only build one of the source files. The header is the same in either case.

Here’s an excerpt from the header (you can find the entire project online here):

/** Open the GP3 serial port.
 *   @param port The name of the port (varies by platform, e.g., COM1, /dev/cua0, etc.)
 *   @return -1 for error
int gp3openport(const char *port);  // returns -1 for error
/** Close the GP3 port.
void gp3closeport();
/** Write a byte to the GP3.
 * This function blocks if the GP3 is not ready.
 * @param byte The byte to write
 * @return 1 for success
int gp3write(int byte);   // returns 1 for success
/** Read a byte from the GP3.
 * This function blocks until a byte is received
 * @return Character or -1 in case of error
unsigned int gp3read(void);  // returns character or -1 for error

Simple enough functions: open, read, write, and close. I was mainly concerned with the Linux side for this project, but I did bring both source files into the new project. The Linux side sounds like it should be deceptively simple, right? Open some file in /dev and read and write from it.

It isn’t quite that simple. First, you have to open a serial port in a certain way.

  if (fd<0)
      return -1;

This opens the port for reading and writing and does not make the serial port into a controlling terminal (the O_NOCTTY flag). The O_NDELAY flag prevents some systems from waiting for a carrier detect line. The problem is, it also means reads and writes will return immediately. If you call a read, for example, and there’s no data (yet) the call just returns and your program has to deal with that. If you don’t mind just spinning and waiting, that’s ok, but it makes it a lot harder to do smart things like time out.