Hardware: I/O and Sensors


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.

Back to top

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);
  }
}			
			

There is a set of methods in the LCD class to write to the screen using pixel co-ordinates. These are mainly there to support the Graphics class, but can be called directly:

  • void drawString(String str,int x, int y, boolean invert)

    This variant of drawString uses pixel co-ordinates and supports inverting the text drawing white characters on a black background.

  • void drawChar(char c, int x, int y, boolean invert)

    This draws a character at text co-ordinate (x, y) with optional inversion of the character.

  • void setPixel(int rgbColor, int x, int y)

    Set or unset the pixel at (x,y). rgbColor=1 sets the pixel and zero unsets it.

Back to top

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        RIGHT     ESCAPE

Code             1                 2               4                 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.

Back to top

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            Short beep
code = 1            Double beep
code = 2            Descending arpeggio
code = 3            Ascending arpeggio
code = 4            Long, low buzz

Individual methods to play a particular system sound, if you don’t 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 don’t 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.

Back to top

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);
  }
}
			

Back to top

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);
  }
}

			

Back to top

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.readValue(), 4, 0, 0);
      LCD.drawInt(light.readNormalizedValue(), 4, 0, 1);
      LCD.drawInt(SensorPort.S1.readRawValue(), 4, 0, 2);
      LCD.drawInt(SensorPort.S1.readValue(), 4, 0, 3);
    }
  }
}			
			

Back to top

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);
      }
    }
  }
}
			

Back to top

Ultrasonic Sensor

To create an instance, use the constructor:

  • UltrasonicSensor( Port aSensorPort)

The sensor operates in two modes, continuous (defalt) 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

Back to top