MathCS - Robotics

Programming the NXT using Eclipse


This page has been translated into spanish, thanks to Maria Ramos

Ground Rules of Java Programming

Java is a computer programming language, i.e. a language that is used to provide instructions to a computer. As with every language, you must learn its grammar and vocabulary to use it effectively. There are hundreds of programming languages - Perl, C++, Basic, Java, Scheme, Ruby, NXT-G, and many more - each with its own advantages and disadvantages. We will focus on the programming language Java, developed by SUN Microsystems and widely used. The Java language ground rules are as follows:

  1. A Java program is written by a human (the programmer) as a plain text file called the source code. It is checked for typos and other errors by the Java compiler, which translates the source code into machine code if it finds no errors. The machine code can then be executed by a computer one line after the other in sequence. The computer will do no more and no less than what your sequence of instructions specify.
  2. A Java program is written as a sequence of statements, one per line, in accordance with the the grammar and syntax rules of the Java programming language.
  3. Java is case-sensitive, i.e. the word “LCD” and “Lcd” are considered to be different.
  4. A valid Java statement must end with a semi-colon ; unless it starts a group.
  5. Java names can not contain any spaces or strange symbols and cannot start with numbers.
  6. Java uses three sets of parenthesis/brackets:
    • curly brackets “{ … }” to group statements together
    • regular parenthesis “(…)” to denote inputs to functions and for math expressions
    • square brackets “[ … ]” to denote what’s called arrays
  7. Java uses two types of comments you can provide "plain English" explanations inside a program. Comments can be written in any language and are ignored by the computer
    • Single-line comments start with //
    • Multi-line comments are enclosed in /* … */
  8. The computer is hopelessly detail-oriented: when you create a Java program, everything must be exactly right – forget a semicolon, miss-spell a word ever so slightly, use an extra space in a word, or forget the right kind of closing brackets and your program will not be understood.
  9. Just because your program is spelled correctly (grammar and vocabulary) does not mean it will also work correctly – all programs indeed require extensive testing.
  10. Every (almost) Java program has a unique program name and looks like  this as a minimum(most specific programs, of course, contain additional lines of code):
public class ProgramName
{
	// This is a one-line comment	
	public static void main(String[] args) throws Exception 
	{
         /*
            This is a multi-line comment
         */	
   }
}

To help us generate programs, compile them, and download them to our NXT brick for execution we will be using a widely known and handy program called Eclipse (known in the trade as an IDE – Integrated Developing Environment). It should already be installed on your laptop and configured for our purposes; if not, see the setup section.

table of content


Creating a Program with Eclipse

Here is a step-by-step guide to create a Java program for your NXT brick:

  1. Start “Eclipse NXT” by double-clicking the icon on your desktop.
  2. Accept the default choice of workspace (the folder containing all your programs).
  3. In the left “package explorer” window, highlight the NXT project and expand it
  4. Expand the “default package” to see a list of programs already created, if any
  5. Right-click on the NXT project and select “New | Class” (remember that in Java language a class is the same as program)
  6. In the dialog that opens, give your class a unique name, i.e. a name different from any other program already there (remember to use only letters and numbers, no spaces or special characters). Note that by convention class names should start with a capital letter. In this example, name your new class (program) Test2
  7. Leave the remaining options in place and click “Finish”

You should now see an empty program, or class, similar to the following (if your opening curly bracket is not on a line by itself, just edit your document accordingly):

public class Test2 
{
}

This program, or class, is in this case called “Test2” and does – absolutely nothing at all. We need to add additional code that our robot can execute, but to ensure that our robot knows where to start working through its program, the first portion of code is always (almost) the same.

Now type very carefully and exactly as listed the following lines in the right place, just as shown:

public class Test2 
{
	public static void main(String args[]) throws Exception
	{		
	}
}

