O'Reilly Network    
 Published on O'Reilly Network (http://www.oreillynet.com/)
 See this if you're having trouble printing code examples


Parts of this article:

An introduction to NQC (Not Quite C)

Program flow, reading inputs, and variables

A sample program

Lego MindStorms: Programming with NQC

by Jonathan Knudsen
02/25/2000

In the second column in his series on Lego MindStorms robots, O'Reilly & Associates author Jonathan Knudsen introduces us to an alternative programming environment for the robots, NQC. On the first page, he introduces NQC and its properties and tells how to get it. On page two, he explains more details, including program flow, inputs, and variables. On the final page, he lists the code for a more complex program that has the robot seeking out a dark place to hide.

Last month I told you about the Lego MindStorms Robotics Invention System and why it's such an interesting product. This month, you'll get right to the fun stuff: building and programming your own robot. You'll learn how to program using Not Quite C (NQC). Created by Dave Baum, NQC is one of the alternate development environments I talked about in last month's column. NQC is a great piece of software for several reasons:

The RoboTag robot, a programming testbed

Before you start writing programs, you'll need to build a robot for testing. I suggest the RoboTag robot from The Unofficial Guide to Lego MindStorms Robots. RoboTag is a simple, sturdy, tank-style robot. It has a front bumper and a downward-pointing light sensor.

RoboTag
Figure 1. RoboTag, from The Unofficial Guide to Lego MindStorms Robots

RoboTag's building instructions are online. To get the most out of this tutorial, click here for the building instructions and then return to get into the NQC programs. If you're only interested in NQC's capabilities, keep reading.

A first look at NQC

Previous Lego Mindstorm Columns:

Mindstorms in Education

The Straight and Narrow

Tools to Save Your MindStorm Models

Lego MindStorms: Lego Glasnost

Although you could certainly program your robot using RCX code (the programming environment that comes with the Robotics Invention System), there are compelling reasons to make the jump to NQC. Variables are probably the most important thing -- NQC has them and RCX code doesn't. Using variables can make your robot a lot smarter. Later on, for example, you'll see how using a variable makes a program more robust, allowing us to determine a light sensor threshold on the fly rather than hard coding the value. To get started, you'll need to download NQC. There are versions for Mac OS, Linux, and Windows.

You can use any text editor to create NQC source files. If you're running on Windows, consider Mark Overmars' RCX Command Center (RcxCC), a smooth graphic interface that runs over NQC.

RcxCC provides a nice, syntax-colored editor. MacNQC also includes an editor. On Linux or Windows, just use the text editor of your choice. Save the following as "Hello.nqc":

task main() {
  OnFor(OUT_A + OUT_C, 200);
}
To compile this program, just use the nqc command, like this:
C:\nqc Hello.nqc
(With MacNQC, you'll use a menu item instead. With RcxCC, there's a toolbar button that compiles a program.)

If you get any errors, check your typing and try again. Otherwise, you're ready to download the program to the RCX. Make sure your RCX is turned on and use the nqc command with the -d option:

C:\>nqc -d Hello.nqc
(If you need to use a serial port different from the default, use the -S option.)

NQC Resources

The Unofficial Guide to Lego MindStorms Robots contains a chapter about NQC. It's available online.

• The NQC web site includes documentation.

• The RcxCC web site also includes a tutorial about NQC.

Go ahead and run the program by pressing the Run button on the RCX. Your robot should move forward for 2 seconds, then stop.

Controlling outputs

NQC includes a suite of commands for controlling the outputs. The three output properties that can be independently controlled are

You might, for example, want an output to be on (mode) with full power (power) going forward (direction).

In the Hello.nqc program, we took advantage of the fact that outputs are full power and forward by default. All we had to do was turn on outputs A and C to make the robot move forward.

