CS 001: Lejos


LeJOS NXJ Commands

  • nxjflash - flashes the firmware. Note the NXT brick has to be in firmware reset mode for this to work. To put your brick in firmware mode use a paperclip to press the reset button in the top left hole on the back of the NXT brick til it beeps
  • nxjc [something.java] - compiles a Java program for leJOS
  • nxj [something] - links, uploads and optionally runs a leJOS program

Example Useage - to compile and upload/run the program Test.java:

  • if the lejos firmware is not loaded, press the reset button in the top left hole on the back of the NXT brick then type the command 'nxjflash'
  • nxjc Test.java
  • nxj Test

Limitations and Bugs


Known limitations

Due to size constraints, some Java language features have been omitted. Others just haven't been implemented yet. Known limitations with all versions of leJOS are:
Garbage collection is not performed yet. Hence, the number of objects in your program should be limited
Switch statements are not supported
Arithmetic operations on variables of type long are not supported, although you can cast ints to longs and vice versa
Maximum array length is 511
The instanceof operation will always be true for interfaces. In bytecode, CHECKCAST succeeds on all interface classes (which is a bit unsafe)
The instanceof and checkcast operations are rejected by the linker when they are performed on array classes (e.g. b instanceof byte[])
There are no objects of type java.lang.Class, which means that the ".class" construct won't work. Class.forName() throws ClassNotFoundException at all times. As a consequence, reflection is not supported by leJOS
Most JDK APIs are unavailable


Known Bugs

The following bugs apply to all versions of leJOS:

MONITOREXIT ignores null objects (it assumes MONITORENTER has handled them). Furthermore, it doesn't check monitor validity. Until this bug is fixed (actually, at all times) all monitors should be constants
Initializers (static or otherwise) are not implicitly synchronized. This could lead to access of statics before they are initialized, when multiple threads use a class
Known Bugs and Limitations in leJOS NXJ

The following bugs and limitations apply to the current version of leJOS NXJ:

Occasionally, after flashing the firmware with with nxjflash, the battery level reads as 0.0 and buttons do not respond. If this happens, a battery must be removed and re-inserted. It is not necessary to repeat the nxjflash.
leJOS NXJ does not work on Windows Vista systems. There are possible circumventions for Vista 32-bit systems, but not for Vista 64-bit systems. This is due to limitations in libusb-win32 that the leJOS NXJ Windows USB tools, including nxjflash, rely on
Occasionally, after pressing the orange (ENTER) button to start leJOS NXJ, the LCD remains blank. If this occurs, and is not due to low batteries, it is necessary to shut down leJOS, by pressing the orange and dark gray buttons (ENTER + ESCAPE), and try again.
Bluetooth on Windows is supported by the Bluecove open source library. The NXJ tools over Bluetooth and Java streams over Bluetooth both use this. It only works with the Micosoft Bluetooth stack, not the Widcomm stack. You must use a Bluetooth dongle that supports the Microsoft stack, and uninstall the Widcomm stack (or otherwise ensure that your dongle uses the Microsoft stack) . iCommand can still communicate with leJOS NXJ using the Widcomm stack.
When plugging in the USB cable, or starting leJOS NXJ with the cable plugged in, the keys on the NXT do not respond for several seconds. This particularly affects Windows systems, and may be dependent on the speed of the host PC.
NXT to NXT communications over Bluetooth is not yet supported by the lejos.nxt.comm API. It does work if you write low-level Java code to send commands to the Bluecore 4 chip.
I2C sensors such as the Ultrasonic sensor do not work in port 4.
The Ultrasonic sensor needs a gap of at least 200 milliseconds between calls of getDistance().