If you make any mistake, like using the wrong kind of brackets, misspelling a word, changing the capitalization of a word, adding or forgetting anything, your program will not work and Eclipse will underline your error in red and put a an exclamation mark on the left side of your source code, where the error occurs. You can right-click on the underlined error, just as in Word, but this will usually not be very helpful: fixing your error is often more complicated than picking the right word (but see below for more info). For now, carefully check your typing to fix any problems (red underlines) in your program.

Now our program should be complete. It contains one function called “main”, but it still does – nothing. To make the robot perform some action, you need to add code to the function called “main”. That code is the heart of your program, the rest is merely a framework that remains the same for most programs. For now, just add two lines exactly as follows:

public class Test2 
{
	public static void main(String args[]) throws Exception
	{
		LCD.drawString("Yo, dude", 1, 1);
		Thread.sleep(3000);
	}
}

Automatically Fixing Mistakes (Auto-Correct)

When you typed the above program, you will likely see at least one error, indicated in multiple ways, because the word LCD is not a standard Java vocabulary and is hence unknown to Eclipse.

Simple error

You will see that:

That light bulb on the left is the most useful error indicator. It shows that Eclipse has an idea how your error could be fixed, possibly. Scroll to the line containing the error (if necessary), then left-click once on the error light bulb.

error with suggested fixes

A window will pop up, showing you multiple suggestions on how this error could be fixed automatically. The suggestions depend on your error, and not all suggestions are good ones, usually - sometimes none will work - but often the first or second might work. In our case the first suggestion is "Import 'LCD' (lejos.nxt)". It means that Eclipse can import the LCD statement from a known Java package (lejos.nxt), after which Eclipse will understand the term LCD and its related commands. This is similar to loading a new dictionary into Word, after which previously unknown words are known.

Thus, if you pick that first choice by double-clicking on it, Eclipse will insert the following line into your source code near the top:

import lejos.nxt.LCD;

Now Eclipse will understand all LCD-related commands, the error indicators are all off, and your code should be error-free now. Sometimes Eclipse will only turn off the error indicates when you save your work, so hit CTRL-S to save your work now, just to be on the save side (you should get into the habit of saving your work every few minutes)!

Compiling and Executing Our First Java Program

We have just created our first Java program- but actually it is just a typed-up document, much like a foreign-language paper written in Word. As we mentioned, the technical term for this file is the source code. We need to (1) translate it into machine code and then (2) transfer it to our “brick” before we it can finally execute.

1. Compiling the Source Code

This process translates the source code from the Java language to machine language (strings of 0’s and 1’s). For this to work at all there can be no errors flagged in your source code, so make sure all flagged errors have been fixed! With your cursor anywhere in the source code file, click the drop-down arrow in the “toolbox” icon in the button bar:

Compile and Download

Pick on “leJOS Compile” item to translate the source code. You might be reminded to save your file (do so), but you should not see any problems or errors; in fact you should see very little or even nothing: no news is good news.

2. Transferring the Program to the NXT

Once the program compiled, you need to transfer it to your brick:

  1. Connect your brick to your computer via the USB cable
  2. Turn on the brick or verify it is turned on. You should hear a soft “ding” from your computer when it recognizes the brick.
  3. With your cursor anywhere in the source code file, click again the drop-down arrow in the “toolbox” icon in the button bar (see above)
  4. CClick on the “leJOS Download” item

You should see (and hear) confirmation that your program was downloaded properly. If you get an error, it is usually due to one of two facts:

3. Executing the Program

On your brick, maneuver to “Files” and press enter (the orange button). The program you just downloaded should be one of the choices, named Test2. Select it and press enter. Then select “Execute” – your program finally runs.

Every time you change your source code you need to move through these steps again. Try it now! What does our simple program do?

table of content


Programming fixed NXT Components

Any robot built with the NXT brick includes many named components such as LCD, Sound, etc. Some have fixed properties; others must be initialized with various parameters. Programming the fixed components is easy: use them by name and call on their built-in functions using the syntax Component.function(optional input) (in proper Java lingo such functions are called static methods). Here is an overview.

