package josx.platform.rcx; /** * Low-level API for infra-red (IR) communication between * an RCX and the IR tower or between two RCXs. * For protocol details, Kekoa Proudfoot's Opcode Reference * is highly recommended. See Kekoa's * RCX Internals page. * Kekoa Proudfoot has also written a C based tool * (Send) * which you can use to send packets to the RCX. If you prefer * to write everything in Java, you should become familiar with the * Java Communications API. * Frameworks based on this API have already been developed * by Dario Laverde * (see RCXLoader) * and Scott Lewis (see RCXPort). * The Java Communications API is officially supported on Windows and Solaris. *
* Examples that use the leJOS Serial class can be found in: *
examples/serial
--- Receiver for certain opcodes, such as MotorOn.
* examples/serial2rcx
--- Communication between two RCXs.
* examples/remotectl
--- Receiver for Lego remote control.
* * The basic pattern for a Receiver is: *
*
*
*/
public class Serial
{
static final byte[] buffer = new byte[6]; // opcode + at most 5 data
private static final byte[] iAuxBuffer = new byte[4];
private static final int iAuxBufferAddr = Memory.getDataAddress (iAuxBuffer);
private static SerialListener[] iListeners = null;
private static int iNumListeners;
private static SerialListenerCaller singleton;
private Serial()
{
}
/**
* Reads a packet received by the RCX, if one is available.
* The first
* byte in the buffer is the opcode. Opcode
* 0x45 (Transfer Data) is received in a special way: If you
* had previously called setDataBuffer(), packet data will
* be copied into the buffer provided. Note the caveats regarding
* setDataBuffer() use.
*
* @return The number of bytes received.
* @see josx.platform.rcx.Serial#isPacketAvailable
* @see josx.platform.rcx.Serial#setDataBuffer
*/
public static int readPacket (byte[] aBuffer)
{
synchronized (Memory.MONITOR)
{
// Receive packet data
iAuxBuffer[2] = (byte) 0;
ROM.call ((short) 0x33b0, (short) Memory.getDataAddress (aBuffer),
(short) aBuffer.length, (short) (iAuxBufferAddr + 2));
return (int) iAuxBuffer[2];
}
}
/**
* Sets the buffer that will be used to save data
* transferred with opcode 0x45.
* byte[] packet = new byte[8];
* for (;;)
* {
* if (Serial.isPacketAvailable())
* {
* Serial.readPacket (packet);
* byte opcode = packet[0];
* if (opcode == AN_OPCODE)
* ...
* ...
* // Possibly send a response here
* packet = ~packet[0];
* Serial.sendPacket (packet, 0, PACKET_LENGTH);
* }
* }
*
*
* Note: This method must be used with caution.
* A pointer to the data buffer is passed to the ROM
* for asynchronous use.
* If more data is received than can be stored in the
* buffer, the VM's memory will be corrupted and
* it will crash or at least misbehave.
*/
public static void setDataBuffer (byte[] aData)
{
// Set data pointer
ROM.call ((short) 0x327c, (short) 0x1771,
(short) Memory.getDataAddress (aData), (short) 0);
}
/**
* Checks to see if a packet is available.
* Call this method before calling receivePacket.
*/
public static boolean isPacketAvailable()
{
synchronized (Memory.MONITOR)
{
// Check for data
ROM.call ((short) 0x3426, (short) (iAuxBufferAddr + 3), (short) 0);
return (iAuxBuffer[3] != 0);
}
}
/**
* Sends a packet to the IR tower or another RCX.
* In general, the IR tower will only receive responses
* to messages it has sent. The call returns immediately.
* @return false if a packet is already being sent.
*/
public static boolean sendPacket (byte[] aBuffer, int aOffset, int aLen)
{
if (isSending())
return false;
ROM.call ((short) 0x343e, (short) 0x1775, (short) 0,
(short) (Memory.getDataAddress (aBuffer) + aOffset),
(short) aLen);
return true;
}
/**
* Sets long range transmision.
*/
public static void setRangeLong()
{
ROM.call ((short) 0x3250, (short) 0x1770);
}
/**
* Sets short range transmision.
*/
public static void setRangeShort()
{
ROM.call ((short) 0x3266, (short) 0x1770);
}
/**
* Resets serial communications. It can be used
* to disable buffers set with setDataBuffer
.
*/
public native static void resetSerial();
/**
* Return true if a message is being sent.
*/
public static boolean isSending()
{
return Memory.readByte(0xef93) != 0x4f;
}
/**
* Wait until a message has been sent.
*/
public static void waitTillSent() throws InterruptedException
{
while (isSending())
{
Thread.sleep(20);
}
}
/**
* Adds a listener of receive events. There can be at most
* 4 listeners.
*/
public static synchronized void addSerialListener (SerialListener aListener)
{
if (iListeners == null)
{
iListeners = new SerialListener[4];
singleton = new SerialListenerCaller();
}
iListeners[iNumListeners++] = aListener;
ListenerThread.get().addSerialToMask(singleton);
}
/**
* private static inner class which allows a ListenerCaller object
* to be registered to call the Serial Listeners.
*/
private static class SerialListenerCaller implements ListenerCaller {
public synchronized void callListeners()
{
int length = Serial.readPacket (Serial.buffer);
for( int i = 0; i < Serial.iNumListeners; i++) {
Serial.iListeners[i].packetAvailable (Serial.buffer, length);
}
}
}
}