Você está na página 1de 183

Introduction

What is leJOS NXJ

leJOS NXJ is a Java programming environment for the LEGO MINDSTORMS NXT
. It allows you to program LEGO robots in Java.
It consists of:

Replacement firmware for the NXT that includes a Java Virtual Machine.
A library of Java classes (classes.jar) that implement the leJOS NXJ
Application Programming Interface (API).
A linker for linking user Java classes with classes.jar to form a binary file that
can be uploaded and run on the NXT.
PC tools for flashing the firmware, uploading programs, debugging, and many
other functions.
A PC API for writing PC programs that communicate with leJOS NXJ
programs using Java streams over Bluetooth or USB, or using the LEGO
Communications Protocol (LCP).
Many sample programs

As leJOS is a firmware replacement, the new leJOS NXJ firmware must be flashed
onto the NXT, and will replace the standard LEGO MINDSTORMS firmware. This
wipes out any files currently held on the LEGO firmware. The LEGO firmware can be
restored using the LEGO supplied software.
leJOS is an open source project hosted in the sourceforge repository. It was originally
created from the TinyVM project that implemented a Java VM for the LEGO
Mindstorms RCX system. The RCX version of leJOS proved very popular with the
LEGO Mindstorms Robotic Inventions Systems owners, and close to 200,000
downloads of all versions of leJOS have been done. The original author of TinyVM
and the RCX version of leJOS was Jose Solorzano.
The NXT has given the leJOS development team the opportunity to greatly expand
the capability of leJOS.

Advantages of leJOS NXJ

There are many advantages of using leJOS NXJ rather than the NXT-G or other
programming environments for the NXT. These include:

It uses the industry-standard Java language.


It supports object-oriented programming.
It is an open source project with many contributors.
It allows you a choice of professional Integrated Development Environments
including Eclipse and Netbeans.
It has plugins for both Eclipse and Netbeans.
It has cross platform support Windows, Linux and Mac OS X.
It is much faster than NXT-G.
It has full support for Bluetooth, USB, I2C and RS485 protocols.
It provides highly accurate motor control.
It support the latest Java 1.6 language features.
It has advanced navigation support.
It supports localization including Monte Carlo Localization (MCL).
It supports other probabilistic robotics algorithms such as Kalman filters.
It provides Behavior classes that support the subsumption architecture for ease
of programming of complex robot behaviors.
It supports many third party sensors.
It supports remote logging to the PC over Bluetooth or USB, optionally by
redirecting System.out and System.err.
It supports remote monitoring and tracing of your leJOS NXJ program from the
PC.
It provides floating point Math, trigonometry and other Math functions.
It supports the JME LCD user Interface including many graphics functions.
It supports multithreading.
It supports listeners and events.
It supports safe memory management with garbage collection.
It supports standard Java input/output streams over Bluetooth, USB and RS485.
It has a flash file system accessed by the standard java.io classes.
It supports data logging and remote capturing of the logs.
It has sound support including playing 8-bit WAV files.
It provides dozens of sample programs.
It supports remote execution of a large subset of the API from the PC.
The Web site has online forums to help solve any problems you might have, to
share projects ideas, and to communicate with the development team.
It has telerobotics support via standard TCP/IP sockets.
It supports NXT to NXT Bluetooth and RS485 communications.
It supports Bluetooth communication with other devices, such as GPS
receivers.
It has support for GPS devices including support for the
javax.microedition.location API.

It supports Bluetooth keyboards that use the Bluetooth Serial Port Profile
(SPP).
It supports two-way communication with RCX via third party adapters such as
the Mindsensors NRLink.
It supports servo motors and Power Function (PF) motors.
It provides compatibility with Lego Communications Protocol (LCP), so that
many tools that work with the standard LEGO firmware, also work with leJOS.
It has an easy to use menu system.
It is widely used by universities and other education establishments.
It has support for simple computer vision applications.

Getting Started
Operating Systems

leJOS NXJ is currently supported on three operating systems.


Please select which operating system you are using:

Microsoft Windows
Linux
Mac OS X

Getting Started on Microsoft Windows


This version of the tutorial is for the 0.9 release of leJOS NXJ.
Prerequisites
USB Driver

To run leJOS NXJ on Microsoft Windows you will need a suitable USB driver on
your PC. If you have installed the standard LEGO Mindstorms software, a suitable
driver will already be installed. If you do not wish to install the LEGO software on
your PC you can get a Fantom driver from The LEGO Mindstorms site.

Java Development Kit

You will also need a Java Development Kit (JDK) on your PC. Note that a Java
Runtime Environment (JRE) is not sufficient as it does not allow you to compile Java
programs. You can download the latest JDK
from http://www.oracle.com/technetwork/java/index.html. Follow the instructions for
installing it. leJOS NXJ has been tested with JDK versions 1.5 and 1.6, and will not
work with earlier versions. JDK 1.6 is recommended, and some PC sample programs
will not work with JDK 1.5.
leJOS NXJ only works with 32-bit version of the JDK and JRE, so even if you have a
64-bit system, you should select a 32-bit version of the JDK.
You will need to add the JDK bin directory to your system or user PATH, so that
commands such asjavac and java can be called from a command prompt. If you do
not know how to do this, see the "Setting up environment variables" section below.
You should set the environment variable JAVA_HOME to the folder where you
installed the JDK. If you have multiple JDKs on your PC, and do not want to change
JAVA_HOME, you can set LEJOS_NXT_JAVA_HOME instead.
Bluetooth Stack

If you want to communicate with the NXT over Bluetooth, you will need a Bluetooth
dongle or built-in support on your PC, and a Bluetooth software stack. leJOS NXJ has
been tested with the Microsoft and Widcomm stacks, but should work with others.
You can use leJOS NXJ without Bluetooth.

Downloading the software

You can download the leJOS NXJ software from The leJOS NXJ download page.
It is recommended that you use the Windows installer, but there is a ZIP file release
that you can use if you prefer to do a manual installation.

Running the GUI installer

Run leJOS_NXJ_0.9.0-Setup.exe and you will see the following screen:

Select "Yes" and you will see:

Note that Installjammer is the open source software we have used to produce the
installer.
Click "Next and you will see:

You can change the destination if you need to. Click on the "Browse" button to
change the destination.
The JDK version that we have detected is displayed on this screen
Once you have selected the destination, click "Next" and you will see:

Again you can change the destination for the leJOS projects. It defaults to
leJOSNXJProjects in the users home directory. On Windows 7 or Vista this is
C:\Users\user and on XP it is C:\Documents and Settings\user.
All the Java source in leJOS NXJ is issued as projects that can be opened in Eclipse
and Netbeans.
Once you have selected the projects destination, the installation will start. If you
already have leJOS NXJ installed in the selected destination, you will see:

Click next and the previous version will be uninstalled:

Once you have confirmed that you want the old version uninstalled, you will see:

and then:

After any previous version has been uninstalled, the installation of the new version
starts and you see:

Check that the destination directories are correct and click "Next". You will see:

When the software installation is complete, you see:

When you click "Finish" the GUI version of the NXJ firmware flash utility will start
and allow you to update the firmware on one of more NXTs.
You will see:

Click "Start Program" and you will be asked:

Click OK and you will be asked:

If you select "Yes" all current files on the NXT are erased. It is a good idea to select
this as old .nxj files may not work with the new firmware.
You will then see:

Make sure that your NXT is connected to the PC by the USB cable and witched on.
Then press "OK" and you should see some progess messages, such as:

When your NXT has been updated, you will be asked:

If you have more NXTs to update, click "Yes". When you have finished click "No"
and the flash utility will terminate and then the installer will terminate, and you are
done.
Skip to the section below on testing your installation.

Manual installation

Skip this section and go to "Testing the Installation" if you are using the GUI installer.
Unzipping the release

Unzip the release to a folder such as c:\lejosbeta9. The zip file includes a directory
called lejos_nxj, so your NXJ_HOME folder will be c:\lejosbeta9\lejos_nxj.
Setting up environment variables

You need to set:


Variable

Value

Example

The folder you


NXJ_HOME

installed leJOS NXJ C:\lejosbeta9\lejos_nxj


into
The folder where

JAVA_HOME

C:\Program Files\Java\jdk1.6.0_24
you installed the

JDK
Add the bin folders
C:\Program
PATH

for the JDK and


Files\Java\jdk1.6.0_24\bin;C:\lejosbeta9\lejos_nxj\bin;
leJOS

Note that you can set LEJOS_NXT_JAVA_HOME instead, if you do not want to
change JAVA_HOME. The bin directory for the JDK may already be on your PATH.
You can set these environment variables by going to Control Panel > System >
Advanced > Environment Variables and creating them or editing existing values. You
can set them either as user or system variables depending on whether you want leJOS
NXJ to be available to all users or just the current user.
Using a Command Window

You will need to run command-line commands to test your installation and optionally
to flash the firmware. If you plan to use IDE such as Eclipse or Netbeans, once you
have installed your IDE, you should not need to use the command window any more.
You can start a command window on Windows XP by Start > Run and typing cmd.
On Vista or Windows 7, type "Command Prompt".
Type set to list environment variables and check they are all set up correctly.
Flashing the Firmware

As leJOS NXJ is a firmware replacement, you will need to flash the firmware to your
NXT. Note that this will overwrite any existing firmware. If you have the standard
LEGO firmware or other third-party firmware on your NXT, existing files will be lost.
The 0.85 release increases the amount of flash memory used by the firmware and the
startup menu, so the first time you flash this version of the firmware, existing leJOS
NXJ files will be deleted.
Make sure your NXT is attached to the PC by its USB cable, and switch it on by
pressing the orange button.

You can either use the command line nxjflash command or the nxjflashg GUI
program.
Using the command line:
Type nxjflash to flash the leJOS NXJ firmware. If your NXT is in firmware update
mode, the firmware will be updated. You will see some messages on your command
window, and the NXT should show the leJOS splash screen and then the leJOS NXJ
menu. If your NXT has a previous version of the leJOS or LEGO firmware on it, a list
of the NXTs connected to the USB will be shown, and you will be asked to input the
number in the list of the NXT you want updated - this will be 1 if a single NXT is
connected to your PC. If your NXT has other firmware on it, or if nxjflash fails, you
must put your NXT into firmware update mode. Press the reset button (at the back of
the NXT , upper left corner) for more than 4 seconds. A straightened paper clip could
be useful for this. Your NXT will audibly tick when it is firmware update mode. Then
try nxjflash again.
Using the GUI version:
The GUI version of nxjflash is NXJFlashG. You start it by running the file
nxjflashg.bat from the leJOS NXJ bin folder. When the program window opens, click
on Start Program and follow the instructions. A more complete explanation is in the
tutorial "PC GUI Tools".

Testing your Installation

You can check that you have successfully installed leJOS NXJ on your PC and your
NXT by compiling and running your first program.
Compiling and running your first program

Java programs need to be compiled to class files before they can be run. For leJOS
NXJ, all the class files that are to be run on the NXT needed to be linked to produce a
binary file (with the extension .nxj) and this must then be uploaded to the NXT.
To run a sample program, such as the View.java sample, follow these steps:
Start a command window, and change directory to the View sample folder:
cd leJOSNXJProjects\samples\View

Compile the program with the nxjc command:


nxjc View.java

Then link, upload and run it with the nxj command:


nxj -r View

You should see the menu of the View sample on your NXT.

Getting Started on Linux


This version of the tutorial is for the 0.9 release of leJOS NXJ.
Prerequisites
Java Development Kit

You will also need a Java Development Kit (JDK) on your PC. Note that a Java
Runtime Environment (JRE) is not sufficient as it does not allow you to compile Java
programs. You can download the latest JDK
from http://www.oracle.com/technetwork/java/index.html. Follow the instructions for
installing it. leJOS NXJ works has been tested with JDK versions 1.5 and 1.6, but will
not work with earlier versions. JDK 1.6 is recommended as some PC samples do not
work with JDK 1.5. Note that leJOS has only been tested with the official Oracle
JDK. It is likely to work with the Open JDK, but not with gcj.
You will need to add the JDK bin directory to your system or user PATH, so that
commands such asjavac and java can be called from a command prompt. If you do
not know how to do this, seeSetting up environment variables below.
You should set the environment variable JAVA_HOME to the folder where you
installed the JDK. If you have multiple JDKs installed and do not want to have
JAVA_HOME, you can set LEJOS_NXT_JAVA_HOME instead.
libusb

In order to use a USB connection to your NXT brick on Linux, you will need libusb
installed on your sysyem. leJOS requires the legacy 0.1.12 release. On most Linux

distributions the 0.1.12 version of the libusb package will normally be installed, but
the libusb development package may not be.. You can get more information
from http://libusb.sourceforge.net
Package Dependencies

You will need to ensure that the packages that leJOS NXJ is dependent on are on your
system. To build the jlibnxt JNI library, which is used for USB access, you need
the Development files for libusb (libusb-devel). Note that leJOS NXJ uses libusb
(legacy release 0.1.12), not libusb1.
Accessing USB devices

If you are running leJOS NXJ from a non-root user, you will need to ensure that you
have read and write access the NXT USB device in /dev/bus/usb. If you can identify
the device in /dev/bus/usb, you can do this by:
sudo chmod a+w /dev/bus/usb/xxx/yyy
However, the yyy number will count up each time the NXT is disconnected and
reconnected.
A better solution is to use udev rules. How to do this may vary with different Linux
systems.
To use udev rules, set up a file such as /etc/udev/rules.d/70-lego.rules and populate it
with the following lines:
# Lego NXT
BUS=="usb", SYSFS{idVendor}=="03eb", GROUP="lego", MODE="0660"
BUS=="usb", SYSFS{idVendor}=="0694", GROUP="lego", MODE="0660"

This relies on the username you are using being in the lego group. You can modify the
file to your requirements. The two vendors are LEGO and Atmel (for the samba driver
used in firmware update mode). You may need to reload the rules or restart udev. On
some Linux systems, the command to reload the rules is udevadm control --reloadrules.

Bluetooth

If you want to communicate with the NXT over Bluetooth, you will need a Linux
supported Bluetooth dongle or built-in support on your PC. leJOS uses the Bluecove
libraries which are included in the distribution.
You can use leJOS NXJ without Bluetooth.

Downloading the software

You can download the leJOS NXJ software from The leJOS NXJ download page.
On Linux, leJOS is distributed as a .tar.gz file.

Installing leJOS
Unpacking the release

Unpack the release into a directory of your choice, e.g. /opt/lejos/


Setting up environment variables

You need to set:


Variable

Value

Example

The folder you installed leJOS


NXJ_HOME

/opt/lejos_nxj
NXJ into
The folder where you installed

JAVA_HOME

/usr/java/
the JDK

PATH

Add the bin folders for the JDK $PATH:$JAVA_HOME/bin:$NXJ_HOME/bin

and leJOS

Note that you can set LEJOS_NXT_JAVA_HOME if you prefer. The bin directory
for the JDK may already be on your PATH.
With most Linux distributions, you can set these environment variables for the current
user in .bash_profile or for all users in /etc/profile.
You should check that the contents of the $NXJ_HOME/bin directory to check that all
the files have execute permission, and set it if if it is not set.
Building the release

To build the release, change directory to lejos_nxj/build and type ant. If you have the
dependent packages installed the release should build without errors.
Flashing the Firmware

As leJOS NXJ is a firmware replacement, you will need to flash the firmware to your
NXT. Note that this will overwrite any existing firmware. If you have the standard
LEGO firmware or other third-party firmware on your NXT, existing files will be lost.
Note that the 0.9 release changes the amount of flash memory reserved for the
firmware and the startup menu, so when you first flash the 0.9 firmware any existing
files will be lost
Make sure your NXT is attached to the PC by its USB cable, and switch it on by
pressing the orange button.
You can either use the command line nxjflash command or the nxjflashg GUI
program.
Using the command line:
Type nxjflash to flash the leJOS NXJ firmware. You will see some messages on your
command window. If your NXT has a previous version of the leJOS or LEGO
firmware on it, a list of the NXTs connected to the USB will be shown, and you will
be asked to input the number in the list of the NXT you want updated - this will be 1 if
a single NXT is connected to your PC. If your NXT has other firmware on it, or
if nxjflash fails, you must put your NXT into firmware update mode. Press the reset

button (at the back of the NXT , upper left corner) for more than 4 seconds. A
straightened paper clip could be useful for this. Your NXT will audibly tick when it is
firmware update mode. Then try nxjflashagain. When flashing is successful, your
NXT will reboot with the 0.9 version of the menu.
Using the GUI version:
To run the GUI version type nxjflashg. When the program window opens, click on
Start Program and follow the instructions. A more complete explanation is in the PC
GUI Tools tutorial page.

Testing your Installation

You can check that you have successfully installed leJOS NXJ on your PC and your
NXT by compiling and running your first program.
Compiling and running your first program

Java programs need to be compiled to class files before they can be run. For leJOS
NXJ, all the class files that are to be run on the NXT needed to be linked to produce a
binary file (with the extension .nxj) and this must then be uploaded to the NXT.
To run a sample program, such as the View.java sample, follow these steps:
Start a shell session, and change directory to the lejos_nxj/projects/samples/View
folder:
Compile the program with the nxjc command:
nxjc View.java

Then link, upload and run it with the nxj command:


nxj -r View

You should see the menu of the View sample on your NXT.

Getting Started on OSX


This version of the tutorial is for the 0.9 release of leJOS NXJ and is compatible with
OSX10.4 or higher and both intel and ppc processors.
Prerequisites
Standard Lego Software

To run leJOS NXJ on OSX you will need the standard Lego software installed so that
you can connect to your NXT using USB.
Intel macs require the 10.5 firmware (fix) update. This is true for OSX10.4 intel users
as well. We have had several users report that this works... (the 10.5 update installer
actually checks if your OS is 10.3+). PPC users need the 1.02 driver.
Java Development Kit

You will also need a Java Development Kit (JDK). Note that a Java Runtime
Environment (JRE) is not sufficient as it does not allow you to compile Java
programs. You can download the latest JDK from http://java.sun.com/. Follow the
instructions for installing it. A 32 bit version of Java is required and for Leopard only
Java 1.5 is 32 bit. Snow Leopard Java 1.6 is both 32 and 64 bit. LEJOS scripts such
as nxjbrowse now set the -d32 flag to specify 32 bit.

Downloading and Untarring LEJOS Software

You can download the leJOS NXJ software from The leJOS NXJ download page.
Un-tar the release (you do not need to run the build script)

(Optional) Bluetooth Stack

If you want to communicate with the NXT over Bluetooth, you will need a Bluetooth
dongle or built-in support on your Mac, and a Bluetooth software stack. LEJOS is
distributed with a 3rd party stack that is configured by default -- bluecove.
Pairing the NXT with your Mac should be done by pair from the computer. Browse
for the device and enter a passcode of 1234 on the mac.

Setting up the Environment


Environmental Variables

LEJOS scripts such as nxjbrowse will set their own paths so that it isn't necessary to
set up Environmental Variables in order to use them. Setting Permissions is still
required
The build files for the samples contained in /projects/samples do look for the
NXJ_HOME.
For convenience and to use the samples' build files you need to set the following:
Variable

Value

Example

The
folder
you
NXJ_HOME

installed /Users/me/lejos_nxj
leJOS
NXJ
into

JAVA_HOME

The

/System/Library/Frameworks/JavaVM.framework/Versions/1.

folder

5.0/Home

where
you
installed
the JDK
Add the
bin
folders
PATH

for the

$JAVA_HOME/bin:$NXJ_HOME/bin;

JDK
and
leJOS
(Only
necessar
y for
using
DYLD_LIBRARY_PA the
$NXJ_HOME/bin;
TH

Eclipse
plugin)
Add the
bin
folders

for
Fantom
driver

You can set these environment variables either as user or system variables depending
on whether you want leJOS NXJ to be available to just the current user or to all users.
Set Up for Current User

Create or Edit ~/.profile and set your environment there.


If it does not exist, you can create it with TextEdit. If it exists, you can open it from a
command prompt:
$ open ~/.profile

Example:
export
JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home
export NXJ_HOME=/Users/me/lejos_nxj
export DYLD_LIBRARY_PATH=$NXJ_HOME/bin
export PATH=$PATH:$JAVA_HOME/bin:$NXJ_HOME/bin

Set Up for All Users

Same as for Current User set up except use:


/etc/profile

Example:
# System-wide .profile for sh(1)
if [ -x /usr/libexec/path_helper ]; then

eval `/usr/libexec/path_helper -s`


fi
if [ "${BASH-no}" != "no" ]; then
[ -r /etc/bashrc ] & . /etc/bashrc
fi
## setloginpath added /usr/local/bin start at Fri Nov

2 18:44:47 EDT

2007
## Do not remove the previous line
if [ `whoami` != "root" ]
then
export
JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home"
export NXJ_HOME="/Applications/lejos_nxj"
export DYLD_LIBRARY_PATH="$NXJ_HOME/bin"
PATH="$PATH:$NXJ_HOME/bin:$JAVA_HOME/bin"
export PATH
fi
## Do not remove the next line
## setloginpath added /usr/local/bin end at Fri Nov

2 18:44:47 EDT

2007

Setting Permissions

Make the script files in the leJOS bin directory executable by cd'ing to that directory
and executing the command.
chmod +x *

Configuring USB

The fantom driver is now distributed with leJOS and configured as the default. There
is no need any longer to do so in$NXJ_HOME/bin/nxj.properties
This means no additional driver download is necessary. The Standard Lego
Software is the only prerequisite.
There is no need to compile jlibnxt as OSX does not use libusb for leJOS USB
connection

(Optional) Testing with nxjbrowse

Test it by running nxjbrowse

-u

you should be able to see your NXT.

Flashing the Firmware

As leJOS NXJ is a firmware replacement, you will need to flash the firmware to your
NXT. Note that this will overwrite any existing firmware. If you have a previous
version of leJOS NXJ on your PC your existing files will be preserved, but if you
have the standard LEGO firmware or other third-party firmware on your NXT,
existing files will be lost.
Make sure your NXT is attached by its USB cable, and switch it on by pressing the
orange button.
You can either use the command line nxjflash command or the nxjflashg GUI
program.
Using the command line:
Type nxjflash to flash the leJOS NXJ firmware. If your NXT is in firmware update
mode, the firmware will be updated. You will see some messages on your command
window, and the NXT should show the leJOS splash screen and then the leJOS NXJ
menu. If your NXT has a previous version of the leJOS or LEGO firmware on it, a list
of the NXTs connected to the USB will be shown, and you will be asked to input the
number in the list of the NXT you want updated - this will be 1 if a single NXT is
connected to your PC. If your NXT has other firmware on it, or if nxjflashfails, you
must put your NXT into firmware update mode. Press the reset button (at the back of
the NXT , upper left corner) for more than 4 seconds. A straightened paper clip could
be useful for this. Your NXT will audibly tick when it is firmware update mode. Then
try nxjflash again.
Using the GUI version:
The GUI version of nxjflash is NXJFlashG. You start it by running the file nxjflashg
from the leJOS NXJ bin folder. When the program window opens, click on Start
Program and follow the instructions. A more complete explanation is in the Windows
tutorial "PC GUI Tools" section.

(Optional) Eclipse
Installation

When installing the plug-in from http://lejos.sourceforge.net/tools/eclipse/plugin/nxj/,


adding the site alone doesn't work. You need to unselect the option, group by
category. The software seems to not be categorized. Then you should see the option to
install the plug-in.
USB Connections

The eclipse plugin does not find the libjfantom.jnilib driver we use for USB
connections and USB connection will not work. In eclipse, go to the lejos panel in the
preferences window and select the "use bluetooth" options as USB is not available.
Creating a New Project

