Skip to main content

Controlling Lego Mindstorms with Java (Lejos)

Posted by jfalkner on February 2, 2008 at 9:02 PM PST

I just had the chance to play around with Bryan E. Smith's Lego Mindstorms robotics kit. We installed Lejos and I was coding programs directly from Netbeans. It was a lot of fun and surprisingly easy to use Java to control the robot. I've just added a Lego Minstorms kit to my wishlist, but I'm not sure that I'd spend $250 to buy a new kit versus another Nokia n810.

We started with a standard NXT brick, a lego mindstorms kit assembled in to the standard sumo bot, and my laptop running Ubuntu. The robot was already running a program where it'd guard an area and push any intruders away. Originally the program was coded using Lego's GUI; however, it was clear that it would be much easier if we could hack around in Java syntax. The choice was made to see if we could install Lejos and crank out some Java code to replicate the robot's original program. We were successful and the whole process took about an hour. Here is how we did it.

  1. Make sure you have the following (Ant and JDK)
  2. Compile Lejos (you'd think that they'd do this?)
  3. Install Lejos to the NXT brick
  4. Setup Netbeans to code
  5. Hack out a Java program for the robot

Make sure that you have the following

The Lejos project is going to need Ant to compile itself, which means that you'll also need an install of Java. I'm not entirely clear if you need the JDK or not, but I used JDK 1.5. When compiling Lejos code you do not use the standard Java compiler. Instead you use Lejos's nxj tool -- it might use javac behind the scenes.

Compiling Lejos

Compiling the code is theoretically easy. Simply go to the ./build directory and run 'sudo ant'. It'll auto compile everything and you'll be ready to go. Of course, if you are like me, you will have glossed over the Lejos install instructions and have some GCC errors appear. Here are the two that I had. I've copied and pasted a large chunk of the error message so that Googlers will easily find this blog.

The Problem: My Java Native Interface (JNI) header files weren't in the compilers include path.

[...]
jlibnxt:
       [cc] 1 total files to be compiled.
       [cc] In file included from /home/jfalkner/Desktop/mindstorms/lejos_nxj/src/libnxt/main_jlibnxt.c:29:
       [cc] /home/jfalkner/Desktop/mindstorms/lejos_nxj/src/libnxt/jlibnxt.h:2:17: error: jni.h: No such file or directory
[...on and on with some jni.h related compile errors...]

Solution: Inlude jni.h and jni_md.h in the C compiler's path. This can be done a few ways. I chose possibly the least elegant way: hard coding in the path to the Ant 'cc' action. That is where the C compiler is invoked. You can find out where to look for the element by reading the Ant error message

BUILD FAILED
/home/jfalkner/Desktop/mindstorms/lejos_nxj/build/build.xml:65: The following error occurred while executing this line:
/home/jfalkner/Desktop/mindstorms/lejos_nxj/src/libnxt/build.xml:144: gcc failed with return code 1

The above lines are stating that the first build.xml invoked another build.xml in the ./src/libnxt directory. On line 144 is where the action threw the error. That is where I hard coded the following (well actually at line 154).

<includepath>
  <pathelement location="."/>
  <!-- next 2 line added by jayson -->
  <pathelement location="/opt/jdk1.5.0_11/include"/>
  <pathelement location="/opt/jdk1.5.0_11/include/linux"/>
</includepath>

Recompile the code. It might work, or if you have my luck, you'll get another missing header exception. This time because the bluetooth.h header file is missing.

[...]
jbluez:
       [cc] 1 total files to be compiled.
       [cc] /home/jfalkner/Desktop/mindstorms/lejos_nxj/src/jbluez/jbluez.c:8:33: error: bluetooth/bluetooth.h: No such file or directory
       [cc] /home/jfalkner/Desktop/mindstorms/lejos_nxj/src/jbluez/jbluez.c:9:27: error: bluetooth/hci.h: No such file or directory
       [cc] /home/jfalkner/Desktop/mindstorms/lejos_nxj/src/jbluez/jbluez.c:10:31: error: bluetooth/hci_lib.h: No such file or directory
       [cc] /home/jfalkner/Desktop/mindstorms/lejos_nxj/src/jbluez/jbluez.c:11:30: error: bluetooth/rfcomm.h: No such file or directory