The LCD screen

To use the LCD screen, you can use commands such as:

Example:

public class LCDTest 
{
	public static void main(String args[]) throws Exception
	{
		LCD.clear();
		LCD.drawString("Yo, dude", 1, 1);
		Thread.sleep(3000);
	}
}

You might need to "auto-correct" any mistakes with the LCD statement. For more info about the LCD class, click here.

Delay (timed Pause)

Many times you need a delay, or a timed break, in your program. For example, you might want to display some text for 2 seconds, then some additional text. Or you might want to engage the motor(s) for 3 seconds, then stop them. To do that you use the command

Example:

public class DelayTest 
{
	public static void main(String args[]) throws Exception
	{
		LCD.clear();
		LCD.drawString("Yo", 1, 1);
		Thread.sleep(1000);
		LCD.drawString("Dude", 2, 1);
		Thread.sleep(5000);
	}
}

Remember that 1000 milliseconds are 1 second.

The Buttons

The brick contains four buttons: LEFT, RIGHT, ENTER and ESCAPE. . Use the appropriate button name together with the command isPressed() or waitForPressAndRelease(), as in:

Example:

public class ButtonTest 
{
	public static void main(String args[]) throws Exception
	{
		LCD.clear();
		LCD.drawString("Yo", 1, 1);
		Button.ENTER.waitForPressAndRelease()
	}
}

You might need to "auto-correct" any mistakes with the Button statements. For more info about the Button class, click here.

Sound

You can program the robot to play some music, using the Sound component:

where freq is the frequency in hertz and duration is the length of the note in milliseconds.

Example:

public class SoundTest 
{
	public static void main(String args[]) throws Exception
	{
		Sound.playTone(360, 1000);
		Thread.sleep(1100);
		Sound.playTone(620, 1000);
		Thread.sleep(1100);
		Sound.playTone(440, 1000);
		Thread.sleep(1100);		
	}
}

You might need to "auto-correct" any mistakes with the Sound statements. For more info about the Sound class, click here.

The Motors

There are three motors, each of which can be connected to port A, B, or C. They can be referred to as Motor.A, Motor.B, or Motor.C, respectively, depending on the port they are connected to. To control each motor use any of the following commands, as in: Motor.B.rotate(360):

forward() causes the motor to rotate forward indefinitely or until told to stop
backward() causes the motor to rotate backwards indefinitely or until told to stop
stop() stops a motor immediately
flt() lets the motor coast to a stop
setSpeed(speed) speed is in degrees per second, up to 900 is possible
resetTachoCount() resets the motor’s tachometer to zero
rotate(angle) rotates motor by angle degrees
rotate(angle, true) rotates motor by angle degrees but returns right away
rotateTo(angle) rotates motor to specified degree
isMoving() is the motor still moving?
isRotating() is the motor still rotating?

Example: We assume that you have a single motor hooked up to the port labeled A. In that case:

public class MotorTest 
{
	public static void main(String args[]) throws Exception
	{
		Motor.A.setSpeed(900);
		Motor.A.rotate(720);
		Motor.A.setSpeed(200);
		Motor.A.rotate(-720);
	}
}

You might need to "auto-correct" any mistakes with the Motor statements. For more info about the Motor class, click here.

Exercises

  1. Write a program to display “Hello” on the brick’s LCD screen.
  2. Write a program to display “Hello” on the LCD while playing the first few notes of Beethoven’s 5th (E-E-E-C). You will need to know the frequencies of "C" and "E" - you can Google them.
  3. Write a program to display “Hello”, play some Beethoven, and engage a motor to "drive" forward some distance.
  4. Write a program to drive a differential drive robot forward for exactly 2 seconds.
  5. Write a program to drive a differential drive robot forward for exactly 1 meter.
  6. In a differential drive robot, what happens when you:
    • rotate both motors forward?
    • rotate one motor forward, the other backward?
    • rotate both motors forward but at different speeds?

table of content


Programming flexible Components with Parameters

Some components have fixed, or “static” properties, which are pretty straight-forward to deal with. For example, the LCD screen exists exactly once and has a fixed number of rows and columns that do not change. Other components – more flexible ones – can be created with variable parameters. For example, a TouchSensor can be plugged in to any of the 4 ports. Thus, before you can use it you need to specify which port it uses, i.e. you need to initialize it with a particular sensor port as input. You can also build new components - even entire robots - and model them with a single programming term, using specific parameters to adjust the model to a particular situation. The “new” keyword does exactly that:

new creates a new instance of an object that is initialized with particular parameters

Example:

TouchSensor touch = new TouchSensor(SensorPort.S1);

From then on you can call on the touch sensor’s function by using the newly defined reference variable touch (not the component TouchSensor). We will discuss the various sensors soon, but first let's discover how to use our Java keyword new.

The Navigator

A very useful component called a “navigator” will serve nicely to illustrate the usage of new. A navigator models an entire differential drive robot with two independently controllable motors, like the one we built previously. Differential Drive RoboIt uses the rotational sensors (or tachometers) to keep track of each motor’s state (how much each motor has rotated), and through some elaborate – but thankfully hidden - math it can figure out the robot’s current position.

But specific differential drive robots differ from one another, for example by having different wheel radii r (or diameters) and axle lengths R. These differences are important, because if both motors, for example, rotate by 360 degrees, a differential drive robot with large wheels will go further than one with small wheels (why is the axle length important?). They can also differ in other ways, such as height, weight, and color, but these differences do not impact the way it works. Thus, to capture a particular differential drive robot you need to specify, at a minimum, its wheel radius (or diameter) and its axle length.

In other words, to create a new Navigator object whose driving behavior matches a particular, real existing robot, you need to provide the following information:

Thus, to make for a new Navigator with, for example, a wheel diameter of 8cm and an axle width of 16cm and motors connected to ports A (as left motor) and C (as right motor) you’d say:

SimpleNavigator robot = new SimpleNavigator(8, 16, Motor.A, Motor.C);

Note that since your input values are in cm, all distances this navigator object will use are also going to be in cm. Some of the functions you could call on for the robot variable after it has been defined are as follows:

backward() Moves robot backward until stop() is called.
forward() Moves robot forward until stop() is called.
getAngle() Returns the angle the robot is facing relative to the +X axis direction
getX() Returns the current x coordinate
getY() Returns the current y coordinate
goTo(x, y) Rotates and moves robot to the target point (x,y)
goTo(x, y, true) as goTo but returns right away
isMoving() returns true if the robot is moving under power
rotate(angle) Rotates the robot a specific number of degrees in a direction (+ or -).
rotate(angle, true) as rotate but returns right away
setMoveSpeed(speed) Set the movement speed in distance units/seconds. For distance units of cm, a value of 45 cm/sec would be plenty fast (about 1 mile per hour)
setTurnSpeed(speed) Set the rotation speed in degree/seconds. A value of 360 degree/sec (once full turn per second) would be plenty fast 
stop() Halts the robot and calculates new x, y and angle coordinates.
travel(distance) Moves the robot a specific distance.
travel(distance, true) as travel but returns right away

Example: We assume that you have constructed a differential drive robot with the left motor hooked up to port A, the right motor to port C, a wheel diameter of 5.6cm, and an axle length of 11.4cm, similar to the one we described before (use a ruler to measure the parameters of your robot; they are likely slightly different). In that case:

public class NavigatorTest 
{
	public static void main(String args[]) throws Exception
	{
		SimpleNavigator robot = new SimpleNavigator(5.6f, 11.8f, Motor.A, Motor.C);
		robot.setMoveSpeed(30);
		robot.setTurnSpeed(90);
		robot.travel(50);
		robot.rotate(90);
		robot.travel(50);
		robot.rotate(90);
		robot.travel(50);
		robot.rotate(90);
		robot.travel(50);
		robot.rotate(90);
	}
}

A few notes are in order here. First you will likely need to "auto-correct" for the term SimpleNavigator. Second, you might see a little yellow light bulb with a yellow warning sign in the line where the SimpleNavigator is initialized. That symbol indicates a warning, not an error, and your program will compile (with another warning) even if you do not fix it. In our case, the warning is caused by an "over-simplified" initialization of a SimpleNavigator, and you can ignore it safely. And finally, you might wonder why we use the values 5.6f and 11.4f instead of simply 5.6 and 11.4.The "f" indicates a "floating-point" value, or decimal, and needs to be present when you need decimal values (more about that later). Finally, if everything works correctly, what pattern will your robot drive?

For more info about the SimpleNavigator class, click here.

table of content


Touch Sensor

The touch sensor is like a button or switch. It registers if something depresses it and reacts accordingly.  This Sensor can be plugged in to any of 4 ports. Thus, you need to initialize it with a particular sensor port as input. The “new” keyword does exactly that: new creates a new instance of an object that is initialized with particular parameters:

Example:

TouchSensor touch = new TouchSensor(SensorPort.S1);

From then on you can call on the touch sensor’s function by using the newly defined reference variable touch (not TouchSensor) as well as the command:

For more info about the TouchSensor class, click here.

table of content


Sound Sensor

The sound sensor is like a micophone (and not a very good one). It registers noise level, i.e. if there is a loud or a soft noise. This sensor can be plugged in to any of 4 ports. Thus, you need to initialize it with a particular sensor port as input. The “new” keyword does exactly that: new creates a new instance of an object that is initialized with particular parameters:

Example:

SoundSensor mic = new SoundSensor(SensorPort.S2);

From then on you can call on the sound sensor’s function by using the newly defined reference variable mic (not SoundSensor) as well as the command:

For more info about the SoundSensor class, click here.

table of content


Light Sensor

The light sensor really has two parts: a light emitting diode (the lower bulb) and a light-measuring sensor (the upper sensor). You can operate it in active or passive mode. In active mode you turn on the lower light and measure its reflection; in passive mode you turn the lower light off and measure the ambient light intensity. Note that this sensor can not sense colors, it only senses light intensities. It could therefore distinguish two colors that have different grey-scale values (intensities) but not between all colors. This sensor can be plugged in to any of 4 ports and can start with the light on or off. Thus, you need to initialize it with two input parameters: one sensor port and whether the light should initially be on (true) or off (false). The “new” keyword does exactly that: new creates a new instance of an object that is initialized with particular parameters:

Example:

LightSensor light = new LightSensor(SensorPort.S3, true);

This would create a light sensor with its light initially on. If you use false instead of true, the light would be off initially. From then on you can call on the light sensor’s function by using the newly defined reference variable light (not LightSensor) as well as the commands:

For more info about the LightSensor class, click here.

table of content


Ultrasound Sensor

The ultrasound sensor is the most advanced sensor. It sends out a pulse of ultrasound (high frequency sound waves) called a ping and measures the time delay until it hears the echo. From that time and the speed of sound in air it can compute the distance to the object (if any) that reflected the sound wave. There are two problems with this sensor: (1) it only detects objects inside its "sound cone", and (2) each pulse takes time to measure, so it can take up to 80ms before the sensor can detect anything each time it pings. This sensor can be plugged in to any of 4 ports. Thus, you need to initialize it with a sensor port as input parameters. The “new” keyword does exactly that: new creates a new instance of an object that is initialized with particular parameters:

Example:

UltrasonicSensor light = new UltrasonicSensor(SensorPort.S4);

This would create an ultrasound sensor connected to port 4. From then on you can call on the sensor’s function by using the newly defined reference variable echo (not UltrasonicSensor) as well as the commands:

For more info about the UltrasonicSensor class, click here.

table of content


Bert G. Wachsmuth
Last modified: 05/13/13