package lejos.nxt.remote;
import java.io.*;
/*
* WARNING: THIS CLASS IS SHARED BETWEEN THE classes AND pccomms PROJECTS.
* DO NOT EDIT THE VERSION IN pccomms AS IT WILL BE OVERWRITTEN WHEN THE PROJECT IS BUILT.
*/
/**
* Sends LCP requests to the NXT and receives replies.
* Uses an object that implements the NXTComm interface
* for low-level communication.
*
*/
public class NXTCommand implements NXTProtocol {
private NXTCommRequest nxtComm = null;
private static NXTCommand singleton = null;
private boolean verifyCommand = false;
private boolean open = false;
private static final String hexChars = "01234567890abcdef";
private static final int MAX_BUFFER_SIZE = 58;
/**
* Create a NXTCommand object.
*/
public NXTCommand() {
super();
}
/**
* Set the NXTComm used to communicate with the NXT.
*
* @param nxtComm a nxtComm instance which must be connected to a NXT
*/
public void setNXTComm(NXTCommRequest nxtComm) {
open = true;
this.nxtComm = nxtComm;
}
/**
* Toggle the verify flag.
*
* @param verify true causes all commands to return a response.
*/
public void setVerify(boolean verify) {
verifyCommand = verify;
}
/**
* Small helper method to send DIRECT COMMAND request to NXT and return
* verification result.
*
* @param request
* @return
*/
private byte sendRequest(byte[] request, int replyLen) throws IOException {
byte verify = 0; // default of 0 means success
if (verifyCommand)
request[0] = DIRECT_COMMAND_REPLY;
byte[] reply = nxtComm.sendRequest(request,
(request[0] == DIRECT_COMMAND_REPLY ? replyLen : 0));
if (request[0] == DIRECT_COMMAND_REPLY) {
verify = reply[2];
}
return verify;
}
/**
* Small helper method to send a SYSTEM COMMAND request to NXT and return
* verification result.
*
* @param request the request
* @return the status
*/
private byte sendSystemRequest(byte[] request, int replyLen)
throws IOException {
byte verify = 0; // default of 0 means success
if (verifyCommand)
request[0] = SYSTEM_COMMAND_REPLY;
byte[] reply = nxtComm.sendRequest(request,
(request[0] == SYSTEM_COMMAND_REPLY ? replyLen : 0));
if (request[0] == SYSTEM_COMMAND_REPLY) {
verify = reply[2];
}
return verify;
}
/**
* Starts a program already on the NXT.
*
* @param fileName the file name
* @return the status
*/
public byte startProgram(String fileName) throws IOException {
byte[] request = { DIRECT_COMMAND_NOREPLY, START_PROGRAM };
request = appendString(request, fileName);
return sendRequest(request, 22);
}
/**
* Forces the currently executing program to stop.
* Not implemented by leJOS NXJ.
*
* @return Error value
*/
public byte stopProgram() throws IOException {
byte [] request = {DIRECT_COMMAND_NOREPLY, STOP_PROGRAM};
return sendRequest(request, 3);
}
/**
* Name of current running program.
* Does not work with leJOS NXJ.
*
* @return the program name
*/
public String getCurrentProgramName() throws IOException {
byte [] request = {DIRECT_COMMAND_REPLY, GET_CURRENT_PROGRAM_NAME};
byte [] reply = nxtComm.sendRequest(request, 23);
return new StringBuffer(new String(reply)).delete(0, 2).toString();
}
/**
* Opens a file on the NXT for reading. Returns a handle number and file
* size, enclosed in a FileInfo object.
*
* @param fileName
* e.g. "Woops.wav"
* @return fileInfo object giving details of the file
*/
public FileInfo openRead(String fileName) throws IOException {
byte[] request = { SYSTEM_COMMAND_REPLY, OPEN_READ };
request = appendString(request, fileName); // No padding required
// apparently
byte[] reply = nxtComm.sendRequest(request, 8);
FileInfo fileInfo = new FileInfo(fileName);
fileInfo.status = reply[2];
if (reply.length == 8) { // Check if all data included in reply
fileInfo.fileHandle = reply[3];
fileInfo.fileSize = (0xFF & reply[4]) | ((0xFF & reply[5]) << 8)
| ((0xFF & reply[6]) << 16) | ((0xFF & reply[7]) << 24);
}
return fileInfo;
}
/**
* Opens a file on the NXT for writing.
*
* @param fileName
* e.g. "Woops.wav"
*
* @return File Handle number
*/
public byte openWrite(String fileName, int size) throws IOException {
byte[] command = { SYSTEM_COMMAND_REPLY, OPEN_WRITE };
byte[] asciiFileName = new byte[fileName.length()];
for (int i = 0; i < fileName.length(); i++)
asciiFileName[i] = (byte) fileName.charAt(i);
command = appendBytes(command, asciiFileName);
byte[] request = new byte[22];
System.arraycopy(command, 0, request, 0, command.length);
byte[] fileLength = { (byte) size, (byte) (size >>> 8),
(byte) (size >>> 16), (byte) (size >>> 24) };
request = appendBytes(request, fileLength);
byte[] reply = nxtComm.sendRequest(request, 4);
if (reply == null || reply.length != 4) {
throw new IOException("Invalid return from OPEN WRITE");
} else if (reply[2] != 0) {
if (reply[2] == (byte) 0xFB) throw new IOException("NXJ Flash Memory Full");
else if (reply[2] == (byte) 0xFC) throw new IOException("NXJ Directory Full");
else throw new IOException("OPEN WRITE failed");
}
return reply[3]; // The handle number
}
/**
* Closes an open file.
*
* @param handle
* File handle number.
* @return Error code 0 = success
* @throws IOException
*/
public byte closeFile(byte handle) throws IOException {
byte[] request = { SYSTEM_COMMAND_NOREPLY, CLOSE, handle };
return sendSystemRequest(request, 4);
}
/**
* Delete a file on the NXT
*
* @param fileName the name of the file
* @return the error code 0 = success
* @throws IOException
*/
public byte delete(String fileName) throws IOException {
byte[] request = { SYSTEM_COMMAND_REPLY, DELETE };
request = appendString(request, fileName);
return sendSystemRequest(request, 23);
}
/**
* Find the first file on the NXT. This is a NXJ-specific version that returns the
* start page number as well as the other FileInfo data
*
* @param wildCard
* [filename].[extension], *.[extension], [filename].*, *.*
* @return fileInfo object giving details of the file
*/
public FileInfo findFirstNXJ(String wildCard) throws IOException {
byte[] request = { SYSTEM_COMMAND_REPLY, NXJ_FIND_FIRST };
request = appendString(request, wildCard);
byte[] reply = nxtComm.sendRequest(request, 32);
FileInfo fileInfo = null;
if (reply[2] == 0 && reply.length == 32) {
StringBuffer name = new StringBuffer(new String(reply))
.delete(0, 4);
int lastPos = name.indexOf("\0");
if (lastPos < 0 || lastPos > 20) lastPos = 20;
name.delete(lastPos, name.length());
fileInfo = new FileInfo(name.toString());
fileInfo.status = 0;
fileInfo.fileHandle = reply[3];
fileInfo.fileSize = (0xFF & reply[24]) | ((0xFF & reply[25]) << 8)
| ((0xFF & reply[26]) << 16) | ((0xFF & reply[27]) << 24);
fileInfo.startPage = (0xFF & reply[28]) | ((0xFF & reply[29]) << 8)
| ((0xFF & reply[30]) << 16) | ((0xFF & reply[31]) << 24);
}
return fileInfo;
}
/**
* Find the first file on the NXT.
*
* @param wildCard
* [filename].[extension], *.[extension], [filename].*, *.*
* @return fileInfo object giving details of the file
*/
public FileInfo findFirst(String wildCard) throws IOException {
byte[] request = { SYSTEM_COMMAND_REPLY, FIND_FIRST };
request = appendString(request, wildCard);
byte[] reply = nxtComm.sendRequest(request, 28);
FileInfo fileInfo = null;
if (reply[2] == 0 && reply.length == 28) {
StringBuffer name = new StringBuffer(new String(reply))
.delete(0, 4);
int lastPos = name.indexOf("\0");
if (lastPos < 0 || lastPos > 20) lastPos = 20;
name.delete(lastPos, name.length());
fileInfo = new FileInfo(name.toString());
fileInfo.status = 0;
fileInfo.fileHandle = reply[3];
fileInfo.fileSize = (0xFF & reply[24]) | ((0xFF & reply[25]) << 8)
| ((0xFF & reply[26]) << 16) | ((0xFF & reply[27]) << 24);
fileInfo.startPage = -1;
}
return fileInfo;
}
/**
* Find the next file on the NXT. This is a NXJ-specific version that returns the
* start page number as well as the other FileInfo data
*
* @param handle
* Handle number from the previous found file or from the Find
* First command.
* @return fileInfo object giving details of the file
*/
public FileInfo findNextNXJ(byte handle) throws IOException {
byte[] request = { SYSTEM_COMMAND_REPLY, NXJ_FIND_NEXT, handle };
byte[] reply = nxtComm.sendRequest(request, 32);
FileInfo fileInfo = null;
if (reply[2] == 0 && reply.length == 32) {
StringBuffer name = new StringBuffer(new String(reply))
.delete(0, 4);
int lastPos = name.indexOf("\0");
if (lastPos < 0 || lastPos > 20) lastPos = 20;
name.delete(lastPos, name.length());
fileInfo = new FileInfo(name.toString());
fileInfo.status = 0;
fileInfo.fileHandle = reply[3];
fileInfo.fileSize = (0xFF & reply[24]) | ((0xFF & reply[25]) << 8)
| ((0xFF & reply[26]) << 16) | ((0xFF & reply[27]) << 24);
fileInfo.startPage = (0xFF & reply[28]) | ((0xFF & reply[29]) << 8)
| ((0xFF & reply[30]) << 16) | ((0xFF & reply[31]) << 24);
}
return fileInfo;
}
/**
* Find the next file on the NXT
*
* @param handle
* Handle number from the previous found file or from the Find
* First command.
* @return fileInfo object giving details of the file
*/
public FileInfo findNext(byte handle) throws IOException {
byte[] request = { SYSTEM_COMMAND_REPLY, FIND_NEXT, handle };
byte[] reply = nxtComm.sendRequest(request, 28);
FileInfo fileInfo = null;
if (reply[2] == 0 && reply.length == 28) {
StringBuffer name = new StringBuffer(new String(reply))
.delete(0, 4);
int lastPos = name.indexOf("\0");
if (lastPos < 0 || lastPos > 20) lastPos = 20;
name.delete(lastPos, name.length());
fileInfo = new FileInfo(name.toString());
fileInfo.status = 0;
fileInfo.fileHandle = reply[3];
fileInfo.fileSize = (0xFF & reply[24]) | ((0xFF & reply[25]) << 8)
| ((0xFF & reply[26]) << 16) | ((0xFF & reply[27]) << 24);
fileInfo.startPage = -1;
}
return fileInfo;
}
/**
* Helper code to append a string and null terminator at the end of a
* command request.
*
* @param command the command
* @param str the string to append
* @return the concatenated command
*/
private byte[] appendString(byte[] command, String str) {
byte[] buff = new byte[command.length + str.length() + 1];
for (int i = 0; i < command.length; i++)
buff[i] = command[i];
for (int i = 0; i < str.length(); i++)
buff[command.length + i] = (byte) str.charAt(i);
buff[command.length + str.length()] = 0;
return buff;
}
/**
* Helper method to concatenate two byte arrays
* @param array1 the first array (e.g. a request)
* @param array2 the second array (e.g. an extra parameter)
*
* @return the concatenated array
*/
private byte[] appendBytes(byte[] array1, byte[] array2) {
byte[] array = new byte[array1.length + array2.length];
System.arraycopy(array1, 0, array, 0, array1.length);
System.arraycopy(array2, 0, array, array1.length, array2.length);
return array;
}
/**
* Get the battery reading
*
* @return the battery level in millivolts
* @throws IOException
*/
public int getBatteryLevel() throws IOException {
byte[] request = { DIRECT_COMMAND_REPLY, GET_BATTERY_LEVEL };
byte[] reply = nxtComm.sendRequest(request, 5);
int batteryLevel = (0xFF & reply[3]) | ((0xFF & reply[4]) << 8);
return batteryLevel;
}
/**
* Call the close() command when your program ends, otherwise you will have
* to turn the NXT brick off/on before you run another program.
*
*/
public void close() throws IOException {
if (!open) return;
open = false;
byte[] request = { SYSTEM_COMMAND_NOREPLY, NXJ_DISCONNECT };
nxtComm.sendRequest(request, 0); // Tell NXT to disconnect
nxtComm.close();
}
/**
* Put the NXT into SAMBA mode, ready to update the firmware
*
* @throws IOException
*/
public void boot() throws IOException {
byte[] request = {SYSTEM_COMMAND_NOREPLY, BOOT};
request = appendString(request, "Let's dance: SAMBA");
nxtComm.sendRequest(request, 0);
// Connection cannot be used after this command so we close it
nxtComm.close();
open = false;
}
/**
* Write data to the file
*
* @param handle the file handle
* @param data the data to write
* @return the status value
*
* @throws IOException
*/
public byte writeFile(byte handle, byte[] data) throws IOException {
byte[] command = { SYSTEM_COMMAND_NOREPLY, WRITE, handle };
int remaining = data.length;
int chunkStart = 0;
while (remaining > 0) {
int chunkLen = MAX_BUFFER_SIZE;
if (remaining < chunkLen) chunkLen = remaining;
byte [] request = new byte[chunkLen + 3];
System.arraycopy(command, 0, request, 0, command.length);
System.arraycopy(data, chunkStart, request, 3, chunkLen);
byte status = sendSystemRequest(request, 6);
if (status != 0) return status;
remaining -= chunkLen;
chunkStart += chunkLen;
}
return 0;
}
/**
* Upload a file to the NXT
*
* @param file the file to upload
* @param nxtFileName the name of the file on the NXT
* @return a message saying how long it took to upload the file
*
* @throws IOException
*/
public String uploadFile(File file, String nxtFileName) throws IOException {
byte[] data = new byte[MAX_BUFFER_SIZE];
int len;
byte handle;
FileInputStream in = null;
long millis = System.currentTimeMillis();
try {
in = new FileInputStream(file);
} catch (FileNotFoundException e) {
throw new IOException("File not found");
}
handle = openWrite(nxtFileName, (int) file.length());
try {
while ((len = in.read(data)) > 0) {
byte[] sendData = new byte[len];
for(int i=0;i<len;i++) sendData[i] = data[i];
writeFile(handle, sendData);
}
} catch (IOException ioe) {
throw new IOException("Failed to upload");
}
setVerify(true);
closeFile(handle);
return "Upload successful in " + (System.currentTimeMillis() - millis) + " milliseconds";
}
/**
* Returns requested number of bytes from a file. File must first be opened
* using the openRead() command.
*
* @param handle
* File handle number (from openRead method)
* @param length
* Number of bytes to read.
* @return the bytes requested
*/
public byte[] readFile(byte handle, int length) throws IOException {
int remaining = length;
int chunkStart = 0;
byte[] reply = new byte[length];
while (remaining > 0) {
int chunkLen = MAX_BUFFER_SIZE;
if (chunkLen > remaining) chunkLen = remaining;
byte[] request = { SYSTEM_COMMAND_REPLY, READ, handle, (byte) chunkLen,
(byte) (chunkLen >>> 8) };
byte[] reply1 = nxtComm.sendRequest(request, chunkLen + 6);
int dataLen = (reply1[4] & 0xFF) + ((reply1[5] << 8) & 0xFF);
for (int i = 0; i < dataLen; i++)
reply[chunkStart+i] = reply1[i + 6];
chunkStart += chunkLen;
remaining -= chunkLen;
}
return reply;
}
/**
* A NXJ extension to defrag the file system
*
* @return the status byte
* @throws IOException
*/
public byte defrag() throws IOException {
byte[] request = { SYSTEM_COMMAND_NOREPLY, NXJ_DEFRAG };
return sendSystemRequest(request, 3);
}
/**
* Get the friendly name of the NXT
* @return the friendly name
* @throws IOException
*/
public String getFriendlyName() throws IOException {
byte[] request = { SYSTEM_COMMAND_REPLY, GET_DEVICE_INFO };
byte[] reply = nxtComm.sendRequest(request, 33);
char nameChars[] = new char[16];
int len = 0;
for (int i = 0; i < 15 && reply[i + 3] != 0; i++) {
nameChars[i] = (char) reply[i + 3];
len++;
}
return new String(nameChars, 0, len);
}
/**
* Set the friendly name of the NXT
*
* @param name the friendly name
* @return the status byte
* @throws IOException
*/
public byte setFriendlyName(String name) throws IOException {
byte[] request = { SYSTEM_COMMAND_NOREPLY, SET_BRICK_NAME };
request = appendString(request, name);
return sendSystemRequest(request, 3);
}
/**
* Get the local address of the NXT.
*
* @return the address (used by USB and Bluetooth)
* @throws IOException
*/
public String getLocalAddress() throws IOException {
byte[] request = { SYSTEM_COMMAND_REPLY, GET_DEVICE_INFO };
byte[] reply = nxtComm.sendRequest(request, 33);
char addrChars[] = new char[14];
for (int i = 0; i < 7; i++) {
addrChars[i * 2] = hexChars.charAt((reply[i + 18] >> 4) & 0xF);
addrChars[i * 2 + 1] = hexChars.charAt(reply[i + 18] & 0xF);
}
return new String(addrChars);
}
/**
* Get input values for a specific NXT sensor port
*
* @param port the port number
* @return the InputValues structure
* @throws IOException
*/
public InputValues getInputValues(int port) throws IOException {
byte [] request = {DIRECT_COMMAND_REPLY, GET_INPUT_VALUES, (byte)port};
byte [] reply = nxtComm.sendRequest(request, 16);
InputValues inputValues = new InputValues();
inputValues.inputPort = reply[3];
// 0 is false, 1 is true.
inputValues.valid = (reply[4] != 0);
// 0 is false, 1 is true.
inputValues.isCalibrated = (reply[5] == 0);
inputValues.sensorType = reply[6];
inputValues.sensorMode = reply[7];
inputValues.rawADValue = (0xFF & reply[8]) | ((0xFF & reply[9]) << 8);
inputValues.normalizedADValue = (0xFF & reply[10]) | ((0xFF & reply[11]) << 8);
inputValues.scaledValue = (short)((0xFF & reply[12]) | (reply[13] << 8));
inputValues.calibratedValue = (short)((0xFF & reply[14]) | (reply[15] << 8));
return inputValues;
}
/**
* Retrieves the current output state for a port.
* @param port - 0 to 3
* @return OutputState - returns a container object for output state variables.
*/
public OutputState getOutputState(int port) throws IOException {
// !! Needs to check port to verify they are correct ranges.
byte [] request = {DIRECT_COMMAND_REPLY, GET_OUTPUT_STATE, (byte)port};
byte [] reply = nxtComm.sendRequest(request,25);
OutputState outputState = new OutputState(port);
outputState.status = reply[2];
outputState.outputPort = reply[3];
outputState.powerSetpoint = reply[4];
outputState.mode = reply[5];
outputState.regulationMode = reply[6];
outputState.turnRatio = reply[7];
outputState.runState = reply[8];
outputState.tachoLimit = (0xFF & reply[9]) | ((0xFF & reply[10]) << 8)| ((0xFF & reply[11]) << 16)| ((0xFF & reply[12]) << 24);
outputState.tachoCount = (0xFF & reply[13]) | ((0xFF & reply[14]) << 8)| ((0xFF & reply[15]) << 16)| ((0xFF & reply[16]) << 24);
outputState.blockTachoCount = (0xFF & reply[17]) | ((0xFF & reply[18]) << 8)| ((0xFF & reply[19]) << 16)| ((0xFF & reply[20]) << 24);
outputState.rotationCount = (0xFF & reply[21]) | ((0xFF & reply[22]) << 8)| ((0xFF & reply[23]) << 16)| ((0xFF & reply[24]) << 24);
return outputState;
}
/**
* Retrieves tacho count.
* @param port - 0 to 3
* @return tacho count
*/
public int getTachoCount(int port) throws IOException {
synchronized(this) {
byte [] request = {DIRECT_COMMAND_REPLY, GET_OUTPUT_STATE, (byte)port};
byte [] reply = nxtComm.sendRequest(request, 25);
int tachoCount = (0xFF & reply[13]) | ((0xFF & reply[14]) << 8)| ((0xFF & reply[15]) << 16)| ((0xFF & reply[16]) << 24);
return tachoCount;
}
}
/**
* Tells the NXT what type of sensor you are using and the mode to operate in.
* @param port - 0 to 3
* @param sensorType - Enumeration for sensor type (see NXTProtocol)
* @param sensorMode - Enumeration for sensor mode (see NXTProtocol)
*/
public byte setInputMode(int port, int sensorType, int sensorMode) throws IOException {
// !! Needs to check port to verify they are correct ranges.
byte [] request = {DIRECT_COMMAND_NOREPLY, SET_INPUT_MODE, (byte)port, (byte)sensorType, (byte)sensorMode};
return sendRequest(request, 3);
}
/**
* Returns the status for an Inter-Integrated Circuit (I2C) sensor (the
* ultrasound sensor) via the Low Speed (LS) data port. The port must first
* be configured to type LOWSPEED or LOWSPEED_9V.
* @param port 0-3
* @return byte[0] = status, byte[1] = Bytes Ready (count of available bytes to read)
*/
public byte [] LSGetStatus(byte port) throws IOException{
byte [] request = {DIRECT_COMMAND_REPLY, LS_GET_STATUS, port};
byte [] reply = nxtComm.sendRequest(request,4);
byte [] returnData = {reply[2], reply[3]};
return returnData;
}
/**
* Reads data from an Inter-Integrated Circuit (I2C) sensor (the
* ultrasound sensor) via the Low Speed (LS) data port. The port must
* first be configured to type LOWSPEED or LOWSPEED_9V.
* Data lengths are limited to 16 bytes per command. The response will
* also contain 16 bytes, with invalid data padded with zeros.
* @param port
* @return the response
*/
public byte [] LSRead(byte port) throws IOException {
byte [] request = {DIRECT_COMMAND_REPLY, LS_READ, port};
byte [] reply = nxtComm.sendRequest(request, 20);
byte rxLength = reply[3];
if(reply[2] == 0 && rxLength >= 0) {
byte [] rxData = new byte[rxLength];
System.arraycopy(reply, 4, rxData, 0, rxLength);
return rxData;
} else {
return null;
}
}
/**
* Used to request data from an Inter-Integrated Circuit (I2C) sensor (the
* ultrasound sensor) via the Low Speed (LS) data port. The port must first
* be configured to type LOWSPEED or LOWSPEED_9V.
* Data lengths are limited to 16 bytes per command.
* Rx (receive) Data Length MUST be specified in the write
* command since reading from the device is done on a
* master-slave basis.
* @param txData Transmitted data.
* @param rxDataLength Receive data length.
* @param port 0-3
* @return the status (0 = success)
*/
public byte LSWrite(byte port, byte [] txData, byte rxDataLength) throws IOException {
byte [] request = {DIRECT_COMMAND_NOREPLY, LS_WRITE, port, (byte)txData.length, rxDataLength};
request = appendBytes(request, txData);
return sendRequest(request, 3);
}
/**
* @param remoteInbox 0-9
* @param localInbox 0-9
* @param remove True clears the message from the remote inbox.
* @return the message as an array of bytes
*/
public byte[] messageRead(byte remoteInbox, byte localInbox, boolean remove) throws IOException {
byte [] request = {DIRECT_COMMAND_REPLY, MESSAGE_READ, remoteInbox, localInbox, (remove ? (byte) 1 : (byte) 0)};
byte [] reply = nxtComm.sendRequest(request, 64);
byte[] message = new byte[reply[4]];
System.arraycopy(reply, 5, message, 0, reply[4]);
return message;
}
/**
* Sends a message to an inbox on the NXT for storage(?)
* For future reference, message size must be capped at 59 for USB.
* UNTESTED
* @param message String to send. A null termination is automatically appended.
* @param inbox Inbox Number 0 - 9
* @return the status (0 = success)
*/
public byte messageWrite(byte [] message, byte inbox) throws IOException {
byte [] request = {DIRECT_COMMAND_NOREPLY, MESSAGE_WRITE, inbox, (byte)(message.length)};
request = appendBytes(request, message);
return sendRequest(request, 3);
}
/**
* Plays a tone on NXT speaker. If a new tone is sent while the previous tone is playing,
* the new tone command will stop the old tone command.
* @param frequency - 100 to 2000?
* @param duration - In milliseconds.
* @return - Returns true if command worked, false if it failed.
*/
public byte playTone(int frequency, int duration) throws IOException {
byte [] request = {DIRECT_COMMAND_NOREPLY, PLAY_TONE, (byte)frequency, (byte)(frequency>>>8), (byte)duration, (byte)(duration>>>8)};
return sendRequest(request, 3);
}
public byte playSoundFile(String fileName, boolean repeat) throws IOException {
byte boolVal = 0;
if(repeat) boolVal = (byte)0xFF; // Convert boolean to number
byte [] request = {DIRECT_COMMAND_NOREPLY, PLAY_SOUND_FILE, boolVal};
byte[] encFileName = null;
try {
encFileName = AsciizCodec.encode(fileName);
} catch (UnsupportedEncodingException e) {
System.err.println("Illegal characters in filename");
return -1;
}
request = appendBytes(request, encFileName);
return sendRequest(request, 3);
}
/**
* Stops sound file playing.
* @return the status (0 = success)
*/
public byte stopSoundPlayback() throws IOException {
byte [] request = {DIRECT_COMMAND_NOREPLY, STOP_SOUND_PLAYBACK};
return sendRequest(request, 3);
}
/**
* Resets either RotationCount or BlockTacho
* @param port Output port (0-2)
* @param relative TRUE: BlockTacho, FALSE: RotationCount
* @return the status (0 = success)
*/
public byte resetMotorPosition(int port, boolean relative) throws IOException {
// !! Needs to check port to verify they are correct ranges.
// !!! I'm not sure I'm sending boolean properly
byte boolVal = 0;
if(relative) boolVal = (byte)0xFF;
byte [] request = {DIRECT_COMMAND_NOREPLY, RESET_MOTOR_POSITION, (byte)port, boolVal};
return sendRequest(request, 3);
}
/**
*
* @param port - Output port (0 - 2 or 0xFF for all three)
* @param power - Setpoint for power. (-100 to 100)
* @param mode - Setting the modes MOTORON, BRAKE, and/or REGULATED. This parameter is a bitfield, so to put it in brake mode and regulated, use BRAKEMODE + REGULATED
* @param regulationMode - see NXTProtocol for enumerations
* @param turnRatio - Need two motors? (-100 to 100)
* @param runState - see NXTProtocol for enumerations
* @param tachoLimit - Number of degrees(?) to rotate before stopping.
* @return the status (0 = success)
*/
public byte setOutputState(int port, byte power, int mode, int regulationMode, int turnRatio, int runState, int tachoLimit) throws IOException {
// !! Needs to check port, power to verify they are correct ranges.
byte [] request = {DIRECT_COMMAND_NOREPLY, SET_OUTPUT_STATE, (byte)port, power, (byte)mode, (byte)regulationMode, (byte)turnRatio, (byte)runState, (byte)tachoLimit, (byte)(tachoLimit>>>8), (byte)(tachoLimit>>>16), (byte)(tachoLimit>>>24)};
return sendRequest(request, 3);
}
/**
* Gets device information
*
* @return a DeviceInfo structure
* @throws IOException
*/
public DeviceInfo getDeviceInfo() throws IOException {
// !! Needs to check port to verify they are correct ranges.
byte [] request = {SYSTEM_COMMAND_REPLY, GET_DEVICE_INFO};
byte [] reply = nxtComm.sendRequest(request, 33);
DeviceInfo d = new DeviceInfo();
d.status = reply[2];
d.NXTname = new StringBuffer(new String(reply)).delete(18,33).delete(0, 3).toString();
d.bluetoothAddress = Integer.toHexString(reply[18]) + ":" + Integer.toHexString(reply[19]) + ":" + Integer.toHexString(reply[20]) + ":" + Integer.toHexString(reply[21]) + ":" + Integer.toHexString(reply[22]) + ":" + Integer.toHexString(reply[23]) + ":" + Integer.toHexString(reply[24]);
d.signalStrength = (0xFF & reply[25]) | ((0xFF & reply[26]) << 8)| ((0xFF & reply[27]) << 16)| ((0xFF & reply[28]) << 24);
d.freeFlash = (0xFF & reply[29]) | ((0xFF & reply[30]) << 8)| ((0xFF & reply[31]) << 16)| ((0xFF & reply[32]) << 24);
return d;
}
/**
* Get the fimrware version.
* leJOS NXJ returns the version of the LEGO firmware that it emulates,
* not its own version number.
*
* @return a FirmwareInfo structure.
* @throws IOException
*/
public FirmwareInfo getFirmwareVersion() throws IOException {
byte [] request = {SYSTEM_COMMAND_REPLY, GET_FIRMWARE_VERSION};
byte [] reply = nxtComm.sendRequest(request, 7);
FirmwareInfo info = new FirmwareInfo();
info.status = reply[2];
if(info.status == 0) {
info.protocolVersion = reply[4] + "." + reply[3];
info.firmwareVersion = reply[6] + "." + reply[5];
}
return info;
}
/**
* Deletes user flash memory.
* Not implemented by leJOS NXJ.
* @return the status (0 = success)
*/
public byte deleteUserFlash() throws IOException {
byte [] request = {SYSTEM_COMMAND_REPLY, DELETE_USER_FLASH};
byte [] reply = nxtComm.sendRequest(request, 3);
return reply[2];
}
/**
* Get the singleton NXTCommand object. Use of this is optional.
*
* @return the singleton NXTCommand instance
*/
public static NXTCommand getSingleton() {
if (singleton == null) singleton = new NXTCommand();
return singleton;
}
/**
* Test is connection is open
*
* @return true iff the connection is open
*/
public boolean isOpen() {
return open;
}
}