1 package lejos.nxt.comm;
8 * Implements the Lego Communication Protocol,
9 * with some extensions for lejos NXJ.
13 private static byte[] i2cReply = new byte[16];
14 private static int i2cLen = 0;
15 private static File[] files = null;
16 private static String[] fileNames = null;
17 private static int fileIdx = -1;
18 private static String currentProgram = null;
19 private static File file = null;
20 private static FileOutputStream out = null;
21 private static FileInputStream in = null;
22 private static int numFiles;
24 // Command types constants. Indicates type of packet being sent or received.
25 public static byte DIRECT_COMMAND_REPLY = 0x00;
26 public static byte SYSTEM_COMMAND_REPLY = 0x01;
27 public static byte REPLY_COMMAND = 0x02;
28 public static byte DIRECT_COMMAND_NOREPLY = (byte)0x80; // Avoids ~100ms latency
29 public static byte SYSTEM_COMMAND_NOREPLY = (byte)0x81; // Avoids ~100ms latency
32 public static final byte START_PROGRAM = 0x00;
33 public static final byte STOP_PROGRAM = 0x01;
34 public static final byte PLAY_SOUND_FILE = 0x02;
35 public static final byte PLAY_TONE = 0x03;
36 public static final byte SET_OUTPUT_STATE = 0x04;
37 public static final byte SET_INPUT_MODE = 0x05;
38 public static final byte GET_OUTPUT_STATE = 0x06;
39 public static final byte GET_INPUT_VALUES = 0x07;
40 public static final byte RESET_SCALED_INPUT_VALUE = 0x08;
41 public static final byte MESSAGE_WRITE = 0x09;
42 public static final byte RESET_MOTOR_POSITION = 0x0A;
43 public static final byte GET_BATTERY_LEVEL = 0x0B;
44 public static final byte STOP_SOUND_PLAYBACK = 0x0C;
45 public static final byte KEEP_ALIVE = 0x0D;
46 public static final byte LS_GET_STATUS = 0x0E;
47 public static final byte LS_WRITE = 0x0F;
48 public static final byte LS_READ = 0x10;
49 public static final byte GET_CURRENT_PROGRAM_NAME = 0x11;
52 public static byte NXJ_DISCONNECT = 0x20;
53 public static byte NXJ_DEFRAG = 0x21;
56 public static final byte OPEN_READ = (byte)0x80;
57 public static final byte OPEN_WRITE = (byte)0x81;
58 public static final byte READ = (byte)0x82;
59 public static final byte WRITE = (byte)0x83;
60 public static final byte CLOSE = (byte)0x84;
61 public static final byte DELETE = (byte)0x85;
62 public static final byte FIND_FIRST = (byte)0x86;
63 public static final byte FIND_NEXT = (byte)0x87;
64 public static final byte GET_FIRMWARE_VERSION = (byte)0x88;
65 public static final byte OPEN_WRITE_LINEAR = (byte)0x89;
66 public static final byte OPEN_READ_LINEAR = (byte)0x8A;
67 public static final byte OPEN_WRITE_DATA = (byte)0x8B;
68 public static final byte OPEN_APPEND_DATA = (byte)0x8C;
69 public static final byte BOOT = (byte)0x97;
70 public static final byte SET_BRICK_NAME = (byte)0x98;
71 public static final byte GET_DEVICE_INFO = (byte)0x9B;
72 public static final byte DELETE_USER_FLASH = (byte)0xA0;
73 public static final byte POLL_LENGTH = (byte)0xA1;
74 public static final byte POLL = (byte)0xA2;
76 public static final byte NXJ_FIND_FIRST = (byte)0xB6;
77 public static final byte NXJ_FIND_NEXT = (byte)0xB7;
81 public static final byte FILE_NOT_FOUND = (byte)0x86;
88 * Emulates a Lego firmware Direct or System command
89 * @param cmd the buffer containing the command
90 * @param cmdLen the legth of the command
92 public static int emulateCommand(byte[] cmd, int cmdLen, byte[] reply)
96 for(int i=0;i<reply.length;i++)reply[i] = 0;
98 reply[0] = REPLY_COMMAND;;
104 if (cmdId == START_PROGRAM) {
105 int filenameLength = 0;
107 for(int i=0;i<20 && cmd[i+2] != 0;i++) filenameLength++;
108 char[] chars = new char[filenameLength];
109 for(int i=0;i<filenameLength;i++) chars[i] = (char) cmd[i+2];
110 currentProgram = new String(chars,0,filenameLength);
111 if (fileNames != null) {
112 for(int i=0;i<fileNames.length;i++) {
113 if (currentProgram.equals(fileNames[i])) {
122 // GET CURRENT PROGRAM NAME
124 if (cmdId == GET_CURRENT_PROGRAM_NAME) {
125 if (currentProgram != null) {
126 for(int i=0;i<currentProgram.length() && i < 19;i++)
127 reply[3+i] = (byte) currentProgram.charAt(i);
132 if (cmdId == GET_BATTERY_LEVEL) {
133 int mv = Battery.getVoltageMilliVolt();
135 reply[3] = getLSB(mv);
136 reply[4] = getMSB(mv);
141 if (cmdId == PLAY_TONE)
143 Sound.playTone(getInt(cmd,2), getInt(cmd,4));
146 // GET FIRMWARE VERSION
147 if (cmdId == GET_FIRMWARE_VERSION)
157 if (cmdId == GET_DEVICE_INFO)
159 byte [] name = Bluetooth.getFriendlyName();
160 for(int i=0;i<15;i++) reply[3+i] = name[i];
161 byte [] address = Bluetooth.getLocalAddress();
162 for(int i=0;i<7;i++) reply[18+i] = address[i];
163 int freeMem = File.freeMemory();
164 reply[29] = (byte) (freeMem & 0xFF);
165 reply[30] = (byte) ((freeMem >> 8) & 0xFF);
166 reply[31] = (byte) ((freeMem >> 16) & 0xFF);
167 reply[32] = (byte) ((freeMem >> 24) & 0xFF);
172 if (cmdId == SET_BRICK_NAME)
174 byte [] name = new byte[16];
175 for(int i=0;i<16;i++) name[i] = cmd[i+2];
176 Bluetooth.setFriendlyName(name);
181 if (cmdId == GET_OUTPUT_STATE) {
189 int tacho = m.getTachoCount();
192 reply[4] = (byte)(m.getSpeed() * 100 / 900); // Power
195 if (m.isMoving()) mode = 0x01; // 0x01 = MOTORON
196 reply[5] = mode; // Only contains isMoving (MOTORON) at moment
197 // REGULATION_MODE CALCULATION:
198 byte regulation_mode = 0; // 0 = idle
199 if (m.isMoving()) mode = 0x01; // 0x01 = MOTOR_SPEED
200 // !! This returns same as run state (below). Whats the diff?
201 reply[6] = regulation_mode; // Regulation mode
202 // TURN RATIO CALC (ignored):
203 byte turn_ratio = 0; // NXJ uses Pilot. Omitting.
204 reply[7] = turn_ratio; // Turn ratio
205 // RUN_STATE CALCULATION:
207 if (m.isMoving()) run_state = 0x20; // 0x20 = RUNNING
208 reply[8] = run_state; // Run state
209 // 9 - 12 = Tacho Limit. Ignored?
210 reply[13] = (byte) (tacho & 0xFF);
211 reply[14] = (byte) ((tacho >> 8) & 0xFF);
212 reply[15] = (byte) ((tacho >> 16) & 0xFF);
213 reply[16] = (byte) ((tacho >> 24) & 0xFF);
218 if (cmdId == GET_INPUT_VALUES) {
220 int raw = SensorPort.PORTS[port].readRawValue();
221 int scaled = SensorPort.PORTS[port].readValue();
222 int norm = 1024 - raw;
227 reply[6] = (byte) SensorPort.PORTS[port].getType();
228 reply[7] = (byte) SensorPort.PORTS[port].getMode();
229 reply[8] = getLSB(raw);
230 reply[9] = getMSB(raw);
231 reply[10] = getLSB(norm);
232 reply[11] = getMSB(norm);
233 reply[12] = getLSB(scaled);
234 reply[13] = getMSB(scaled);
241 if (cmdId == SET_INPUT_MODE) {
243 int sensorType = (cmd[3] & 0xFF);
244 int sensorMode = (cmd[4] & 0xFF);
245 SensorPort.PORTS[port].setTypeAndMode(sensorType, sensorMode);
249 if(cmdId == SET_OUTPUT_STATE) {
250 byte motorid = cmd[2];
252 int speed = (Math.abs(power) * 900) / 100;
254 byte regMode = cmd[5];
255 byte turnRatio = cmd[6];
256 byte runState = cmd[7];
257 int tacholimit = (0xFF & cmd[8]) | ((0xFF & cmd[9]) << 8)| ((0xFF & cmd[10]) << 16)| ((0xFF & cmd[11]) << 24);
264 if(motorid == 0 || (motorid < 0 && i == 0))
266 else if (motorid == 1 || (motorid < 0 && i == 1))
268 else if (motorid == 2 || (motorid < 0 && i == 2))
273 if(power < 0) tacholimit = -tacholimit;
275 // Check if command is to STOP:
276 if(power == 0) m.stop();
278 // Check if doing tacho rotation
280 m.rotate(tacholimit, true); // Returns immediately
282 if((mode | 0x01) != 0 && power != 0 && tacholimit == 0) { // MOTORON
283 if(power>0) m.forward();
287 if (motorid >= 0) break;
291 // RESETMOTORPOSITION
292 if (cmdId == RESET_MOTOR_POSITION)
294 MotorPort.resetTachoCountById(cmd[2]);
298 if (cmdId == KEEP_ALIVE)
304 if (cmdId == LS_WRITE)
309 SensorPort.i2cEnableById(port);
310 try {Thread.sleep(100);} catch(InterruptedException ie) {}
311 int ret = SensorPort.i2cStartById(port, cmd[5] >> 1, cmd[6], rxLen, i2cReply, rxLen, 0);
313 while (SensorPort.i2cBusyById(port) != 0) {
316 try {Thread.sleep(100);} catch(InterruptedException ie) {}
321 if (cmdId == LS_READ)
323 reply[3] = (byte) i2cLen;
324 for(int i=0;i<16;i++) reply[i+4] = i2cReply[i];
330 if (cmdId == LS_GET_STATUS)
332 reply[3] = (byte) i2cLen;
337 if (cmdId == OPEN_READ)
339 int filenameLength = 0;
341 for(int i=0;i<20 && cmd[i+2] != 0;i++) filenameLength++;
342 char[] chars = new char[filenameLength];
343 for(int i=0;i<filenameLength;i++) chars[i] = (char) cmd[i+2];
344 file = new File(new String(chars,0,filenameLength));
346 in = new FileInputStream(file);
347 int size = file.length();
348 cmd[4] = (byte) (size & 0xFF);
349 cmd[5] = (byte) ((size >> 8) & 0xFF);
350 cmd[6] = (byte) ((size >> 16) & 0xFF);
351 cmd[7] = (byte) ((size >> 24) & 0xFF);
352 } catch (Exception e) {
353 reply[2] = FILE_NOT_FOUND;
359 if (cmdId == OPEN_WRITE)
361 int filenameLength = 0;
363 for(int i=0;i<20 && cmd[i+2] != 0;i++) filenameLength++;
364 char[] chars = new char[filenameLength];
365 for(int i=0;i<filenameLength;i++) chars[i] = (char) cmd[i+2];
366 file = new File(new String(chars,0,filenameLength));
367 int size = cmd[22] & 0xFF;
368 size += ((cmd[23] & 0xFF) << 8);
369 size += ((cmd[24] & 0xFF) << 16);
370 size += ((cmd[25] & 0xFF) << 24);
375 file.createNewFile();
376 fileNames = new String[++numFiles];
377 for(int j=0;j<numFiles;j++) fileNames[j] = files[j].getName();
378 out = new FileOutputStream(file);
384 if (cmdId == OPEN_WRITE_LINEAR)
390 if (cmdId == OPEN_WRITE_DATA)
396 if (cmdId == OPEN_APPEND_DATA)
398 reply[2] = FILE_NOT_FOUND;
403 if (cmdId == NXJ_DEFRAG)
409 if (cmdId == FIND_FIRST || cmdId == NXJ_FIND_FIRST)
412 if (cmdId == FIND_FIRST) len = 28;
416 reply[2] = FILE_NOT_FOUND;
420 for(int i=0;i<fileNames[0].length();i++) reply[4+i] = (byte) fileNames[0].charAt(i);
422 int size = files[0].length();
423 reply[24] = (byte) (size & 0xFF);
424 reply[25] = (byte) ((size >> 8) & 0xFF);
425 reply[26] = (byte) ((size >> 16) & 0xFF);
426 reply[27] = (byte) ((size >> 24) & 0xFF);
428 if (cmdId == NXJ_FIND_FIRST) {
429 int startPage = files[0].getPage();
430 reply[28] = (byte) (startPage & 0xFF);
431 reply[29] = (byte) ((startPage >> 8) & 0xFF);
432 reply[30] = (byte) ((startPage >> 16) & 0xFF);
433 reply[31] = (byte) ((startPage >> 24) & 0xFF);
439 if (cmdId == FIND_NEXT || cmdId == NXJ_FIND_NEXT)
441 if (cmdId == FIND_NEXT) len = 28;
443 if (fileNames == null || fileIdx >= fileNames.length) reply[2] = FILE_NOT_FOUND;
446 for(int i=0;i<fileNames[fileIdx].length();i++) reply[4+i] = (byte) fileNames[fileIdx].charAt(i);
447 int size = files[fileIdx].length();
448 reply[24] = (byte) (size & 0xFF);
449 reply[25] = (byte) ((size >> 8) & 0xFF);
450 reply[26] = (byte) ((size >> 16) & 0xFF);
451 reply[27] = (byte) ((size >> 24) & 0xFF);
453 if (cmdId == NXJ_FIND_NEXT) {
454 int startPage = files[fileIdx].getPage();
455 reply[28] = (byte) (startPage & 0xFF);
456 reply[29] = (byte) ((startPage >> 8) & 0xFF);
457 reply[30] = (byte) ((startPage >> 16) & 0xFF);
458 reply[31] = (byte) ((startPage >> 24) & 0xFF);
468 int numBytes = ((cmd[4] & 0xFF) << 8) + (cmd[3] & 0xFF);
472 bytesRead = in.read(reply,6, numBytes);
473 } catch (IOException ioe) {}
474 reply[4] = (byte) (bytesRead & 0xFF);
475 reply[5] = (byte) ((bytesRead << 8) & 0xFF);
482 int dataLen = cmdLen - 3;
484 out.write(cmd,3,dataLen);
485 } catch (Exception ioe) {
486 //LCD.drawString("Exception", 0, 7);
489 reply[4] = (byte) (dataLen &0xFF);
490 reply[5] = (byte) ((dataLen >> 8) & 0xFF);
497 int filenameLength = 0;
498 boolean deleted = false;
500 for(int i=0;i<20 && cmd[i+2] != 0;i++) filenameLength++;
501 char[] chars = new char[filenameLength];
502 for(int i=0;i<filenameLength;i++) chars[i] = (char) cmd[i+2];
503 String fileName = new String(chars,0,filenameLength);
504 if (fileNames != null) {
505 for(int i=0;i<fileNames.length;i++) {
506 if (fileName.equals(fileNames[i])) {
508 for(int j=0;j<filenameLength;j++) reply[j+3] = (byte) chars[j];
510 fileNames = new String[--numFiles];
511 for(int j=0;j<numFiles;j++) fileNames[j] = files[j].getName();
516 if (!deleted) reply[2] = FILE_NOT_FOUND;
526 } catch (Exception ioe) {
527 //LCD.drawString("Exception",0,7);
538 private static int getInt(byte [] cmd, int i)
540 return (cmd[i] & 0xFF) + ((cmd[i+1] & 0xFF) << 8);
543 private static byte getLSB(int i)
545 return (byte) (i & 0xFF);
548 private static byte getMSB(int i)
550 return (byte) ((i >> 8) & 0xFF);
553 private static void init_files() {
555 files = File.listFiles();
557 for(int i=0;i<files.length && files[i] != null;i++) numFiles++;
558 fileNames = new String[numFiles];
559 for(int i=0;i<numFiles;i++) fileNames[i] = files[i].getName();