The OnFor() command turns one or more outputs on for a certain amount of time, measured in hundredths of a second. Outputs are specified with some combination of the constants OUT_A, OUT_B, and OUT_C. Multiple outputs can be specified by adding the constants together. If you want to control more than one output, you can just add the constants together (equivalent to a boolean "OR" here). In our simple program, we used OUT_A + OUT_C to specify outputs A and C.

Let's look at the modes first. You can turn outputs on and off with the On() and Off() commands. There are two flavors of off: braking and floating. The default, Off(), puts the motors in a braking mode, so they resist turning. The other flavor, Float(), turns off power to the motors but does not resist turning.

To see how this works, let's try a simple modification of our first program. Save the following as "Coaster.nqc":

task main() {
  OnFor(OUT_A + OUT_C, 200);
  Float(OUT_A + OUT_C);
}
After this program exits, you'll notice that RoboTag coasts to a stop, rather than stopping abruptly. Also, you can freely turn the treads with your fingers.

The OnFor() command is an interesting variation that turns outputs on for a certain amount of time, then off (in brake mode).

The outputs have variable power, from 1 to 7. NQC defines some handy constants, like OUT_LOW (1), OUT_HALF (4), and OUT_FULL (7). You can set the power of one or more outputs with the SetPower() command.

The direction of the outputs is either forward or reverse. You can control this with the Fwd() and Rev() commands. Two combination commands, OnFwd() and OnRev(), allow you to specify a mode and a direction at the same time.

By default, the outputs have full power in the forward direction. Instead of relying on the defaults, we could rewrite the first program as follows:

task main() {
  SetPower(OUT_A + OUT_C, OUT_FULL);
  Fwd(OUT_A + OUT_C);
  OnFor(OUT_A + OUT_C, 200);
}

On the next page, I'll go into more details of program flow, reading inputs, and variables.

Next PageNext

Lego MindStorms: Programming with NQC
by Jonathan Knudsen |

Program flow and waiting

If you've programmed in C, you'll be very comfortable with NQC. You get the standard for and while loops. NQC also supports if and if else. The syntax is just like C. If you haven't programmed with C, it's not hard to learn.

NQC supports two commands that allow you to delay execution of a program. The Wait() command is a simple delay, where you specify the time in hundredths of a second. If you want to wait for a certain condition to become true, use until() instead. This is actually a kind of loop, with this form:

until([condition])
  [statements]
The condition is evaluated; as long as it is false, the statements are executed repeatedly. The until() loop follows C syntax, such that multiple statements can be enclosed in brackets. In the next section, we'll write a program with a simple until() loop.

Reading inputs

You can make your robot dance now, but to program interesting behavior you'll need to respond to RoboTag's sensors. Here's another program that allows RoboTag to avoid obstacles. Save the following program as "Bounce.nqc":

task main() {
  SetSensor(SENSOR_1, SENSOR_TOUCH);
  while (true) {
    // Move forward.
    OnFwd(OUT_A + OUT_C);
    // Wait for a bump.
    until (SENSOR_1 == 1)
      ;
    // Spin left.
    Rev(OUT_A);
    Fwd(OUT_C);
    Wait(50);
  }
}

There are several new things in this program. Let's look at the sensor-reading stuff first. The RCX needs to know what kind of sensors are attached to its inputs in order to interpret their signals correctly. In this program, we use SetSensor() to tell the RCX that a touch sensor is attached to input 1:

  SetSensor(SENSOR_1, SENSOR_TOUCH);
NQC includes constants that represent each input: SENSOR_1, SENSOR_2, and SENSOR_3. There are also constants representing all the basic sensor types, like SENSOR_LIGHT, SENSOR_ROTATION, SENSOR_CELSIUS for temperature sensors, and others. If you build your own sensors, there are more flexible commands for configuring the inputs, but I won't get into them here.

Once you configure an input, you can read its value by using SENSOR_1, SENSOR_2, and SENSOR_3. In this program, we configured input 1 to have a touch sensor. It will have a value of either 0 or 1.

