Communications
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 java.io.*;
import lejos.nxt.*;
import lejos.nxt.comm.*;

/**
 * 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.waitForAnyPress() == Button.ID_RIGHT){
      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); 
    }
  }   
}

Back to top

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:

import java.io.*;
import lejos.nxt.*;
import lejos.nxt.comm.*;

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

      connection.close();
      LCD.clear();
    }
  }
}

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

Back to top

NXT Initiator

To initiate a Bluetooth connection from one NXT to another NXT, you first need to add the receiver NXT to the initiator NXT’s 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 initiator’s 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 RemoteDevice class on the initiator NXT:

Example:

RemoteDevice 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(RemoteDevice remoteDevice)

  • BTConnection connect(byte[] device_addr)

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

Example:

RemoteDevice btrd = Bluetooth.getKnownDevice(name);

if (btrd == null) {
  LCD.clear();
  LCD.drawString("No such device", 0, 0);
  Button.waitForAnyPress();
  System.exit(1);
}

BTConnection btc = Bluetooth.connect(btrd);

if (btc == null) {
  LCD.clear();
  LCD.drawString("Connect fail", 0, 0);
  Button.waitForAnyPress();
  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:

import java.io.*;
import javax.bluetooth.*;
import lejos.nxt.*;
import lejos.nxt.comm.*;

public class BTConnectTest {
  public static void main(String[] args) throws Exception {
    String name = "NXT";
    LCD.drawString("Connecting...", 0, 0);
    RemoteDevice btrd = Bluetooth.getKnownDevice(name);

    if (btrd == null) {
      LCD.clear();
      LCD.drawString("No such device", 0, 0);
      Button.waitForAnyPress();
      System.exit(1);
    }

    BTConnection btc = Bluetooth.connect(btrd);

    if (btc == null) {
      LCD.clear();
      LCD.drawString("Connect fail", 0, 0);
      Button.waitForAnyPress();
      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.waitForAnyPress();
  }
}

Back to top

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(int protocol, String name, String address)

Example:

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

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

NXTInfo[] nxtInfo = nxtComm.search("NXT");

Back to top

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

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

Back to top

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.

Back to top

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);
  NXTCommConnector connector = Bluetooth.getConnector();
  RemoteNXT nxt = new RemoteNXT("NXT", connector);
  LCD.clear();
  LCD.drawString("Connected", 0, 0);
} catch (IOException ioe) {
  LCD.clear();
  LCD.drawString("Conn Failed", 0, 0);
  Button.waitForAnyPress();
  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();

Back to top

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'};
RemoteDevice btGPS = Bluetooth.getKnownDevice(name); 

if (btGPS == null) {
  LCD.drawString("No such device", 0, 0);
  Button.waitForAnyPress();
  System.exit(1);
}

BTConnection btCon = Bluetooth.connect(btGPS.getDeviceAddr(), NXTConnection.RAW, pin);

if(btCon == null) {
  LCD.drawString("No Connection", 0, 1);
  Button.waitForAnyPress();
  System.exit(1);
}

LCD.drawString("Connected!", 0, 1);

GPS gps = null;
InputStream in;

try {
  in = btCon.openInputStream();
  gps = new GPS(in);
  LCD.drawString("GPS Online", 0, 6);
} catch(Exception e) {
  LCD.drawString("GPS Connect Fail", 0, 6);
  Button.waitForAnyPress();
  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()

Back to top

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:

Back to top