I2C writes are not supported. This means that I2C multplexers and similar sensors that require data to be written to them are not supported. It also means that callibrarion of I2C sensors is not supported.
The light sensor does not seem to work correctlly on port 4. The floodlight does not come on.
Multidimensional arrays of objects do not work correctly. They can cause exceptions, data aborts and other problems.
leJOS NXJ closes down five seconds after a user program exits. Press ENTER to restart it.
Only one file can be open at a time.
leJOS NXJ only supports program upload and download, tools such as nxjbrowse, and LEGO communications Protocol commands when the start-up menu is running, not when a user program is running.
The number of files that can be uploaded to lejos NXJ is limited by the fact that the whole file table must fit in a 256-byte page. If the average filename length is 15 characters, approximately 10 files are supported. This limitations is not checked, and will cause an exceptions when the file table becomes full.
The maximum filename length is 20 characters.
Not all LEGO Communications Protocol command are supported, and the semantics of some of them are different from that of the standard LEGO firmware. In particular only one file can be open at a time and the file handle is always 0.
The maximum packet size over Bluetooth is 254 bytes (not 64k-1 that the standard Lego firmware supports).
The leJOS NXJ USB driver does not return a unique serial number for each NXT, in the way that the standard LEGO software does. The affects of this are not known, but it may be the cause of lejos NXJ not working with the LEGO fantom API over USB.
Closing Bluetooth connections on the PC is not detected by leJOS NXJ. New Bluetooth connections will fail unless the connection is closed at the NXJ end. The tools cope with this by sending a termination message that is dected by NXJ. Java streams connections need to be closed at the NXJ end, to circumvent this issue, e.g. close the connection when all expected data has been transmitted or send a termination message.
Java streams over USB are never closed down, and are always open.
The RCX limitations of the Java VM also apply to NXJ. In particular, there is no garbage collection.
Graphics support on the LCD is limited. If text are graphics are mixed, the graphics must be written first.
There is no support for image files.
Sound support is limited. Playing tones and system sounds is supported, but not sound files. The volume is always set to maximum.
Bluetooth is always on and the device visible. There is currently no way of switching it off.
The name of a NXT can be changed using nxjbrowse over a USB connection. If this is attempted over a Bluetooth connection it may apprear to work, but will not.
Defragging the file system is supported from nxjbrowse. This sometimes causes the NXT to appear to hang. It is then necessary to remove and reinsert a battery. The defrag will usually have worked successfully.
The last parameter to nxjflash (FMCN) is ignored. it is not normally necessary to use any parameters with nxflash as the defaults are fine.
There are 767 256-byte pages of user flash memory avaiable. There is no check when this is filled, attempts to write to higher page number will probably fail silently.
The lejos NXJ menu system leaks memory every time a file operation is done. This is reclaimed when a user program is run. In practice, due to the amount of memory available on the NXT, this should not be a problem.
The lejos linker (nxj or nxjlink commands), in verbose mode, does not list the special classes used by a program. To decode the class number displayed in a leJOS NXJ exception, you need to consult src/nxtvm/javavm/secialclasses.h.
No buzz occurs when an exception happens (as it does on the RCX).
Data aborts occur if there is a failure in the NXJ VM. If a data abort occurs, it is necessary to remove and re-insert a battery.
Some aspects of leJOS NXJ are much slower than they should be. In particular, a future release will make refreshing the LCD display much faster.
leJOS NXJ does not work with the LEGO Mindstorms PC software, or the LEGO Mindstorms Fantom API.
On Windows, using the Bluecove library, NXTs that have been paired with the PC are in the list of available NXTs, even when they are not switched on. This does not happen with Bluez on Linux.
Is usually necessary to pair your NXT using the Operating System before it can be connected to by the lejOS NXJ PC tools and libraries.
The Bluetooth PIN used by leJOS NXJ is always 1234.
Discovery of Bluetooth devices is slow, taking at least 10 seconds. It is slower still with Bluez on Linux. For Java streams connections from user programs, you can connect directly by Bluetooth address, which is much faster.
To get permissions to the lejos NXJ usb devices from non-root users on Linux, you typically need to user pamconsole or udev rules. There are currently no instructions for setting up udev rules.
Some Linux systems may require you to delete the entries for your NXT device in /var/lib/bluetooth, to enable connections over Bluetooth.
lejos NXJ on Windows should not be installed to a folder with a space in its name (e.g a subfolder of "Program Files")

API : http://lejos.sourceforge.net/nxt/nxj/api/index.html

Tutorials : http://lejos.sourceforge.net/nxt/nxj/tutorial/index.htm

Program Template:

import lejos.nxt.*;

public class HelloWorld
{

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

// program logic goes here

// just run until ESCAPE button is pressed again
Button.ESCAPE.waitForPressAndRelease();

}

}

 

Exiting from the program gracefully

To wait for the escape button to be pressed you can use the command:

Button.ESCAPE.waitForPressAndRelease();

Or you can check the value of Button.ESCAPE.isPressed(). This is more useful if you are running a loop and need to check and see if the button has been pressed inside that loop. The first command may be more useful if you are using sensor listeners rather than polling the sensors inside a loop.

 