The algorithm in this program is simple: move forward until the bumper is hit. Then spin left for half a second. The whole thing is enclosed in an infinite while() loop.

Variables

NQC supports 31 integer variables. To use a variable, just declare it as in C. Here's a simple example:

int x;

task main() {
  x = 14;
  x++;
}

Multitasking

It gets even better. The default RCX firmware supports multitasking programs, i.e. programs that do more than one thing at a time. Up until now, all of the sample programs have had one task, main. Every program must have a main; it's the task that is executed when you press the Run button on the RCX. However, you're free to start other tasks from main:

task main() {
  start secondTask;
  start thirdTask;
}

task secondTask() {
}

task thirdTask() {
}

You can stop tasks, too, using stop. Programs can have up to ten tasks. To communicate information between tasks, you'll have to use variables. NQC supports several flavors of subroutines too, but I'm not going to cover them here.

To close, I'll detail a program that instructs RoboTag to roam around avoiding obstacles, until it finds a nice dark place to hide.

Next PageNext

Lego MindStorms: Programming with NQC
by Jonathan Knudsen |

Going out with a bang

As a final example, let's develop a dark seeking program for RoboTag. We'll make the robot roam around on the floor, avoiding obstacles. When it gets to some place dark, like under a piece of furniture, it will stop.

The light sensor returns a value from 0 (dark) to 100 (light). In theory, the whole range of values can be returned. In practice, you'll probably never see readings outside the 30-70 range. We could hardcode a threshold into our program so the robot knows when it's in a dark place. But this is not very reliable; the same program might not work at different times of day. Instead, we'll take a baseline reading of the light sensor when the program starts. We'll stop the robot when the light sensor value falls significantly below this baseline.

The obstacle avoidance is similar to what you saw before, with a twist. This time, the robot picks a random direction to turn when it bumps into something. It turns for a random amount of time, too, from 0.5 s to 2.5 s. The key to this is the Random() command, as you'll see.

The program is split across three different tasks for clarity. The main task initializes the inputs and starts up the other tasks. The avoid task takes care of obstacle avoidance, responding to pushes on the bumper. The dark task shuts down the robot's motors, and the program, when the robot moves into a dark place.

Here's the whole program:

int baseline;

task main() {
  // Initialize.
  SetSensor(SENSOR_1, SENSOR_TOUCH);
  SetSensor(SENSOR_2, SENSOR_LIGHT);
  baseline = SENSOR_2;
  
  // Start tasks.
  start avoid;
  start dark;
}

task avoid() {
  On(OUT_A + OUT_C);
  while (true) {
    if (SENSOR_1 == 1) {
      // Back away.
      Rev(OUT_A + OUT_C);
      Wait(50);
      // Turn a random direction.
      if (Random(1) == 0) {
        Fwd(OUT_A);
        Rev(OUT_C);
      }
      else {
        Rev(OUT_A);
        Fwd(OUT_C);
      }
      // Turn for a random duration.
      Wait(50 + Random(200));
      // Go forward again.
      Fwd(OUT_A + OUT_C);
    }
  }
}

task dark() {
  until (SENSOR_2 <= baseline - 5)
    ;
  Off(OUT_A + OUT_C);
  stop avoid;
}

New directions

Now that you've got a feel for NQC, there are all sorts of interesting things you can try. NQC lets you do things like send and receive data with the infrared port (to communicate with other robots or PCs), play sounds, control the display, use timers, and store data in a datalog. Here are three suggestions:

I hope you've enjoyed this quick tour through NQC. It's a great way to get involved with robot programming.

Building a robot of your own? Creating interesting programs for it? Tell us what you're up to in our Mindstorms forums.


Jonathan Knudsen is an author at O'Reilly & Associates. His books include The Unofficial Guide to Lego MindStorms Robots, Java 2D Graphics, and Java Cryptography.

Copyright © 2009 O'Reilly Media, Inc.