Building Better Device Interfaces

I recently encountered an interesting example how not to implement a control interface for an external device. The devices in question here are two laser products from the same company that do essentially the same thing. The problem is that the software for each of the controller units seems to have been developed in sealed rooms with little or no cross-communication between them. I’m not going to name any names, mainly because other than the command set silliness these are good products, but I’ve suffered enough at the hands of some unknown yahoo (or group of yahoos) that I felt compelled to write up a quick “lessons learned”. I will refer to these two products as controller A and controller B.

The basic product consists of a controller unit and a laser head connected via a thick multi-conductor cable. Both of the controller units will accept either GPIB or RS-232 for command and data transfers. One of the units, controller A, provides a consistent command-response format, where even commands that don’t return any data at least return “OK”. The other unit, controller B, also has commands which don’t return any data, but instead of “OK” these commands return nothing but a carriage-return linefeed pair (or <CR><LF>, if you prefer). To make things even more interesting, many of the no-response commands used by controller B are identical to commands used by controller A for the same functions.

Controller A and controller B also use different commands for the same functions, such as setting the operating mode of the laser head. And, finally, controller B provides some very useful commands, such as returning the cumulative amount of time the laser has been active, but controller A does not. There’s no way to query controller A to get the cumulative operating time of the laser. The sad part is that the laser connected to controller A is one I really need to monitor closely for usage time (the lasers are life-limited units).

Another interesting feature is how each controller interprets commands. One controller is perfectly happy with either a carriage return <CR> or a linefeed <LF> at the end of a command string, but the other doesn’t like a linefeed and insists on seeing a carriage return.

So here we have two laser controllers made and sold by the same company, that effectively do the same thing with slightly different lasers, and they don’t have command set commonality. As a result what should have been a neat, clean interface module ended up being a large collection of special cases, and it consumed far more time in tweaking and debugging than it should have. This is just wrong.

Unfortunately this kind of silliness seems to more the norm than the exception. Defining a rational command set for an instrument or device seems to be a real challenge for some folks.

Here’s another example: There is an AC power controller that I’ve used on several different projects because it’s relatively inexpensive, robust, it fits in a 1U space in a 19″ rack and it provides eight channels of AC power control. Unfortunately someone again made the assumption that only a human would want to enter commands and get responses through a terminal window connected to the unit’s remote control interface. While this might be the case when the unit is in its intended environment (as a power controller for servers in a data center), it make things very clumsy when attempting to use the unit under complete computer control with no direct human interaction. There are unnecessry white-space characters, and the command set isn’t orthogonal. Again, the interface code requires special cases and the ability to strip out junk characters from the response strings.

I could go on, but I won’t bore you with numerous other examples of interface silliness. Instead, I’d like to present the lessons I’ve learned from working with these types of devices over the years:

Do Not Assume A Human Will Always Send Commands

If a device provides a means for remote control then it should at least have the ability to support a non-human mode of interaction. The whole point of providing a control interface is to allow a remote system to control the device.

It’s Either Binary or it Isn’t, Don’t Mix Modes

If an interface is intended for use by a human operator, even on an occasional basis, then one should never, ever use binary values such as 0xff or 0x03 in either the command or response strings (I’ve actually seen this done). The only time I would consider it acceptable to use binary codes is when the interface is entirely binary and a human being will never need to interact with it directly. Another way to look at it is this: If the interface utilizes a serial port, then stick to alphanumeric ASCII characters; if it uses a USB, Ethernet or custom interface, then the use of binary might be justified (but someone will need to provide a low-level interface module or driver to deal with it).

Avoid Extraneous White-space Characters

Do not put a <CR><LF> pair at the start of a command response. It is not necessary if a <CR><LF> follows the responses. It just makes more work for someone else down the road when they have to strip it off and flush input buffers to keep things sane.

Limit Command String Length

Set a maximum command string length and stick to it. If possible all command strings should be a same length. If the interface is not intended to be used by a human (or at least not on a regular basis) then commands should be as short as possible, even if it does make the string cryptic.

Use A Consistent Syntax

Formally define a command-response syntax and insure that the products adhere to it. Do not use a syntax that requires that commands have different prefix characters (i.e. ‘*’ for some commands and ‘:’ for others). Avoid the use of non-alphanumeric ASCII values such as punctuation characters if at all possible. It is a Good Idea to do an EBNF description or Wirth style syntax graph of the command-response syntax. Future software developers will thank you for it.

Always Employ the Command-Response Model

Every command should cause the external device to generate a response, even if the response is nothing more than “OK”. Adhering to this rule allows the control software to use straightforward send-receive-match/return actions. Don’t send just a <CR><LF> in response to a command. Sending nothing back is also bad form, as it provides no indication that the command was received and processed (or that the device is even connected and powered on).

Maintain Consistency Between Products

Related products should have a high degree of compatibility in their command sets. This allows one interface object to handle multiple devices and reduces (or even eliminates) the need for special cases in the software to handle variations between device models. This tends to make customers, and their developers, happy, and that’s a good thing.

Test the Interface Using Software

When implementing a command interface the developer should test it by writing programs to actually control the instrument or device in all possible modalities and also generate and capture all possible error returns. If the test code needs to strip out white-space characters, flush I/O buffers or employ regular expressions in order to communicate with the external device, then the interface needs to be reworked.

As a side note, I was once confronted with a custom-built controller where the EE had done all of his “testing” using an FPGA simulator, some toggle switches and a logic analyzer. I was assured that it would respond to its command set as documented. It didn’t, and he got to revise and re-flash the FPGA parts all over again.

Summary

The Keep It Simple, Stupid (KISS) principle applies here. Simple is good, and helps make life easier for everyone involved. Just remember that unnecessary complexity and inconsistencies are usually a sign of poor design decisions.

Advertisements

0 Responses to “Building Better Device Interfaces”



  1. Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




Follow Crankycode on WordPress.com

Little Buddy

An awesome little friend

Jordi the Sheltie passed away in 2008 at the ripe old age of 14. He was the most awesome dog I've ever known.


%d bloggers like this: