import java.io.*;
import java.util.*;
import javax.comm.*;
/**
* This class implements the lowest-level communication
* between the desktop and one or more Lego Mindstorms, using
* the Java SerialComm API (available from the JDC).
*/
public class IRProtocol implements SerialPortEventListener {
private SerialPort port;
private OutputStream out;
private InputStream in;
private Vector listeners;
private MessageBuffer mbuf;
private boolean debugOn = false;
/**
* Create a new IRProtocol instance. This will try to acquire the
* serial port "/dev/term/a", and configure it for 2400,8,O,1.
*/
public IRProtocol() {
listeners = new Vector();
mbuf = new MessageBuffer();
CommPortIdentifier portID = null;
try { portID = CommPortIdentifier.getPortIdentifier("/dev/term/a"); }
catch (Exception e) { e.printStackTrace(); }
if (portID.getPortType() == CommPortIdentifier.PORT_SERIAL) {
try {
port = (SerialPort) portID.open("RCXRemote", 2000);
out = port.getOutputStream();
in = port.getInputStream();
port.setSerialPortParams(2400, SerialPort.DATABITS_8,
SerialPort.STOPBITS_1, SerialPort.PARITY_ODD);
port.addEventListener(this);
port.notifyOnDataAvailable(true);
} catch(Exception e) { e.printStackTrace(); }
System.out.println("using port " + portID.getName()
+ ". Please make sure the IR transmitter is "
+ "connected to that port!\n");
System.out.println("parity is set to "
+ (port.getParity() == SerialPort.PARITY_ODD ? "ODD" : "NONE"));
System.out.println("receive buffer size is " + port.getInputBufferSize());
return ;
}
System.out.println("Could not find a serial port! Make sure "
+ "you have a 'javax.comm.properties' file in the "
+ "same directory as the 'comm.jar' file, and that "
+ "it lists the correct serial port driver.");
throw new RuntimeException();
}
/*
* Enable or disable debugging messages. Passing 'true'
* as the argument will enable debugging, 'false' will
* turn it off.
*/
public void enableDebugging(boolean d) {
debugOn = d;
}
private void debug(String txt) {
if (debugOn) {
System.out.print(txt);
}
}
private void debugln(String txt) {
if (debugOn) {
System.out.println(txt);
}
}
private void debugln() {
debugln("");
}
/** add a MessageListener for asynchronous receiving */
public void addMessageListener(MessageListener l) {
mbuf.addMessageListener(l);
}
/** remove a MessageListener for asynchronous receiving */
public void removeMessageListener(MessageListener l) {
mbuf.removeMessageListener(l);
}
/** SerialPortEventListener method */
public void serialEvent(SerialPortEvent e) {
int type = e.getEventType();
if (type == SerialPortEvent.DATA_AVAILABLE) {
int available = 0;
try {
available = in.available();
debugln("IRProtocol.serialEvent(): available = " + available);
byte[] chunk = new byte[available];
in.read(chunk);
mbuf.append(chunk);
} catch(Exception x) { x.printStackTrace(); }
}
}
private String toHex(byte x) {
return ((x & 0xff) < 16 ? "0" : "") + Integer.toHexString(x & 0xff).toUpperCase();
}
/** Send a raw message, i.e. do not use the Lego IR protocol */
public void sendRaw(byte[] data) throws IOException {
debug("IRProtcol.sendRaw(): ");
for (int i = 0; i < data.length; i++) {
debug(toHex(data[i]) + " ");
}
debugln();
out.write(data);
out.flush();
}
/** Receive a raw message, i.e. assume the Lego IR protocol is not used */
public void receiveRaw(byte[] data) throws IOException {
in.read(data);
debug("IRProtcol.receiveRaw(): ");
for (int i = 0; i < data.length; i++) {
debug(toHex(data[i]) + " ");
}
debugln();
}
/**
* Send a message using the Lego IR protocol. All messages are
* prefaced with a three byte header (0x55, 0xff, 0x00). Each
* data byte is followed by its two's complement. The message
* is followed by a simple checksum, and the two's complement
* of that checksum. The checksum is computed by doing an 8-bit
* accumulation of all the data bytes. A little more formally,
* messages look like this:
*
* 0x55 0xff 0x00 D1 ~D1 D2 ~D2 ... Dn ~Dn CS ~CS
*/
public void send(byte[] data) throws IOException {
byte[] buf = new byte[2 * data.length + 5];
byte sum = 0;
int i;
buf[0] = (byte)0x55;
buf[1] = (byte)0xff;
buf[2] = (byte)0x00;
for (i = 0; i < data.length; i++) {
buf[2 * i + 3] = data[i];
buf[2 * i + 4] = (byte)~data[i];
sum += data[i];
}
buf[buf.length - 2] = sum;
buf[buf.length - 1] = (byte)~sum;
debug("IRProtcol.send(): ");
for (i = 0; i < buf.length; i++) {
debug(toHex(buf[i]) + " ");
}
debugln();
out.write(buf);
out.flush();
}
/**
* Receive a message assuming the Lego IR protocol is used. All
* messages are prefaced with a three byte header (0x55, 0xff, 0x00).
* Each data byte is followed by its two's complement. The message
* is followed by a simple checksum, and the two's complement
* of that checksum. The checksum is computed by doing an 8-bit
* accumulation of all the data bytes. A little more formally,
* messages look like this:
*
* 0x55 0xff 0x00 D1 ~D1 D2 ~D2 ... Dn ~Dn CS ~CS
*/
public void receive(byte[] data) throws IOException {
byte[] buf = new byte[2 * data.length + 5];
byte sum = 0;
int i;
in.read(buf);
debug("IRProtcol.receive(): ");
for (i = 0; i < buf.length; i++) {
debug(toHex(buf[i]) + " ");
}
debugln();
if (!(buf[0] == (byte)0x55 && buf[1] == (byte)0xff && buf[2] == (byte)0x00)) {
System.out.println("IRProtocol.receive(): invalid header: "
+ toHex(buf[0]) + " " + toHex(buf[1]) + " "
+ toHex(buf[2]) + " ");
throw new RuntimeException();
}
for (i = 0; i < data.length; i++) {
data[i] = buf[2 * i + 3];
if (buf[2 * i + 4] != ~data[i]) {
System.out.println("IRProtocol.receive(): wrong two's complement for data[" + i + "]");
throw new RuntimeException();
}
sum += data[i];
}
if (buf[buf.length - 2] != sum) {
System.out.println("IRProtocol.receive(): wrong checksum");
throw new RuntimeException();
}
if (buf[buf.length - 1] != ~sum) {
System.out.println("IRProtocol.receive(): wrong two's complement for checksum");
throw new RuntimeException();
}
}
/**
* Call this method when you're done using the IR protocol.
* This will release the serial port.
*/
public void close() {
port.close();
}
}