Easy Portable Serial Ports


Modern operating systems insulate us — as programmers, especially — from so much work. Depending on how far back you go, programmers had to manage their own fonts, their own allocation space on mass storage, or even their own memory allotments. Every year, though, it seems like things get easier and easier. So why is it so annoying to open a simple serial port? It isn’t hard, of course, but on every operating system it seems to be painful — probably in an attempt to be flexible. And it is even worse if you want portability. I needed to write some C code that read data from an FPGA’s embedded logic analyzer, and I was annoyed at having to write yet more serial port code. I have my own shim library, but it isn’t well tested and isn’t all that flexible — it does what I need, but I wanted something better. What I wound up with the serial library from Sigrok. You know Sigrok? The logic analyzer software.

 You might counter that the serial port is old hat, so no one wants to support it with modern systems. While the physical serial port might be on life support, there’s no shortage of equipment that connects via USB that appears to be a serial port. So while I was talking to an FTDI chip on an FPGA board, you could just as well be talking to an Arduino or a USB voltmeter or anything.

I guess the Sigrok developers had the same problem I did and they took the time to write a nice API and port it to major platforms. Although Sigrok uses it, they maintain it as a separate project and it was just what I needed. Sort of. I say sort of because the version installed with Ubuntu was old and I needed some features on the newest release, but — as usual — the Internet came to the rescue. A quick Git command, and four lines of build instructions and we were ready to go.

Getting Started

Since I use Linux, the build instructions on the page worked fine. The install goes into /usr/local so I removed the libserialport-dev package just to be sure I didn’t get the wrong one by mistake.

The library provides a header file, libserialport.h. That file defines an sp_port type that is opaque. Since you don’t know the size of it, you can only create a pointer to it. You can get the pointer filled in by calling sp_get_port_by_name. You can also have the library give you a list of ports available. Once you have the port you can open it. There are simple calls for setting most things, but you usually only need to call sp_set_baudrate.

There’s also simple calls for doing blocking and non-blocking reads and writes and figuring out if there are characters waiting to be read.

My Example

I wound up writing about 50 lines of code to ping the analyzer with two commands and pull the data out to the terminal. You can find it below. It was just quick and dirty — I didn’t try to optimize the reads or anything.

I doubt you’ll implement my code since you don’t have the logic analyzer. I will share that eventually in another post, though. But if you do want to have a go, you could easily code an Arduino to pick up two bytes on the serial port and then dump out 4,096 bytes.

Of course, this wasn’t my final code, but it shows how easy it was to write some portable code to work with the serial port.


// WordPress loves to eat angle brackets, so if the 3 includes below are blank
// they are: stdio.h stdlib.h and libserialport.h
#include <stdio.h>
#include <stdlib.h>
#include <libserialport.h>

#define BAUD 9600
// Commands to LA
#define USERCMD_RESET 0
#define USERCMD_RUN 1


int main(int argc, char *argv[])
{
  struct sp_port *port;
  int err;
  int i,cmd;
  int count=4096;
// We need a serial port name
  if (argc<2)
    {
    fprintf(stderr,"Usage la portn");
    exit(1);
    }
// Open serial port
  err=sp_get_port_by_name(argv[1],&port);
  if (err==SP_OK)
  err=sp_open(port,SP_MODE_READ_WRITE);
  if (err!=SP_OK)
    {
    fprintf(stderr,"Can't open port %sn",argv[1]);
    exit(2);
    }
// set Baud rate
  sp_set_baudrate(port,BAUD);

// write reset
  cmd=USERCMD_RESET;
  sp_blocking_write(port,&cmd,1,100);
// write run
  cmd=USERCMD_RUN;
  sp_blocking_write(port,&cmd,1,100);
// read data 
  for (i=0;i<count;i++) 
    {
    int waiting;
    int c=0;
    do 
      {
      waiting=sp_input_waiting(port);
      } while (waiting<=0);
// sort of inefficient -- could read a bunch of bytes at once
// could even block for all of them at once
    sp_nonblocking_read(port,(void *)&c,1);
    if (i%16==0) putchar('n');
    printf("%02X ",c);
    }
  putchar('n');
  sp_close(port);
  return 0;
}

Portability and Support

The library works on Linux, Mac, FreeBSD, Windows and Android. No Commodore 64 or VAX support, but we can let that slide. It is well documented, too. You might wonder what the big deal is. Well, let’s take a little detour. Keep in mind that you may have a favorite library that hides a lot of this from you and that’s great. Maybe it is even cross-platform, at which point that’s great, too. But I’m talking using the OS’s native API.

Linux borrows heavily from Unix so it thinks serial ports might just be modems or teletypes. That means there are dozens and dozens of obscure options for serial ports, mostly in the wacky termios structures. Everything is a file, so opening the port isn’t that stressful. You do have to decide if you want to set strange bits like O_NOCTTY and O_NDELAY. Oh, and you probably want to tell the port not to hang if no data is available by setting the FNDELAY bit which is via another API call. Plus set the baud rate.

I won’t bore you with all the exact code, but if you really want an idea of what’s involved, here’s a pretty good reference. Just remember the benign code to open the port is just the tip of the iceberg. Keep reading at least until Chapter 3.

Windows isn’t much better. The actual API interface is similar — but not the same — as the Linux code. You create a file almost in the usual way. But then you get a device control block (DCB) and fill it all in to configure the port. Want timeouts? That’s another call. There’s a whole different API for non-blocking I/O although you have a few choices there. There’s also EscapeCommFunction to do other odd things.

None of this is that hard and, in fact, Windows is a bit easier only because it is less flexible. But it is very easy to just use one portable open source library.

Going Forward

Next time you want to talk serial with a PC using any language that can call a library, you ought to consider this one. Even if you don’t need the portability, it is a pleasant enough API and you don’t have to switch gears later if you do port or if you just start a different project.

If you don’t like it, there are other choices. A quick search turned up a C++ library that supports Windows, Linux, and Mac. Some popular libraries like Boost for C++ have serial port handlers, too. Here’s a simple C-language library that is pretty minimal. There are doubtless others and I’d imagine the comments will turn up a few gems, as well.

Photo Credit: Fun with VMWare by [Bill Bradford] CC-By-2.0.



Source link

Leave a Reply

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