When creating a new project simply create a java project and create a class (it will be
a .java file). Right-click on the project in the project explorer and select Lejos and
Convert to Lejos project. This is only for projects that are intended to run on the NXT.
This is not for projects that run on your Mac.
If conversion fails, go to the preferences for eclipse (not the project settings), be sure
that the LEJOS_HOME text box is set to the top level lejos_nxj directory.
Make sure to omit the normal Mac OS X libraries from projects that will run on the
NXT. Standard Java libraries can not run on the custom NXT JVM.
PC API

For programs running on your Mac (PC API), you need to add the bluecove.jar file to
the classpath for the "Run configuration" for the project. To do that, go to Project
Properties, then Run/Debug settings, then edit the Launch configuration you've been
using. It will be in the "Launch Configurations" window. Then select "User Entries"
and "Add external jars...". Add bluecove.jar from lejos_nxj/3rdparty/lib

You also need to force the jvm to run in 32-bit mode. To do this, modify the project
properties by adding the -d32argument to the "VM Arguments" section of the
"Arguments" tab.
Standard Mac OS X Java libraries are used to compile PC API programs
Additional information on using Eclipse

Writing your first leJOS NXJ program


The HelloWorld program

Let us start with a simple Hello World program. We will create a HelloWorld class
in the default java package:
public class HelloWorld
{
}

leJOS requires the standard main method for the program entry point:
public class HelloWorld {
public static void main (String[] args) {
}
}

leJOS NXJ supports the standard java System.out.println method and scroll the output
on the NXT LCD screen.
public class HelloWorld {
public static void main (String[] args) {
System.out.println("Hello World");
}
}

If you run this program as it is, it will display Hello World and then immediately
return to the menu, so you will not be able to see what is displayed (unless you are
very quick).
We either need the program to sleep for a while to allow the text to be read, or to wait
for a button to be pressed. Let us wait for a button to be pressed. To do this we need to
include the leJOS NXJ Button class in the program. Button is in the lejos.nxt package.
We can either include lejos.nxt.Button or lejos.nxt.* to allow any of the standard
lejos.nxt classes to be used in the program. The Button class has a method
waitForPress() that waits for any button to be pressed. You can find out what methods
a class supports by looking at the API documentation.
The API documentation is on the leJOS web site here and included in the leJOS
download in the docs folder of the classes project.
The complete HelloWorld program is:
import lejos.nxt.*;
public class HelloWorld {
public static void main (String[] args) {
System.out.println("Hello World");
Button.waitForPress();
}
}

Read the next section to learn how to compile and run this program

Compiling and Running Programs


This section describes how to compile and run leJOS NXJ programs.
Most people will want to write Java programs that run on the NXT brick, but that is
not the only option. leJOS NXJ also supports programs that run on the PC and control
a NXT robot remotely. Such PC programs can control a NXT robot running the leJOS
NXJ menu. They can also remotely control a robot running the standard LEGO
firmware. An even more interesting option that leJOS NXJ supports is to write a

program that runs partly on the NXT and partly on a PC with the two parts
communicating with each other over Bluetooth or USB. The part of your program that
runs on the PC has more memory and processing power available to it and can do
more complex processing. It can also display interesting user interfaces such as maps
of where your robot is exploring. The part of the program that runs on the NXT can
respond quickly to sensors and can accurately control motors. If you are interested in
writing PC programs see the PC API command line tools section below.
While we are on the subject, it is also worth mentioning that leJOS NXJ programs can
also run on mobile phones or other devices that support the Java MicroEdition
Environment (JME). Such programs can communicate with the NXT over Bluetooth.
We plan to add a section to the tutorial soon that will describe how to develop
programs that use the leJOS NXJ JME API.
You can also communicate with leJOS NXJ programs from Android phones - see
the Android tutorial page.
Finally, leJOS NXJ programs can be distributed across multiple NXT bricks which
can communicate with each other over Bluetooth or via RS485 communication using
NXT cables linking port 4 of two or more NXT bricks. Oh, and NXT programs can
also communicate with external devices such as Bluetooth GPS Receivers. This is all
described in Communications tutorial page.
We shall start, however, with writing programs that run on the NXT brick. You can
either compile and run your programs using the leJOS NXJ command line tools or
you can use an Integrated Development Environment (IDE).
While command line tools are very useful, programming for leJOS NXJ is best done
using an IDE. IDEs have syntax-directed editors that immediately show you any
syntax errors in your program, rather than waiting until you compile the program and
then showing a list of errors. This, together with color coding of the source, automatic
formatting of the code, prompting for method names and signatures, expanding and
collapsing parts of your program, and many other editing features, makes creating
your program a much faster and more enjoyable experience. But the advantages of the
IDE do not end there: they also help you with creating and building projects,
debugging, generating documentation, and creating user interfaces. Java IDEs put all
the Sun Java tools and a variety of third-party tools at your fingertips. They make
supporting new tools simple, either by use of plug-ins or by integration of external
tools.

IDEs are easy to set up and use and you should use them for all your leJOS
programming even the simplest of projects. Any type of leJOS NXJ program can be
created using an IDE.
You can produce leJOS NXJ programs with any Java IDE. This tutorial currently has
sections on how to use two of the most popular Jave IDEs: Eclipse and Netbeans.
leJOS NXJ supports plugins for these two IDEs which makes writing and testing your
programs even simpler.
To learn how to create, compile and run your programs from an Integrated
Development Environment, go to one of the following sections:

Eclipse
Netbeans

This section of the tutorial will teach you how to use the command line tools. If you
decide to use the command line tools you can write your program using the editor of
your choice. Many programmer's editors will let you invoke the tools directly from
the editor.
Another option you can use to compile and run your leJOS NXJ programs is to
use ant build scripts. ant build scripts are usually used from an IDE. Netbeans does all
compiling and building of programs using ant, and it is an option in Eclipse. However,
you can also use ant from the command line. ant build scripts are provided for all the
leJOS NXJ samples. To run ant build scripts you just change directory to the directory
containing the build.xml file and type ant.
The scripts described in the following sections are Windows .cmd or .bat files or Unix
shell scripts, depending on which operating system you are using. They set up the
class path, library path and boot class path needed by leJOS NXJ and then call a Java
class that does all the work. The exception to this is nxjc which just calls javac. Note
that ant build scripts use the same underlying Java classes, but do not use the scripts.

Using the leJOS NXJ command line tools

leJOS uses the standard Sun Java compiler for compiling programs. However, it needs
to replace the standard Java library with leJOS's own version of this - classes.jar. For
this reason we provide a command called nxjc that sets the boot class path to
classes.jar. Its parameter are the same as those asjavac

leJOS NXJ programs are different from normal Java programs in that they do not
support dynamic class loading. Instead all the classes used in the program are
collected together and packaged in a binary file with a .nxj extension. This process is
referred to as linking. The linked binary is then uploaded to the NXT.
The tools for compiling, linking and uploading leJOS NXJ programs are:

nxjc
nxjlink
nxjupload
nxj

Note that you normally only need to use the nxjc and nxj commands, as nxj does the
equivalent of nxjlink followed by nxjupload.
You need to open a command window to run these commands.
nxjc compile a program

Compiles one or more java files.


Usage: nxjc <java-files>
Example:
nxjc View.java

nxjc calls javac with parameters:

-bootclasspath <path to classes.jar>


<java-files>

-bootclasspath is set because leJOS does not use the standard java.lang classes but has
its own versions in classes.jar.

nxjlink link a program

Calls the leJOS NXJ linker.

Usage: nxjlink [-v|--verbose] [-g|--debug] [-gr|--remotedebug] [-a|--all] [-dm|-disablememcompaction] [-ea|--enableassertions] [-ec|--enablechecks] [-od|-outputdebug <debug-file> ] -o <binary> main-class
Example:
nxjlink -v Tune -o Tune.nxj

Links the specified main class with any classes that it references in the current
directory and with the standard leJOS classes from classes.jar to produce a binary
NXJ program that can be uploaded and run.
The -v or --verbose flag causes a list of class names and method signatures included in
the binary to be sent to the standard output. This output is extremely useful for
debugging.
The -g or --debug flag causes a debug monitor to be included with the program. This
allows the program to be interrupted while is running (by pressing ENTER+ESCAPE)
and gives stack dumps when untrapped exceptions occur.
The -gr or --remotedebug flag is used to switch on remote debugging, which works
with the nxjconsole or nxjconsoleviewer tools.
The -od or --outputdebug flag is used to specify an output debug file, which is used by
remote debugging and by nxjdebugtool.
The -ec or --enablechecks flag is used to enable additional run time checks. These
checks are relatively expensive (and rarely generate errors) and so are off by default.
Currently the only check that this setting enables is the testing for
ArraystoreExceptions.
The -ea or --enableassertions flag is used to enable the checking of assert statements
with the program.
The -dm or --disablememcompactions flag is used to disable memory compaction.
Normally the leJOS garbage collector will attempt to move large objects in memory to
maximise the amount of contiguous free space, this option disables this feature.
The linker removes methods that are not used. Specify -a or --all to include all
methods whether they are used or not. This should never be necessary.
Use the -h or --help flag to print out the options.

nxjupload upload a program

Usage: nxjupload [-b|--bluetooth] [-u|--usb] [-d|--address address] [-n|--name name] [r|--run] <binary>
Example:
nxjupload Tune.nxj
Uploads the binary (.nxj) file. By default USB is tried first and then Bluetooth. If the -bluetooth flag is specified, only Bluetooth is tried. If --usb is specified, only USB is
tried.
When Bluetooth is used, a search for Bluetooth devices is done, unless the -address
flag is set, when a device with the given address is connected to.
The --name parameter limits the search for a NXT with the given name. If this is not
specified, nxjupload tries to connect to each NXT that it finds and will upload to the
first NXT that is successfully connects to.
If the --run parameter is specified, the program is run after it has been uploaded.

nxj link, upload and run a program

Usage: nxj [options] main-class


Example:
nxj -r Tune

The nxj command links and uploads a leJOS NXJ program. It is the equivalent of
nxjlink followed by nxjupload.
Any of the options for nxjlink and nxjupload can be specified.
The default binary name is <main-class>.nxj, e.g. Tune.nxj.

Using PC API command line tools

The tools for compiling and running leJOS PC API programs are:

nxjpcc
nxjpc

Reminder: If you are compiling or running a PC API program without using these
tools, you needpccomm.jar and bluecove.jar (Linux users also need bluecove-gpl) in
your CLASSPATH and classes.jar must be removed. Also, the java library
path needs to be set.
nxjpcc compile a PC API program for your pc

Compiles one or more PC API java files.


Usage: nxjpcc [javac-options] <java-files>
Example:
nxjpcc SensorTest.java

nxjpc run a PC API program on your pc

Usage: nxjpc [java-options] <main-class>


Calls java to run your PC API program.
Example:
nxjpc SensorTest

The leJOS NXJ Menu System


Main menu

When leJOS NXJ starts, it displays the leJOS NXJ logo for 3 seconds and then
displays the main menu:

The top line shows the battery voltage icon,the name of the NXT, indicators for active
USB and Bluetooth connections, and the Bluetooth power icon. In this example, the
power is on, but USB and Bluetooth are not connected.
It then shows a graphic menu of icons. The selected icon is raised in the center and its
name is displayed. You can use the keys to navigate the menu:
Key

Name

Use

Left,Right

Move left or right

Enter

Select the menu item

Escape

Quit a submenu and return to higher


level menu.
If on a top level menu, shut down the
NXT.

Pressing Enter when "Run Default" is displayed will run the default program if one is
set. The default program can be set from the Files menu. Note that if you set the "Auto
Run" option from the System menu, the default program will run automatically on
start up, instead of this menu.

Using Eclipse
Installing Eclipse

You can download Eclipse from Eclipse downloads. If you are only going to use
Eclipse for leJOS NXJ programs, you will only need standard Java development
capabilities - you will not need a version that supports the Java Enterprise Edition or
other languages such as C++ or Eclipse plugin development. A package such as
"Eclipse IDE for Java Developers" is sufficient for leJOS NXJ. Make sure you
download a package for your operating system. Note, however that for Windows,
even if you are on a 64-bit system, you need a 32-bit version of Eclipse, for leJOS
NXJ. The examples in this section are for Microsoft Windows, but the installation
process is similar for other operating systems. If you are using Linux, your
distribution may have its own Eclipse package, which you may prefer to use.
Unzip the package you downloaded to a folder. If you extract all files to C:\ then your
Eclipse folder will be C:\eclipse.
You can start Eclipse by running eclipse.exe from your Eclipse folder. You may want
to put a shortcut to eclipse.exe on your desktop.
When you first start Eclipse, you are asked to select a workspace:

You can leave this as the default, or you can work with multiple workspaces and
create one just for your leJOS projects.
When you click OK, the IDE will open and you will see a welcome screen. You can
close the Welcome window and start developing leJOS programs.
The tutorial takes you through running the samples and PC samples that come with
leJOS and then looks at an example project that uses an antbuild file. It then shows

you how to create your own project that uses an ant build file. If you are not interested
in this and only want to use the Eclipse plugin, skip to Installing the Eclipse plugin.

Importing the leJOS NXJ projects into Eclipse

You should first import the leJOS projects into your Eclipse workspace. The easiest
way to do this is to select File > Import... and then choose General > Existing Projects
into Workspace. Browse to the folder where you installed the leJOS projects (e.g.
C:\Users\Me\leJOSNXJProjects or C:\Documents and
Settings\Me\leJOSNXJSettings), and select them all to import.
Note that if you import the leJOS NXJ projects this way, the source does not get
moved to the workspace, but stays in leJOSNXJProjects. You may prefer to copy your
projects from leJOSNXJProjects into the Eclipse workspace and then import them by
selecting the Eclipse workspace as the source folder. That way you have always got a
clean copy of the leJOS NXJ projects and the Eclipse versions will not be uninstalled
when you install a new version of leJOS.

Using the samples project

You should then see several projects in your Package Explorer window including
samples. Try exploring the samples project. You can open up folders by clicking on
the expand/collapse toggles next to the folder name, and you can open files by doubleclicking on file names. Try expanding the "BlueStats" folder, and its "default
package" folder and opening BlueStats.java. You should see:

There is a source folder within the samples project for each sample. Each folder
contains a build.xml file which is a build file for the sample written using "ant". Ant is
a build system specially designed for Java - it comes bundled with Eclipse.
There are other ways of building, uploading and running leJOS NXJ programs, such
as using the leJOS Eclipse plugin, but the ant build files are easy to use, so we will
start with them.
Ant build files have different "targets". The default target for the sample build.xml
files is "uploadandrun". This means that if you run the build.xml file it will compile
the Java source, link it with the standard leJOS classes (classes.jar) to produce a
binary file and then upload ad run the binary file.
Turn on your NXT, make sure it is connected by USB or can be connected to via
Bluetooth. Then right-click on the build.xml and select "Run As" and "Ant Build":

You should see output like the following in the Console window:
Buildfile: C:\Users\Lawrie\leJOSProjects\samples\BlueStats\build.xml
clean:
compile:
[javac] Compiling 1 source file to
C:\Users\Lawrie\leJOSProjects\samples\BlueStats
link:
[java] Class 0: java.lang.Object
[java] Class 1: java.lang.Thread
[java] Class 2: java.lang.String
[java] Class 3: java.lang.Throwable
[java] Class 4: java.lang.Error
[java] Class 5: java.lang.OutOfMemoryError
[java] Class 6: java.lang.NoSuchMethodError
[java] Class 7: java.lang.StackOverflowError
[java] Class 8: java.lang.NullPointerException
[java] Class 9: java.lang.ClassCastException
...
[java] Class 38: lejos.nxt.Flash
[java] Method 0: Class: java.lang.Object Signature: <init>()V PC 3222
Signature id 2

[java] Method 1: Class: java.lang.Object Signature: notifyAll()V Native


id 5
[java] Method 2: Class: java.lang.Object Signature: wait()V Native id 6
[java] Method 3: Class: java.lang.Object Signature: wait(J)V Native id
7
[java] Method 4: Class: java.lang.Object Signature:
toString()Ljava/lang/String; PC 3223 Signature id 95
...
[java] Method 147: Class: lejos.nxt.Flash Signature: writePage([BI)V
Native id 75
[java] Method 148: Class: lejos.nxt.Flash Signature: <clinit>()V PC
9337 Signature id 3
[java] Master record

: 16 bytes.

[java] Class records

: 39 (390 bytes).

[java] Field records

: 80 (80 bytes).

[java] Static fields

: 53 (106 bytes).

[java] Static state

: 53 (202 bytes).

[java] Constant records : 26 (104 bytes).


[java] Constant values

: 26 (252 bytes).

[java] Method records

: 149 (1788 bytes).

[java] Exception records: 67 (536 bytes).


[java] Code

: 117 (6122 bytes).

[java] Total

: 9597 bytes.

uploadandrun:
[java] Found nxt name NOISY address 001653007848
[java] leJOS NXJ> Upload successful in 1974 milliseconds
BUILD SUCCESSFUL
Total time: 4 seconds

This verbose output from the leJOS linker tells you all the classes and methods that
have been included in your program. It is very useful for debugging your programs.
You should see the Bluetooth statistics on your NXT LCD screen. You can stop the
BlueStats program by pressing the ESCAPE button on the NXT.
You can browse through the samples and try them out. Some of the samples need to
communicate with a program on the PC - see Using the PC samples project below.
Other samples need specific sensors attached to the NXT or a robot with specific
characteristics - such as a steerable wheeled vehicle. See the comments at the start of
each of the Java files to understand the requirements of the sample.
Note that the samples project uses default package names for simplicity. This is not
good practice for Java programming so when we create our own project, we will use a
proper package name.

Note also that the samples project contains multiple programs each with a main
method. This is convenient for showing lots of examples of leJOS NXJ programming
in one project, but is not best practice. When we create our own projects, we will have
a separate Eclipse project for each leJOS NXJ program

Using the PC samples project

If you imported all the leJOS NXJ projects into your workspace, you should have a
pcsamples project. Open it up and look at one of the samples such as BTSend.
BTSend works with the BTReceive or NXTReceive samples. BTSend.java runs on
the PC and BTReceive.java (or NXTReceive.java) runs on the NXT.
There are no build files for the PC samples as they are not necessary. You can run
BTSend.java by right-clicking on it and selecting, "Run as Java application", but first
we need BTReceive running on the NXT.
Go to the samples application, select the build.xml file for BTReceive, and run it (Run
as Ant Build) to upload BTReceive to the NXT.
Now go back to the pcsamples project and run BTSend.java. BTSend should now run
and connect to BTReceive, send it 100 integers and display the values it gets back.
BTReceive will then wait for another connection.
You can use BTSend with the NXTReceive sample instead. NXTReceive is similar to
BTReceive but it lets you choose which protocol and which mode you want to use to
send data.
You can look through the other PC samples and try them out in a similar way. Some
of them are paired with NXT samples. Look at the comment at the start of the Java
files. Some of them run a subset of the leJOS NXJ API on the PC using remote
execution. AccelDemo, SensorTest and TachoCount are examples of this.
TachoCount shows you how to control which NXT brick you connect to if you have
multiple NXT bricks available.

Using the org.lejos.example project

The org.lejos.example is a good example of how to create an Eclipse project for leJOS
that uses an ant build file.

It is good practice to include the name of your organization in the project name to
avoid name clashes with other projects in the work space. However if you are the only
user of a project, this is not necessary.
The project has a src directory that holds all the source packages. The example project
has a single package: org.lejos.example. Again it is a good idea to follow the standard
Java conventions for package names, but if you are not sharing the project with
anyone, it is not necessary.
The org.lejos.example has a single class file: HelloWorld.
The project is built by the ant build file: build.xml. build.xml uses a properties file
build.properties which has properties that define which version of leJOS you are using
(nxj.home) and the name and package of your masin class.
The build.xml has ant targets for linking and uploading the leJOS binary. The default
target is uploadandrun, which compiles the source, links the binary, uploads the
binary and runs it
Try right-clicking on build.xml and selecting "Ant Build". You should see the verbose
output from the linker in the Eclipse console windows and the binary will be uploaded
to the NXT and run.

If you want to run a different ant target, right-click on build.xml and select "Ant Build
...".

Creating your own project

This section describes how to create a project that uses an ant build file. If you prefer
to use the Eclipse plugin, skip to Installing the Eclipse pluginbelow.
To create a project, select File > New > Java Project. Give the project a name, e.g.
"org.me.myproject" and press Finish to accept all the defaults.
You should now see "org.me.myproject" in the Package Explorer.
You will need to add classes.jar to your project. To do this right-click on
"org.me.myproject", select "Properties" and then "Java Build Path". Then select
Libraries and then "Add external Jar". Browse for classes.jar in your NXJ installation
and select it. As classes.jar replaces the standard Java run time library, you should
remove it by selecting "JRE System Library" and clicking "Remove". You will now
see classes.jar (and not JRE System Library) under "Referenced Libraries" in
org.me.myproject.
You should use a package name for your project. We will also call our package
"org.me.mypackage".
Select the "src" folder in org.me.myproject, right-click on it and select New > Package
and type your package name.
We now want to create main class.To do this, select the package you have just
created, right-click on it and select New > Class. Type in the class name - we will use
"HelloWorld". Select the "public static void main(String[] args)" option. You should
now have a HelloWorld.java program containing:
package org.me.mypackage;
public class HelloWorld {
/**
* @param args
*/
public static void main(String[] args) {

// TODO Auto-generated method stub


}
}

Edit the source so it reads:


package org.me.mypackage;
import lejos.nxt.*;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
Button.waitForPress();
}
}

You will now need to build your project. One way to do this is to use an ant build file
in the same way that the org.lejos.example project does. To do this, select a build.xml
file from org.lejos.example, right-click on it and select "Copy". Then go to the src
directory, right-click on it and select "Paste". Do the same for build.properties
Double-click on build.xml to edit it and change the name and description attributes.
Then change the main.class property to the name of your class, e.g. "HelloWorld" and
the "package" property to ""org.me.mypackage".
You are now ready to upload and run your first leJOS NXJ project. This is done in the
same way as for the samples: and org.lejos.example: by right-clicking on the
build.xml file and selecting "Ant Build".
Make sure your NXT is switched on.
You should the verbose linker output in the Console window, and your program
should be uploaded to the NXT and run. You should see "Hello World" on the NXT
LCD screen. Press any key on the NXT to stop the program.

Installing the Eclipse plugin

An easier way to set up a project for leJOS is to use the Eclipse plugin.
The plugin will automatically convert your projects to leJOS NXJ projects.
To install the leJOS, click on the Help menu and select "Software Updates...". You
should see the following screen:

Select "Add Site..." and you will be prompted for the site name. Type
"http://lejos.sourceforge.net/tools/eclipse/plugin/nxj/".

Click on OK and the site is added. Select the newly added site:

Click on install and Eclipse will check for dependencies and then prompt you to
install the plugin:

Click on "Finish" and the plugin will be installed. When it is finished, you will be
prompted to restart Eclipse:

Click on "Yes" and Eclipse will restart.

When Eclipse has restarted, you can read the plugin help by select clicking on the
Help menu and selecting "Help Contents and then "leJOS NXJ".

It is a good idea to read the help page to familiarize yourself with the plugin.
To Configure the plugin for your system and preferences, click on the "Window"
menu and select Preferences and then "leJOS NXJ".

Browse to where you installed leJOS NXJ and select it as NXJ_HOME. Select any
other options you require. It is a good idea to check the "Verbose" option.
When you have set your preferences, click on "Apply" and then "OK".
The plugin is now set up and ready to use.
Using the Eclipse plugin

You can upload the leJOS NXJ firmware to your NXT from the plugin, by clicking on
the "leJOS NXJ" menu item and selecting "Upload Firmware" or by clicking on the
button on the toolbar.