Motor Control

The motors have built-in tachometers to keep track of axle rotation. There are 3 predefined motor variables:

  • Motor.A
  • Motor.B
  • Motor.C

Example of use with motor methods:

Motor.A.setSpeed(speed); // where speed is an int and represents speed is in degrees per second (up to 900)
Motor.C.forward();
Motor.B.stop();
Motor.A.backward();
Motor.C.changeDirection();
Motor.C.flt() // glide to a stop (don't apply the brake)
Motor.A.rotate(angle); // where angle is an int and represents the number of degrees to rotate

Motors - moving multiple wheels at once

The Pilot class gives you the ability to control a wheeled vehicle - ie control two motors at once. To use the Pilot class you must first create an instance of the class. To use it you must import lejos.navigation.*;

The constructor:

Pilot(float wheelDiameter,float trackWidth,Motor leftMotor, Motor rightMotor)
or
Pilot(float wheelDiameter,float trackWidth,Motor leftMotor, Motor rightMotor, boolean reverse)
(Use this constructor if you need to set the reverse boolean to true)

Example of use:

Pilot myPilot = new Pilot(5.6f, 16.0f, Motor.A, Motor.C);

Methods you can use on Pilot

myPilot.setSpeed(speed); // speed is an int measuring degrees/second
myPilot.forward();
myPilot.backward();
myPilot.stop();
myPilot.travel(distance); // distance is a float in the same units as the wheel diameter

Sensor Control

There are 4 predefined sensor variables - one per sensor port:

  • SensorPort.S1
  • SensorPort.S2
  • SensorPort.S3
  • SensorPort.S4

Touch Sensor

To construct a touch sensor:
TouchSensor touch = new TouchSensor(SensorPort.S1);

To use a touch sensor:
touch.isPressed(); // returns a boolean (true/false)

Light Sensor - measures intensity of light and has 2 modes, active and passive (LED off)

To construct a light sensor:
LightSensor light = new LightSensor(SensorPort.S1);

To use a light sensor:
light.readValue();
light.readNormalizedValue();

Ultrasonic Sensor - distance sensor accurate from 6-180cm

UltrasonicSensor ultra = new UltrasonicSensor(SensorPort.S1);
ultra.getDistance(); // returns an integer representing the distance in cm

Sound Sensor - measures loudness of sound in dB

SoundSensor sound = new SoundSensor(SensorPort.S1);
sound.readValue();

 

Sensor Usage - Polling vs Sensor Listeners

Using the above methods you can 'poll' any of the sensors to see their current value. There are appropriate times to use this approach, however another way of accessing sensor information is to implement sensor listeners which 'listen' for changes in the sensor readings and then perform actions based on the new values.

To use a sensor listener we need to implement a SensorPortListener and add it to the sensor we want to listen. Below is a basic example of what the main method might look like when using a sensor listener. In this example we create an anonymous listener and add it to the sensor at the same time :

public static void main (String[] aArg) throws Exception
{

TouchSensor ts = new TouchSensor(SensorPort.S1);
SensorPort.S1.addSensorPortListener(new SensorPortListener() {

public void stateChanged(SensorPort port, int oldValue, int newValue){

// this part just displays the value - you should add your own logic code here
LCD.clear();
LCD.drawString(changed, 0, 0);
LCD.drawInt(value, 7, 1);
LCD.refresh();

}

});
Button.ESCAPE.waitForPressAndRelease();

}

Or we can implement the Listener as a separate class:

public static void main (String[] aArg) throws Exception
{

TouchSensor ts = new TouchSensor(SensorPort.S1);
SensorPort.S1.addSensorPortListener(new myTouchListener() );
Button.ESCAPE.waitForPressAndRelease();

}

class myTouchListener implements SensorPortListener
{

public void stateChanged(SensorPort port, int oldValue, int newValue)){

// this part just displays the value - you should add your own logic code here
LCD.clear();
LCD.drawString(changed, 0, 0);
LCD.drawInt(value, 7, 1);
LCD.refresh();

}

}

 

 

LCD

LCD.clear();
LCD.drawInt(intvalue, xcoordinate, ycoordinate);
LCD.drawString(string, xcoordinate, ycoordinate);
LCD.refresh();

To add a pause in your program:

try{ Thread.sleep(amountOfTime); }
catch(Exception e) {}

 

Playing Sounds

Sound.beep()
Sound.playTone(frequency, duration);
Sound.buzz()