Use Linux to Control Real-World Hardware

PJ Radcliffe

Issue #207, July 2011

With Open-USB-IO, your Linux system can drive motors and LEDs, read switches, talk to RS-232 and more. You can add your own code to the ATMEGA32 microprocessor on the board and debug it with a symbolic debugger.

Linux is wonderful for many things, such as manipulating data and working with the Web. But, pull your gaze away from your computer's screen for an instant, and you'll notice that Linux (and any other operating system) can't do much in the real world. What might you want to do in the real world using Linux? How about controlling motors, solenoids and lights? Add to this the ability to sense switches, light levels and any analog voltage. What could you do with all this? That really depends on your imagination. How about an automated beer-brewing controller or automatic blinds? How about controlling a robot arm to throw a paper dart or creating a grey-water controller? After you have proved it works under Linux control, if need be, you can move the program into a micro-controller board so your Linux box is free to do other things.

If this appeals to you, the FOSS Open-USB-IO board shown in Figure 1 may be exactly what you need. There also is a live DVD that has a detailed manual, example projects and the source code for all the software. The live DVD is the easiest way to get results quickly. It also has a huge range of Linux development tools and helpful documentation. The manual can be downloaded free from my Web site listed at the end of this article.

Figure 1. Open-USB-IO Board

Historically, hobbyists used the parallel printer port or a serial port to drive hardware. You still can find a lot of projects on the Web that use this approach. These legacy ports started disappearing from laptops long ago and are beginning to disappear from desktops. Hardware control via USB rapidly is becoming the only viable approach.

Open-USB-IO Features

Key hardware on the board includes digital and analog inputs and outputs, eight switches, eight LEDs, a light-dependent resistor, seven motor control lines that can take up to 50v and 500ma each, an RS-232 port and three Pulse Width Modulators, which are great for motor-speed control. All the connections come to plugs, which can be connected to external hardware with an old IDE cable (Figure 2).

Figure 2. Cable Connection to External Hardware

There are three key bits of software. This first is a command-line program that drives the USB interface on the PC. It doesn't need any special drivers and runs like any other command-line program. The second is the USB client program that comes programmed into the Open-USB-IO board. This interfaces with the USB and provides a whole range of useful commands to control the hardware. The third software program is also on the board: a USB bootloader that allows you to download your own code into the ATMEGA32 microprocessor using just the USB cable.

Control Where?

The Open-USB-IO hardware can be controlled from several different levels. You can use:

  • GUI (coming soon or make your own).

  • Command line.

  • Bash script on the PC.

  • C/C++ or almost any other language on the PC.

  • C or assembler code that runs on the microprocessor on the board. User code can run at the same time as the USB interface, which includes a powerful symbolic debugger.

One neat approach is to write in C code on the PC and play around until you get what you want. Next, move this C code to a shell ATMEGA32 project, and make the code run on the board, which then does not need a PC connection.

Playing with the Command Line

Open-USB-IO comes with a command-line program called ousb that can be downloaded from my Web site (see Resources). Put this in the path, typically /usr/local/bin, and ensure that it's executable. Next, plug in a USB cable to the board and you're ready to go.

To see all the commands, just type ousb. To turn on all eight lights, open a terminal window and type the command:

$ ousb io portb 255

To turn on alternate lights and then turn them off, type:

$ ousb io portb 85
$ ousb io portb 0

If you want the state of the switches, type:

$ ousb -b io pinc

The -b will make the response in binary so you can see each bit clearly.

Let's control a motor. A Pulse Width Modulator (PWM) puts out a square wave where the on period is adjustable from 0% to 100%. Type the following commands:

$ ousb pwm-freq 1 700
$ ousb pwm 1 50

This turns on PWM 1 at a frequency of about 700Hz and a duty cycle (on period) of 50%. You should see LED3 glow at half intensity. If you have a small DC motor that can run off five volts, connect it as shown in Figure 3. Connect the motor to pins 39 and 27 of plug J5 (blue wires), and connect pin 39 to 37 (red wire). The motor should start turning slowly. To make it run at full speed, 100% duty cycle, type:

