A Not Quite C Workshop for Robofest
(With a Little Not eXactly C)

The programming language we will use this morning is Not Quite
C. NQC was invented by Dave Baum for his students at the
University of Utrecht. Our practice sessions will be on the
playing field for Robofest 2007. If you attended Dr. Chung's
session earlier this year, you will notice that we are going to
attempt many of the same missions in NQC that you tried
previously in RCX code.

Why NQC? Of course, one important reason is that NQC is a
powerful language that can help your team do well in Robofest
2007. NQC allows the most fine grained control of your robot
when using the RCX Mindstorms firmware. Later we will touch on
NXC for the Lego NXT robot. To fully use
the newer platform, you are going to need something like NXC.
A second very important reason extends beyond Robofest.
Programs written in text based languages are easy to document.

In a world where a long term job commitment might be 1 year,
and teamwork is much more common than solo work, becoming
literate programmers is very important. We need to be able to
communicate clearly what our programs do and how they do it.
Otherwise they will be not much help to our team and useless,
as soon as we move to our next job. Using #define's and //
comments in NQC make programs easier to read. Learn to use
them!

In related pages, reached via the Handouts link, I have recommended several books. If you
have time for just one book, it should be the Definitive
Guide to LEGO Mindstorms, second edition, by Dave Baum.

This page probably has more missions than we will get to today.
I hope the rest will give you ideas to try along the road to
Robofest.

For coaches that may have used C before, you may be interested
in some comments on NQC compared to C near
the bottom of this page.

Today I am assuming that you have either Windows or OS X on
your laptops. If Windows, you will be using the Bricx Command
Center. If OS X, you can use MacNQC. In the Bricx Command Center,
as your tower tries to connect to your robot, you probably
want to select USB1; and you definitely want to select RCX2
(to be able to use all of the power of NQC)!

If your team uses Windows, OS X and Linux, you might want to
look at Emacs with Lego robots toward
the bottom of this page.

Once connected, use the Tools | Diagnostics menu to see if your
robot and batteries are alive. (In OS X the menu choices are
RCX | Information.) If your battery level is less than 7500,
or 7.5 volts, it is time to change your batteries. If your
battery level is Ok, use the same menu to open the piano
keyboard and tap out a little victory tune.

Mission 2: Go forward for 2 seconds and come back.

For Mission 2 we will concentrate on the RCX outputs.
As you know from RCX code or Robolab code, outputs are usually
connected to motors. The outputs are named OUT_A, OUT_B and
OUT_C in NQC. Note: NQC is case sensitive -- not like
Basic! Our robots will have the left wheel driven by a
motor wired to OUT_A and the right wheel driven by a motor
wired to OUT_C. To make our programs easy to read, and revise
if we change the motor wiring later, we will use NQC #define
directives to give nicknames to OUT_A and OUT_C. Motors have 3
separate attributes

Direction (OUT_FWD or OUT_REV or OUT_TOGGLE),

Power (0 : OUT_LOW, 1, 2, 3 : OUT_HALF, 4, 5, 6, 7 : OUT_FULL), and

Mode or state (On : OUT_ON, Off with brake : OUT_OFF,
Off without brake : OUT_FLOAT).

Time in NQC is in 1/100th's of a second. (Except in the
confusing case of the built in timers, which like in RCX code,
are in 1/10th's of a second. Using the function FastTimer()
instead of Timer() most of the time is a way to avoid
forgetting which is which.)

A NQC program is divided into tasks and functions. These
divisions are marked like chapters and sections in a book:
tasks begin with the word "task" followed by the name of the
task and functions begin with the word "void" followed by the
name of the function. Each task or function has at least 1
block, like a paragraph of text. Instead of beginning with
indentation like a paragraph, a block begins with a "{" and
ends with a "}". Each statement in a block ends with a ";"
instead of a "." like a sentence does.

Let's try this program. As you look at the program
notice the way capitalization is used in NQC. Also notice
the way the Bricx editor window has different colors for
different parts of the NQC language. These things will
help you get the spelling right. Explore using the handy
Function Template.