To create a new leJOS NXJ project using the plugin, create a Java project, by (for
example) clicking on File and selecting "New" and then "Java Project". Give your
project a name and accept the defaults for project creation.
When your project has been created, right-click on it, and select "leJOS NXJ" and
"Convert to leJOS NXJ Project".

This will mark your project as a leJOS NXJ project and replace the JRE System
Library with classes.jar from your NXJ_HOME installation.
You can now add packages and classes to your project and build it in the normal way
for Java projects (e.g. by setting the Build Automatically flag).
When you are ready to upload your program to the brick, right-click on your main
program and select "Upload Program to the NXT Brick".

You will see output on a leJOS NXJ Console and a progress screen:

The upload will use USB or Bluetooth depending on which option you set on the
leJOS NXJ Preferences page. You can change this and other preferences whenever
you want. If you selected the "Run program after upload" option, the program will
start running on your NXT when it has finished uploaing.
Setting up the leJOS GUI Tools

leJOS NXJ comes with a set of GUI tools that can be using for flashing the firmware,
exploring files on the NXT, monitoring and debugging programs on the NXT,
downloading data logs, etc. It is useful to be able to run these tools within Eclipse, and
to do this you need to set them up as external tools.
To create external tools click on the down array next to the Run External Tools Icon
on the toolbar and select "External Tools Configuration":

Then select Program and click on the New Launch Configuration button and you
should see:

Type the name of your configuration, such as NXJ Flash and click on Browse File
System and find the bin directory of where you installed leJOS NXJ and select
nxjflashg.bat.
If you have multiple installations of leJOS you can go to the Environment tab and set
NXJ_HOME to the one you wish to use.
Other GUI tools you should set up include:

nxjbrowse - an Explorer for NXJ files


nxjconsoleviewer - GUI viewer for RConsole debug output
nxjmonitor - Remote monitoring of programs running on the NXT
nxjdataviewer - GUI tool to download data logs from NXT
nxjcontrol - a GUI tool that combines the function of all of the above tool, and
adds a few more functions.

You may also want to set up command line tools such as:

nxjflash - command line firmware flash tool


nxjconsole - command line viewer for RConsole debugging output
nxjsocketproxy - proxy for communicating with PC and Internet program using
sockets

Files menu

Pressing the right button will display the Files icon:

Selecting "Files" will show a list of files using the TextMenu class.

You can move through the menu using the left and right buttons. It will scroll up and
down if there are more files than will fit on the screen and it will wrap round when
you reach the top or bottom of the list.
When you select a particular file with the Enter button, the file submenu is displayed:

For any file a "Delete File" option is available. For program files (.nxj), you can
execute the program or set it as the default program. For sound files, you can play the
sound sample.

Bluetooth menu

Pressing the right arrow from the Files menu icon, displays the Bluetooth menu icon:

Power On/Off

Pressing ENTER shows the Power On/Power Off Bluetooth submenu. This is the
version when the power is on:

The status lines at the top show if power is on or off to the Bluetooth chip, and
whether the device is visible to Bluetooth searches.
Switching the power off to the Bluetooth chip, increases battery life, but no Bluetooth
capability is available until the power is switched back on.
Press ENTER to switch the power off or back on.
Search/Pair

Pressing the right Button from the Power On/Off submenu, shows the Search/Pair
submenu.

Pressing Enter starts a Bluetooth inquiry. The display is cleared and the first line
displays Searching. This takes about 10 seconds.
When the search is finished a menu of all the Bluetooth devices found, is displayed:

All discoverable devices that support an SPP profile are displayed. This includes other
NXT, GPS devices, mobile phones etc.
Selecting one of the devices gives the following display:

Pressing Enter lets you pair the selected device with your NXT and adds it to the list
of known devices. This makes connecting to the device from leJOS programs much
easier.
When you press Enter to pair the device, a submenu is displayed for you to enter the
pin for the selected device. You may need to go to the selected device and Enter the
pin on that device as well.

To enter the 4 digit pin on the NXT, press the left and right buttons to increment or
decrement the current digit, and Press Enter to go on to the next one. When you press
Enter on the last digit, the pairing will take place. The current digit has is displayed in
a box to highlight it.
Devices

Pressing the right button from the Search/Pair submenu icon, takes you to the Devices
submenu icon:

Pressing enter shows the devices currently in the Known Devices list for your NXT:

Selecting a specific device will show details of it:

The first line gives the name and the second the address.
Press Enter to remove the device from the Known devices list.
Visibility

Pressing the right button from the Devices submenu, shows the Visibility submenu:

Pressing Enter on the Visibility submenu switches visibility of the NXT device to
Bluetooth inquiries on and off. The status is shown on the Visibility line at the top of
the screen.
Change Pin

Pressing ENTER from the Visibility submenu, displays the Change Pin submenu:

Press Enter to get the Enter NXT Pin submenu:

This lets you change the pin for your NXT. The pin is required when you pair your
NXT from another device. It is preset to 1234.
To enter the 4 digit pin, press the left and right buttons to increment or decrement the
current digit, and Press Enter to go on to the next one. When you press Enter on the
last digit, the pairing will take place. The current digit has is displayed in a box to
highlight it.

Sound

Press the right button from the Bluetooth menu item, displays the Sound menu icon:
Press Enter and the Volume submenu is displayed. Click the Enter key to increase the
main volume. After 10 it wraps round to mute (sound off). Presing it again increases
the sound from level 1 upwards.

Press the right button from the Volume submenu and the Key Click submenu is
displayed.

This sets the volume of the sound when a key is clicked. It works the same as the
Volume submenu.
System menu

Pressing the right button from the Sound menu icon, displays the System menu icon:

Selecting this shows information about the current state of the NXT brick, and allows
you to format (wipe clean) the file system.

This submenu shows some information about the status of the NXT, and allows you to
change some of these variables.
The free flash memory and free RAM are displayed in bytes. An "R" after the battery
voltage indicates that a rechargable battery pack is in use.
Press Enter to format the file system and wipe out all files. A submenu of Yes and No
is diaplayed, defaulted to No, to confirm the operation. Press the left or right arrows to
select Yes, and then press Enter to format the filesystem.

Pressing right arrow when the Format submenu is diaplayed, shows the sleep time
submenu:

Press Enter to increment the sleep time. It is in minutes. The maxiumum value is 10
minutes. Pressing Enter when 10 is displayed, sets the sleep timer off. Pressing Enter
again starts incrementing it starting at 1.
Pressing the right button when the Sleep time submenu is displayed, takes you to the
Auto Run submenu:

Auto run defaults to off. It can only be switched on if a default program has been
defined. When it is on the default program will be run automatically on start up,
instead of this menu. You can disable this and cause the menu to be displayed by
pressing the left button during start up.

Version

Pressing the right button from the System menu icon, displays the Version menu icon:

Selecting this item, displays the version of the firmware and the menu.

This menu is for information only.

Controlling the Motors


Introduction to the Motor class.

This Motor class provides access to the NXT motors. To be useful, a motor must be
connected to one of the three NXT motor ports. This class provides an instance for
each port. They are: Motor.A, Motor.B and Motor.C.
Each of these three objects is an instance of the class NXTRegulatedMotor. This class
provides methods for controlling the motor, and for finding out what the motor is
doing.
This tutorial contains a set of five programs for you to write. With them, you can
perform experiments to understand how the NXT motor performs. They are simple
enough so you dont need much Java experience to write them. Finally, there is a
discussion of other motor methods , not used in the programs, that you might find
useful.
Program 1 - Basic movement controls.

This program uses the basic motor methods that control movement.
Methods used in this program
Class

Method name

Notes

NXTRegulatedMotor,
e.g. Motor.A

forward()

Start the motor


rotating forward

backward()

Start rotating
backward

changeDirection()

Reverse the
direction of
rotation

stop()

Stop quickly

Button

waitForPress()

Wait till any


button is pressed

LCD

drawString(String
str, int x, int
y)

Draw a string.

What the program should do:

1. Run motor A in the forward direction.


2. Display FORWARD in the top line.
3. Wait until a button is pressed.
4. Run the motor backward.
5. Display BACKWARD in next line.
6. Wait until a button is pressed.
7. Run motor A in the forward direction.
8. Display FORWARD in the next line.
9. Wait until a button is pressed.
10. Stop the motor.
It is possible to write this program using each Motor method only once.
Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14

import lejos.nxt.*; // this is required for all programs that run on the NXT
/**
*Motor runs forward then backward as button is pressed.
* @author Roger
*/
public class BasicMotorTest
{
public static void main(String[] args)
{

15
16
17
18
instead
19
20
21
22
23
24
25
}
26 }
27
28

Motor.A.forward();
LCD.drawString("FORWARD", 0, 0);
Button.waitForPress();
Motor.A.backward(); // you could use

Motor.A.reverseDirection

LCD.drawString("BACKWARD", 0, 1);
Button.waitForPress();
Motor.A.reverseDirection();
LCD.drawString("FORWARD", 0, 2);
Button.waitForPress();
Motor.A.stop();

Program 2 - Using the Tachometer.

The NXT motor has a built in tachometer that keeps track of the current angle (in
degrees) of the motor axle. The purpose of this program is to use the tachometer to
find out how quickly the motor stops.
New methods used in this program
Class

Method name

Notes

NXTRegulatedMotor,
e.g. Motor.A

flt()

Abbreviation for float. Turns


off the power, but does not
apply the brake

getTachoCount()

Return s the motor angle in


degrees

resetTachocount()

Sets the counter to zero

setSpeed(int
speed)

speed degrees per second..


The maximum speed that can
be accurately maintained is
about 110 times the battery
voltage.

int
getActualSpeed()

Returns the actual motor


speed (deg/sec)

LCD

drawInt()

What the program should do

1.
2.
3.
4.
5.
6.
7.
8.
9.

Run Motor.A forward.


Wait till the tacho count reaches 360.
Stop the motor.
Wait until the motor has stopped.
Display the tachometer reading on the on the LCD.
Wait for a button press to give you time to read the screen display.
Set the motor speed to 720 (the default is 360).
Reset the tachometer count to zero.
Repeat steps 1 through 6. Note: you can avoid duplicated code by writing a
method that executes these steps.

Repeat the entire experiment by modifying your code to use flt() instead of stop(); ( flt
is the abbreviation for float, a Java reserved word). This method sets the motor power
to zero, but does not apply the brake.
Observe that, even using the brake, the motor does not stop immediately, because
motor has inertia.
Solution
import lejos.nxt.*;
/**
* Test the overshoot after stop() at different speeds;
* @author Roger
*/
public class InertiaTest
{
public static void main(String[] args)
{
LCD.drawString("Inertia Test", 0, 0); // just so the program will
not start immeditely
Button.waitForPress();
rotate720();
Motor.A.resetTachoCount();
Motor.A.setSpeed(800);
rotate720();
}
/**
* helper method - rotates to 720, stops, display tacho count, wait;
*/
public static void rotate720()
{
Motor.A.forward();

int count = 0;
while( count < 720 )count = Motor.A.getTachoCount();
Motor.A.stop();
Motor.A.flt();
LCD.drawInt(count , 0, 1);
while(Motor.A.getActualSpeed()>0);
LCD.drawInt(Motor.A.getTachoCount(), 7,1 );
Button.waitForPress();
LCD.clear();

//

}
}

Program 3 - Accurate rotation control.

The Motor class has a regulator thread that runs all the time. It has two principle jobs,
one of which is to stop the motor at a specified angle. This program will test the
accuracy of the rotate() method.
New methods used in this program
Class

Method name

Notes

NXTRegulatedMotor,
e.g. Motor.A

rotate(angle)

Rotates
through angle degrees

rotateTo(angle)

Rotates to angle

rotateTo(angle,true)

Sets the counter to zero

What the program should do

1.
2.
3.
4.
5.
6.
7.
8.

Rotate the motor one complete revolution.


Display the tachometer reading on the on the LCD, row 0.
Rotate the motor to angle 0.
Display the tachometer reading on the on the LCD, row 1.
Wait for a button press to give you time to read the LCD.
Clear the LCD.
Set the speed to 720.
Repeat steps 1 through 6.

Note: you can avoid duplicated code by writing a method that executes these steps.

Observe the motor usually stops within 1 degree of the specified angle if the motor
regulator is doing its job. It works by calculating how far the motor will continue to
turn after the brake has been applied. It applies the brake before reaching the specified
angle. It then makes minor adjustments to the motor position till it is close enough.
Observe: Once the motor has stopped if you try to turn it by hand it will resist you and
will even return nack to the stopped position. This is because when you use the stop
method the regulator will continue to control the position of the motor. If you don't
want this to happen you can call the flt() method. This will release the motor from
regulation and you will be able to turn it easily by hand. If you call one of the rotate
methods and allow them to complete, then the default state at the end of the rotation
will be as if you had called stop, so the motor position will be maintained.
Solution
import lejos.nxt.*;
/**
* A more object - oriented code.
* @author Roger
*/
public class RotationTest
{

the main method just starts the program

public static void main(String args[])


{
LCD.drawString("Rotation", 0, 0);//display program name before
running
Button.waitForPress();
LCD.clear();
RotationTest robot = new RotationTest();
robot.go();// create an instance of the class and calls go
}
/**
* this method does the actual work
*/
public void go()
{
rotate720();
Motor.A.setSpeed(800);
rotate720();
}
/**
* helper method for go(); stops motor when button ia pressed
*/
public void rotate720()
{
Motor.A.rotate(720);

LCD.drawInt(Motor.A.getTachoCount(), 4, 0, 1);
Motor.A.rotateTo(0);
LCD.drawInt(Motor.A.getTachoCount(), 4, 0, 2);
Button.waitForPress();
}
}

Program 4.Interrupting rotation

Sometimes you will want the motor stop (or do something else) before it reaches the
specified angle. This program will detect a button press to interrupt the rotation task if
you press a button soon enough. The rotate() methods will not return until the motor
has stopped at the target angle. But the new methods in this program can return
immediately. The motor will still stop at the specified angle unless a new motor
method is called in the meantime.
New methods used in this program
Class

Method name

NXTRegulatedMotor,
e.g. Motor.A

rotate(angle,immediateReturn)

Button

Notes

The method returns immediately


if the boolean
rotateTo(angle,immediateReturn) parameterimmediateReturn is true
(boolean)isRotating()

Returns false when the motor has


stopped at the specified angle

int readButtons()

Returns the ID of the button


pressed.

What the program should do

1.
2.
3.
4.
5.
6.
7.

Start a rotation of 720 degrees.


While the motor is rotating, display the tacho count at the position of row 1.
When a button is pressed, stop the motor.
After the motor has stopped, display the tacho count in the center of the row.
Wait for a button press.
Start a rotation to 0.
Repeat steps 2,3,5.

Observe: if you press the button before the rotation is complete, the motor will stop
without completing its rotation. Otherwise, the stop() method has no effect.
Solution
import lejos.nxt.*;
/**
* allows interruption of a rotation task
* @author Roger
*/
public class RotInterrupt
{
/**
* This method has the logic - rotate 720, return, wait for button
* then rotate to 0 - wait for button
* Another way to get results to the lcd
*/
public void go()
{
System.out.println("Interrupt \n rotation");
Sound.twoBeeps();
Button.waitForPress();
Motor.A.rotate(720,true);
showRotation(1);
Motor.A.rotateTo(0,true);
showRotation(1);
}
/**
* helper method - saves repeated code
*/
public void showRotation(int row )
{
while(Motor.A.isRotating())
{
LCD.drawInt(Motor.A.getTachoCount(),4, 0, row);
if(Button.readButtons()>0) Motor.A.stop();
}
while(Motor.A.getActualSpeed()>0);
LCD.drawInt(Motor.A.getTachoCount(), 4,8,row);
Button.waitForPress();
}
/**
* Main reduced to one line
*/
public static void main(String[] args)
{
new RotInterrupt().go();
}
}

Program 5: Regulating motor speed

The other principle task of the regulator thread is to control the motor speed. One
reason for doing this is that a two wheel vehicle will only travel in a straight line if
both motors run at the same speed.(obviously). The standard Lego software solves this
problem by directly synchronizing two motors. NXJ takes a different approach:
keeping each motor rotation synchronized to the system clock. The regulator
compares the tacho count (minus its reference count) with speed times elapsed time,
and adjust the power to keep these two quantities closely matched. The regulator
resets its reference count and its elapsed time to zero and begins its comparison again
whenever you call any of the motor methods you have used so far.
New methods used in this program
Class

Method name

Notes

Stopwatch

elapsed()

Returns elapsed time in


milliseconds

reset()

Resets the watch to 0.

The Stopwatch class is in the package lejos.util.


What the program should do:

1.
2.
3.
4.
5.

Create a new stopwatch.


Start all three motors running at 2 revolutions/second.
Every 200 ms, display all 3 tacho count values in the same row.
Repeat step 3, 8 times, using a different row each time.
Write down maximum difference you see between the motor tacho counts.

The motors should remain within a few degrees of each other. You may want to
modify the program and try the same test without regulation, to do this you will need
to use the NXTMotor class and setPower method (see below). When regulation is not
used, the difference in angle of rotation get larger with time unless you have an
unusually well matched set of motors.
Solution
mport lejos.nxt.*;
import lejos.util.*;

/**
* Test the accuracy of motor speed regulation.
* @author Roger
*/
public class RegulateTest
{
Stopwatch sw = new Stopwatch();
Motor [] m = {Motor.A, Motor.B, Motor.C}; //build an array of motors
/**
* Display program name, wait for button, then call go/
* @param args
*/
public static void main( String[] args)
{
LCD.drawString(" Reg Test", 0, 0);
Button.waitForPress();
LCD.clear();
new RegulateTest().go();
}
/**
* helper method - does the detailed work; resets tacho count, runs
motors,
* displays data
*/
public void go()
{
LCD.clear();
sw.reset();
for( int i = 0; i<3; i++)m[i].resetTachoCount();
for( int i = 0; i<3; i++)m[i].setSpeed(720);
for( int i = 0; i<3; i++)m[i].forward();
for(int r = 0 ; r<8; r++)
{
while(sw.elapsed() < 200* r)Thread.yield();
for( int i = 0; i<3; i++)
LCD.drawInt(m[i].getTachoCount(),5*i,r);
}
for( int i = 0; i<3; i++)m[i].stop();
Button.waitForPress();
}
}

When might you want to turn off speed regulation?

In some robots, the motor speed should not be constant but changed in response to a
sensor reading as for example in a line follower or a balancing robot. If the speed
corrections happen frequently, there is no advantage in the regulator thread using CPU

cycles in adjusting the motor power to maintain constant speed between adjustments.
To use an unregulated Motor you must create an instance of the class NXTMotor. For
example:
NXTMotor m1 = new NXTMotor(MotorPort.A);

NXTMotor has many of the methods of NXTRegulatedMotor, but instead of the


setSpeed method, it has a setPower method, and, as it has no regulation thread, it does
not support any of the rotate methods.
When should you use speed regulation?

If you specify a very slow speed, the power to maintain it may not be enough
overcome the motor internal friction, so the motor never moves. In this case, a call to
rotate() will never return. But with speed regulation on, the regulator will keep
increasing the power until the motor comes up to speed.
Can you mix the two methods of control?

Yes you can. Simply create an instance of both NXTRegulatedMotor and NXTMotor
classes for the same motor port. When you want to use setPower simply turn off
motor regulation by calling suspendRegulation on the NXTRegulatedMotor instance.

Other Motor Methods


Finding out what the motor is doing

boolean isMoving();
This is useful to test if the motor has finished rotating. isMoving() returns true
when the Motor is moving for any reason (e.g. forward() or backward() have
been called or a rotate() task is in progress)

int getLimitAngle()
Returns the angle to which the motor is currently rotating

int getSpeed()
Returns the current speed setting.

int getActualSpeed()
Returns the actual speed of the motor.

boolean isStalled()
Tells you if the motor is stalled or if the regulation of the motor speed has
failed (have you asked it to go faster then is possible?).

Various other motor methods

resetTachoCount()
This method not only sets the tacho count to 0 but also resets the origin used by
the regulator thread in deciding when to stop a rotation task.

void setAcceleration(int acceleration)


Lets you control how fast the motor speed will change from one speed to
another. The acceleration is set in degrees per second per second. Use small
values (try 200 or 500) to have your robot smoothly accelerate up to speed.

void getAcceleration()
Returns the current acceleration value for the motor

suspendRegulation()
Turns of the regulation of the motor. Use this method if you want to mix
regulated and unregulated control of the same motor.

NXTMotor methods

setPower(int aPower)

Used to control motor power directly. Use a value between 0 and 100. Dont
call this method if speed regulation is turned on because then the regulator
thread is continuously adjusting the power.

int getPower()
returns the current power setting in the range of 0 to 100.

Controlling Wheeled Vehicles


Overview

A common type of robot is the two wheeled vehicle with independently controlled
motors. This design uses differential steering and can turn in place. The classes that
control vehicles of this design deal with several levels of abstraction. At bottom, there
are the motors that turn the wheels, controlled by theNXTRegulatedMotor class. The
DifferentialPilot class uses the motors to control elementary moves: rotate in place,
travel in a straight line, or travel in an arc. At the next level, the NavPathController
uses a DifferentialPilot to move the robot through a complicated path in a plane. To
do navigate, the path controller needs the robot location and the direction it is heading.
It uses a OdometeryPoseProvider to keep this information up to date. The
relationships among these classes is shown in the in the table.
Class

Uses
DifferentialPilot

NavPathController
OdometryPoseProvider
OdometryPoseProvider DifferentialPilot
DifferentialPilot

RegulatedMotor

The flow of control is from the top down: the path controller controls the pilot which
controls the motors. But the flow of information is from bottom up. The pilot uses
information from the motors to control them. The pose provider uses odometry
information from the pilot to update its current estimate of the robot pose. The pose
consists of the robot's coordinates (x and y) and its heading angle (the direction it is

facing ). The robot heading uses Cartesian coordinates, with angles in degrees; 0
degrees is the direction of the positive x axis, 90 degrees is the positive y axis. The
path controller uses this data to calculate the distance and direction to its destination.
This flow of information uses the listener and event model. The pilot registers as a
listener to with motors, which inform it when a motor rotation is started or completed.
The pose provider registers as a listener with the pilot, which informs it of the start
and completion of every movement. This event driven information flow automatic.
The path controller can also requests a pose estimate whenever it needs to, even while
the robot is moving,
DifferentialPilot

The DifferentialPilot class steers the vehicle by controlling the speed and direction of
rotation of its motors. The pilot object needs to know the wiring diagram of the robot,
i.e. which ports the motors are connected to and whether driving the motors forward
makes the robot move forward or backward (reverse). It also needs to know the
diameter of the wheels and the width of the track, i.e. the distance between the centres
of the tracks of the two wheels. DifferentialPilot uses the wheel diameter to calculate
the distance it has traveled. It uses the track width to calculate how far it has rotated.
Obviously, both parameters must be in the same units, but they can be anything you
wish. With proper adjustment of these parameters, errors in distance traveled and
angle of rotation can be held do 2% or perhaps less. This information is passed to the
pilot constructor. DifferentialPilot is in the lejos.robotics.navigation package. The
documentation for this class is here.
Constructors:

DifferentialPilot(float wheelDiameter, float trackWidth, Motor leftMotor,


Motor rightMotor)
DifferentialPilot(float wheelDiameter, float trackWidth, Motor leftMotor,
Motor rightMotor, boolean reverse)
Use this constructor if you need to set the reverse boolean to true . Then the
motors will rotate backward to make the robot move forward,

Straight line movement

To control the robot moving in a straight line, use:

void setTravelSpeed(double travelSpeed)