$ ousb pwm 1 100

Figure 3. DC Motor Connections

What's the light level on the board? Try typing:

$ ousb adc 6

Try holding your finger over the LDR just above the LEDs, and try again.

The Open-USB-IO manual has a complete list of all the commands you can use and lots of examples.

Script Programming on the PC

Bash script programs are great for small jobs and are quick and relatively easy to develop. For our purposes, they really are just ousb command lines plus any control flow required. Below is one example from the live DVD that turns the LEDs into a light chaser and prints out the value of the switches:

#!/bin/bash
PATTERN=1
until  [ 0 != 0 ]
do
    ousb io PORTB $PATTERN          # write to the LEDs
    READ=$(ousb io PORTC)           # read the switches.
    echo " LED Output= $PATTERN, Input on PORTC= $READ."
    sleep 0.3
    let "PATTERN = PATTERN + PATTERN"
    if [ $PATTERN == 256 ]          # Check for roll over
        then PATTERN=1
    fi
done

C, C++ and Other Languages on the PC

Script programs are great, but they have a few problems. The syntax is a little arcane at times—for example, white space matters in some places. There is no real error detection. If a line has an error, it may not be detected until the line executes. If you use the set -u command, you sometimes can catch a misspelled variable.

For these and other reasons, larger applications are written in high-level languages, such as C, C++ and Python. Open-USB-IO is easy to control from these high-level languages, providing that the language has some way to invoke a command line and read the response. When programming Open-USB-IO applications on the PC, I usually program in C, because it's then a small step to moving the code into the microprocessor on the board.

Here is a simple C code fragment that runs on the PC and will read and write from the board:

//--- function to read and write Open-USB-IO.
int do_ousb_command(char* command)
{
  char   line[100];
  FILE*  fpipe;
  if ( !(fpipe = popen(command,"r")) ) {
      printf("pipe error\n");
      exit(1);
  }
  fgets(line, sizeof line, fpipe);
  pclose(fpipe);
  return(atoi(line)) ;
}

...