// mission2.nqc
// your-team-name
// This mission is to go forward for 2 seconds and then come back.
// The program assumes a robot with output A connected to a motor
// driving the left wheel and output C, the right wheel.
#define LEFT OUT_A
#define RIGHT OUT_C
// every NQC program has at least 1 task, that task is called
task main()
{ // this is the start of task main
SetDirection(LEFT+RIGHT,OUT_FWD);
SetPower(LEFT+RIGHT,OUT_FULL);
SetOutput(LEFT+RIGHT,OUT_ON);
Wait(200); // a 2 second wait
SetDirection(LEFT+RIGHT,OUT_REV);
SetOutput(LEFT+RIGHT,OUT_ON);
Wait(200); // a 2 second wait
SetOutput(LEFT+RIGHT,OUT_OFF);
} // this is the end of task main

Download and run your program. How far did the robot go?
Try changing OUT_OFF to OUT_FLOAT. Is there any difference?

Of course NQC is a descendant of C, so there are lots of
shortcuts. Remember your program will begin running with the
direction set to forward and the power to full.
Try these variations.

// mission3.nqc
// your-team-name
// This mission is to go forward for 1.5 seconds, turn left 180 degrees,
// and stop.
// The program assumes a robot with output A connected to a motor
// driving the left wheel and output C, the right wheel.
#define LEFT OUT_A
#define RIGHT OUT_C
// Sometimes it is nice to have all your run-time adjustable constants up at
// the top of your program to make them easy to find in the confusion
// of game day.
#define TURN_TIME 200
task main()
{
OnFwd(LEFT+RIGHT);
Wait(150);
Off(LEFT); // begin a turn to the left
Wait(TURN_TIME);
Off(RIGHT);
}

Adjust your turn until it is about 180 degrees. Try using

Rev(LEFT);
Wait(TURN_TIME);
Off(LEFT+RIGHT);

Do you need to readjust TURN_TIME?

Mission 4: Wait to start. Using the touch sensor.

In order to complete this mission we will need to know how
to count and compare numbers in NQC, so our robot can decide
when to start. We also need to learn to use the touch sensor.
We will also learn a different way to wait, using "until"
instead of "Wait". until looks like this

until (some-condition-is-true) {
do-something;
}
// or
until (some-condition-is-true) {
// do-nothing;
}
// the latter is usually abbreviated:
until (some-condition-is-true) ;

NQC has two similar looking operators, "=" and "==". The
single equal sign is called the assignment operator. It
assigns or gives the value on its right to the variable on
its left. The double equal sign compares 2 numbers for equality.
== yields the answer "true" (1) if the numbers are the same and
"false" (0) otherwise. The other comparison operators like
>, <, >=, <= are not so confusing and do what you
would expect.

This mission is also important because we will try to solve
a more complex problem by breaking it into smaller pieces.

// mission4.nqc
// your-team-name
// This mission is to wait until the touch sensor is pressed and released
// 5 times and then to go forward for 3 seconds, turn left 90 degrees, and
// stop.
// This program assumes a touch sensor is wired to input 1
#define LEFT OUT_A
#define RIGHT OUT_C
#define TOUCH SENSOR_1
#define TURN_TIME 100
#define START_COUNT 5
// Variables
int count;
task main()
{
// Inputs have to be initialized before the robot can use them..
SetSensor(TOUCH,SENSOR_TOUCH);
wait_touch_start(); // Task waits here until wait_touch_start is done.
OnFwd(LEFT+RIGHT);
Wait(300);
Off(RIGHT); // begin a turn to the right
Wait(TURN_TIME);
Off(LEFT);
}
void wait_touch_start() // A subroutine is like a My Block in RCX code.
{
count = 0; // Always remember to initialize your variables.
SetUserDisplay(count,0); // A trick so you can watch the count on the LED
until (count == START_COUNT) {
until (TOUCH == 1) ;
count = count + 1;
// If this does not work, why doesn't it?
// Would adding a line like this help?
// until (TOUCH == 0) ; // Keep the RCX from counting too fast.
}
}

Mission 5: Stop at the edge of the table.