sets the speed of the motors in distance (wheel diameter)units per second

void forward()
starts the robot moving forward
void backward()
void stop()

To control the distance the robot moves, use:

void travel(double distance)


void travel(double distance, boolean immediateReturn)
distance is in the same units as wheel diameter; a negative distance means
travel backwards.
getMovement().getDistanceTraveled() - returns the distance the vehicle
traveled since the last call of resetTachoCount()

Example:
import lejos.nxt.*;
import lejos.navigation.Pilot;
/**
* Robot that stops if it hits something before it completes its travel.
*/
public class TravelTest {
DifferentialPilot pilot;
TouchSensor bump = new TouchSensor(SensorPort.S1);
public void go() {
pilot.travel(20, true);
while (pilot.isMoving()) {
if (bump.isPressed()) pilot.stop();
}
System.out.println(" "+pilot.getMovement().getDistanceTraveled());
Button.waitForPress();
}
public static void main(String[] args) {
TravelTest traveler = new TravelTest();
traveler.pilot = new DifferentialPilot(2.25f, 5.5f, Motor.A, Motor,C);
traveler.go();
}
}

You can cause the robot to rotate in place by a specified angle by using

void rotate(double degrees)


You must have accurate values for wheelDiameter and trackWidth for this
method to produce accurate results.

Program SquareTracer

Write a program that uses a DifferentialPilot to trace out a square, using the travel and
rotate methods.
Solution
import lejos.nxt.*;
import lejos.robotics.navigation.DifferentialPilot;
/**
* Trace a square
* @author Roger
*/
public class SquareTracer
{
DifferentialPilot pilot ;
public void drawSquare(float length)
{
for(int i = 0; i<4 ; i++)
{
pilot.travel(length);
pilot.rotate(90);
}
}
public static void main(String[] args)
{
SquareTracer sq = new SquareTracer();
sq.pilot = new DifferentialPilot(2.25f, 5.5f, Motor.A, Motor.C);
sq.drawSquare(20);
}
}

Program SquareTracer2

Write a program that traces 2 squares with increasing angle at the corners, then
retraces the same path in the opposite direction.. Modify the traceSquare method of
program DifferentialPilot 1 so it can trace a square in either direction, and use it in
this program. This is stringent test of the accuracy of the wheel diameter and track
width constants you use in you pilot.
Solution
import lejos.nxt.*;
import lejos.robotics.navigation.DifferentialPilot;
/**
* Trace two squares, twice.
* @author Roger
*/
public class SquareTracer2
{
DifferentialPilot pilot ;
public void drawSquare(float length)
{
byte direction = 1;
if(length < 0 )
{
direction = -1;
length = -length;
}
for(int i = 0; i<4 ; i++)
{
pilot.travel(length);
pilot.rotate(direction * 90);
}
}
public static void main( String[] args)
{
System.out.println(" Square Tracer 2");
Button.waitForPress();
SquareTracer2 sq = new SquareTracer2();
sq.pilot = new DifferentialPilot(2.25f, 5.5f, Motor.A, Motor.C);
byte direction = 1;
int length = 20;
for(int i = 0; i<4; i++)
{
sq.drawSquare(direction * length );
if( i == 1)
{
sq.pilot.rotate( 90);
direction = -1;
}
}

}
}

Movement along a curved path

The pilot can turn the robot in place by driving one wheel forward and the other
backward. The methods that do it are:

void rotate(int angle)


void rotate(int angle, boolean immediateReturn )
If angle is positive, the robot turns to the left. The immediateReturn parameter
works as in the Motor methods allowing the calling thread to do other work
while the rotation task in progress.

DifferentialPilot can also control the robot to move in a circular path using these
methods:

void steer(double turnRate) follows a circular path until another method is


executed
void steer(double turnRate, int angle)
void steer(double turnRate, int angle, boolean immediateReturn)

The turnRate parameter determines the radius of the path. A positive value means that
center of the circle is to the left of the robot (so the left motor drives the inside wheel).
A negative value means the left motor is the outside wheel. The absolute value is
between 0 and 200, and this determines the ratio of inside to outside motor speed. The
outside motor runs at the set speed of the robot; the inner motor is slowed down to
make the robot turn. At turn rate 0, the speed ratio is 1.0 and the robot travels in a
straight line. At turn rate 200, the speed ratio is -1 and the robot turns in place. Turn
rate 100 gives speed ratio 0, so the inside motor stops. The formula is: speed ratio =
100 - abs(turnRate).
The angle parameter determines the rotation angle at which the robot stops. If the
angle is negative, the robot follows the circular path defined by the turn rate, but it
moves backwards.

getMovement().getAngleTurned() - returns the angle of vehicle rotation since


the last call of resetTachoCount()

Program SteerTester

Write a program that uses the ButtonCounter to enter the turn rate and angle variables,
and then calls the steer() method. It does this in a loop so you can try different values
of these parameters to control the robot path.
Solution
import lejos.nxt.*;
import lejos.navigation.*;
import lejos.util.*;
/**
* Test the turn() method
* Left botton enters 100's digit, right - 10's digit.
*/
public class SteerTest
{
public static void main( String[] args)
{
DifferentialPilot pilot = new DifferentialPilot(2.25f, 4.8f, Motor.A,
Motor.C); //units: inches
ButtonCounter bc = new ButtonCounter();
while(true)
{
bc.count("Turn Rate x10");
int turnRate = 100 * bc.getLeftCount() + 10 * bc.getRightCount();
bc.count("Angle x 10");
int angle = 100 * bc.getLeftCount() + 10 * bc.getRightCount();
pilot.steer(turnRate,angle);
}
}
}

Communicating with OdometryPoseProvider

The OdometryPoseProvider keeps track of the robot position and heading. To do this,
it needs to know about every move made by the DifferetnialPilot. So the pilot needs to
register as a listener with the pose provider by calling the addListener method. After
the pilot does this,it will automatically call the moveStarted method on the pose
provider every time a movement starts, andmoveStopped when the move is complete. If
the pose provider needs to know the status of a movement that is in progress, it calls
the getMovement() method on the pilot.

Other methods for DifferentialPilot

void resetTachocount()
Resets the count for both motors.
boolean isMoving()
Returns true if either motor is moving. Useful if you have used the
immediateReturn parameter and need to know if the task is still in progress.
boolean stalled()
returns true if either motor actual speed is zero. Remember, the actual speed is
calculated every 100ms. So stalled() will return true for the first 100ms after
the robot begins its move.

If you really need to deal with individual motors, you can use:

Motor getLeft()
Motor getRight()

Compass Pilot

The CompassPilot is an extension of the DifferentialPilot class. It implements the


same methods, but uses a Compass Sensor to ensure that the pilot does not deviate
from the correct angle of robot heading.
It needs a HiTechnic or Mindsensors compass sensor plugged in to one of the sensor
ports. Its constructors are similar those of DifferentialPilot, but with the additional
information of the compass sensor port.
Constructors:

CompassPilot(SensorPort compassPort, float wheelDiameter, float


trackWidth,Motor leftMotor, Motor rightMotor)
CompassPilot(SensorPort compassPort, float wheelDiameter, float
trackWidth,Motor leftMotor, Motor rightMotor, boolean reverse)

Additional methods in CompassPilot:

void calibrate()
calibrate the compass sensor; rotates the robot slowly through 360 degreees.

setHeading(int angle)
set the desired robot heading, in degrees in Cartesian coordinates (a left turn
increases the heading)
int getHeading()
return the desired robot heading
int getAngle()
return the compass Cartesian angle. Also the actual robot heading assuming the
compass sensor points forward.

Additional methods in CompassPilot:

Write a program that does these steps:


1. Calibrate the compass.
2. Rotate the robot to a heading or 90 degrees
3. Reset the Cartesian zero of the compass sensor to correspond the current
heading.
4. Move the robot a fixed distance forward.
5. Rotate 90 degrees to the left.
6. Move the robot the same distance backwards.
7. Display the compass reading and the distance traveled at the end of each move.
Suggestion: while the robot is moving, nudge it off course and watch it steer back to
the correct heading.
Solution
import lejos.nxt.*;
import lejos.navigation.*;
/**
* Testing the compass pilot.
* @author Roger
*/
public class CompassPilotTst
{
public static void main(String[] args)
{
System.out.println("CompassPilot Test");
Button.waitForPress();
CompassPilot pilot = new CompassPilot(SensorPort.S3, 2.25f, 4.8f,
Motor.A, Motor.C);
pilot.calibrate();
LCD.drawInt(pilot.getAngle(), 4, 0, 0);

Button.waitForPress();
pilot.rotateTo(90);
pilot.getCompass().resetCartesianZero();
pilot.travel(20);
LCD.drawInt((int) pilot.getTravelDistance(), 4, 0, 1);
LCD.drawInt(pilot.getAngle(), 5, 8, 1);
pilot.rotateTo(180);
pilot.travel(-20);
LCD.drawInt((int) pilot.getTravelDistance(), 4, 0, 2);
LCD.drawInt(pilot.getAngle(), 5, 8, 2);
Button.waitForPress();
}
}

OdometryPoseProvider

The responsibility of this class is to maintain a current estimate of the robot location
and the direction in which it is heading. This information is stored in a Pose object.
The API for Pose is here. For the OdometryPoseProvider documentation click here.
The only constructor for this class is:

OdometryPoseProvider(MoveProvider mp) )
It registers the new pose provider object as a listener with the MoveProvider.

The methods that the move provider uses at the obvious times are:

moveStarted(Move move, MoveProvider mp)


public void moveStopped(Move move, MoveProvider mp)

The odometry data is contained in the Move object, which the pose provider uses to
calculate the new robot pose.
The only methods you are likely to use are:

setPose(Pose aPose)
Pose getPose()
If this method is called while the robot is moving, the pose provider will call
getMovement() on the move provider. This guarantees that the pose is current.

NavPathController

NavPathController (the navigator) uses a pilot and a PoseProvider to follow a route, a


series of locations. Each location is stored in a WayPoint object. The WayPoint API
is here. The route stored as a queue. When the a way point is reached, it is removed
from the route and the robot goes to the next one. New way points can be added to the
route at any time. Documentation for the NavPathController is here.
There are three ways to construct an instance of this class:
Constructors:

NavPathController(MoveControlle aPilot)
To use this constructor, you must construct a pilot and use it as the parameter.
The consttructor will create its own OdometryPoseProvider
NavPathController(MoveController pilot, PoseProvider poseProvider
Use this if you want to use another PoseProvider class
NavPathController(MoveController pilot, PoseProvider poseProvider,
PathFinder pathFinder)
This constructor also attaches a PathFinder.

Navigation methods

The primary methods for navigating the route in the absence of obstacles are listed
below. The methods that do not have a immediateReturn are non-blocking (they will
return immediately).

followRoute(Collection<WayPoint> aRoute, boolean immediateReturn)


This method does exactly what its name suggests. If immediateReturn is false,
the method will return when the last way point is reached. Otherwise, the
method is non-blocking
addWayPoint(WayPoint aWayPoint)
The WayPoint is added to the route. If the route was empty, the robot
immediately starts moving.
goTo(WayPoint destination, boolean immediateReturn)
If the queue is not empty, it is flushed. The destination is added to the route and
the robot starts moving.
goTo(WayPoint destination)
The non-blocking version of the previous method.
goTo(double x,double y)
Just another way to specify the destination. Acts like the previous method.
interrupt()
Stops the robot but preserves the route.

resume()
Start following the route again. use after calling interrupt().
isGoing()
Returns the navigator status. false if the last way point has not yet been reached
or the route has been interrupted and not resumed.

ShortestPathFinder

Suppose your robot is in a known location and needs to get to a destination. But there
are obstacles in the way. These obstacles are shown on a map. So what route should it
follow? This class can find the shortest path to the destination, and produce a route (a
collection of WayPoints) that the NavPathController can use. The map this class
needs is a LineMap which, as its name suggests, consists of straight lines.The
complete documentation for this class is here. Using this class is very simple. The
constructor is:

ShortestPathFinder(LineMap map)

After you constructed the path finder, you can get the route by using either of the
route finding methods. They both use a Pose as the starting point, which might be
returned returned by the a pose provider, and a WayPoint as the destination. The both
will throw the DestinationUnreachableException if no route cab be found.

findRoute(Pose start,WayPoint finish)


findRoute(Pose start, WayPoint finish, LineMap theMap) throws
DestinationUnreachableException
If you don't want to use the map specified in the constructor, you can use a
different one in this method.

The shortest path, if not a direct line, will have way points at the ends of lines, such as
the corners of obstacles on the map. But the physical robot is not a point, so if the
center of the robot tries to pass through a corner, there will be a crash. One solution to
this difficulty is to enlarge the map obstacles to allow clearance for the robot.
However, this may be tedious to by hand, and complex to do with software. A simpler
scheme is to extends all the lines on the original map so that a corner now is
represented by two points, each some distance from the corner. To make this
modification of the map,use the method :

lengthenLines(float delta)

which adds a segment of length delta to each line on the map. The extension delta
should probably be at least the track width of the robot, plus an allowance for
uncertainties in the robot location.

Object Detection
Object detection allows a robot to detect objects in its path and take some action, such
as avoiding the object. We chose the term feature to describe an object rather than
obstacle, because sometimes the object is something the robot wants to seek, rather
than avoid (such as a soccer ball). We didn't use the word "object" because it was a
potentially confusing class name with object oriented Java programming.
Feature Detectors

A FeatureDetector detects objects using a sensor, such as a touch sensor or ultrasonic


sensor. It is the main interface in the object detection package from which all data
originates. There are many benefits of using a FeatureDetector:

Automatic scanning and reporting of data


A listener interface to segregate the "action-response" code
Allows one code segment to respond to data from multiple sensors

There are currently two implementations:

RangeFeatureDetector - uses RangeFinder classes, such as the LEGO


UltrasonicSensor.
TouchFeatureDetector - uses Touch classes, such as the LEGO TouchSensor.

The RangeFeatureDetector allows you to specify some parameters for the sensor, such
as the maximum range you want it to report findings for, and the time between
performing scans. Construct a simple RangeFeatureDetector as follows:
int MAX_DISTANCE = 50; // In centimeters
int PERIOD = 500; // In milliseconds
UltrasonicSensor us = new UltrasonicSensor(SensorPort.S4);
FeatureDetector fd = new RangeFeatureDetector(us, MAX_DISTANCE, PERIOD);

Once you have a FeatureDetector instantiated, you can perform a scan on it to retrieve
data:
Feature result = fd.scan();
if(result != null)
System.out.println("Range: " + result.getRangeReading().getRange());

Warning: Make sure to check for a null object before trying to read data from the
returned Feature object, otherwise your code will throw a null pointer exception.
The main benefit of a FeatureDeteector is the ability to automatically notify other
classes when an object is detected via a listener interface. The next section discusses
this in more detail.
Feature Listeners

Once you have a FeatureDetector instantiated, you can add a FeatureListener to it.
The FeatureListener code will be notified when an object is detected via the
FetureListener.featureDetected() method. There you can make your robot react to the
detected object by performing some sort of action. The following code shows how to
use a FeatureListener:
import lejos.nxt.*;
import lejos.robotics.objectdetection.*;
public class ObjectDetect implements FeatureListener {
public static int MAX_DETECT = 80;
public static void main(String[] args) throws Exception {
ObjectDetect listener = new ObjectDetect();
UltrasonicSensor us = new UltrasonicSensor(SensorPort.S4);
RangeFeatureDetector fd = new RangeFeatureDetector(us,
MAX_DETECT, 500);
fd.addListener(listener);
Button.ENTER.waitForPressAndRelease();
}
public void featureDetected(Feature feature, FeatureDetector
detector) {

int range = (int)feature.getRangeReading().getRange();


Sound.playTone(1200 - (range * 10), 100);
System.out.println("Range:" + range);
}
}

Feature data

The most basic class to implement the Feature interface is the RangeFeature class.
This class is a data container, and the data is retrieved by these methods:

getRangeReading() - returns a RangeReading object


getRangeReadings() - returns a RangeReadings collection
getTimeStamp() - returns the system time (in ms) when this data was collected

Some scanners are capable of returning multiple object detections, such as the LEGO
ultrasonic sensor. Other sensors are only capable of producing a single object
detection. In either case, they are both capable of producing data to fulfill both
methods, as described below.
Single Reading

When the getRangeReading() method is called, it returns the closest object that was
detected by a scanner, even if it is capable of returning more than one hit.
Multiple Readings

When the getRangeReadings() method is called, it returns the all objects it detected. If
the scanner is only capable of returning one hit, the RangeReadings object will
contain only one RangeReading.
Multiple Feature Detectors

It is also possible to combine several different FeatureDetectors into one


FeatureDetector using the FusorDetector class. This is useful for robots that have a
number of detector sensors located on the robot (several bumpers at different locations
and range sensors). The code to fuse many FeatureDetectors looks like this:
UltrasonicSensor us = new UltrasonicSensor(SensorPort.S4);

FeatureDetector detector1 = new RangeFeatureDetector(us,


MAX_DETECT,RANGE_READING_DELAY);
Touch leftBump = new TouchSensor(SensorPort.S2);
FeatureDetector detector2 = new TouchFeatureDetector(leftBump, 10,
TOUCH_Y_OFFSET);
Touch rightBump = new TouchSensor(SensorPort.S3);
FeatureDetector detector3 = new TouchFeatureDetector(rightBump, -10,
TOUCH_Y_OFFSET);
FusorDetector fusion = new FusorDetector();
fusion.addDetector(detector1);
fusion.addDetector(detector2);
fusion.addDetector(detector3);
fusion.addListener(myFeatureListener);

Hardware: I/O and Sensors


This section covers the classes for user input and output, and also the standard NXT
sensors. The NXT hardware has buttons for input, a Liquid Crystal Display (LCD)
and a small speaker for output. leJOS NXJ provides software abstractions for all these
bits of hardware.
LCD

The LCD class has no instances (there being only one LCD on the NXT), so all the
methods are static. It can be used in text mode and graphics mode.
LCD Text methods

As a text display, the NXT LCD screen is 16 characters wide and eight characters
deep. It is addressed using (x, y) co-ordinates as follows:

x ranges from 0 to 15, and y from 0 to 7.


The methods to write to the LCD in text mode are :

void drawString(String str, int x, int y)


This draws a string of text to the LCD screen starting at text co-ordinate (x, y).

void drawInt(int i, int x, int y)


This draws an integer starting at text co-ordinate (x,y).The integer is left
aligned and takes up as many characters as are necessary.

void drawInt(int i, int places, int x, int y)


This variant of drawInt right-aligns the integer and always uses the number of
characters indicated by places. This means that it always writes to a fixed
number of character places and, if used in a loop, the previous value will
always be fully overwritten.

void clear()
Clears the display.

Example:
import lejos.nxt.*;
import java.io.*;
public class LCDTest {
public static void main(String[] args) throws Exception {

LCD.drawString("Free RAM:", 0, 0);


LCD.drawInt((int) System.getRuntime().freeMemory(), 6, 9, 0);
Thread.sleep(2000);
}
}

Note that you can also write to the LCD display with System.out.println(String str).
This scrolls the display up one line and writes to the bottom line of the screen.
Note, also, that by default, the LCD display is refreshed automatically. If you want to
control when the LCD is refreshed, you can call LCD.setAutoRefresh(0) to turn off
auto-refreshing and call LCD.refresh() when you want to refresh the display.

LCD Graphics methods

As a graphics display, the NXT LCD screen is 100 pixels wide and 64 pixels deep. It
is addressed using (x, y) pixel co-ordinates in the same way as for text co-ordinates:

x ranges from 0 to 99, and y from 0 to 63.


To display graphics on the LCD screen, you can use the Graphics class from the
package javax.microedition.lcdui. See the Graphics class API. With this class, you can
draw lines, rectangles, arcs, and position strings with pixel accuracy.
Example:
import javax.microedition.lcdui.Graphics;

public class GraphicsSample {


public static void main(String [] options) throws Exception {
Graphics g = new Graphics();
g.drawLine(5,5,60,60);
g.drawRect(62, 10, 25, 35);
Thread.sleep(2000);
}
}

Buttons

The Button class has four instances, accessed by static fields:

Button.ENTER
Button.ESCAPE
Button.LEFT
Button.RIGHT

To test if a button is pressed, you use:

boolean isPressed()

Example:
import lejos.nxt.*;
public class ButtonPresses {
public static void main(String[] args) throws Exception {
while (true) {
LCD.clear();
if (Button.ENTER.isPressed()) LCD.drawString("ENTER", 0, 0);
if (Button.ESCAPE.isPressed()) LCD.drawString("ESCAPE", 0, 0);
if (Button.LEFT.isPressed()) LCD.drawString("LEFT", 0, 0);
if (Button.RIGHT.isPressed()) LCD.drawString("RIGHT", 0, 0);
}
}
}

To wait for a specific button to be pressed and released, you use:

void waitForPressAndRelease() throws InterruptedException

Example:
import lejos.nxt.*;
public class ButtonTest
{
public static void main (String[] args)
throws Exception
{
Button.ENTER.waitForPressAndRelease();
LCD.drawString("Finished", 3, 4);
Thread.sleep(2000);
}
}

To wait for any button to be pressed, you use:

static int waitForPress()


The returns the id code of the button that is pressed.

button

ENTER

LEFT

Code

RIGHT
4

ESCAPE
8

To specify a listener to listen for button events for this button, you: use

void addButtonListener (ButtonListener aListener)


See Listeners and Events for how button listeners work.

To read the current state of all the buttons, you use:

static int readButtons()


The return value is the sum of the codes of the buttons that are pressed.

Sound

This class controls the single speaker so it has no instances and all the methods are
static.
To play a single tone, use

void playTone(int aFrequency, int aDuration)

Example:
import lejos.nxt.*;
public class PlayTones {
private static final short [] note = {2349,115, 0,5, 1760,165, 0,35};
public static void main(String [] args) throws Exception {
for(int i=0;i <note.length; i+=2) {
short w = note[i+1];
int n = note[i];
if (n != 0) Sound.playTone(n, w*10);
Thread.sleep(w*10);
}
}
}

There are two ways to play system sounds. One is:

void systemSound (boolean aQueued, int aCode)


The aQueued parameter is ignored on the NXT, it is here to be backwards
compatible with the RXC.

The values of code are:


code = 0
code = 1
code = 2

Short beep
Double beep
Descending arpeggio

code = 3
code = 4

Ascending arpeggio
Long, low buzz

Individual methods to play a particular system sound, if you dont remember the code,
are

void beep()
void twoBeeps()
void beepSequence()
void beepSequenceUp()
void buzz();int playSample(File aWAVfile)
int playSample(File aWAVfile, int volume)

There is also a method to produce a rest when playing a tune; time in milliseconds

void pause(int time)


You can use this method anytime you want your program wait, and dont want
to bother with the try/catch block required by Thread.sleep().

leJOS NXJ has methods that can also play 8-bit WAV files:

int playSample(File aWAVfile)


int playSample(File aWAVfile, int volume)

The return value of milliseconds the sample will play for or < 0 if there is an error.
To play a musical note, use:

void playNote(int[] inst,int freq, int len)


The inst array contains the attack, decay, sustain and release parameters for the
note. The static constants for some predefined instruments are: FLUTE, PIANO
and XYLOPHONE. You can also experiment with defining you own.

Battery

There are two static methods to get the battery voltage:

int getVoltageMilliVolt()
float getVoltage()

Example:
import lejos.nxt.*;
public class BatteryTest {
public static void main(String[] args) throws Exception {
LCD.drawString("Battery: " + Battery.getVoltage(), 0, 0);
Thread.sleep(2000);
}
}

Sensors

The NXT comes with four sensors; the touch sensor, the sound sensor, the light sensor
and the ultrasonic sensor. leJOS NXJ provides software abstractions of all these
sensor types, as well as many provided by third parties.
A physical sensor must be connected to a port, and the sensor object must know which
port this is. To provide this information, you create an instance of the sensor, and pass
this information in its constructor. The possibilities are: SensorPort.S1, S2, S3 or S4.
Touch Sensor

To use a touch sensor, you create an instance of it, using the constructor:

TouchSensor(SensorPort port)

To test if the touch sensor is pressed, you use the isPressed() method:

boolean isPressed()

Example:
import lejos.nxt.*;
public class TouchTest {
public static void main(String[] args) throws Exception {
TouchSensor touch = new TouchSensor(SensorPort.S1);

while (!touch.isPressed() ;
LCD.drawString("Finished", 3, 4);
}
}

Light Sensor

To use a light sensor, you create an instance of it using the constructor:

public LightSensor(SensorPort port)

Example:
import lejos.nxt.*;
public class LightTest {
public static void main(String[] args) throws Exception {
LightSensor light = new LightSensor(SensorPort.S1);
while (true) {
LCD.drawInt(light.getLightValue(), 4, 0, 0);
LCD.drawInt(light.getNormalizedLightValue(), 4, 0, 1);
LCD.drawInt(SensorPort.S1.readRawValue(), 4, 0, 2);
LCD.drawInt(SensorPort.S1.readValue(), 4, 0, 3);
}
}
}

Sound Sensor

The sound sensor supports two modes: DB and DBA. These modes give different
frequency response, so that it may be possible to get an idea of the frequency of a
sound by switching between modes.

There are two constructors:

SoundSensor(SensorPort port)
creates a sound sensor using DB mode.

SoundSensor(SensorPort port, dba)


creates a sound sensor using DBA mode if the second parameter is true.

You can switch modes with:

void setDBA(boolean dba)

Example using DB mode only:


The above example gives a graphical display of the way the sound reading varies over
a two-second period.
import lejos.nxt.*;
public class SoundScope {
public static void main(String[] args) throws Exception {
SoundSensor sound = new SoundSensor(SensorPort.S1);
while (!Button.ESCAPE.isPressed()) {
LCD.clear();
for (int i = 0; i < 100; i++) {
LCD.setPixel(1, i, 60 - (sound.readValue() / 2));
Thread.sleep(20);
}
}
}
}

Ultrasonic Sensor

To create an instance, use the constructor:

UltrasonicSensor( Port aSensorPort)

The sensor operates in two modes, continuous (default) and ping. When in continuous
mode the sensor sends out pings as often as it can and the most recently obtained
result is available via a call to

int getDistance()
The return value is in centimeters. If no echo was detected, the returned value is
255. The maximum range of the sensor is about 170 cm.

Example:
import lejos.nxt.*;
public class SonicTest {
public static void main(String[] args) throws Exception {
UltrasonicSensor sonic = new UltrasonicSensor(SensorPort.S1);
while (!Button.ESCAPE.isPressed()) {
LCD.clear();
LCD.drawString(sonic.getVersion(), 0, 0);
LCD.drawString(sonic.getProductID(), 0, 1);
LCD.drawString(sonic.getSensorType(), 0, 2);
LCD.drawInt(sonic.getDistance(), 0, 3);
}
}
}

When in ping mode, a ping is sent only when a call is made to

void ping()
This sets the sensor in ping mode and sends a single ping and up to 8 echoes
are captured. These may be read by making a call to

int readDistances(int [] distances)


You provide an integer array of length that contains the data after the method
returns. A delay of approximately 20ms is required between the call to ping and
getDistances. This delay is not included in the method. Calls to getDistances

before this period may result in an error or no data being returned. The normal
getDistance call may also be used with ping, returning information for the first
echo.
Calling ping() will disable the default continuous mode. Toto switch back to
continuous mode, call

int continuous()

Program: multiple echoes

Write a program that displays the distances from multiple echoes in a column. The
program should make four calls to ping(), and display the four columns of results,
then wait for a button press. Exit if the ESCAPE button was pressed.
Solution
import lejos.nxt.*;
/**
* Ultrasonic Sensor test of ping multiple distances
* @author Roger
*/
public class USPingTest
{
public static void main( String[] args)
{
LCD.drawString(" USPing",0,0);
Button.waitForPress();
LCD.clear();
UltrasonicSensor sonar = new UltrasonicSensor(SensorPort.S3);
int[] distances = new int[8];
int col = 0;
boolean more = true;
while(more)
{
sonar.ping();
Sound.pause(30);
sonar.getDistances(distances);
for(int i = 0; i<distances.length;i++)
{
LCD.drawInt(distances[i], 4, 4*col,i);
}
col ++;
if(col >4 )
{
more = Button.waitForPress()<8;
col = 0;
}
}

}
}

Threads, Listeners and Events


Java Threads

When a Java program starts, there is a single thread running the main thread.
Many of the leJOS classes start extra threads running for various purposes, for
example:

Button and SensorPort start a listener thread if listeners are used


NXTRegulatedMotor (and hence Motor) start a single motor regulator thread
The Bluetooth class starts a thread to talk to the separate Bluetooth chip
Each Timer object starts a timer thread
Some of the navigation classes, e.g. NavPathController, start extra threads

User programs can create their own threads by subclassing Thread, and then using the
start method to start the thread. In leJOS NXJ 0.7 and later, threads can be created
from any class that implements the Runnable interface.
Background threads that do not need to terminate in order for the user program to
terminate, should be marked as daemon threads by calling setDaemon(true).
When using threads, care should be taken with concurrency issues. When data items
are accessed by multiple threads, synchronization is necessary to ensure that data is
not read when it is in an inconsistent state.
leJOS NXJ supports the standard Java synchronization mechanisms: synchronized
methods and synchronized statements using a monitor object.
As an example of a leJOS thread, consider the Indicators thread in the leJOS
StartUpText menu. This is used to keep the display of the battery level up to date, by
reading its value every second, and to indicate when the menu is uploading files or
doing other communication from the PC:
import lejos.nxt.LCD;
class Indicators extends Thread {

private boolean io = false;


public void ioActive() {
io = true;
}
public void run() {
String[] ioProgress = { ".

", " .

", "

. " };

int ioIndex = 0;
boolean rewrite = false;
while (true) {
try {
if (io) {
StartUpText.g.drawString("

", 76, 0);

ioIndex = (ioIndex + 1) % ioProgress.length;


StartUpText.g.drawString(ioProgress[ioIndex], 78, 0);
io = false;
rewrite = true;
} else if (rewrite) {
LCD.drawString("

", 13, 0);

// invert when power is off


StartUpText.g.drawString(" BT", 82, 0, !StartUpText.btPowerOn);
StartUpText.g.refresh();
rewrite = false;
}
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
}
}

The main method starts this thread by:


Indicators ind = new Indicators();
ind.setDaemon(true);
ind.start();

Listeners and Events

leJOS implements a listener thread that listens for particular events.


The listener thread supports:

Button Listeners
Sensor Port Listeners

Button listeners are used to detect when a button is pressed, whatever your program is
doing at the time.
To listen for a press of a specific button, you register as listener for that button.
Example:
import lejos.nxt.*;
public class ListenForButtons {
public static void main(String[] args) throws Exception {
Button.ENTER.addButtonListener(new ButtonListener() {
public void buttonPressed(Button b) {
LCD.drawString("ENTER pressed", 0, 0);
}
public void buttonReleased(Button b) {
LCD.clear();
}
});
Button.ESCAPE.waitForPressAndRelease();
}
}

Error Handling and Debugging


leJOS NXJ provides several features for error handling and debugging, including:

Exceptions
Data Aborts
Debugging

The Remote Monitoring and Tracing facility, which is described in its own section
below, can also be used for debugging.
Exceptions

Most of the standard Java language exception classes are supported by leJOS, and
user can create their own exception classes.
Example:
The following ExceptionTest example demonstrates what happens for an exception
that is not caught in this case an ArrayIndexOutOfBounds exception.
public class ExceptionTest {
static void m1()
{
int test[] = new int[2];
// Force an exception
test[0] = test[1] + test[2]; // This is line 6
}
static void m2()
{
m1();
}
public static void main (String[] aArg) throws Exception
{
System.out.println("Running");
m2();
}
}

When this code is run the NXT will display the uncaught exception screen. Which
looks something like this:
Exception: 28
at:

20(11)

at:

21(1)

at:

22(9)

But what are these numbers telling us? The first line tells us the number of the
exception class that has been thrown, in this case class 28. The following lines show
us a mini stack trace, one for each method in the call stack, showing the method
number and the program counter location within the method. The top most item is the
location that the exception was thrown, which in this case was at location 11 in
method number 20.
So we now understand what the numbers are, but how do we relate them to our
program? Well one way is to use the verbose output from the linker, an abbreviated
version of which looks like this:
Class 0: java.lang.Object
Class 1: java.lang.Throwable
Class 2: java.lang.Error
Class 3: java.lang.OutOfMemoryError
Class 4: boolean
Class 5: char
Class 6: float
Class 7: double
Class 8: byte
Class 9: short
Class 10: int
Class 11: long
Class 12: void
Class 13: java.lang.Object[]
Class 14: java.lang.NoSuchMethodError
Class 15: java.lang.StackOverflowError
Class 16: java.lang.NullPointerException
Class 17: boolean[]
Class 18: char[]
Class 19: float[]
Class 20: double[]
Class 21: byte[]
Class 22: short[]
Class 23: int[]
Class 24: long[]
Class 25: reserved
Class 26: java.lang.ClassCastException
Class 27: java.lang.ArithmeticException
Class 28: java.lang.ArrayIndexOutOfBoundsException

Class 29: java.lang.IllegalArgumentException


Class 30: java.lang.InterruptedException
Class 31: java.lang.IllegalStateException
Class 32: java.lang.IllegalMonitorStateException
Class 33: java.lang.ThreadDeath
.... other classes omitted
Method 0: java.lang.Object.<init>() PC 1976 Signature id 2
Method 1: java.lang.Object.getClass() PC 1977 Signature id 121
Method 2: java.lang.Object.toString() PC 1985 Signature id 123
Method 3: java.lang.Throwable.i<init>() PC 2013 Signature id 2
Method 4: java.lang.Throwable.<init>(java.lang.String) PC 2023 Signature id
124
Method 5: java.lang.Throwable.getLocalizedMessage() PC 2038 Signature id
125
Method 6: java.lang.Throwable.getMessage() PC 2043 Signature id 29
Method 7: java.lang.Throwable.toString() PC 2048 Signature id 123
Method 8: java.lang.Throwable.fillInStackTrace() PC 2096 Signature id 126
Method 9: java.lang.NullPointerException.<init>() PC 2109 Signature id 2
Method 10: java.lang.Class.isInterface() PC 2114 Signature id 133
Method 11: java.lang.Class.toString() PC 2131 Signature id 123
Method 12: java.lang.String.<init>(int) PC 2177 Signature id 128
Method 13: java.lang.String.<init>(char[], int, int) PC 2189 Signature id
141
Method 14: java.lang.String.charAt(int) PC 2206 Signature id 145
Method 15: java.lang.String.length() PC 2231 Signature id 155
Method 16: java.lang.String.toString() PC 2237 Signature id 123
Method 17: java.lang.String.valueOf(java.lang.Object) PC 2239 Signature id
167
Method 18: java.lang.Thread.run() PC 2253 Signature id 1
Method 19: java.lang.Thread.currentThread() Native id 12
Method 20: ExceptionTest.m1() PC 2273 Signature id 175
Method 21: ExceptionTest.m2() PC 2288 Signature id 176
Method 22: ExceptionTest.main(java.lang.String[]) PC 2292 Signature id 0
Method 23: lejos.nxt.VM.<clinit> PC 2304 Signature id 3
Method 24: lejos.nxt.VM.<init>() PC 2323 Signature id 2
Method 25: lejos.nxt.VM.getVM() PC 2358 Signature id 177
Method 26: lejos.nxt.VM.memPeek(int, int, int) Native id 103
.... other methods omitted

We can use the class table to look up the exception class, in this case class 28 is
java.lang.ArrayIndexOutOfBoundsException so we know we have an array out of
bounds exception. Now we can look up the method names. Method 20 is
ExceptionTest.m1, method 21 is ExceptionTest.m2 and method 22 is

ExceptionTest.main. Which tells us that the exception has been thrown in method m1,
that was called from method m2, that was in turn called from method main. So far so
good, but how do we use the value of the location counter to narrow down where
exactly the exception occurred. Well we could decompile the byte code classes and
use that but there has to be an easier way right? Well yes there is. leJOS comes with
two tools that make this process much easier, one helps you decode the above
numbers, the other will do all of the work for you. Both tools require an additional
linker option -od which tells the linker to output extra debug information to the
specified file. Once we have this file we can use the NXJDebugTool to decode the
exception data as shown below:
C:\samples\ExceptionTest>nxjc ExceptionTest.java
C:\samples\ExceptionTest>nxjlink -o ExceptionTest.nxj -od ExceptionTest.nxd
ExceptionTest
C:\samples\ExceptionTest>nxjdebugtool -di ExceptionTest.nxd -c -m 28 20 11
The class number 28 refers to:
java.lang.ArrayIndexOutOfBoundsException
(ArrayIndexOutOfBoundsException.java)

The method number 20 refers to:


ExceptionTest.m1()V (ExceptionTest.java)
PC 11 refers to:
line 6 in ExceptionTest.java

Which as you can see tells you exactly which line in the source code the exception
was thrown on.
The final option is to make use of the leJOS remote console application (see below for
more details). again we need some extra linker options, this time -od to create the
debug information and -gr to add the remote console debug code to the program. If we
link with these options and run the program again, then three extra things happen, first
the program will wait for the remote console application to connect to it, secondly any
output that uses the System.out stream will be re-directed to the console display (in
this case "Running") and finally the exception information will be displayed by the
remote console in an easy to understand form. See below for an example of this:
C:\samples\ExceptionTest>nxjc ExceptionTest.java

C:\samples\ExceptionTest>nxjlink -gr -o ExceptionTest.nxj -od


ExceptionTest.nxd ExceptionTest
C:\samples\ExceptionTest>nxjupload -r ExceptionTest.nxj
Found NXT: nxt2 001653015066
leJOS NXJ> Connected to nxt2
leJOS NXJ> Upload successful in 2904 milliseconds
C:\samples\ExceptionTest>nxjconsole -di ExceptionTest.nxd
Debug attached
Found NXT: nxt2 001653015066
leJOS NXJ> Connected to nxt2
Connected to nxt2 001653015066
Console open
Running
Exception: java.lang.ArrayIndexOutOfBoundsException
at: ExceptionTest.m1(ExceptionTest.java:6)
at: ExceptionTest.m2(ExceptionTest.java:12)
at: ExceptionTest.main(ExceptionTest.java:18)
Console closed

Data Aborts

If the leJOS firmware crashes you will normally a Data Abort. The screen shows the
PC value where the failure occurred, and other details of the failure.
The screen is something like:
DATA ABORT
PC 00140BAC
AASR 1831BF01
ASR 00020601
OPCODE ???
DEBUG1 00020010
DEBUG2 00000000
The most common reason for data aborts is executing a file that is not a leJOS NXJ
binary, or executing an incomplete leJOS NXJ file.
If you get a data abort in any other case, you should report the error to the leJOS
development team by posting the details on the leJOS NXJ forums.

Remote Debugging

You can use your PC as a remote console to display tracing statements generated your
NXJ program. The lejos.nxt.comm.RConsole class has methods to it. Since there are
no instances of this class, all methods are static.
To start debugging, you use one of these methods:

void open()
opens a USB connection with no timeout

void openUSB(int timeout)


void openBluetooth(int timeout)

The NXT displays USB

Console..

or BT

Console.

and waits for the PC based monitor to connect.


Then execute the nxjconsole program on your PC. When the connection is
established, the NXT displays Got Connection and, after some seconds, both the NXT
and the PC display Console open.
If you use the variant of open with a timeout, it waits the specified number of seconds
and if the debug monitor has not connected, proceeds without debugging. If the
timeout is zero, it waits indefinitely.
You can also use the ConsoleViewer application to display the output.
Debug statements can be output using one of the methods:

void println(String s)
void print(String s);

If no successful open statement has been executed, the debug output is discarded. If
there was a successful output, the string appears on standard out in the window or
terminal that nxjconsole was run from, on the PC.
When debugging is completed, you should call:

void close()

This closes the USB or Bluetooth connection.

Example:
import lejos.nxt.*;
import lejos.nxt.comm.*;
/** * example using RConsole*/
public class TestRConsole {
public static void main(String[] args) {
RConsole.open();
RConsole.println("Start for loop ");
for (int i = 0; i < 5; i++) {
RConsole.print(" " + i);
LCD.drawInt(i, 2, 2 * i, 4);
}
RConsole.println("\n done ");
RConsole.close();
Button.waitForPress();
}
}

Communications
leJOS NXJ supports communications using Bluetooth and USB. The NXJ
communications classes are designed so that most of your code is independent of
whether you are using Bluetooth or USB you can write applications that work with
both (see the example below). And you can use Java streams, which are a very
flexible and easy to use.
USB has the advantage of speed, but the cable can be used only to connect a NXT to a
PC. Bluetooth is slower, but much flexible. It supports a multitude of methods of
communicating NXT to NXT, PC to NXT, Mobile phone to NXT, NXT to remote
Bluetooth device, etc.
The first step in communicating is to establish a connection. A connection has an
initiator and a receiver. The receiver waits for a connection from the initiator. The
initiator connects to a specific device that must be waiting for a connection. When the
connection has been established, both ends of the connection can use it to open input

and output streams and read and write data. In this tutorial we do not deal with the
case where the NXT is an initiator and the PC is a receiver, although this is possible
for Bluetooth connections. In most cases of PC to NXT or mobile phone to NXT
communications, it is more convenient for the NXT to be the receiver and the PC the
initiator.
The initiator program may run on a PC (always for USB), another NXT, a mobile
phone or another device that supports the Bluetooth Serial Port Profile (SPP). Some
external devices, such as GPS Bluetooth devices only act as a receiver, so when
communicating with these devices, the NXT must act as the initiator. Note that such
external devices must implement SPP - this is the only profile that the NXT supports.
Receiver

A receiver program on the NXT waits for a connection by calling the


waitForConnection() method in the Bluetooth or USB class. These are:

BTConnection waitForConnection();
USBConnection waitForConnection();

Even though the Bluetooth class returns a BTconnection object, and the USB class
returns a USBConnection object, both of classes implement the NXTConnectiion
interface. So an object of either class can be assigned to an reference variable that
implements that interface.
Bluetooth Example:
NXTConnection connection = Bluetooth.waitForConnection();

You need to ensure that Bluetooth power and visibility are on before calling this
method. The leJOS NXJ start-up menu can be used to do this.
USB Example:
NXTConnection connection = USB.waitForConnection();

You need to ensure that the USB cable is connected before calling this method.
Here is an example program that selects between USB and Bluetooth at run time:

import lejos.nxt.*;
import lejos.nxt.comm.*;
import java.io.*;
/**
* sample of selecting channel at run time
*/
public class CommTest
{
public static void main( String[] args) {
LCD.drawString("right BT",0, 0);
NXTConnection connection = null;
if(Button.waitForPress() == 4){
LCD.drawString("waiting for BT", 0,1 );
connection = Bluetooth.waitForConnection();
} else {
LCD.drawString("waiting for USB", 0,1 );
connection = USB.waitForConnection();
}
DataOutputStream dataOut = connection.openDataOutputStream();
try {dataOut.writeInt(1234);}
catch (IOException e ) {System.out.println(" write error "+e);}
}
}
}

Streams

Once a connection has been established, streams can then be opened by calling any of
the following methods in the NXTConnection interface:

InputStream openInputStream() throws IOException;


OutputStream openOutputStream() throws IOException;

DataInputStream openDataInputStream() throws IOException; (the example


above did this)
DataOutputStream openDataOutputStream() throws IOException;

Data items can then be read from the DataInputStream by:

int read(byte b[]) throws IOException


int read(byte b[], int off, int len)throws IOException
boolean readBoolean() throws IOException
byte readByte() throws IOException
short readShort() throws IOException
readInt() throws IOException
char readChar() throws IOException
float readFloat() throws IOException
String readLine() throws IOException

Be aware: The stream read methods are blocking that is, they do not return until
data is read. If your program has other tasks that need attending to while waiting for
data, calls to the read methods should be made in a separate thread.
Data can be written to the DataOutputStream by:

void write(byte b[], int off, int len) throws IOException


void writeBoolean(boolean v) throws IOException
void writeByte(int v) throws IOException
void writeShort(int v) throws IOException
void writeChar(int v) throws IOException
void writeInt(int v) throws IOException
void writeFloat(float v) throws IOException;
void writeChars (String value) throws IOException

Example of reading and writing integers using data streams (dis and dos are a
DataInputStream and DataOutputStream that has been opened):
for(int i=0;i <100;i++) {
int n = dis.readInt();
LCD.drawInt(n,7,0,1);
dos.writeInt(-n);
dos.flush();
}

Be aware: you must flush the output stream to be sure the data is actually transmitted.
Furthermore, it is possible for transmission to fail without throwing an exception.
The DataInputStream, DataOutputstream and NXTConnection can then be closed
using the close() method.
The full example using BlueTooth is:
public class BTReceive {
public static void main(String [] args) throws Exception {
String connected = "Connected";
String waiting = "Waiting...";
String closing = "Closing...";
while (true) {
LCD.drawString(waiting,0,0);
NXTConnection connection = Bluetooth.waitForConnection();
LCD.clear();
LCD.drawString(connected,0,0);
DataInputStream dis = connection.openDataInputStream();
DataOutputStream dos = connection.openDataOutputStream();
for(int i=0;i<100;i++) {
int n = dis.readInt();
LCD.drawInt(n,7,0,1);
dos.writeInt(-n);
dos.flush();
}
dis.close();
dos.close();
LCD.clear();
LCD.drawString(closing,0,0);
btc.close();
LCD.clear();
}
}
}

To modify this example to work with USB, you only have to change
Bluetooth.waitForConnection() to USB.waitForconnection()

NXT Initiator

To initiate a Bluetooth connection from one NXT to another NXT, you first need to
add the receiver NXT to the initiator NXTs Bluetooth devices.
To do this, you go to the Bluetooth menu in the leJOS NXJ start-up menu and select
Search. Providing the Bluetooth power is on and visibility is on for the receiving
NXT, it will be found and you can select Add to add it to the initiators Bluetooth
devices.
To check it is in the Devices list, you can select Devices from the Bluetooth menu
of the initiator NXT.
You can then create a BTRemoteDevice class on the initiator NXT:
Example:
BTRemoteDevice btrd = Bluetooth.getKnownDevice(name);

You can connect to the remote device by its address, which you can get by:

public byte[] getDeviceAddr()

You can then connect to the remote device by calling one of the connect() methods in
the Bluetooth class:

BTConnection connect(BTRemoteDevice remoteDevice)

BTConnection connect(byte[] device_addr)


BTConnection connect(byte[] device_addr, byte[] pin)

Example:
BTRemoteDevice btrd = Bluetooth.getKnownDevice(name);
if (btrd == null) {

LCD.clear();
LCD.drawString("No such device", 0, 0);
Button.waitForPress();
System.exit(1);
}
BTConnection btc = Bluetooth.connect(btrd);
if (btc == null) {
LCD.clear();
LCD.drawString("Connect fail", 0, 0);
Button.waitForPress();
System.exit(1);
}

Having got a BTconnection object you can open the data input and output streams and
read data as in the receiver example above.
The complete BTConnectTest example, which works as the initiator program for the
BTReceive receiver program, is:
public class BTConnectTest {
public static void main(String[] args) throws Exception {
String name = "NXT";
LCD.drawString("Connecting...", 0, 0);
BTRemoteDevice btrd = Bluetooth.getKnownDevice(name);
if (btrd == null) {
LCD.clear();
LCD.drawString("No such device", 0, 0);
Button.waitForPress();
System.exit(1);
}
BTConnection btc = Bluetooth.connect(btrd);
if (btc == null) {
LCD.clear();
LCD.drawString("Connect fail", 0, 0);
Button.waitForPress();
System.exit(1);

}
LCD.clear();
LCD.drawString("Connected", 0, 0);
DataInputStream dis = btc.openDataInputStream();
DataOutputStream dos = btc.openDataOutputStream();
for(int i=0;i<100;i++) {
try {
LCD.drawInt(i*30000, 8, 0, 2);
dos.writeInt(i*30000);
dos.flush();
} catch (IOException ioe) {
LCD.drawString("Write Exception", 0, 0);
}
try {
LCD.drawInt(dis.readInt(),8, 0,3);
} catch (IOException ioe) {
LCD.drawString("Read Exception ", 0, 0);
}
}
try {
LCD.drawString("Closing... ", 0, 0);
dis.close();
dos.close();
btc.close();
} catch (IOException ioe) {
LCD.drawString("Close Exception", 0, 0);
}
LCD.drawString("Finished",3, 4);
Button.waitForPress();
}
}

PC Initiator

A PC program can initiate a connection to a NXT and open a Java data stream.
The API on the PC is different to the NXT API. See pcapidocs. and how to compile
and run PC API Programs
To connect to the NXT, you need a NXTComm object that can be obtained using the
NXTCommFactory class:

static NXTComm createNXTComm(int protocol)

Bluetooth Initiator

A NXTComm object to connect via Bluetooth can be obtained by:


NXTComm nxtComm = NXTCommFactory.createNXTComm(NXTCommFactory.BLUETOOTH);

The reason for using a factory method is that there are several implementations of
comms drivers for Bluetooth and USB on the PC and the one that is used depends on
what operating system you are using and the contents of he nxj.properties file.
You can connect to the NXT by address or by do a Bluetooth inquiry:
To connect by address, you create a NXTInfo object using the constructor:

public NXTInfo(String name, String address)

Example:
NXTInfo nxtInfo = new NXTInfo("NXT", "00:16:53:00:78:48");

To find the available NXTs doing a Bluetooth inquiry, you do:


NXTInfo[] nxtInfo = nxtComm.search("NXT",NXTCommFactory.BLUETOOTH)

USB Initiator

The initiator sequence using USB is almost the same as for Bluetooth; You just have
to use USB instead of Bluetooth.in your code. While USB cannot use a device address
, but you can use the NXT name.
NXTComm nxtComm = NXTCommFactory.createNXTComm(NXTCommFactory.USB);

To find the available NXT , you do:


nxtComm.search("MYNXT", NXTCommFactory.BLUETOOTH);

If there is only one NXT connected, you can use a null name parameter in the search()
method.

Using the NXTInfo object

Once you have a NXTInfo object,you can call the open() method of the NXTComm
object to connect to the NXT:

public boolean open(NXTInfo nxt) throws NXTCommException;

Once the NXT is open, you can obtain an InputStream and an OutputSttream, by
calling the getInputStream() anf getOutputStream() methods of the NXTComm
object:

public OutputStream getOutputStream();


public InputStream getInputStream();

From these you can construct a DataInputStream and a DataOutputStream and send
data to the receiving NXT.
The complete BTSend sample is in the samples folder.

Advanced Communications

In this section you will learn how to:

Control one NXT with another NXT over Bluetooth


Control an external Bluetooth device like a GPS receiver
Communicate between a NXT and an RCX

Controlling a remote NXT

The RemoteNXT class allows one NXT running leJOS NXJ to control another,
remote NXT, running NXJ or the standard LEGO firmware. It uses the LEGO
Communications Protocol (LCP) over Bluetooth to control the remote NXT.
Currently, the class is limited and I2C and RCX sensors are not supported, and the
motors must be used in a simple way as the regulation thread is not used.
To access a remote NXT, you use the constructor:

public RemoteNXT(String name) throws IOException

Example:
try {
LCD.drawString("Connecting...",0,0);
nxt = new RemoteNXT("NXT");
LCD.clear();
LCD.drawString("Connected",0,0);
} catch (IOException ioe) {
LCD.clear();
LCD.drawString("Conn Failed",0,0);
Button.waitForPress();
System.exit(1);
}

The name of the remote NXT must have already been added to the known devices of
the initiating NXT by do a Bluetooth search followed by Add fron the leJOS NXJ
Bluetooth menu.

The constructor opens the connection and creates instances of the remote motor and
sensor ports.
It is then possible to get access to information about the remote NXT by using the
methods:

public String getBrickName()


public String getBluetoothAddress()
public int getFlashMemory()
public String getFirmwareVersion()
public String getProtocolVersion()

Example:
LCD.drawString(nxt.getBrickName(), 0, 6);
LCD.drawString(nxt.getFirmwareVersion(), 0, 7);
LCD.drawString(nxt.getProtocolVersion(), 4, 7);
LCD.drawInt(nxt.getFlashMemory(), 6, 8, 7);

There are also methods that act on the remote NXT:

public byte deleteFlashMemory()

A remote Battery object is created that can be used to get the voltage of the remote
battery using the normal Battery methods.
Example:
LCD.drawString( "Battery: " + nxt.Battery.getVoltageMilliVolt() 0,4);

Objects are also created for the sensor ports of the remote NXT. These are accessed as
S1, S2, S3 and S4.
Local sensor objects can then be created using these ports and use exactly as if they
were connected to a local sensor port.
Example:
LightSensor light = new LightSensor(nxt.S1);

LCD.drawString("Light: " + light.readValue(),0,5);

Motor objects are created for the remote motors. They are named A, B and C.
These can be used in the normal way, e.g:
nxt.A.setSpeed(360);
nxt.A.forward();
nxt.A.stop();
nxt.A.backward();

External Bluetooth devices

The NXT can connect to external Bluetooth devices that implement the Serial Port
Profile (SPP).
Such devices can be searched for on the Bluetooth menu and added to the known
devices.
An example of such a device is an external Bluetooth GPS receiver. These can be
used to obtain to obtain the geographic location of a robot
leJOS supports external Bluetooth GPS receivers that support the NMEA protocol via
the GPS and NMEASentence classes. NMEASentence is a utility class used by GPS,
and is not directly accessed.
One such device that has been tested with leJOS NXJ is the Holux M-1200.
Most such devices have a PIN that is required to connect to them, but it may have a
default value such as 0000.
To connect to a GPS device, you do:
final byte[] pin = {(byte) '0', (byte) '0', (byte) '0', (byte) '0'};
BTRemoteDevice btGPS = Bluetooth.getKnownDevice(name);
if (btrd == null) {

LCD.drawString("No such device", 0, 0);


Button.waitForPress();
System.exit(1);
}
btGPS = Bluetooth.connect(btrd.getDeviceAddr(), pin);
if(btGPS == null)
LCD.drawString("No Connection", 0, 1);
Button.waitForPress();
System.exit(1)
}
LCD.drawString("Connected!", 0, 1);
GPS gps = null;
InputStream in;
try {
in = btGPS.openInputStream();
gps = new GPS(in);
LCD.drawString("GPS Online", 0, 6);
} catch(Exception e) {
LCD.drawString("GPS Connect Fail", 0, 6);
Button.waitForPress();
System.exit(1);
}

As you see from this example, the GPS constructor takes the input stream from the
Bluetooth connection as a parameter:

public GPS(InputStream in)

The GPS class starts thread and uses the NMEASentence class to process messages
(known as sentences) from the Bluetooth device. Messages such as the $GPGGA
sentence that gives the current latitude, longitude and altitude, are processed.
To read the current values of latitude, longitude and altitude, you use the methods:

public float getLatitude()


public float getLongitude()
public float getAltitude()

You can also get the time stamp corresponding to these values by:

public int getTime()

Communicating with the RCX

IR communication with the RCX can be done using the Mindsensors NRLink RCX IR
adapter. The software abstraction of this device is the RCXLink class. The constructor
is:

public RCXLink(I2CPort port)

For example:
RCXLink link = new RCXLink(SensorPort.S1);

The NRLink-Nx supports a set of macros in ROM and EEPROM that can be used to
send messages to the RCX using the LEGO RCX IR protocol. The EEPROM macos
can be overwritten allowing the user to define their own macros.
Macros can be run by:

public void runMacro(int addr)

There are convenience methods for running the ROM macros:

public void beep()


public void runProgram(int programNumber)
public void forwardStep(int id)
public void backwardStep(int id)
public void setRCXRangeShort()
public void setRCXRangeLong()
public void powerOff()
public void stopAllPrograms()

A new macro in the EEPROM address range can be defined by:

public void defineMacro(int addr, byte[] macro)

There is a convenience method to define and run a macro:

PC GUI Tools
The NXJ Browser

leJOS NXJ includes a PC-based file browser for viewing and manipulating the files
on the NXT.
It is started by the nxjbrowse script. If you are using an IDE, you can set NXJ Browse
up as an external tool.
The NXJ Browser need the NXT it is connecting to, to be turned on and to be running
the leJOS start-up menu. Although it should work while a user program is running, if
the program starts the LCPBTResponder thread, this is not recommended.
The NXJ Browser first looks for a NXT connected by USB, and if it finds one shows
just that in the selection window.
If it cannot find a USB-connected NXT, it does a Bluetooth inquiry to find any NXTs
in range and shows them in the selection window. You then select the one you wish to
connect to, and press connect.

After pressing Connect, the main browser screen is shown:

The NXJ browser shows you all the files on your NXT, together with their size in
bytes and their location in the file system (start page, end page). See Understanding
the leJOS NXJ File System to understand the meaning of the start and end pages.
The friendly name of the NXT you are connected to is shown in the title bar of the
Windows the name is NXT in this case.
From this screen, you can:

Delete programs
Upload programs and other files to the NXT
Download files from the NXT to the OC

Run programs
De-fragment the file system
Set the friendly name of the NXT

Deleting programs

To delete files from the NXT, click the Delete tick boxes and press Delete Files. The
next will make a chirping sound for each file deleted, and the NXJ Browser display
will be updated. If the NXT is on the Files menu, its LCD will also be updated. An
automatic defrag will occur when files are deleted, so there should be no gaps in the
file system.
Uploading files

When you click Upload File, an Open file dialog box appears. Browse for the file you
want to upload, select it, and click Open. NXJ Browse is not normally used for
uploading program files as this is better done using the nxj or nxjupload tool or the
leJOS Eclipse plugin. However, it is useful for uploading WAV files and data files for
programs.
Downloading files

Files can be downloaded from the NXT to the PC by selecting the NXT file and
pressing Download. This open a dialog that allows you to select the folder and
filename to download the file to.
Running programs

A program on the NXT can be run by selecting it and pressing Run. This shuts
down NXJ Browser.
De-fragmenting the file system

The NXT file system cab be defragmented (removing any gaps between files) by
pressing Defrag. In the latest versions of leJOS the file system does not normally
need to be defragmented.

Setting the friendly name of the NXT

The Bluetooth friendly name of the NXT can be set by pressing Set Name, filling in
the name (up to 16 characters) and pressing OK. Note that the Bluetooth power be on
in order to set the friendly name, even if the connection to the NXT is by USB.

Remote Monitoring and Tracing

You can monitor a leJOS program over a Bluetooth connection while it is running by
starting the LCPBTResponder thread and running the NXJ Monitor tool on the PC.
You an example of this, see the MonitorTest sample.
NXJ Monitor lets you see the values of sensors, and the values of motor tachometers,
in near real-time, while your program executes.
It also lets you program displaying tracing messages to indicate what it is doing.
These are displayed on the NXJ Monitor screen.
The program being monitored should include the following code in its start-up
sequence:
LCPBTResponder lcpThread = new LCPBTResponder();
lcpThread.setDaemon(true);
lcpThread.start();

On the PC, the NXJ Monitor tool should be run. NXJ Monitor lists the NXTs
available over Bluetooth in the same way as the NXJ Browser tool, and lets you
choose which one you wish to connect to.
When a connection has been made, the following window is displayed:

Data Viewer

The Data Viewer displays the output from DataLogger. You can cut and paste the data
into a spreadsheet for further analysis. It uses USB by default, but you can select
Bluetooth if you wish. When you start the program, this window appears:

It connects to the NXT via USB by default, but if you click the Use BlueTooth button,
it will.. For the fastest BlueTooth connection, enter the Bluetooth address. If you dont
know it, enter the NXT name. Otherwise, the program will take some time to search
and complete the connection. By default, the program displays 2 data values per row,
but you can enter any number you wish before starting to download. The DataLogger
gives you the option to resending its data if you want to see it again (with a different
row length for example). If you do, click the Start Download button again..

The Console viewer

The Console Viewer displays the output from RConsole, which is described in the
section of Error Handling and Debugging. When you start the program, this window
appears:

It connects to the NXT via USB by default, but if you click the Use BlueTooth button,
it will. For the fastest BlueTooth connection, enter the Bluetooth address. If you dont
know it, enter the NXT name. Otherwise, the program will take some time to search
and complete the connection.
The Connect button will initiate the connection with the NXT provided that the
RConsole program is running on the NXT.

NXJFlashG

This is the GUI version of NXJFlash. It installs the firmware into the flash memory of
your NXT. If your NXT has a recent version of the firmware already installed, it only
needs to be turned on and connected to the PC with the USB cable. Otherwise, it
needs to be put into firmware update mode by pressing the reset button for 4 seconds
or more.
When the program opens, it looks like this:

When you click start you see this :

The next message is :

If you click Yes, the entire flash memory of the NXT will be cleared. If you click no
and the installation fails, it might be because the the flash memory has been badly
corrupted. In which case, try again and click Yes.
As the installation proceeds, the progress log will be written. At the end, you have a
chance to install firmware again.

leJOS Utilities
ButtonCounter

This class allows simple data entry by pressing the NXT buttons. It counts the number
of presses of theLeft and Right buttons, Pressing Left (or Right) simultaneously
with Enter decrements the count.
To start counting, call one of these methods:

void count(String message)


void count(String message, int left, int right)

These methods exit when the user presses ESC. Then the values can be retrieved by

int getLeftCount()
int getRightCount()

The complete API for Button Counter is here


DataLogger

This class stores float values in an array and transmits them to


the DataViewer application running on a PC. It will use BlueTooth or USB as selected
by the user at run time.
Methods:

void writeLog(float value)

Writes a value to the array


void writeLog(float v0, float v1)

Writes two values to the array


void transmit()
Transmits the data to the PC. When this method is called, a message is
displayed on the NXT screen allowing the user to select USB or BlueTooth as
the transmission channel

The complete API of Data:logger is here.


Stopwatch

This class represents an elapsed time stopwatch. You can construct as many instances
as you need.
There are only two methods

void reset()
resets the watch to zero

int elapsed()
returns the elapsed time (since the last reset() in milliseconds.

The API of Stopwatch is here.

Timer

This class is used with a Timer Listener, to which it sends timedOut() message
periodically. It uses a private thread.
Methods:

void start()
call this first.

void setDelay(int delay)


set the interval between calls to the listener.

void stop()

The Timer API is here.


Timer Listener

To use the Timer, you need a class that implements this interface. This interface has
only one method.

void timedOut()
which is called every time the Timer fires.

Java Utilities

leJOS NXJ supports several of the Java data structures. They are:

ArrayList
BitSet
Hashtable
Queue
Stack
Vector
Enumeration
Iterator

It also supports two other Java utilities:

Random
Properties
StringTokenizer
Timer
TimerTask

Advanced Motors, Sensors and Third Party Hardware


Motor interfaces

leJOS supports a variety of motors, including the NXT motors, RCX motors, PF
motors, and a variety of servos. It supports them directly attached to a NXT motor
port, or connected by a multiplexer, or via RCX or PF converter cables, or connected
to a remote NXT via Bluetooth or RS485, or connected to a remote RCX via an
infrared link.
To help classify the types of motor, there are a set of motor interfaces in the
lejos.robotics packages. These interfaces allow you to write programs that are
essentially independent of the specific type of motor used and the way it is connected.
It is only the constructor for the a specific motor and its connection that needs to
change to use another type of motor or connection.
BaseMotor

The simplest interface that all motors implement is BaseMotor. It supports the
methods:

public void forward()


public void backward()
public boolean isMoving()
public void flt()
public void stop()

All leJOS NXJ motors implement this interface. However, servo motors are not
currently supported as they do not normally support these methods.

DCMotor

The DCMotor interface extends BaseMotor and adds methods for setting the power
and getting the current power setting. It is used for unregulated motors.

public void setPower(int power)


public int getPower()

Unregulated motors without encoders, such as the RCX motor, support this interface.
Encoder

Some motors, such as the NXT motors, have built in encoders. The Encoder interfaces
defines the methods that these support.

public int getTachoCount();


public void resetTachoCount();

EncoderMotor

The EncoderMotor interface extends DCMotor and Encoder and adds no new
methods. It is used for unregulated motors with encoders. The NXTMotor class
implements this interface.
TachoMeter

Tachometers are more sophisticated encoders that not only give a tachometer count,
but also continuously monitor the tachometer readings to give a rotation speed. They
are implemented in software for NXT motors. The tachometer interface extends
Encoder and adds:

public int getRotationSpeed();

RegulatedMotor

The RegulatedMotor interface is used for motors that implement speed regulation and
stopping at a defined encoder count, such as the NXTRegulatedMotor class that
implements the interface for NXT motors.

The RegulatedMotor interface extends BaseMotor and Tachometer and adds the
following methods:

public void addListener(RegulatedMotorListener listener);


public int getLimitAngle(;)
public float getMaxSpeed();
public int getSpeed();
public boolean isStalled();
public void rotate(int angle);
public void rotate(int angle, boolean immediateReturn);
public void rotateTo(int limitAngle);
public void rotateTo(int limitAngle, boolean immediateReturn);
public void setAcceleration(int acceleration);
public void setSpeed(int speed);
public void stop(boolean immediateReturn);
public void waitComplete();

NXTRegulatedMotor (which is the class of Motor.A, Motor.B, and Motor.C) support


this interface. RemoteMotor also supports it but with some restrictions.
RegulatedMotorListener

Regulated motors must generate events when the motor starts and stops. Classes that
listen for these events implement the RegulatedMotorListener interface, which has the
methods:

public void rotationStarted(RegulatedMotor motor, int tachoCount, boolean


stalled, long timeStamp);
public void rotationStopped(RegulatedMotor motor, int tachoCount, boolean
stalled, long timeStamp);

DifferentialPilot is an example of a class that supports this interface.

Unregulated Motor classes


BasicMotor

As well as the interface defined above, leJOS has an abstract motor class that simplify
the implementation of the unregulated motor classes.

BasicMotor is an abstract class which implements the DCMotor interface, and provide
methods common to all implementations of unregulated motors.
NXTMotor

NXTMotor is a very simple implementation of an unregulated motor for NXT motors.


It extends BasicMotor and implements the Encoder interface method, and therefore
implements the EncoderMotor interface.
It is used for applications such as Segway robots that need to directly control the
power supplied to the NXT motors, and do not need speed regulation or rotation to a
limit point.
To use an NXTMotor you create an instance of the NXTMotor class using one of the
constructors:

public NXTMotor(TachoMotorPort port);


public NXTMotor(TachoMotorPort port, int PWMMode);

Example:
NXTMotor m1 = new NXTMotor(MotorPort.A);

RCXMotor

The RCX motors do not have an in-built tachometer and so cannot support the
advanced functions of the NXT motors such as the rotate and rotateTo methods and
the speed regulation.
A simpler class is used to support the RCX motors. It has similar methods to the
Motor class in the RCX version of leJOS (which are the same as those supported by
the BasicMotor class).
To use an RCX motor you create an instance of the RCXMotor class using the
constructor:

public RCXMotor(BasicMotorPort port)

Example:

RCXMotor rcxMotor = new RCXMotor(MotorPort.A);

You can use RCX Motors with leJOS NXJ by connecting them with the conversion
cables that can be purchased from LEGO (and are bundled with the LEGO
MINDSTORMS NXT educational kits). You can also use the RCXMotor class to
control RCX motors connected to a remote RCX - see the "Communications" tutorial.
RCXMotor extends BasicMotor and therefore implements the DCMotor interface.
RCX motors can be connected to a sensor port via the RCXMotorMultiplxer device.
This allows up to 4 RCX motors to be connected and thus entends the number of
motors that a NXT can support.
RCX Motors can be attached to RCX bricks and controlled remotely via the
Mindsensors NRLink device using the RCXRemoteMotorPort class.

Regulated motor classes

The NXTRegulatedMotor class controls the NXT motors, and is described


in Controlling the Motors. It implements speed regulation and rotation by a specified
number of degrees. Motor.A, Motor.B and Motor.C are instances of
NXTRegulatedMotor.
RemoteMotor also implements the RegulatedMotor class but with restrictions. It
sends LCP commands to a remote NXT, which uses an instance of
NXTRegulatedMotor to implement them. RemoteMotor is available on the NXT and
on the PC (via the pccomms API). On the PC, Motor.A, Motor.B and Motor.C are
instances of RemoteMotor.
On the NXT, RemoteMotor can be used to control motors on another NXT either over
Bluetooth, or using RS485, by connecting port 4 of the two NXTs via a NXT cable.
RS485 has lower latency than Bluetooth, so the motors will be more responsive over
RS485.

MotorPort classes

To use the NXT motors it is not necessary to explicitly use the MotorPort class: you
can just use the variables Motor.A, Motor.B and Motor.C. So if you are only using
NXT motors, you can skip this section.
However it is useful to understand how motor ports work as they are used by:

The NXTRegulatedMotor class


The RCXMotor class
The RemoteNXT class
The RCXMotorMultiplexer class
Remote RCX motors accessed via the RCXLink class

There is a hierarchy of interfaces defined for motor ports:

BasicMotorPort
Tachometer
TachoMotorPort

All motor ports support the BasicMotorPort interface which allows control of a
motors power and mode (forward, backward, stop, float).
Ports that supports this include:

NXT ports connected to NXT motors


NXT ports connected via the RCX conversion cable to RCX motors
Ports on the RCXMotorMultiplexer adapter
Ports on remote NXTs accessed via the RemoteNXT class
Ports on remote RCXs accessed via the RCXLink class

The implementations of BasicMotorPort include:

MotorPort
PFMotorPort
RemoteMotorPort
RCXPlexedMotorPort
RCXRemoteMotorPort

The tachometers that are built in to the NXT motors support the Tachometer interface.
NXT motor ports support the TachoMotorPort interface which includes the
BasicMotorPort and Tachometer interfaces.

Implementations of TachoMotorPort include:

MotorPort
RemoteMotorPort

All this sounds rather complicated, but is simple to use:

For NXT motors, you use Motor.A, Motor.B and Motor.C


For RCX motors connected by the conversion cable you use
RCXMotor(MotorPort.A), RCXMotor(MotorPort.B) or
RCXMotor(MotorPort.C)
For NXT motors on a remote NXT, you use remoteNXT.Motor.A,
remoteNXT.Motor.B or remoteNXT.Motor.C where remoteNXT is an instance
of RemoteNXT.
For RCX motors connected by the RCX Motor Multiplexer, you use
rcxMotorMultiplexer.A, rcxMotorMultiplexer.B, rcxMotorMultiPlexer.C or
rcxMotorMultiplexer.D, where rcxMotorMultiplexer is an instance of
RCXMotorMultiPlexer.
For RCX motors connected t remote RCXs via the RCXLink class you use
rcxLink.A, rcxLink.B or rcxLink.C where rcxLink is an instance of the
RCXLink class.

LEGO Power Function motors

LEGO PF motors can be controlled by leJOS NXJ in a variety of ways. They can be
directly connected to the NXT via converter cables, or they can be controlled remotely
using their IR receivers use several different third party devices.
If you use converter cables, then PF motors are best driven using the RCXMotor class.
To drive PF Motors remotely, via the IR Receiver, three different devices can be used.
The Mindsensors PFMate device is designed to drive PF motors directly. This device
is supported by the PFMate and PFMateMotor classes. PFMate implements the
DCMotor interface, and therefore provides similar methods to RCXMotor.
Using the HiTechnic IRLink device, one way to drive PF motors is to use the
PFMotorPort class. This provides a BasicMotorPort interface and so can be used as
the BasicMotorPort parameter for the RCXMotor constructor. This allows RCXMotor
to drive remotely connected PF motors. Another option is to use the methods of the

PFLink class directly. This gives greater control but the methods are specific to this
device.
A third device that can be used to drive PF motors is the Mindsensors NRLink. The
PFLink class can be used to drive this device and send commands to the PF motors.
The methods are specific to this device and none of the motor or motor port interfaces
are supported.

Servos and Linear Actuators

leJOS NXJ has several classes in lejos.nxt.addon to support Servo motors.


The Mindsensors MSC8 device is supported by the MSC and MServo classes. MSC
represents the controller, which allows up to 8 servo motors to be connected. MServo
represents each servo. the main methods are setSpeed and setAngle. None of the
motor interfaces are supported.
The Lattebox range of products is supported by the NXTe, LSC and LServo classes.
Firgelli L12-NXT-50 and L12-NXT-100 Linear Actuators are supported by the
LnrActuator class.

Motor multiplexers

The motor multiplexors supported by leJOS NXJ are:

Mindsensors RCX multiplexer


Mindsensors NXT multiplexer

The RCX multiplexor is supported by the RCXMotorMultiplexor and


RCXPlexdMotorPort classes.

Sensor Ports

If you are using a sensor connected directly to a NXT sensor port, you can use the
SensorPort class and you can probably skip this section.

But if you are using a port splitter, or a remote NXT or RCX, it may be of interest.
The NXT sensor ports support three different types of sensor:

NXT Analog/Digital Sensors


I2C Sensors
Legacy RCX sensors

Corresponding to each of the different types of sensor, there is a corresponding


interface:

ADSensorPort which extends BasicSensorPort


I2CPort extends BasicSensorPort
LegacySensorPort extends ADSensorPort

At the top of the interface hierarchy is the BasicSensorPort. All sensor port classes
implement this interface. This interface allows the type and mode of a sensor to be set.
These type and mode constants are defined by the interface SensorConstants, which is
inherited by the BasicSensorPort interface.
The types of sensors are:
TYPE_NO_SENSOR = 0
TYPE_SWITCH = 1
TYPE_TEMPERATURE = 2
TYPE_REFLECTION = 3
TYPE_ANGLE = 4
TYPE_LIGHT_ACTIVE = 5
TYPE_LIGHT_INACTIVE = 6
TYPE_SOUND_DB = 7
TYPE_SOUND_DBA = 8
TYPE_CUSTOM = 9
TYPE_LOWSPEED = 10
TYPE_LOWSPEED_9V = 11
TYPE_HISPEED = 12
TYPE_COLORFULL = 13
TYPE_COLORRED = 14
TYPE_COLORGREEN = 15
TYPE_COLORBLUE = 16
TYPE_COLORNONE = 17

and the modes are:


MODE_RAW = 0x00
MODE_BOOLEAN = 0x20
MODE_TRANSITIONCNT = 0x40
MODE_PERIODCOUNTER = 0x60
MODE_PCTFULLSCALE = 0x80
MODE_CELSIUS = 0xA0
MODE_FARENHEIT = 0xC0
MODE_ANGLESTEP = 0xE0

The BasicSensorPort interface defines the methods:

public
public
public
public
public

int getmode();
int gettype();
void setmode(int mode);
void settype(int type);
void settypeandmode(int type, int mode);

Most of the time, with leJOS NXJ, these types and modes do not need to be set
explicitly as it is done by the constructor for the sensor class being used, e.g.
TouchSensor, LightSensor and UltrasonicSensor.
The implementation of the NXT sensor port SensorPort supports all these
interfaces. The reason for separating out the different interfaces is that there are other
implementations of sensor ports that only support a subset of these interfaces, and
different types of sensors only require particular interfaces to be implemented:

I2C Sensors just require I2CPort


NXT Analog/Digital sensors just require ADSensorPort
RCX sensors such as the RCX Light sensor require LegacySensorPort

Port splitters like the Mindsensors Split-Nx only support I2C sensors and thus,
effectively, only support the I2CPort interface.
There are other implementations that only support the other interfaces. For example
the current implementation of remote sensor ports RemoteSensorPort currently
only supports the ADSensorPort interface.
The classes for RCX Sensors multiplexers such as the forthcoming Mindsensors
version will only support the LegacySensorPort interface.

Sensors

Each sensor supported by leJOS NXJ has a specific class that is used to access the
sensor. Each of these sensor classes has, as a parameter, a sensor port that supports the
required interface. Any sensor port class that implements the interface can be
specified as the parameter. As the SensorPort class supports all the interfaces, if the
sensor being accessed is directly connected to the NXT, the parameter should be one
of SensorPort.S1, SensorPort.S2, SensorPort.S3 or SensorPort.S4.
If a port splitter is used the parameter again should be one of SensorPort.S1,
SensorPort.S2, SensorPort.S3 or SensorPort.S4. This specifies the port that the splitter
is connected to. If multiple sensors are connected to the splitter they must each have
different I2C addresses. Most I2C sensors can have their address changed see the
manufacturers instructions. To specify the address that a sensor uses, if it is not the
default, then you may need to use an alternate constructor for the sensor class to allow
you to specify the address. If such a constructor does not exist please report this to us
and use the setAddress method of I2CSensor.
The sensor ports supported by leJOS NXJ together with the class that supports them
and the type of sensor port they require is given in the following table:
Hardware Device

Class

SensorPort
interface

LEGO NXT Touch


Sensor

TouchSensor

ADSensorPort

LEGO NXT Light


Sensor

LightSensor

ADSensorPort

LEGO NXT Sound


Sensor

SoundSensor

ADSensorPort

LEGO NXT Color


Sensor

ColorSensor

SensorPort

LEGO NXT Ultrasonic


Sensor

UltrasonicSensor

I2CPort

RCX Light Sensor

RCXLightSensor

LegacySensorPort

RCX Temperature
Sensor

RCXTemperatureSensor ADSensorPort

RCX Rotation Sensor

RCXRotationSensor

ADSensorPort

HiTechnic Compass
Sensor

CompassSensor

I2CPort

HiTechnic Color Sensor ColorSensorHT

I2CPort

HiTechnic Acceleration
Sensor

AccelHTSensor

I2CPort

HiTechnic Gyro Sensor

GyroSensor

ADSensorPort

HiTechnic IR Seeker

IRSeeker

ADSensorPort

HiTechnic IRSeeker v2

IRSeekerV2

ADSensorPort

HiTechnic IRLink

IRLink

I2CPort

HiTechnic EOPD
Sensor

EOPD

ADSensorPort

HiTechnic Sensor
Multiplexer

SensorMux

I2CPort

HiTechnic Angle Sensor AngleSensor

I2CPort

Mindsensors Compass
Sensor

CompassSensor

I2CPort

Mindsensors
Acceleration Sensor

AccelMindSensor

I2CPort

Mindsensors NXTCam

NXTCam

I2CPort

Mindsensors LineLeader LineLeader

I2CPort

Mindsenors NXTMMX

NXTMMX

I2CPort

Mindsensors Dist-Nx

OpticalDistanceSensor

I2CPort

RCX Touch Sensor

TouchSensor

ADSensorPort

Mindsenors NRLink-Nx PFLink

I2CPort

Mindsensors PFMate

PFMate

I2CPort

Mindsensors PSP-Nx

PSPNXController

I2CPort

Mindsensors NRLink

RCXLink

I2CPort

Mindsensors RCX
Multiplexer

RCXMotorMultiplexer

I2CPort

Mindsensors RTC

RealTimeClock

I2CPort

Mindsensors Touch
Multiplexer

TouchMUX

I2CPort

Mindsensors Servo

MSC

I2CPort

Multiplexer
Codatext RFID Sensor

RFIDSensor

I2CPort

Dexter Industries
DSwitch

DSwitch

I2CPort

Dexter Industries dGPS


sensor

GPSSensor

I2CPort

Linear Actuator

LnrActuator

I2CPort

Lattebox LSC

LSC

I2CPort

Lattebox NXTe

NXTe

I2CPort

Micro Infinity Cruizcore


CruizcoreGyro
Gyro

I2CPort

RCX Sensors

RCX sensors, other than the touch sensor, are active sensors that have voltage applied
for all but the short period every three milliseconds when the measurement is taken.
RCX Light Sensor

The RCX light sensor is supported by the RCXLightSensor class.


The constructor is:

public RCXLightSensor(LegacySensorPort port)

For example:
RCXLightSensor light = new RCXLightSensor(SensorPort.S1);

The RCX light sensor is automatically activated, so current is applied to it and the
LED comes on. It can be passivated and activated explicitly.
The methods are:

public int readValue()

public void activate()


public void passivate()

RCX Touch Sensor

As the RCX touch sensor is a passive sensor similar to the NXT version, it is
supported by the standard TouchSensor class.
RCX Rotation Sensor

The RCX rotation sensor is not currently supported by leJOS NXJ.


RCX Temperature Sensor

The constructor is:

public RCXTemperatureSensor(LegacySensorPort port)


The methods are:
int readValue() - returns raw value
float getCelcius() - return the temperature in degrees Celcius
float getFarenheit() - return the temperature in degrees Farenheit

Third party sensors and other devices

leJOS NXJ supports many third party sensors. The two main vendors of third party
sensors are Mindsensors and HiTechnic.
Most of the third party sensors and I2C sensors and extend the I2CSensor class but
there are also Analog/Digital sensors such as the HiTechnic Gyro sensor and the IR
Seeker.
There are also other I2C devices supplied by the third parties, that are not sensors, but
are multiplexers or adapters.
The RCX Motor Multiplexer from Mindsensors is an example of a multiplexer. It
allows up to 4 RCX motors to be connected to a NXT sensor port and to be
independently controlled.

The Mindstorms NRLink-Nx infra-red communications adapter is an example of an


adapter. It allows two-way communication between the NXT and RCXs. It also
allows control of Power Function motors.

I2CSensor

The I2CSensor class implements the basic methods for accessing I2C sensors
including getData and SendData.
It also includes methods that are implemented by all the I2C sensors, including
getVersion, getProductID and getSensorType.
The method signatures are:

public int getData(int register, byte [] buf, int len)


public int sendData(int register, byte [] buf, int len)
public int sendData(int register, byte value)
public String getVersion()
public String getProductID()
public String getSensorType()

Individual I2C devices have registers that can be read and written and registers that
can be used to execute commands. Each I2C device has a class that extends
I2CSensor and has methods to access the registers and execute the commands specific
to that sensor.
The I2CSensor class can be used to implement an I2C device explorer that reports
what devices are connected to which sensor using which address see the I2CDevices
sample. This is possible as all the NXT I2C sensors and other devices support the
getVersion, getProductID and getSensorType methods.
See the table above for the complete list of sensors and other third party devices.

I2CPort

The I2CPort provides low level access to a port being used for I2C communications.
In many cases these operations are not required and the higher level I2CSensor class
is used. However some advanced applications (like those scanning ports to identify
sensors) may require access at this level.

The method signatures are:

public void i2cEnable(int mode)


public void i2cDisable()
public int i2cStatus()
public int i2cTransaction(int deviceAddress, byte[]writeBuf, int writeOffset, int
writeLen, byte[] readBuf, int readOffset, int readLen)

To use a port with I2C it must be enabled. When I2C operation is no longer required it
should be disabled. The enable method also sets the operating mode of the port. The
available operating modes are:
STANDARD_MODE = 0
LEGO_MODE = 1
ALWAYS_ACTIVE = 2
NO_RELEASE = 4
HIGH_SPEED = 8

The mode normally used is LEGO_MODE. This offers compatibility with the
standard Lego firmware. The bus will operate at 9.6Kbps and will add delays to be
compatible with the Lego Ultrasonic sensor. This mode will normally work with all
NXT compatible devices. STANDARD_MODE operates the port in a more I2C
standard compliant way. You may wish to try using this if you are having problems
with a device. HIGH_SPEED mode operates the bus at a much higher speed
(125Kbps), to function at this speed the device will typically need to be using a
hardware implementation of I2C (many of the HiTechnics and Mindsensors devices
can be used at this speed). It should be noted that these modes operate at the port level
not for an individual sensor so if more than one sensor is attached to the same port
they will all share the same operating mode. To set the operating mode of a port a call
to enable(mode) should be made prior to creating any instances of sensors to be
associated with the port.

Advanced Topics: Files, LCP, Memory, NXJ Tools


Understanding the leJOS File System

The NXT has 256kb of flash memory. A fixed section at the start of the flash memory
is allocated to the system. It is used to hold the firmware, followed by the startup
menu. The rest of this system area is unused. The size of the system area varies
between releases.
The firmware is written in C, with some ARM assembly language. The startup menu
is written in Java (in the startup project in SVN).
Flash memory is read and written in 256-byte pages. The first page after the system
area is used for persistent system settings administered by the startup menu.
The rest of the flash memory is used for the user file system. The first two pages hold
the file table (directory), and the rest of the pages hold user files. Files are held as a
contiguous set of bytes i.e they use a single range of page numbers with no gaps.
This allows a file to be addressed as a region of memory.
The Flash class

The Flash class has methods to read and write 256-byte pages of flash memory. It
should not be used by user programs.
The System Settings class

The SystemSettings class is used to administer the system settings page. It should not
be used by user programs.
The File class

The File class has static methods that manipulate the file system as a whole and
instance methods that give access to specify files.
Static methods:

void defrag() - removes unused pages in the file system.


void format() - erases all the files and reformats the flash memory.
int freeMemory() - returns the total free memory in the file system.
File[] listfiles() - returns an array of files in the system

To read and write files, you need to use streams. The stream constructors throw an
IOException so they also must be in a try/catch block.

The file streams read and write individual bytes, so you will usually want to use a data
stream filter.
FileOutputStream

The constructors are:

FileOutputStream(File f) - creates new OutputStream to write to this file,


starting at the beginning of the file. The file is deleted if it already exists. A
new file is then created. Methods:
FileOutputStream(File f, boolean append) - create a new OutputStream to write
to this file
void write(int b) - write 1 byte to the file
void flush() - Flushes this output stream and forces any buffered output bytes to
be written
void close() - Write the buffer to flash memory and update the file parameters
in flash. Be sure to call this method before your program exits.

Example:
import java.io.*;
import lejos.nxt.*;
public class FileWriteTest {
public static void main(String[] args) {
FileOutputStream out = null; // declare outside the try block
File data = new File("log.dat");
try {
out = new FileOutputStream(data);
} catch(IOException e) {
System.err.println("Failed to create output stream");
Button.waitForPress();
System.exit(1);
}
DataOutputStream dataOut = new DataOutputStream(out);
float x = 1f;
int length = 8;

try { // write
for(int i = 0 ; i<length; i++ ) {
dataOut.writeFloat(x);
x = x*-2.2f;
}
out.close(); // flush the buffer and write the file
} catch (IOException e) {
System.err.println("Failed to write to output stream");
}
Sound.beep();
Button.waitForPress();
}
}

After you run this example, look for the newly created file in the Files menu.
You can also use the PrintStream class or the OutputStreamWriter class to write to
files.
FileInputStream

The constructor is:

FileInputStream( File afile)

Methods:

int read() - returns a value between 0 and 255, representing a single byte
int available() - returns the number of bytes available to be read.

Here is an example that reads the file


import java.io.*;
import lejos.nxt.*;
public class FileReadTest {
public static void main(String[] args) {
File data = new File("log.dat");
int i = 0;
try {

InputStream is = new FileInputStream(data);


DataInputStream din = new DataInputStream(is);
while (is.available() > 3) { // at least 4 bytes left to read
float x = din.readFloat();
System.out.println("" + x);
}
din.close();
} catch (IOException ioe) {
System.err.println("Read Exception");
}
Button.waitForPress();
}
}

Understanding LCP

LEGO defines a protocol called the LEGO MINDSTORMS NXT Communications


Protocol (LCP) which is used to send commands to the standard LEGO firmware. The
specification is available athttp://mindstorms.lego.com/Overview/NXTreme.aspx in
the Bluetooth Development Kit. The commands are separated into direct commands
and system commands. The direct commands are described in a separate document:
LEGO MINDSTORMS NXT : Direct Commands.
Direct commands are those that are designed for user programs and tools to use to
control robots. The system commands are designed for tools that upload and
download files and do other administrative tasks.
leJOS NXJ emulates many of the direct and systems commands so that many tools
that work with the standard LEGO firmware also work with leJOS.
Many of the leJOS NXJ tools including nxj, nxjuload, nxjbrowse and nxjmonitor use
LCP. leJOS NXJ has some minor additions to LCP to make its tools work better.
The implementation of LCP is in the lejos.nxt.comm.LCP class. As leJOS sensors and
motors work a bit differently than the standard firmware, the semantics of LCP on
leJOS are not always identical to the standard LEGO firmware.

The start-up menu uses LCP to support the leJOS NXJ tools and third-party tools.
This means that LCP commands can be executed over Bluetooth or USB when the
menu is running.

Understanding leJOS NXJ use of memory

The NXT has 256kb of flash memory and 64kb of RAM.


Flash memory can be read like RAM (access is a bit slower) but can only be written in
256-byte pages by specific hardware instructions,. Flash memory cannot be read while
a page is being written.
The leJOS NXJ firmware is written in a combination of C and ARM assembler code.
It consists of the initialization code, the Java VM and device drivers for all the
hardware subsystems. The leJOS firmware is a complete firmware replacement and
has no reliance on the standard LEGO firmware. Most code is executed from flash
memory, but a small amount (e.g. the code that writes pages of flash memory) is
copied to RAM. Read-only data is held in flash memory but read/write data is copied
to RAM. The firmware uses a fixed size stack and interrupt stack.
The leJOS NXJ Java VM executes one Java program at a time. This can either be a
user program or the leJOS start-up menu. One Java program can execute another.
When this is done the first Java program is removed from memory, and the second
one is then executed. This is how the start-up menu executes user programs.
Java programs execute from flash memory. Static read-only data is held in flash
memory. Static read-write data is copied to RAM. Objects are created in a heap that
starts at the top of the RAM and grows downwards. The Java stack starts at the
bottom of free RAM memory and grows up. A garbage collector frees memory used
by unreferenced objects when the heap becomes full.

Understanding the key leJOS NXJ tools


nxjflash

In order to flash firmware to the NXT, it is necessary that the NXT is in firmware
update mode. If you have the standard LEGO firmware installed (or if you have a very
recent version of leJOS), then the nxjflash program will do this automatically for you.

If not or if there is some problem with the automatic method then you will need to
place the NXT into firmware update mode by pressing the reset button for 4 seconds
or more. This causes the NXT to run a small boot assistant program called SAM-BA.
The was written by Atmel, the maker of the ARM chipset that the NXT uses. SAMBA includes a USB driver and accepts commands sent over the USB link. These
command allows data to be uploaded to RAM and code to be executed. Early version
of leJOS used this mechanism to run in RAM before there was a flash version of
leJOS NXJ. The latest version of nxjflash will put the NXT into update mode if it is
not already so.
On Microsoft Windows and MAC OS X, the standard LEGO USB drivers are used.
These come with the LEGO software, which is why this software must be installed.
On Microsoft windows, when the NXT is in firmware update mode, a USB cable is
attached to your PC and the NXT is switched on (by pressing the orange button), if
you go to Control Panel > System > Hardware > Device Manager you will see under
Lego Devices an entry for the USB driver, labelled LEGO MINDSTORMS NXT
Firmware Update Mode.
To flash new firmware, nxjflash uploads the firmware image a 256-byte page at a time
and then executes a small RAM-resident routine to write the page to flash memory. In
this way the leJOS NXJ firmware, lejos_nxt_rom.bin is written to flash memory.
nxjflash also uploads the leJOS NXJ start-up menu StartUpText.bin. This menu is
written in Java and built like any other leJOS NXJ Java programs. It implements the
leJOS NXJ menu system and supports threads for executing Lego Communication
Protocol (LCP) commands over USB and Bluetooth. When both the firmware and the
menu have been uploaded, nxjflash sends a SAM-BA command to the NXT causing it
to jump to address zero and the leJOS NXJ firmware executes.

nxjupload

nxjupload uploads programs or other files over USB or Bluetooth. It is a command


line interface that is suitable for use from command windows, ant scripts, and as an
external command from IDEs such as Eclipse or Netbeans.
nxjupload sends LCP system commands to the NXT to upload the file. The commands
are OPEN_WRITE, WRITE and CLOSE.

By default nxjupload first looks for a NXT connected by USB. If it does not find one,
it tries Bluetooth. It does a Bluetooth inquiry looking for NXTs, and saves the results
is in a cache file which it uses in preference to doing a Bluetooth inquiry when it can .
It it finds any it tries to connect to each one, and uploads the file to the first one it
successfully connects to. This means that if you have multiple NXTs, it will upload to
the one that is currently switched on.
nxjupload uses the apache Commons CLI command line processor.

nxjlink

nxjlink calls the linker (class js.tinyvm.TinyVM).


The linker first looks for the specified class in the linker classpath, and then looks for
all classes that this references to form a closure of the classes. The linker class path
should include classes.jar and all the user classes in the program. The class specified
on the command line must be the one containing the main method. The Jakarta apache
Byte Code Engineering Library (BCEL) is used to process the class files.
The linker omits methods that have not been referenced unless the a or all flag is
specified. The way this is done uses a simple algorithm and does not manage to omit
all unreferenced methods.
The linker produces a leJOS NXJ binary file and writes it to the file specified in the
o parameter.
nxjlink needs to know the byte order of the processor it is producing the binary for.
For the NXT this is set by writeorder LE for Little-Endian.
If the verbose option is set, a list of the classes and methods in the binary is output to
standard out. This is very useful for debugging.
nxjlink uses the apache Commons CLI library for command line processing.

Behavior programming
Programming Behavior with leJOS NXJ

When most people start programming a robot, they think of the program flow as a
series of if-thens, which is remeniscent of structured programming (Figure 1). This
type of programming is very easy to get started in and hardly requires any thought or
design beforehand. A programmer can just sit at the computer and start typing (
although a little thought before the typing may avoid a lot of grief later. )

Fig 1 Structured programming visualized.


The problem is, the code ends up as spaghetti code; all tangled up and difficult to
expand.
The behavior control model, in contrast, requires a little more planning before coding
begins, but the payoff is that each behavior is nicely encapsulated within an easy to
understand structure. This will theoretically make your code easier to understand by
other programmers familiar with the behavior control model, but more importantly it
becomes very easy to add or remove specific behaviors from the overall structure,
without negative repercussions to the rest of the code. It also makes it possible to test
and debug each behavior by itself.
The concepts of Behavior Programming as implemented in leJOS NXJ are very
simple.:

Only one behavior can be active and in control of the robot at any time.
Each behavior has a fixed priority.
Each behavior can determine if it should take control.
The active behavior has higher priority than any other behavior that should take
control.

The Behavior API

The Behavior API is is composed of only one interface and one class. The Behavior
interface defines the individual behavior classes. The Behavior interface is very

general and defines three public methods. Each task that the robot must perform can
be defined in its own class. It works quite well because, even though the individual
implementations of a behavior vary widely, they are all treated alike. Once all the
behaviors are created, they are given to an Arbitrator to regulate which behavior
should be activated at any time. The Arbitrator class and the Behavior interface are
located the lejos.subsumption package. The API for the Behavior interface is as
follows.
lejos.subsumption.Behavior

boolean takeControl()
Returns a boolean value to indicate if this behavior should become active. For
example, if a touch sensor indicates the robot has bumped into an object, this
method should return true. This method should return quickly, not perform a
long calculation.

void action()
The code in this method begins performing its task when the behavior becomes
active. For example, if takeControl() detects the robot has collided with an
object, the action() code could make the robot back up and turn away from the
object.
A behavior is active as long as its action() method is running, so the action()
method should exit when it's task is complete. Also, the action() method should
exit promptly when suppress() is called. When it exits, it should leave the robot
in a safe state for the next behavior.

void suppress()
The code in the suppress() method should immediately terminate the code
running in the action() method. It also should exit quickly.

As you can see, the three methods in the Behavior interface are quite simple. If a robot
has three discreet behaviors, then the programmer will need to create three classes,
with each class implementing the Behavior interface. Once these classes are complete,
your code should hand the behavior objects off to the Arbitrator to deal with.
lejos.subsumption.Arbitrator

The constructor is:

public Arbitrator(Behavior[] behaviors, boolean returnWhenInactive)


Creates an Arbitrator object that regulates when each of the behaviors will
become active.
Parameter: behaviors. The index of each behavior is its index in the array. So
behaviors[0] has the lowest priority.
Parameter: boolean returnWhenInacative; If true, the program exits when there
no behavior that should take control. Otherwise, the program runs until shut
down by pressing the Enter and Escape buttons.
Parameter: an array of Behaviors

Public Methods:

public void start()


Starts the arbitration system.

The Arbitrator class is even easier to understand than Behavior. When an Arbitrator
object is instantiated, it is given an array of Behavior objects. Once it has these, the
start() method is called and it begins arbitrating; deciding which behavior will become
active. The Arbitrator calls the takeControl() method on each Behavior object, starting
with the object with the highest index number in the array. It works its way down
through the array, (in decreasing priority order) till it finds a behavior that wants to
take control. If the priority index of this behavior is greater than that of the current
active behavior, the active behavior is suppressed. The action method is then called on

the behavior of this index. As a result, if several behaviors want to take control, then
only the only the highest priority behavior will become active. (Figure 2).

Figure 2: Higher level behaviors suppress lower level behaviors.


Coding Behaviors

For reliable performance of the behavior control system, it is essential that action()
method terminates promptly when suppress() is called. One way to guarantee this is to
define a boolean flag suppressed in each behavior. This variable is set to true by the
suppress() method and tested in every loop of the action() method. Of course, the first
thing an action() method must do is set this flag to false. The action() method might
be quite complex, such as a separate thread for line tracking or wall following, but it
must be coded to ensure prompt exit from action() when necessary. This is
the recommended design pattern . .
Now that we are familiar with the Behavior API under leJOS, let's look at a simple
example using three behaviors. For this example, we will program some behavior for
a simple robot with differential steering. This robot will drive forward as it's primary
low-level behavior. . This activity continues unless the robotic hits an object,then a
high priority behavior will become active to back the robot up and turn it 90 degrees.
There will also be a third behavior which we will insert into the program after the first
two have been completed. Let's start with the first behavior.
As we saw in the Behavior interface, we must implement the methods action(),
suppress(), and takeControl(). The behavior for driving forward will take place in the
action() method. It simply needs to make motors A and C rotate forward and it exits
when the motors are no longer moving, or suppress is called; This behavior remains
active as long as the motors are turning. The method code is::
public void action() {
suppress = false;

// standard first line of action

Motor.A.forward();
Motor.C.forward();
while( !suppressed)
Thread.yield(); // wait for

suppressed to be called}

Motor.A.stop(); // clean up
Motor.C.stop();
}

That was easy enough! Now the standard suppress() method will stop this action when
it is called:
public void suppress() {
suppressed = true;
}

So far, so good. Now we need to implement a method to tell Arbitrator when this
behavior should become active. As we outlined earlier, this robot will drive forward
always, unless something else suppresses it, so this behavior should always want to
take control (it's a bit of a control freak). The takeControl() method should return true,
no matter what is happening. This may seem counter intuitive, but rest assured that
higher level behaviors will be able to cut in on this behavior when the need arises. The
method appears as follows:
public boolean takeControl() {
return true;
}

That's all it takes to define our first behavior to drive the robot forward. The complete
code listing for this class is as follows:
import lejos.subsumption.*;
import lejos.nxt.*;
public class DriveForward

implements Behavior {

private boolean suppressed = false;

public boolean takeControl() {


return true;
}
public void suppress() {
suppressed = true;
}
public void action() {
suppressed = false;
Motor.A.forward();
Motor.C.forward();
while( !suppressed ) Thread.yield();
Motor.A.stop(); // clean up
Motor.C.stop();
}

The second behavior is a little more complicated than the first, but still very similar.
The main action of this behavior is to reverse and turn when the robot strikes an object
or detects one close by. In this example, we would like the behavior to take control
only when the touch sensor strikes an object, or the ultrasonic sensor gets an echo
from a close object. Here is the takeConrol() method that does it:
public boolean takeControl() {
return touch.isPressed() || sonar.getDistance() < 25;
}

This assumes that a TouchSensor object has been created in an instance variable
called touch and a new UltraSonicSensor object has been assigned to the
variable sonar.
For the action, we want the robot to back up and rotate when it detects an object, so
we will define the action() method as follows:
public void action()
{
suppressed = false;

Motor.A.rotate(-180,true);
Motor.C.rotate(-360,true);
while(Motor.C.isRotatinng() && !suppressed)
Thread.yield();

// wait till turn is complete or suppressed is

called}
if(suppressed ){
Motor.A.stop();
Motor.C.stop();
}
}

We use the standard suppress() method here too.


public void suppress {
suppressed = true;
}

The complete listing for this behavior is as follows:


import lejos.subsumption.*;
import lejos.nxt.*;
public class HitWall implements Behavior {
public HitWall(SensorPort port )
{
sonar = new UltrasonicSensor(port );
}
private boolean suppressed = false;
private TouchSensor touch;
private UltrasonicSensor sonar;
private boolean suppressed = false;
public boolean takeControl() {
return touch.isPressed() || sonar.getDistance() < 25;
}
public void suppress() {

suppressed = true;
}

public void action() {


// Back up

and turn

suppress = false;
Motor.A.rotate(-180,true);
Motor.C.rotate(-360,true);
while( Motor.C.isRotating() & !suppressed )
Thread.yield();
Motor.A.stop();
Motor.C.stop();
}

We now have our two behaviors defined, and it's a simple matter to make a class with
a main() method to get things started. All we need to do is create an array of our
behavior objects, and instantiate and start the Arbitrator as shown in the following
code listing:
import lejos.subsumption.*;
public class BumperCar {
public static void main(String [] args) {
Behavior b1 = new DriveForward();
Behavior b2 = new HitWall(SensorPort.S2);
Behavior [] bArray = {b1, b2};
Arbitrator arby = new Arbitrator(bArray);
arby.start();
}
}

The above code is fairly easy to understand. The first two lines in the main() method
create instances of our behaviors. The third line places them into an array, with the

lowest priority behavior taking the lowest array index. The fourth line creates the
Arbitrator, and the fifth line starts the Arbitration process. When this program is
started the robot will scurry forwards until it bangs into an object, then it will retreat,
rotate, and continue with its forward movement until the power is shut off.
This seems like a lot of extra work for two simple behaviors, but now let's see how
easy it is to insert a third behavior without altering any code in the other classes. This
is the part that makes behavior control systems very appealing for robotics
programming. Our third behavior could be just about anything. We'll have this new
behavior monitor the battery level and play a tune when it dips below a certain level.
Examine the completed Behavior:
import lejos.subsumption.*;
import lejos.nxt.*;
public class BatteryLow implements Behavior {
private float LOW_LEVEL;
private boolean suppressed = false;
private static final short [] note = {
2349,115, 0,5, 1760,165, 0,35, 1760,28, 0,13, 1976,23,
0,18, 1760,18, 0,23, 1568,15, 0,25, 1480,103, 0,18,
1175,180, 0,20, 1760,18, 0,23, 1976,20, 0,20, 1760,15,
0,25, 1568,15, 0,25, 2217,98, 0,23, 1760,88, 0,33, 1760,
75, 0,5, 1760,20, 0,20, 1760,20, 0,20, 1976,18, 0,23,
1760,18, 0,23, 2217,225, 0,15, 2217,218};
public BatteryLow(float volts) {
LOW_LEVEL = volts;
}
public boolean takeControl() {
float voltLevel = Battery.getVoltage();
System.out.println("Voltage " + voltLevel);
return voltLevel < LOW_LEVEL;
}
public void suppress() {
suppressed = true;
}
public void action() {

suppressed = false;
play();
System.exit(0);
}
public static void play() {
for(int i=0;i <note.length; ; i+=2) {
final short w = note[i+1];
Sound.playTone(note[i], w);
Sound.pause(w*10);
if (suppressed) return; // exit this method if suppress is called
}
}
}

The complete tune is stored in the note array at line 6 and the method to play the notes
is at line 30. This behavior will take control only if the current battery level is less the
voltage specified in the constructor. The takeControl() method looks a little inflated,
and that's because it also displays the battery charge to the LCD display. The action()
method is comparatively easy. Action makes a bunch of noise, then exits the program.
Since this behavior stops the program, we might get away without a a suppress()
method. But just in case we detect a wall, while playing the tune, we use one.
To insert this behavior into our scheme is a trivial task. We simply alter the code of
our main class as follows:
import lejos.subsumption.*;
public class BumperCar {
public static void main(String [] args) {
Behavior b1 = new DriveForward();
Behavior b2 = new BatteryLow(6.5f);
Behavior b3 = new HitWall();
Behavior [] bArray = {b1, b2, b3};
Arbitrator arby = new Arbitrator(bArray);
arby.start();
}
}

Note: The voltage level of the NXT at rest is different from the voltage when in
action. The voltage level at rest might be 7.8 V, but when motors are activated they
naturally cause a drop in the voltage reading. Make sure the voltage threshold used in
the BatteryLow constructor is low enough.
This example beautifully demonstrates the real benefit of behavior control coding.
Inserting a new behavior, no matter what the rest of the code looks like, is simple. The
reason for this is grounded in object oriented design; each behavior is a self contained,
independent object.
TIP: When creating a behavior control system, it is best to program each behavior one
at a time and test them individually. If you code all the behaviors and then upload
them all at once to the NXT brick, there is a good chance a bug will exist somewhere
in the behaviors, making it difficult to locate. By programming and testing them one
at a time it makes it easier to identify where the problem was introduced.

Notes on the recommended design pattern

In order to understand why we recommend this design pattern, it is useful to dig a


little deeper into the inner workings of the arbitrator. The Arbitrator contains a
monitor thread that cycles through each of the behaviors, checking the takeControl()
method to see if the behavior should become active. It starts with behavior of largest
index (because they are are stored in increasing priority order) and works down the
array. As soon as it comes across a behavior that should take control, this is the
highest priority behavior that wants control at the moment. If this behavior has higher
priority than the active behavior, the monitor thread executes suppress() on the active
behavior, and then starts checking each behavior from the top again. The main thread
of the Arbitrator is very simple. It just calls the action() method on the highest priority
behavior, (as determined by the Monitor thread) and that behavior becomes the active.
When action() exits, either because its task is complete or because the Monitor thread
has called suppress in it, that behavior is no longer active, and the loop continues,
calling action() on the behavior that is now has the highest priority as determined by
the Monitor thread. It is possible that the same behavior repeatedly becomes active.
It would be nice if all behaviors were as simple as the examples given above, but in
more complex coding there are some unexpected results that can sometimes be
introduced. If it is necessary to have a behavior that uses its own Thread, for example,
can sometimes be difficult to halt it from the suppress() method, which can lead to
two different threads fighting over the same resources - often the same motor! But in
this pattern, the suppress() method does not try to halt anything. It is the responsibility

of the action() method to keep testing the suppressed variable, exit as soon as it
become true, and leave the robot is a safe state for the next behavior.
Another advantage of this design pattern is that each behavior is coded in the same
way without making any assumptions about its priority. You can then change the
priority order of your behaviors or reuse them in other applications without
reprogramming any of them.
Note: If you would like to remove any mystery about what goes on in the Arbitrator
class, take a look at the source code located in
src/classes/lejos/subsumption/Arbitrator.java.
Summary

Behavior coding is predominantly used for autonomous robots - robots that work
independently, on their own free will. A robot arm controlled by a human would
likely not use behavior programming, although it would be possible. For example, a
robot arm with four joystick movements could have a behavior for each direction of
movement. But as you may recall, behaviors are ordered with the highest order taking
precedence over lower order behaviors. Who is to say that pushing left on the joystick
would take precedence over pushing up? In other words, behavior control in anything
other than autonomous robots is largely overkill.
So why use the Behavior API? The best reason is because in programming we strive
to create the simplest, most powerful solution possible, even if it takes slightly more
time. The importance of reusable, maintainable code has been demonstrated
repeatedly in the workplace, especially on projects involving more than one person. If
you leave your code and come back to it several months later, the things that looked
so obvious suddenly don't anymore. With behavior control, you can add and remove
behaviors without even looking at the rest of the code, even if there are 10 or more
bhaviors in the program. Another big plus of behavior control is programmers can
exchange behaviors with each other easily, which fosters code reusability. Hundreds
of interesting, generic behaviors could be uploaded to websites, and you could simply
pick the behaviors you want to add to your robot (assuming your robot is the correct
type of robot). This reusability of code can be taken forward even more by using
standard leJOS NXJ classes such as the Navigation API.

Using leJOS with Android

leJOS can be used in Android applications (.apks) via the leJOS PC API that
communicates using Bluetooth with either:

leJOS NXJ programs running on your NXT. [This requires the leJOS firmware
to be on your NXT]
the LEGO Communications Protocol (LCP). [This DOES NOT require the
leJOS firmware to be on your NXT, and works with the standard LEGO
firmware]

This tutorial is not a complete explanation of how to create Android applications


which can quickly become far more complicated than the normal difficulty of using
leJOS. Three leJOS sample programs have been included in one Android .apk, and
this tutorial will explain the most relevant aspects of getting them to work under
Android. Please note: The robotics package is not supported.
Android specific questions should be directed to theAndroid Developer Mailing List,
while leJOS specific ones can go to our forum
Prerequisites
Android SDK

To build .apks that run on Android or to upload the sample .apk to your Android
device, you need to download the SDK for your OS and install it.
IDE Plugins

There is an Eclipse Plugin or a Netbeans Plugin to simplify developing and building


.apks. The LeJOSDroid sample project was set up using the Eclipse Android Plugin
and can be imported into Eclipse by selecting File --> Import --> General --> Existing
Projects into Workspace and selecting the android/leJOS-Droid folder
To use leJOS in your own Android project within Eclipse (same principles apply for
Netbeans), make sure:

the lejos pccomms.jar (.9 or later) is in your project/libs dir

the lejos pccomms.jar is in your build path

Sdcard Cache File

leJOS uses a nxj.cache file to record the MAC addresses of paired NXT devices.
Running the sample LeJOSDroid.apk will set this up for you. To enable this
otherwise, create a leJOS directory in the root dir of your Android's sdcard. Without
this cache file, leJOS will iterate through the list of paired devices on your Android
handset and connect to the first NXT it finds.
Device Setup

To develop with an Android device, which is necessary for anything involving a


BlueTooth connection to the NXT, you will need to set up your Android device for
debugging. The Android emulator can be used to develop the GUI of your application,
but the emulator can not do bluetooth connections.
Logging can be seen in the Eclipse window or via a shell by using logcat via adb

LeJOSDroid Samples

The LeJOSDroid.apk Android application includes three leJOS samples modified


from /pcsamples that typically run on your PC or MAC:

TachoCount
BTSend
RCNavigationControl

The samples have been tailored for Android, and it is important to understand the
modifications required by Android Application Fundamentals
The LeJOSDroid.apk can be installed with the adb tool in your-android-sdklocation/platform-tools/. Please uninstall the distributed LeJOSDroid.apk from your
Android device (via Settings --> Applications --> Manage Applications) before
reinstalling a new LeJOSDroid.apk or there will be an error about conflicting
signatures. How to sign an Application.
TachoCount

TachoCount works with either LEGO or leJOS firmware via LCP. Nothing needs to
be uploaded to or run on your brick.
BTSend

The BTSend example requires that samples/BTRecieve must be uploaded and running
on a NXT with leJOS firmware. See the section on flashing the leJOS firmware
in getting started for your OS, and compiling and running leJOS programs on the
NXT.
RCNavigationControl

The BTSend example requires that samples/RCNavigator must be uploaded and


running on a NXT with leJOS firmware. See the section on flashing the leJOS
firmware in getting started for your OS, and compiling and running leJOS
programs on the NXT.

Application Fundamentals

Below are some Android application fundamental considerations:


Further details on the topics touched on below, as well as complete information on UI
design and other topics are available from the Android Developer Site

Connection

You will need to pair with the NXT from your Android device before being able to
connect.
leJOS connection is done via the standard lejos.pc.comm.NXTConnector method. leJOS
will detect the Android environment and attempt to
loadlejos.pc.comm.NXTCommAndroid. NXTCommAndroid is not included in the
pccomm.jar due to Android dependencies and must be included in the src of your
project.

You'll notice the LeJOSDroid method is static and returns a connected


NXTConnector. The reason is that the two examples which use this code (BTSend
and TachoCount), call it from another thread that those examples create. The reasons
will be discussed in the next section on threading.
Example -- LeJOSDroid connection method:

private final static String TAG = "LeJOSDroid";

<--- used to label the logging

public static NXTConnector connect(final CONN_TYPE connection_type) {


<--- method to return connection
Log.d(TAG, " about to add LEJOS listener ");
NXTConnector conn = new NXTConnector();
conn.setDebug(true);
conn.addLogListener(new NXTCommLogListener() {
public void logEvent(String arg0) {
Log.e(TAG + " NXJ log:", arg0);
}
public void logEvent(Throwable arg0) {
Log.e(TAG + " NXJ log:", arg0.getMessage(), arg0);
}
});
switch (connection_type) {
i.e. LCP or packet

<--- specifies the connection type

case LEGO_LCP:
conn.connectTo("btspp://NXT", NXTComm.LCP);
break;
case LEJOS_PACKET:
conn.connectTo("btspp://");
break;
}
return conn;
}

Threading

Most work in Android must be done off the main UI thread, otherwise the user will
see a ANR (Android Non-Response) message when there is no response to an input
event (e.g. key press, screen touch) within 5 seconds.
For this reason, the BTSend and TachoCount samples are run in their own thread. In
particular, BTSend has a while loop that could take more than 5 seconds.
Similarly, TachoCount could run into ANR problems if it were run on the main UI
thread and included a method that took longer than 5 seconds to execute which is
possible using LCP as some methods such as Motor.rotate() don't return until after
completing.
It should be noted that while the NXTCommAndroid class creates threads internally for IO
(one to read incoming and one to write outgoing data via bluetooth),
the NXTCommAndroid read()method will block until data is available. Also,
the NXTCommAndroid open() methods used for connection creation will block until the
connection is made and IO streams are available.
Since lejos.pc.comm.NXTConnector.connectTo()methods ultimately call
the NXTCommAndroid open() method that blocks, calls to open connections should be
done off the main UI thread as done in the examples.
The RCNavigationControl example has a more complex UI which isn't easily
combined with LeJOSDroid and so is run as a new activity. This new activity is, in
fact now, the UI thread that creates other threads to handle the connection and reading
which block. If an ANR message appears from work done on the main UI thread, that
work too will need to be threaded. In actuality, the code to connect for
RCNavigationControl works fine even if it is taken out of it's own thread because in
all likelyhood the user won't be creating an input event (via a key press or screen
touch for example) while waiting for a connection to be made.

Internal Messaging

One implication of Android threading is that Message


Handlers or BlockingQueues must be used to exchange data between threads as direct
communication is not advised.

RCNavigationControl is extremely illustrative of this point. In the


original pcsamples/RCNavigationControl, the RCNavComms class is created while passing
in the RCNavigationControl instance in the constructor, and the instance is used to
update the UI.
public class RCNavigationControl extends javax.swing.JFrame{
//example for PC or MAC
...
private RCNavComms communicator = new RCNavComms(this);
<------ This (using an instance to update the UI thread) is
dangerous in
Android with potential memory leaks and unpredictable behavior
}

public class RCNavComms { //example for PC or MAC


RCNavigationControl control;
public RCNavComms(RCNavigationControl control)
{
this.control = control;
}
class Reader extends Thread{
...
public void run(){
...
control.showtRobotPosition(x, y, h);
<------ This (using an instance to update the UI thread) is
dangerous in
Android with potential memory leaks and unpredictable behavior
}
}
}

In comparision, the RCNavComms class for the Android example uses a Handler that
was passed in through the constructor (instead of the RCNavigationControl instance).
public class RCNavComms{ //example for Android
Handler mUIMessageHandler;

public RCNavComms(Handler mUIMessageHandler){


this.mUIMessageHandler=mUIMessageHandler;
}
public void sendPosToUIThread(float x, float y, float h) {
float[] pos= {x,y,h};
Bundle b = new Bundle();
b.putFloatArray(RCNavigationControl.ROBOT_POS, pos);
Message message_holder = new Message();
message_holder.setData(b);
mUIMessageHandler.sendMessage(message_holder);
}
class Reader extends Thread{
...
public void run(){
...
sendPosToUIThread(x, y, h);

<------ Sends a message via a

Handler
}
}
}

public class RCNavigationControl extends TabActivity{ //example for


Android
...
class UIMessageHandler extends Handler {
float[] pos;
@Override
public void handleMessage(Message msg) {
//Log.d(TAG, "handleMessage");
switch (msg.what) {
case LeJOSDroid.MESSAGE:
//Log.d(TAG, (String)
msg.getData().get(LeJOSDroid.MESSAGE_CONTENT));
mMessage.setText((String)
msg.getData().get(LeJOSDroid.MESSAGE_CONTENT));
break;

default:
pos =
msg.getData().getFloatArray(ROBOT_POS);
showtRobotPosition(pos[0], pos[1],
pos[2]);
}
}
}

Please see the documentation to learn more about Handlers. While the
RCNavigationControl example does in fact work even without using a Handler and
passing in an instance to use to update the UI, it's very true that Threads will run
briefly as the UI thread if you try to update the UI by passing in an instance and
modifying the View (as would be done by using the PC or MAC example). Views in
Android are decidedly single threaded and sanity checks may not catch what you are
doing, and thus lead to unpredictable behavior. Also, there is a risk of memory leaks.

LifeCycle Basics

Many of the LeJOSDroid methods such as onCreate(Bundle b), onPause(),


onResume() are part of the Android Activity lifecycle and should be understood before
creating your own application.
For instance, some essential setup is actually done
in RCNavigationControl in onResume() instead of onCreate(). This is to enable correct
functioning when the application resumes from onPause().
@Override
protected void onResume() {
super.onResume();
mUIMessageHandler = new UIMessageHandler();
communicator = new RCNavComms(mUIMessageHandler);
}

Você também pode gostar