Imaginations run wild with Java Lego robots
Learn to program the ultimate geek toy — Lego Mindstorms — in Java
The Lego Mindstorms Robotics Invention System (RIS) is a kit for building Lego robots. It includes two motors, two touch sensors, one light sensor, more than 700 Lego bricks, and a robot brain called the RCX. It’s Lego’s bid for the 11-and-up age group; the company hit this market better than it might have anticipated, seducing a lot of amateur roboticists in their 20s, 30s, and beyond. Although the 00 price tag sounds steep, it’s an excellent value, considering what’s contained in the set.
RIS also includes software for programming robots, a graphical environment called RCX Code. Using RCX Code, you can create robot programs by snapping together functional blocks, just like snapping together Lego bricks. You can download and store completed programs on the RCX via an infrared link (the same way your remote and your television set talk to each other). RCX Code is great for people who have never programmed, but it’s limiting for experienced programmers.
RCX architecture
The RCX, the robotic brain, is a large brick that contains a microcontroller. You can attach three motors and three sensors simply by snapping special “wire bricks” on the RCX. An infrared port is used to communicate with your desktop computer.
Internally, the RCX contains 16KB of ROM and 32KB of RAM. The ROM contains low-level routines for the motors and sensors. You must load the RAM with firmware, which is also provided by Lego. This firmware (which I’ll call the default firmware) contains a byte-code interpreter, which can run programs downloaded from RCX Code.
There are two categories of alternate programming environments for the RCX. The first uses the default firmware on the RCX and provides alternate programming environments on the desktop PC. A good example of this is NQC, a popular environment that lets you write robot programs using C-like source files. Those files are compiled and downloaded to the RCX, but it’s still the default RCX firmware that runs the programs.
The second class of alternate programming environment uses replacement firmware and also provides a set of PC-side tools for programming. LeJOS falls into this category. LeJOS was created by Jose Solorzano, a developer and Lego robot enthusiast. LeJOS is the sophisticated cousin of TinyVM, Solorzano’s first run at a Java environment for the RCX.
LeJOS provides replacement firmware that knows how to run leJOS programs. It also provides PC-side tools for compiling Java source code and downloading leJOS programs to the RCX.
LeJOS Java implementation
Only a small subset of the Java Virtual Machine and APIs can be implemented on a small device like the RCX. LeJOS includes just a few Java classes from java.lang
, java.io
, and java.util
— many of which will be familiar to Java programmers. That’s one of the great things about leJOS; if you already know Java, you’re a few steps up on the learning curve.
LeJOS installation
You can download the entire leJOS package from the leJOS homepage, http://lejos.sourceforge.net/. The page also contains links to installation instructions for both Linux and Windows. I’ll be using the Windows version, but it will be much the same process for Linux users.
As of this writing, the current version is beta 3. It’s distributed as a .tar.gz
file for Linux and a .zip
file for Windows. All you need to do is unpack the file, and then add the bin subdirectory to your PATH. (I assume you have already installed the Java 2 SDK. If not, go get it from http://java.sun.com/j2se/.)
Getting to know the tools
At this point, you’ll probably want to build a robot that you can use for testing your leJOS programs. If you need some help, try following along with the building instructions found at http://www.oreilly.com/catalog/lmstorms/building/. I suggest building RoboTag, a basic tank-style robot with a bumper and a light sensor; it is an excellent test bed for programming.
LeJOS offers simple tools for compiling and downloading robot programs. If you’ve added leJOS’s bin directory to your PATH, then you are ready to use the leJOS tools.
The first step is to download the leJOS firmware to the RCX. You only need to do this once. The RCX keeps the firmware in battery-backed RAM, so it is there even after you turn off the RCX. If you switch batteries, however, you will probably need to reinstall the firmware. Just use leJOS’s lejosfirmdl
command. The firmware download takes a few minutes; lejosfirmdl
shows the percent complete as the download progresses.
C:>lejosfirmdl
Use --help for options.
Transferring "/Apps/lejos/bin/../bin/lejos.srec" to RCX...
100%
C:>
Now you’re ready to start coding in Java. Type in the following simple program and save it in a file called HelloRCX.java
:
import josx.platform.rcx.*;
public class HelloRCX {
public static void main(String[] args) {
Motor.A.forward();
Motor.C.forward();
try { Thread.sleep(2000); }
catch (InterruptedException ie) {}
Motor.A.stop();
Motor.C.stop();
}
}
Compiling this source code is a matter of running the regular javac
and telling it to use the leJOS classes. Fortunately you don’t have to think about it too much; just use the lejosc
utility like this:
C:>lejosc HelloRCX.java
C:>
If there are any errors, lejosc
will report them just like the regular javac
compiler, providing both the error and the line number in the source code.
If lejosc
is successful, it produces .class
files from your .java
source code. The next challenge is to get the class files down onto the RCX so you can run your program. LeJOS encapsulates this functionality in the lejos
command-line tool. You do have to specify which port to use with an environment variable, like this:
C:>set RCXTTY=COM2
C:>lejos HelloRCX
C:>
As the program is downloaded to the RCX, the RCX counts upward, starting from 1. How far it counts depends on how many classes are downloaded and the size of those classes.
This is a deceptively simple step. Behind the scenes, leJOS is compiling the class you specified into leJOS native code and downloading the native code to the leJOS firmware in the RCX. Further, it has to be smart enough to package and send any other classes that are referenced in your class. This is an important point. In this example, for instance, the Motor
class will also be downloaded because you have used it in your code.
Once the program is downloaded, you can run it by pressing the Run button on the RCX. Go ahead and give it a try. Motors A and C will go on for two seconds, and then turn off.
API overview
Once you’ve mastered leJOS’s tools, learning the APIs is straightforward. All the RCX-specific APIs are grouped together in the josx.platform.rcx
package. Because the functionality of the leJOS APIs is very specific to the RCX, you’ll see a lot of static methods and class constants. The Motor
class, for example, has three static member variables representing the RCX’s three motor outputs. Most of the remainder of this article is a tutorial on the leJOS APIs for controlling motors, configuring and reading sensors, using threads, controlling the display, and creating sound.
Controlling outputs
The RCX has three outputs that are represented by instances of josx.platform.rcx.Motor
. Motor
defines three static member variables, aptly named A
, B
, and C
, one for each output. Using a motor is simply a matter of naming one of the Motor
instances and calling one of its methods.
Outputs have a power and a mode that you can control. The mode is set by calling forward()
, backward()
, stop()
, and flt()
. Forward and backward modes set the motor running forward or backward, just as you’d expect. There are two ways to stop a motor, however. Calling stop()
puts the output in stop or brake mode. A motor shaft attached to the output will not move; further, it resists movement, which is why it’s called brake mode. The other mode is float; in this mode, the motor shaft doesn’t move, but it doesn’t resist movement either. If you drive a robot forward and then put the motors in float mode, the robot will coast to a stop rather than stop abruptly.
You can try this out in HelloRCX.java
. Just call flt()
instead of stop()
to halt the motors; watch your robot glide gracefully to a stop.
An output’s power can be set at a level of 1 to 7 using the setPower()
method. Of course, the power of an output has meaning only if the mode is forward or backward.
Reading inputs
The RCX’s versatile inputs can handle a wide variety of sensors, everything from simple passive touch sensors to powered rotation sensors. In leJOS, inputs are represented by instances of josx.platform.rcx.Sensor
. Like the Motor
class, Sensor
provides three static member variables, S1
, S2
, and S3
, that represent the three inputs.
To read a sensor properly, you must first configure the input. Each input has a type and a mode that can be set using the appropriately named setTypeAndMode()
method. You can use constants defined in josx.platform.rcx.SensorConstants
interface. For example, you can configure an input for a light sensor as follows:
Sensor.S2.setTypeAndMode(
SensorConstants.SENSOR_TYPE_LIGHT,
SensorConstants.SENSOR_MODE_PCT);
The type tells the RCX the electrical characteristics of the attached sensor, while the mode determines how the raw input signal is mathematically processed. In this case, I asked that the sensor values from the light sensor be returned as a percent.
You must explicitly configure an input if it is active (powered). Lego’s light sensor is a good example of an active sensor; when it is configured properly, it emits a red light. You can turn on the power as follows:
Sensor.S2.activate();
Finally, to read the value from the input, call readValue()
.
Listening to sensors
LeJOS supports a JavaBeans-style event-listener mechanism, which lets you respond to changes in sensor values. Simply implement the SensorListener
interface and register your listener with the sensor using addSensorListener()
. Whenever the value of the input changes, your sensor listener’s stateChanged()
method will be invoked.
Here’s a little program that beeps every time a touch sensor on input 1 changes state:
import josx.platform.rcx.*;
public class TouchBeeper {
public static void main(String[] args) {
Sensor.S1.setTypeAndMode(
SensorConstants.SENSOR_TYPE_TOUCH,
SensorConstants.SENSOR_MODE_BOOL);
Sensor.S1.addSensorListener(new SensorListener() {
public void stateChanged(Sensor source, int oldValue, int newValue) {
Sound.beep();
}
});
}
}
Threads and timing
Threads are handy in robot programming; you can create a new thread to listen for sensor events, or you can perform tricky calculations in a separate thread. Multithreading in leJOS is similar to multithreading in regular Java. It’s based on java.lang.Thread
, which has a run()
method containing the code that will be executed in a new thread. It also has a start()
method for kicking off the new thread.
LeJOS lacks the java.lang.Runnable
interface, so the only way you can create new threads is by subclassing Thread
and defining the run()
method.
LeJOS also features a timer class and a timer listener interface in the josx.util
package. The basic idea is to set up a timer and kick it off. When the timer fires, all registered listeners are notified. For more details, check the API documentation in Resources.
Using the display
The RCX includes a small display. LeJOS has several classes that you can use for showing things on the display. The capabilities are pretty impressive; basically, you can control every segment of the display. I’ll cover only a couple of the simpler methods here.
First, the josc.platform.rcx.LCD
class has a static setNumber()
method that accepts an integer and shows it on the screen. You can also use clear()
to turn all segments of the display off, although you’ll need to call refresh()
to see the effects. The documentation states that you need to call refresh()
after “certain operations,” but it’s not specific about which ones. If you are trying to change items on the display and don’t see results, try calling refresh()
; it may help.
Finally, you can display text on the display, although it’s not particularly legible and there’s only space for five characters. The following example shows how to display a text string:
TextLCD.print("Hello");
Try it out! See how many five-letter words you can think of.
Making music
The RCX is also capable of playing sounds. You can play arbitrary tones or some prepackaged sequences. Static methods in the josx.platform.rcx.Sound
class expose this functionality.
The simplest methods are the static beep()
, twoBeeps()
, buzz()
, and beepSequence()
methods, none of which accepts a parameter. If you want to play a song, you can pass the frequencies and durations of the notes to playTone()
.
Design tips
Memory really is in short supply on the RCX. After all, you’ve only got 32KB of RAM to play around in, and the leJOS firmware takes up about 17KB of that. The leJOS APIs are structured with an eye toward conserving memory. One step you can take is to examine the Min
classes to see if they’ll be of use. There are, for example, three different classes that you can use to control the RCX’s display. MinLCD
is the smallest, offering the least functionality (basically just the ability to display numbers). Next up is LCD
, which offers control of every segment in the display but takes more space. Finally, there’s TextLCD
, which lets you show words on the display, but is a much bigger class. You can see the difference in the amount of time it takes to download a program to the RCX.
Three levels of functionality are provided by MinLCD
, LCD
, and TextLCD
. A similar scheme is found in the Sound
and MinSound
classes. If you just want to play tones, use MinSound
and save some memory on the RCX. Sound
offers additional access to some of the sound effects that the RCX can produce.
Further (and this is a shocker), leJOS doesn’t actually implement garbage collection. If you create objects in a loop, or do so in response to sensor events, then you’re probably going to run out of memory sooner or later — discarded objects will never be collected as garbage.
Debugging
Debugging on a small device like the RCX is difficult. One of the problems is that the development cycle is awkward — you code and compile on one machine (your desktop computer) and test on another (the RCX). LeJOS appears to include an emulator that you can use to test your code, but I haven’t tried it.
However, there are a couple of things you can do for debugging. The most obvious is to put stuff on the display. This is probably reflexive for programmers who are used to dumping debugging information to System.out
or System.err
. With only five characters, of course, there’s not a whole lot you can show, but it is enough to be a useful tool.
Another possibility is to play sounds. I find this is useful to signal when my program makes certain decisions or when it detects specific sensor conditions.
Finally, you should understand how leJOS displays an uncaught exception. The exception is displayed as XXXX Y
, where XXXX
is the method signature numbers and Y
is the exception class index modulo 10. You can find out method signature numbers and class indexes by using the -verbose
options with lejos
. With this option, lejos
prints out a list of all the classes and signatures that will be downloaded to the RCX. With this list in hand, you can decipher the exception display to figure out what exception was thrown and which method threw it.
Conclusion
LeJOS offers an impressive array of features in a small package. At last, you can program Lego robots using Java. The compact leJOS APIs provide access to the RCX’s outputs, inputs, display, and other facilities. If you already know Java, leJOS gives you a leg up on mobile robot programming. If you don’t know Java, leJOS is probably the most enjoyable way to learn.