// mission5.nqc
// your-team-name
// This mission is to go forward until you detect the edge of
// the table and then stop.
// This program assumes a light sensor is pointed down and wired to input 2.
#define LEFT OUT_A
#define RIGHT OUT_C
#define LIGHT SENSOR_2
// constants that may change with game conditions
#define OFF_TABLE 40
// Variables
task main()
{
// Inputs have to be initialized before the robot can use them..
SetSensor(LIGHT,SENSOR_LIGHT);
OnFwd(RIGHT+LEFT);
until(LIGHT <= OFF_TABLE) ;
Off(RIGHT+LEFT);
}

Mission 6: Stop on the second black line on the playing field.

For this mission, we will detect the lines by using a light
sensor pointed downwards.

// mission6.nqc
// your-team-name
// This mission is to wait until the touch sensor is pressed and released
// 2 times and then to go to the second black line and stop.
// This program assumes a touch sensor is wired to input 1 and
// a light sensor is pointed down and wired to input 2.
#define LEFT OUT_A
#define RIGHT OUT_C
#define TOUCH SENSOR_1
#define LIGHT SENSOR_2
// constants that may change with game conditions
#define TOO_DARK 44
#define TOO_BRIGHT 48
#define TURN_TIME 100
#define START_COUNT 2
// Variables
int count;
task main()
{
// Inputs have to be initialized before the robot can use them..
SetSensor(TOUCH,SENSOR_TOUCH);
SetSensor(LIGHT,SENSOR_LIGHT);
wait_touch_start(); // Task waits here until wait_touch_start is done.
go_to_line(count); // Then waits here until the second line is found.
}
void wait_touch_start()
{
count = 0; // Always remember to initialize your variables.
SetUserDisplay(count,0); // A trick so you can watch the count on the LED
until (count == START_COUNT) {
until (TOUCH == 1) ; // Wait for press.
count = count + 1;
until (TOUCH == 0) ; // Wait for release.
}
}
void go_to_line(int & count) // The & here saves a temporary variable.
// An NQC program only has 35-45 variables.
// In some programs (not this one) conservation
// is important.
// Really, since count is a global variable
// There could be no arguments at all.
{
// Go forward...
// Your code goes here.
until (count == 0) {
until (LIGHT < TOO_DARK) ; // Just keep going
until (LIGHT > TOO_BRIGHT) ; // Continue until off the line. Why???
count--; // shorthand for count = count - 1;
}
Off(LEFT+RIGHT); // then stop.
}

Mission 7: Following a line while watching for the foil.

For this mission, something new, doing two things at once or
"multitasking". Before our robot took 1 step at a time. This
time we will start as usual. When we get to the line and are
ready to follow it, task main will ask 2 helper tasks to work
at the same time. task follow_line will follow the line and
task find_foil will keep watch for the shiny patch of foil
where the straight line meets the loop.

find_foil will have a way that it can say to the other tasks,
"Hey, I found the foil!" (In programming this is
called raising a flag or signaling with a semaphore.) For
times when things are going wrong, adding a little sound
can help the programmer figure out what is going on.

Try taking your robot with the line following program
and see how it works

starting on the black line is Ok,

starting just to the left of the black line is Ok,

starting just to the right of the black line does not
work. The robot gets lost because it only knows
how to turn right looking to get back on the line.
If there is enough space to turn the robot will turn
180 degrees and start following the line the wrong way.

Mission 8: Signaling another robot.

Mission 8 is simply Mission 7 with wait_touch_start() changed
to wait_message_start(). Here we will try out IR communication
from robot to robot. You might want to make a wait_light_start()
subroutine for practice.

You could get a start message from a remote control. You could
also team up with another robot. The other robot would run a
message sending program. The sending program will use a new
function: SendMessage() and a new control statement: repeat
(number-of-times) { }. Your robots have to use the same
START_MESSAGE for the message.

// sender.nqc
// by your-names
// A program to send a start message
#define START_MESSAGE 3
task main()
{
repeat (5) { // Send message several times to be sure
SendMessage(START_MESSAGE); // it is received in a room crowded with
Wait(15); // other robots.
}
}