[...compile errors go on and on...]

This time the fix is slightly different. My computer doesn't actually have bluetooth, which is why Ubuntu didn't install the libraries and header files that are missing in the error message. Tell Ubuntu to add these libraries by launching Synaptic Package Manager and searching for 'libbluetooth-dev'. After installing, you can recompile without having to edit Lejos's build.xml scripts. Note, if you don't have 'libusb-dev' installed, grab that too!

Back to installing Lejos....

At about this point in time I realized that if I hadn't previously been a C/C++ coder I might have been lost. I expected the build script to simply work, but it ended up taking a good 15 minutes to fix it. The above errors weren't at all related to Java, and my friend was amazed that I knew how to fix them. Honestly, it was mostly my fault that they occurred because I didn't really read the Legos install guide. Hopefully you won't hit similar snags, but if you do, at least you'll know how to fix them.

Install Lejos to the NXT Brick

plugin-nxt.jpg

After compiling Lejos you need to flash the NXT brick. We weren't sure if this would replace the normal Lego Mindstorms software on the NXT brick. It does, which seems obvious after the fact. Don't expect to keep any programs that you have on the brick. In order to flash the brick you must plug in the USB cable and run sudo ./nxjflash from the ./bin directory. It'll flash the NXT brick. Make sure that you run 'sudo' or else you won't be able to write to the USB device. You might get a failure message stating that the brick needs to be in "RESET" mode. We did. After reading up on the subject, this means that you have to hit the NXT brick's reset button. It is possibly the most well hidden button ever made. You'll find it on the back side of the brick in one of the connector holes. See the picture.

NXT brick reset button

If you need to, press the reset button with a paper clip and wait for the brick to start beeping. That is when you can flash the NXT brick using 'sudo ./nxjflash'. The brick will only beep for about 30 seconds. If it stops before you can flash, press the reset button again.

That is it. You are now set. Try running the Tune.java program from the included ./samples/Tune folder. You can do this by typeing './nxjc Tune.java' followed by 'sudo ./nxj -r Tune'. Your ears will thank you.

Setup Netbeans for Coding

I'm a big fan Netbeans.org and I use it to code most every project I work on. You can certainly use any other program, including any text editor, to code a Java program and compile it with the nxjc command. On my computer I already had Netbeans installed and I realized that it'd be easy to map the Lejos source code to a new project. This made it trivial to code because Netbeans then provided the standard syntax highlighting, error checking, dot completion, and whatnot.

Make a new project in Netbeans for your Java code that will use the Lejos NXTj API. I picked a normal Java application as the project type. Go in to the properties of the project and get rid of the existing source-code directory mappings. Add in a mapping for each of the four sub-directories in the './src/java/classes' folder of your Lejos download. Also map the directory that you'd like to keep your Java code in. I made up a new directory in the './samples' folder of the Lejos download and named it "MotorTest". From here on out you can create and code directly from Netbeans. If you find yourself missing any libraries, map the .jar files from the './lib' directory in the Lejos download.

Hack out some Java code to control the robot

Remember that Bryan had brought over his preassembled sumo bot. Our goal was to leave the robot alone and attempt to reprogram it in Java to do what it was previously doing. The benefit to this was that originally it took forever to try and drag and drop program using Lego's GUI. Coding in Java would surely be much quicker and let us actually have the robot do what we wanted. Given that the robot was assembled and that we could compile and run Java syntax code on it, the time had come to replicate the sumo bot's behavior.

Telling the robot what to do is relatively straight forward. The NXT brick isn't that complex. It has three motor ports (A, B, and C) and four sensor ports (S1, S2, S3, and S4). You can easily access any of these parts by using static helper members in either the Motor or Sensor class of the Lejos API. Browse the Javadocs and you'll find that you should be able to quickly hack away. For example, to make the wheel connected to Motor A turn the following code is used.