//--- how to use the function, a write then a read-
do_ousb_command("ousb -r io portb 0x55") ;
printf("Value of PORTB is now %i\n",
       do_ousb_command("ousb -r io portb")

As you will see later, to move this code to the ATMEGA32 microprocessor on the Open-USB-IO board, you throw away the function do_ousb_command. Each use of do_ousb_command() must be changed into a native IO port reference. In this case:

PORTB = 0x55;
portb_read_value = PORTB;

Faster and Faster

The examples above all invoke the ousb program once for each command. On most Linux boxes, this limits the speed to about 25 commands per second. This is fast enough for most applications, but sometimes more speed is required. For example, the motor drive lines can be used to drive a stepper motor, such as those found in old floppy drives. A speed of 25 commands a second means it takes some eight seconds to do a full rotation. This is where the ousb -file and -multi command options become useful and allow 200–250 commands per second. The -file option makes ousb take its commands from a file. The -multi option allows ousb to stay in memory and take its commands from a pipe connected to an application program. The live DVD has examples of how to use these options. Your stepper motor now takes only a second for a full rotation! To go even faster, you can write C code for the ATMEGA32 microprocessor.

Cutting Free

So now you have got the hardware working well from your C code on a Linux box. It's easy to move the same code into the microprocessor on the board and have the board operate in standalone mode. The manual and the example projects on the live DVD show you exactly how to do this. Basically, compile your code with the avr-gcc compiler then download it with the USB bootloader already programmed onto the board. This is called Co-USB, where your code cohabitates with the USB code on the ATMEGA32.

Now, unplug the USB cable, plug in a regulated 5-volt wall wart, and the board will happily work by itself!

Perhaps even better, the Co-USB concept allows you to add your code to the existing USB interface code and built-in debugger. The USB code is hidden away as interrupt-driven code and has little effect on the user code. With the USB link active, your application running on the Open-USB-IO board can talk to another program you write on the PC. This can be really useful—for example, for a data logger. The Open-USB-IO board can collect data in a standalone manner. You can come along with your laptop, plug in the USB cable, and then suck up all the data and post-process it.

Let's look at a simple example to show how Co-USB works. This is taken from the Co-USB folder available on the live DVD. A make file handles all the details of compiling and downloading code; type make help to see all the make options:

//====== user constants and variables.
volatile uint8_t  counter;
volatile float    fb[] = { 56789.1, 0.34};

//====== SYSTEM hooks.
void user_system_500us_interrupt()
{}
void user_command(uint8_t*  get_ctrl,
                  uint16_t* get_addr,
                  uint16_t* val)
{}

//====== Executive Loop.
void user_forever_loop()
{
    for ( ; ; ) {
        _delay_ms(200);   // delay 200ms
         PORTB ^= 0x01;   // toggle the PB0 LED.
        ++counter;        // increment once per loop.
        BREAKPOINT(3);    // breakpoint
    }
}

To compile the program, open a terminal and type make all. Download using the USB bootloader with make usbprog. The LED PB0 now should start flashing. The following commands come from a debugging session with a real Open-USB-IO board (text after the # are added comments):

$ ousb symr counter     # read the variable counter.
counter (type 'uc' at 0xc2) length 1 is:  55
$ ousb symw counter 0   # write zero to counter.
$ ousb symr -f fb 2     # read the array.
fb (type 'f' at 0x68) length 2 is:  56789.1  0.34
$ ousb bp 3             # Set breakpoint 3, code stops
$ ousb bp cont          # Continue,  stops on next loop
$ ousb bp -3            # Disable the breakpoint
$ ousb cont             # Continue, no breakpoints now

Note the user_command() function in the code above. From the PC, the command ousb user sends up to three integers to this function. You can put your own code in the function to do whatever you want. You also can use the function user_system_500us_interrupt() to execute your own code. As its name implies, it is called every 500 microseconds. If you don't need it, just leave it empty.

There are many useful options for displaying and writing different data types, such as strings and characters. All the read and write commands also can be applied to the EEPROM memory in the ATMEGA32, which keeps its value when power is removed.

Up, Up and Away

It takes very little knowledge of electronics to get a lot done with Open-USB-IO. Anyone who has done a college or secondary-school option in electronics, or who is a hobbyist, can connect up switches, motors and LEDs to do all manner of things. There are lots of electronics cookbooks and good resources on the Web to help you with ideas and circuits.

I have seen some very exciting projects done by people with little more than basic electronics skills. For example, how about controlling banks of floodlights using Open-USB-IO and optical relays? How about a complete pump controller that handles a dam, tank or grey-water, and has an RF link to a control panel? Or, consider making a white-line tracking model car. In all these projects, the Co-USB concept is very important: program the ATMEGA32 microprocessor with user code and link in the USB interface code with the built-in debugger. Develop and debug the user code on the ATMEGA32 until it works. Next, pull out the USB cable and leave the Open-USB-IO board running. If the program goes wrong, plug in the USB cable and debug what's happening.

Conclusion

Linux by itself is great, but it can be so much more if you can control hardware. Open-USB-IO has a unique combination of hardware and software features that offers a lot to casual users or experienced experts. You can control hardware from the PC command line or your own PC programs. You can program the ATMEGA32 microprocessor on the board, and even keep the USB interface code and its powerful symbolic debugger. It takes only a little knowledge to get real hardware working, and if you have more experience, you are limited only by your imagination.

Dr PJ Radcliffe (pjr@rmit.edu.au) is a senior lecturer at RMIT University in Australia. He was once an ardent Windows programmer but then discovered Linux, which he now teaches along with the control of hardware using Linux. Of all the programming he's done, writing code to control hardware is certainly the most fun.