Mission 9: Finding a ball with a light sensor.

This mission will probably be a take home mission.

Our challenge is to detect a tennis ball wrapped with foil
using a second light sensor. We need to learn another concept:
responding to events.

Remember the clear colored area at the end of the light sensor
detects both infra-red light like that which comes from the IR
port and the visible red light from the red colored area at the
end of the light sensor. Here we make use of the fact that the
infra-red light is really much brighter than the visible red
light even though a human cannot see it. It does not make much
difference what number we use as a message to send from the IR
port. Baum's suggestion of using 85 as the message is as good
as any. The threshold of 52 for using the light sensor as a
proximity sensor as suggested in Baum's book is likely to work
for our robots as well.

Now write a program that heads toward a ball and stops when
the following event is detected: The light sensor reading is at
or above the threshold.

// mission9.nqc
// by your-names
// A program to travel toward a ball and stop when the proximity
// sensor detects the bottle.
// Similar to a program in Chapter 13 of Baum's book.
#define LEFT OUT_A
#define RIGHT OUT_C
#define PS SENSOR_3
#define PS_EVENT 1
#define PS_THRESHOLD 52
#define PS_MESSAGE 85
#define FOREVER 0
task main()
{
SetSensor(PS,SENSOR_LIGHT);
// Set our event to "monitor" or watch for, to be when the light sensor,
// used as a proximity sensor, reads above the threshold.
SetEvent(PS_EVENT,PS,EVENT_TYPE_HIGH);
SetUpperLimit(PS_EVENT,PS_THRESHOLD);
OnFwd(LEFT+RIGHT); // go forward
SetTxPower(TX_POWER_HI); // use high power for IR port
monitor (EVENT_MASK(PS_EVENT)) { // Start watching for a
until (FOREVER) { // proximity-sensor-high event.
SendMessage(PS_MESSAGE);
}
} catch { // When a proximity-sensor-high event is
PlaySound(SOUND_DOUBLE_BEEP); // noticed or caught, stop sending messages
Off(LEFT+RIGHT); // forever and play a tone and stop the
} // motors.
}

Not eXactly C is similar to NQC, but for the
Lego NXT robot. You might want to look at this example from
the NXC site. It is a line following program somewhat similar
to what we tried in NQC.

NQC has a few C++ features that will help new
programmers write more readable code. // style comments
are easy to use and make everything to the end of the
line a comment. You can declare variables near where
you use them. The compiler is not picky about the order
of declaring functions, so you can put task main()
right up top.

NQC has the loop control (inspired by the Logo language)

repeat (n) { // Repeats whatever n times.
whatever;
}

It is much easier to master than for() {whatever;}

Although the (perhaps from Perl)

until (condition-is-true) {
do-whatever;
}
// or more commonly
until (condition-is-true) /* do nothing */ ;

is just a macro for while (! condition-is-true) {whatever;}
it is quite easy to understand. In C you would generally
use for loops when you know or can calculate how many times you
want to do something, and use while loops while waiting for
an unpredictable event. In NQC use repeat for the enumerable
and use until for the unpredictable.

C programmers may use long descriptive variable names
for global variables, but short, one letter names for
local variables. Most NQC variables are global and
should be descriptive enough to make the program readable.

Because of peculiar restrictions on the types of function
arguments, most beginning NQC programmers will find
global variables a lot easier to understand.

NQC's void functions are mostly in-lined and so do not
save code space by consolidating repeated blocks of code.
However, they are still a very important way to make
programs more readable.

C programmers will often use lots of variables to make
more compact code. For NQC, consider two things

You have way more code space than variable space.

NQC does not produce H8 machine code, but bytecode
that is interpreted by the RCX firmware. For example,
you may consider

But On() is one of many interpreted functions that
will only accept a constant specified at compile time.

Using Emacs with Lego robots. Emacs
works well as a programmer's editor and development environment
for Lego robots. It is available for any operating system that
you might want to use with these robots. The following
is a fragment from my .emacs file that you might find helpful
to automate downloading to the RCX. This is in e-Lisp, the Emacs
dialect of Lisp, but that is a talk for another day.