Motor.A.forward();
Motor.A.setSpeed(200);

Simple, no? Remeber that the NXT brick isn't any smarter than your code is. If you have set a wheel to spin forwards and cranked up the speed, next time you use the wheel it'll go in the same direction at the same speed. You should always explicitly initialize the motors and sensors to the setting that you expect them to have.

Moving on to hacking out the sumo bot's logic. Here is the final code. Read the in-line comments -- I bet you can guess exactly what is going on.

import lejos.nxt.*;

public class MotorTest {
   
    public static void main(String [] args) {
        final int pushTime = 3000;
        final int guardDistance = 75;
       
        // wait three seconds
        for (int i=0;i<6;i++) {
            Sound.beep();
            // wait for a little while
            try { Thread.sleep(500); } catch (InterruptedException e) {}
        }

        // ultra sensor
        UltrasonicSensor uss = new UltrasonicSensor(SensorPort.S2);
        // get the LCD
        LCD lcd = new LCD();
        // register a listener
        while (Button.readButtons() == 0) {
            // rotate in opposite ways
            Motor.C.forward();
            Motor.B.backward();
            // set speeds
            Motor.B.setSpeed(200);
            Motor.C.setSpeed(200);
            // ping
            uss.ping();
            try { Thread.sleep(20); } catch (InterruptedException e) {}
            // check for an object
            int dist = uss.getDistance();
            lcd.clear();
            lcd.drawInt(dist, 0, 0);
            if (dist <= guardDistance) {
                // attack!
                Motor.B.backward();
                Motor.C.backward();
                // set speeds
                Motor.B.setSpeed(800);
                Motor.C.setSpeed(800);
                // wait for a little while
                try { Thread.sleep(pushTime); } catch (InterruptedException e) {}
                // attack!
                Motor.B.forward();
                Motor.C.forward();
                // wait for a little while
                try { Thread.sleep(pushTime); } catch (InterruptedException e) {}
            }
            // wait for a little while
            try { Thread.sleep(100); } catch (InterruptedException e) {}
        }
        // stop the motor
        Motor.C.stop();
        Motor.B.stop();

    }
}

I've been programming in Java for quite a long time, and the above code is some of the most fun I've had while programming. I couldn't help but admire the simplicity of the Lejos API. Nor could I resist imagining how awesome it'd be to watch $250 worth of plastic legos, motor, and sensors twirl around and guard my office. The little bot did not disappoint! Check out the video.

P.S. Does anyone have the optional "Deathray" accessory? I can't find it on Lego's website....

Related Topics >>

Comments

hi there i am doing parallel sound bot with java so i need some help to compare stored sound and read sound, can anyone help me online ? I do use skype,msn , gmail soplease let me know if someone know lejos ...

oops sorry thats me being stupid its this error Exception in thread "main" java.lang.UnsatisfiedLinkError: lejos.nxt.Sound.playTone(II)V at lejos.nxt.Sound.playTone(Native Method)

hi i tried what i thought wa everything in your blog, very interesting by the way and i got this error any ideas: Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang at java.lang.ClassLoader.preDefineClass(ClassLoader.java:479) at java.lang.ClassLoader.defineClass(ClassLoader.java:614) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124) at java.net.URLClassLoader.defineClass(URLClassLoader.java:260) at java.net.URLClassLoader.access$000(URLClassLoader.java:56) at java.net.URLClassLoader$1.run(URLClassLoader.java:195) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319) at lejos.nxt.I2CSensor.(I2CSensor.java:17) at lejos.nxt.RCXLink.(RCXLink.java:61) at RCXRemote.main(RCXRemote.java:17) Java Result: 1

Ha, the video is on-line. I can see from the traffic reports for this blog entry that it isn't as popular as the blargs. Hmm....I hope someone enjoys it!

how did you get netbeans to send the compiled file to the nxt??

o duh thats not compiling obvoiusly cause netbeans doesnt hav the sounds the brick will, ignore me lol