AI2 Component  (Version nb184)
NxtDirectCommands.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2020 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
19 
20 import android.util.Log;
21 
22 import java.io.BufferedInputStream;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.util.ArrayList;
27 import java.util.List;
28 
37 @DesignerComponent(version = YaVersion.NXT_DIRECT_COMMANDS_COMPONENT_VERSION,
38  description = "A component that provides a low-level interface to a LEGO MINDSTORMS NXT " +
39  "robot, with functions to send NXT Direct Commands.",
40  category = ComponentCategory.LEGOMINDSTORMS,
41  nonVisible = true,
42  iconName = "images/legoMindstormsNxt.png")
43 @SimpleObject
44 @UsesPermissions(permissionNames = "android.permission.INTERNET," +
45  "android.permission.WRITE_EXTERNAL_STORAGE," +
46  "android.permission.READ_EXTERNAL_STORAGE")
48 
53  super(container, "NxtDirectCommands");
54  }
55 
56  // TODO(user, lizlooney) - Add a property for a "helper program", like MotorControl21.rxe. If
57  // set, then the Connect method would automatically take care of checking for, downloading (if
58  // necessary) and starting the helper program. This would minimize the programming blocks for a
59  // classroom project.
60 
61  @SimpleFunction(description = "Start execution of a previously downloaded program on " +
62  "the robot.")
63  public void StartProgram(String programName) {
64  String functionName = "StartProgram";
65  if (!checkBluetooth(functionName)) {
66  return;
67  }
68  if (programName.length() == 0) {
69  form.dispatchErrorOccurredEvent(this, functionName,
71  return;
72  }
73  if (programName.indexOf(".") == -1) {
74  programName += ".rxe";
75  }
76 
77  byte[] command = new byte[22];
78  command[0] = (byte) 0x80; // Direct command telegram, no response
79  command[1] = (byte) 0x00; // STARTPROGRAM command
80  copyStringValueToBytes(programName, command, 2, 19);
81  sendCommand(functionName, command);
82  }
83 
84  @SimpleFunction(description = "Stop execution of the currently running program on " +
85  "the robot.")
86  public void StopProgram() {
87  String functionName = "StopProgram";
88  if (!checkBluetooth(functionName)) {
89  return;
90  }
91 
92  byte[] command = new byte[2];
93  command[0] = (byte) 0x80; // Direct command telegram, no response
94  command[1] = (byte) 0x01; // STOPPROGRAM command
95  sendCommand(functionName, command);
96  }
97 
98  @SimpleFunction(description = "Play a sound file on the robot.")
99  public void PlaySoundFile(String fileName) {
100  String functionName = "PlaySoundFile";
101  if (!checkBluetooth(functionName)) {
102  return;
103  }
104  if (fileName.length() == 0) {
105  form.dispatchErrorOccurredEvent(this, functionName,
107  return;
108  }
109  if (fileName.indexOf(".") == -1) {
110  fileName += ".rso";
111  }
112 
113  byte[] command = new byte[23];
114  command[0] = (byte) 0x80; // Direct command telegram, no response
115  command[1] = (byte) 0x02; // PLAYSOUNDFILE command
116  copyBooleanValueToBytes(false, command, 2); // play file once only
117  copyStringValueToBytes(fileName, command, 3, 19);
118  sendCommand(functionName, command);
119  }
120 
121  @SimpleFunction(description = "Make the robot play a tone.")
122  public void PlayTone(int frequencyHz, int durationMs) {
123  String functionName = "PlayTone";
124  if (!checkBluetooth(functionName)) {
125  return;
126  }
127 
128  if (frequencyHz < 200) {
129  Log.w(logTag, "frequencyHz " + frequencyHz + " is invalid, using 200.");
130  frequencyHz = 200;
131  }
132  if (frequencyHz > 14000) {
133  Log.w(logTag, "frequencyHz " + frequencyHz + " is invalid, using 14000.");
134  frequencyHz = 14000;
135  }
136  byte[] command = new byte[6];
137  command[0] = (byte) 0x80; // Direct command telegram, no response
138  command[1] = (byte) 0x03; // PLAYTONE command
139  copyUWORDValueToBytes(frequencyHz, command, 2); // 2-3: frequency, Hz (UWORD)
140  copyUWORDValueToBytes(durationMs, command, 4); // 4-5: Duration, ms (UWORD)
141  sendCommand(functionName, command);
142  }
143 
144  @SimpleFunction(description = "Sets the output state of a motor on the robot.")
145  public void SetOutputState(String motorPortLetter, int power, int mode, int regulationMode,
146  int turnRatio, int runState, long tachoLimit) {
147  String functionName = "SetOutputState";
148  if (!checkBluetooth(functionName)) {
149  return;
150  }
151 
152  int port;
153  try {
154  port = convertMotorPortLetterToNumber(motorPortLetter);
155  } catch (IllegalArgumentException e) {
156  form.dispatchErrorOccurredEvent(this, functionName,
158  return;
159  }
160 
161  setOutputState(functionName, port, power, mode,
162  regulationMode, sanitizeTurnRatio(turnRatio), runState, tachoLimit);
163  }
164 
165  @SimpleFunction(description = "Configure an input sensor on the robot.")
166  public void SetInputMode(String sensorPortLetter, int sensorType, int sensorMode) {
167  String functionName = "SetInputMode";
168  if (!checkBluetooth(functionName)) {
169  return;
170  }
171 
172  int port;
173  try {
174  port = convertSensorPortLetterToNumber(sensorPortLetter);
175  } catch (IllegalArgumentException e) {
176  form.dispatchErrorOccurredEvent(this, functionName,
177  ErrorMessages.ERROR_NXT_INVALID_SENSOR_PORT, sensorPortLetter);
178  return;
179  }
180 
181  setInputMode(functionName, port, sensorType, sensorMode);
182  }
183 
184  @SimpleFunction(description = "Reads the output state of a motor on the robot.")
185  public List<Number> GetOutputState(String motorPortLetter) {
186  String functionName = "GetOutputState";
187  if (!checkBluetooth(functionName)) {
188  return new ArrayList<Number>();
189  }
190 
191  int port;
192  try {
193  port = convertMotorPortLetterToNumber(motorPortLetter);
194  } catch (IllegalArgumentException e) {
195  form.dispatchErrorOccurredEvent(this, functionName,
197  return new ArrayList<Number>();
198  }
199 
200  byte[] returnPackage = getOutputState(functionName, port);
201  if (returnPackage != null) {
202  List<Number> outputState = new ArrayList<Number>();
203  outputState.add(getSBYTEValueFromBytes(returnPackage, 4)); // Power (SBYTE -100 to 100)
204  outputState.add(getUBYTEValueFromBytes(returnPackage, 5)); // Mode (UBYTE)
205  outputState.add(getUBYTEValueFromBytes(returnPackage, 6)); // Regulation mode (UBYTE)
206  outputState.add(getSBYTEValueFromBytes(returnPackage, 7)); // TurnRatio (SBYTE -100 to 100)
207  outputState.add(getUBYTEValueFromBytes(returnPackage, 8)); // RunState (UBYTE)
208  outputState.add(getULONGValueFromBytes(returnPackage, 9)); // TachoLimit (ULONG)
209  outputState.add(getSLONGValueFromBytes(returnPackage, 13)); // TachoCount (SLONG)
210  outputState.add(getSLONGValueFromBytes(returnPackage, 17)); // BlockTachoCount (SLONG)
211  outputState.add(getSLONGValueFromBytes(returnPackage, 21)); // RotationCount (SLONG)
212  return outputState;
213  }
214 
215  // invalid response
216  return new ArrayList<Number>();
217  }
218 
219  private byte[] getOutputState(String functionName, int port) {
220  byte[] command = new byte[3];
221  command[0] = (byte) 0x00; // Direct command telegram, response required
222  command[1] = (byte) 0x06; // GETOUTPUTSTATE command
223  copyUBYTEValueToBytes(port, command, 2);
224  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
225  if (evaluateStatus(functionName, returnPackage, command[1])) {
226  if (returnPackage.length == 25) {
227  return returnPackage;
228  } else {
229  Log.w(logTag, functionName + ": unexpected return package length " +
230  returnPackage.length + " (expected 25)");
231  }
232  }
233  return null;
234  }
235 
236  @SimpleFunction(description = "Reads the values of an input sensor on the robot. " +
237  "Assumes sensor type has been configured via SetInputMode.")
238  public List<Object> GetInputValues(String sensorPortLetter) {
239  String functionName = "GetInputValues";
240  if (!checkBluetooth(functionName)) {
241  return new ArrayList<Object>();
242  }
243 
244  int port;
245  try {
246  port = convertSensorPortLetterToNumber(sensorPortLetter);
247  } catch (IllegalArgumentException e) {
248  form.dispatchErrorOccurredEvent(this, functionName,
249  ErrorMessages.ERROR_NXT_INVALID_SENSOR_PORT, sensorPortLetter);
250  return new ArrayList<Object>();
251  }
252 
253  byte[] returnPackage = getInputValues(functionName, port);
254  if (returnPackage != null) {
255  List<Object> inputValues = new ArrayList<Object>();
256  inputValues.add(getBooleanValueFromBytes(returnPackage, 4)); // Valid
257  inputValues.add(getBooleanValueFromBytes(returnPackage, 5)); // Calibrated
258  inputValues.add(getUBYTEValueFromBytes(returnPackage, 6)); // Sensor type
259  inputValues.add(getUBYTEValueFromBytes(returnPackage, 7)); // Sensor mode
260  inputValues.add(getUWORDValueFromBytes(returnPackage, 8)); // Raw A/D value (UWORD)
261  inputValues.add(getUWORDValueFromBytes(returnPackage, 10)); // Normalized A/D value (UWORD)
262  inputValues.add(getSWORDValueFromBytes(returnPackage, 12)); // Scaled value (SWORD)
263  inputValues.add(getSWORDValueFromBytes(returnPackage, 14)); // Calibrated value (SWORD)
264  return inputValues;
265  }
266 
267  // invalid response
268  return new ArrayList<Object>();
269  }
270 
271  @SimpleFunction(description = "Reset the scaled value of an input sensor on the robot.")
272  public void ResetInputScaledValue(String sensorPortLetter) {
273  String functionName = "ResetInputScaledValue";
274  if (!checkBluetooth(functionName)) {
275  return;
276  }
277 
278  int port;
279  try {
280  port = convertSensorPortLetterToNumber(sensorPortLetter);
281  } catch (IllegalArgumentException e) {
282  form.dispatchErrorOccurredEvent(this, functionName,
283  ErrorMessages.ERROR_NXT_INVALID_SENSOR_PORT, sensorPortLetter);
284  return;
285  }
286 
287  resetInputScaledValue(functionName, port);
288  byte[] command = new byte[3];
289  command[0] = (byte) 0x80; // Direct command telegram, no response
290  command[1] = (byte) 0x08; // RESETINPUTSCALEDVALUE command
291  copyUBYTEValueToBytes(port, command, 2);
292  sendCommand(functionName, command);
293  }
294 
295  @SimpleFunction(description = "Write a message to a mailbox (1-10) on the robot.")
296  public void MessageWrite(int mailbox, String message) {
297  String functionName = "MessageWrite";
298  if (!checkBluetooth(functionName)) {
299  return;
300  }
301  // Note from Paul Gyugyi during code review: we are only supporting mailboxes 1-10, but NXT can
302  // use mailboxes above 10 as relays to other NXTs. We've never needed it, but if you ever see
303  // a feature request or bug report, all that might be required is just raising our upper limit
304  // on the range.
305  if (mailbox < 1 || mailbox > 10) {
306  form.dispatchErrorOccurredEvent(this, functionName,
308  return;
309  }
310  int messageLength = message.length();
311  if (messageLength > 58) {
312  form.dispatchErrorOccurredEvent(this, functionName,
314  return;
315  }
316 
317  mailbox--; // send 0-based mailbox to NXT
318 
319  byte[] command = new byte[4 + messageLength + 1];
320  command[0] = (byte) 0x80; // Direct command telegram, no response
321  command[1] = (byte) 0x09; // MESSAGEWRITE command
322  copyUBYTEValueToBytes(mailbox, command, 2);
323  // message length includes null termination byte
324  copyUBYTEValueToBytes(messageLength + 1, command, 3);
325  copyStringValueToBytes(message, command, 4, messageLength);
326  // The command array is already filled with zeros. No need to actually set the last byte to 0.
327  sendCommand(functionName, command);
328  }
329 
330  @SimpleFunction(description = "Reset motor position.")
331  public void ResetMotorPosition(String motorPortLetter, boolean relative) {
332  String functionName = "ResetMotorPosition";
333  if (!checkBluetooth(functionName)) {
334  return;
335  }
336 
337  int port;
338  try {
339  port = convertMotorPortLetterToNumber(motorPortLetter);
340  } catch (IllegalArgumentException e) {
341  form.dispatchErrorOccurredEvent(this, functionName,
343  return;
344  }
345 
346  byte[] command = new byte[4];
347  command[0] = (byte) 0x80; // Direct command telegram, no response
348  command[1] = (byte) 0x0A; // RESETMOTORPOSITION command
349  copyUBYTEValueToBytes(port, command, 2);
350  copyBooleanValueToBytes(relative, command, 3);
351  sendCommand(functionName, command);
352  }
353 
354  @SimpleFunction(description = "Get the battery level for the robot. " +
355  "Returns the voltage in millivolts.")
356  public int GetBatteryLevel() {
357  String functionName = "GetBatteryLevel";
358  if (!checkBluetooth(functionName)) {
359  return 0;
360  }
361 
362  byte[] command = new byte[2];
363  command[0] = (byte) 0x00; // Direct command telegram, response required
364  command[1] = (byte) 0x0B; // GETBATTERYLEVEL command
365  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
366  if (evaluateStatus(functionName, returnPackage, command[1])) {
367  if (returnPackage.length == 5) {
368  return getUWORDValueFromBytes(returnPackage, 3);
369  } else {
370  Log.w(logTag, "GetBatteryLevel: unexpected return package length " +
371  returnPackage.length + " (expected 5)");
372  }
373  }
374  return 0;
375  }
376 
377  @SimpleFunction(description = "Stop sound playback.")
378  public void StopSoundPlayback() {
379  String functionName = "StopSoundPlayback";
380  if (!checkBluetooth(functionName)) {
381  return;
382  }
383 
384  byte[] command = new byte[2];
385  command[0] = (byte) 0x80; // Direct command telegram, no response
386  command[1] = (byte) 0x0C; // STOPSOUNDPLAYBACK command
387  sendCommand(functionName, command);
388  }
389 
390  @SimpleFunction(description = "Keep Alive. " +
391  "Returns the current sleep time limit in milliseconds.")
392  public long KeepAlive() {
393  String functionName = "KeepAlive";
394  if (!checkBluetooth(functionName)) {
395  return 0;
396  }
397 
398  byte[] command = new byte[2];
399  command[0] = (byte) 0x00; // Direct command telegram, response required
400  command[1] = (byte) 0x0D; // KEEPALIVE command
401  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
402  if (evaluateStatus(functionName, returnPackage, command[1])) {
403  if (returnPackage.length == 7) {
404  return getULONGValueFromBytes(returnPackage, 3);
405  } else {
406  Log.w(logTag, "KeepAlive: unexpected return package length " +
407  returnPackage.length + " (expected 7)");
408  }
409  }
410  return 0;
411  }
412 
413  @SimpleFunction(description = "Returns the count of available bytes to read.")
414  public int LsGetStatus(String sensorPortLetter) {
415  String functionName = "LsGetStatus";
416  if (!checkBluetooth(functionName)) {
417  return 0;
418  }
419 
420  int port;
421  try {
422  port = convertSensorPortLetterToNumber(sensorPortLetter);
423  } catch (IllegalArgumentException e) {
424  form.dispatchErrorOccurredEvent(this, functionName,
425  ErrorMessages.ERROR_NXT_INVALID_SENSOR_PORT, sensorPortLetter);
426  return 0;
427  }
428 
429  return lsGetStatus(functionName, port);
430  }
431 
432  @SimpleFunction(description = "Writes low speed data to an input sensor on the robot. " +
433  "Assumes sensor type has been configured via SetInputMode.")
434  public void LsWrite(String sensorPortLetter, YailList list, int rxDataLength) {
435  String functionName = "LsWrite";
436  if (!checkBluetooth(functionName)) {
437  return;
438  }
439 
440  int port;
441  try {
442  port = convertSensorPortLetterToNumber(sensorPortLetter);
443  } catch (IllegalArgumentException e) {
444  form.dispatchErrorOccurredEvent(this, functionName,
445  ErrorMessages.ERROR_NXT_INVALID_SENSOR_PORT, sensorPortLetter);
446  return;
447  }
448 
449  if (list.size() > 16) {
450  form.dispatchErrorOccurredEvent(this, functionName,
452  return;
453  }
454 
455  Object[] array = list.toArray();
456  byte[] bytes = new byte[array.length];
457  for (int i = 0; i < array.length; i++) {
458  // We use Object.toString here because the element might be a String or it might be some
459  // numeric class.
460  Object element = array[i];
461  String s = element.toString();
462  int n;
463  try {
464  n = Integer.decode(s);
465  } catch (NumberFormatException e) {
466  form.dispatchErrorOccurredEvent(this, functionName,
468  return;
469  }
470  bytes[i] = (byte) (n & 0xFF);
471  n = n >> 8;
472  if (n != 0 && n != -1) {
473  form.dispatchErrorOccurredEvent(this, functionName,
475  return;
476  }
477  }
478  lsWrite(functionName, port, bytes, rxDataLength);
479  }
480 
481 
482  @SimpleFunction(description = "Reads unsigned low speed data from an input sensor on the " +
483  "robot. Assumes sensor type has been configured via SetInputMode.")
484  public List<Integer> LsRead(String sensorPortLetter) {
485  String functionName = "LsRead";
486  if (!checkBluetooth(functionName)) {
487  return new ArrayList<Integer>();
488  }
489 
490  int port;
491  try {
492  port = convertSensorPortLetterToNumber(sensorPortLetter);
493  } catch (IllegalArgumentException e) {
494  form.dispatchErrorOccurredEvent(this, functionName,
495  ErrorMessages.ERROR_NXT_INVALID_SENSOR_PORT, sensorPortLetter);
496  return new ArrayList<Integer>();
497  }
498 
499  byte[] returnPackage = lsRead(functionName, port);
500  if (returnPackage != null) {
501  List<Integer> list = new ArrayList<Integer>();
502  int count = getUBYTEValueFromBytes(returnPackage, 3);
503  for (int i = 0; i < count; i++) {
504  int n = returnPackage[4 + i] & 0xFF; // unsigned
505  list.add(n);
506  }
507  return list;
508  }
509 
510  // invalid response
511  return new ArrayList<Integer>();
512  }
513 
514  @SimpleFunction(description = "Get the name of currently running program on " +
515  "the robot.")
516  public String GetCurrentProgramName() {
517  String functionName = "GetCurrentProgramName";
518  if (!checkBluetooth(functionName)) {
519  return "";
520  }
521 
522  byte[] command = new byte[2];
523  command[0] = (byte) 0x00; // Direct command telegram, response required
524  command[1] = (byte) 0x11; // GETCURRRENTPROGRAMNAME command
525  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
526  int status = getStatus(functionName, returnPackage, command[1]);
527  if (status == 0) {
528  // Success
529  return getStringValueFromBytes(returnPackage, 3);
530  }
531  if (status == 0xEC) {
532  // No active program. We don't treat this as an error.
533  return "";
534  }
535  // Some other error code.
536  evaluateStatus(functionName, returnPackage, command[1]);
537  return "";
538  }
539 
540  @SimpleFunction(description = "Read a message from a mailbox (1-10) on the robot.")
541  public String MessageRead(int mailbox) {
542  String functionName = "MessageRead";
543  if (!checkBluetooth(functionName)) {
544  return "";
545  }
546  // Note from Paul Gyugyi during code review: we are only supporting mailboxes 1-10, but NXT can
547  // use mailboxes above 10 as relays to other NXTs. We've never needed it, but if you ever see
548  // a feature request or bug report, all that might be required is just raising our upper limit
549  // on the range.
550  if (mailbox < 1 || mailbox > 10) {
551  form.dispatchErrorOccurredEvent(this, functionName,
553  return "";
554  }
555 
556  mailbox--; // send 0-based mailbox to NXT
557 
558  byte[] command = new byte[5];
559  command[0] = (byte) 0x00; // Direct command telegram, response required
560  command[1] = (byte) 0x13; // MESSAGEREAD command
561  copyUBYTEValueToBytes(0, command, 2); // no remote mailbox
562  copyUBYTEValueToBytes(mailbox, command, 3);
563  copyBooleanValueToBytes(true, command, 4); // remove message from mailbox
564  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
565  if (evaluateStatus(functionName, returnPackage, command[1])) {
566  if (returnPackage.length == 64) {
567  int mailboxEcho = getUBYTEValueFromBytes(returnPackage, 3);
568  if (mailboxEcho != mailbox) {
569  Log.w(logTag, "MessageRead: unexpected return mailbox: " +
570  mailboxEcho + " (expected " + mailbox + ")");
571  }
572  int messageLength = getUBYTEValueFromBytes(returnPackage, 4) - 1;
573  return getStringValueFromBytes(returnPackage, 5, messageLength);
574  } else {
575  Log.w(logTag, "MessageRead: unexpected return package length " +
576  returnPackage.length + " (expected 64)");
577  }
578  }
579  return "";
580  }
581 
591  @SimpleFunction(description = "Download a file to the robot.")
592  public void DownloadFile(String source, String destination) {
593  String functionName = "DownloadFile";
594  if (!checkBluetooth(functionName)) {
595  return;
596  }
597  if (source.length() == 0) {
598  form.dispatchErrorOccurredEvent(this, functionName,
600  return;
601  }
602  if (destination.length() == 0) {
603  form.dispatchErrorOccurredEvent(this, functionName,
605  return;
606  }
607 
608  try {
609  File tempFile = MediaUtil.copyMediaToTempFile(form, source);
610  try {
611  InputStream in = new BufferedInputStream(FileUtil.openFile(form, tempFile), 1024);
612  try {
613  long fileSize = tempFile.length();
614  Integer handle = (destination.endsWith(".rxe") || destination.endsWith(".ric"))
615  ? openWriteLinear(functionName, destination, fileSize)
616  : openWrite(functionName, destination, fileSize);
617  if (handle == null) {
618  return;
619  }
620  try {
621  // Send data to NXT 32 bytes at a time.
622  byte[] buffer = new byte[32];
623  long sentLength = 0;
624  while (sentLength < fileSize) {
625  int chunkLength = (int) Math.min(32, fileSize - sentLength);
626  in.read(buffer, 0, chunkLength);
627  int writtenLength = writeChunk(functionName, handle, buffer, chunkLength);
628  sentLength += writtenLength;
629  }
630  } finally {
631  closeHandle(functionName, handle);
632  }
633  } finally {
634  in.close();
635  }
636  } finally {
637  tempFile.delete();
638  }
639  } catch (IOException e) {
640  form.dispatchErrorOccurredEvent(this, functionName,
642  return;
643  }
644  }
645 
646  private Integer openWrite(String functionName, String fileName, long fileSize) {
647  byte[] command = new byte[26];
648  command[0] = (byte) 0x01; // System command telegram, response required
649  command[1] = (byte) 0x81; // OPEN WRITE command
650  copyStringValueToBytes(fileName, command, 2, 19);
651  copyULONGValueToBytes(fileSize, command, 22);
652  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
653  if (evaluateStatus(functionName, returnPackage, command[1])) {
654  if (returnPackage.length == 4) {
655  return getUBYTEValueFromBytes(returnPackage, 3);
656  } else {
657  Log.w(logTag, functionName + ": unexpected return package length " +
658  returnPackage.length + " (expected 4)");
659  }
660  }
661  return null;
662  }
663 
664  private int writeChunk(String functionName, int handle, byte[] buffer, int length)
665  throws IOException {
666  if (length > 32) {
667  throw new IllegalArgumentException("length must be <= 32");
668  }
669 
670  byte[] command = new byte[3 + length];
671  command[0] = (byte) 0x01; // System command telegram, response required
672  command[1] = (byte) 0x83; // WRITE command
673  copyUBYTEValueToBytes(handle, command, 2);
674  System.arraycopy(buffer, 0, command, 3, length);
675  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
676  if (evaluateStatus(functionName, returnPackage, command[1])) {
677  if (returnPackage.length == 6) {
678  int writtenLength = getUWORDValueFromBytes(returnPackage, 4);
679  if (writtenLength != length) {
680  Log.e(logTag, functionName + ": only " + writtenLength + " bytes were written " +
681  "(expected " + length + ")");
682  throw new IOException("Unable to write file on robot");
683  }
684  return writtenLength;
685  } else {
686  Log.w(logTag, functionName + ": unexpected return package length " +
687  returnPackage.length + " (expected 6)");
688  }
689  }
690  return 0;
691  }
692 
693  private void closeHandle(String functionName, int handle) {
694  byte[] command = new byte[3];
695  command[0] = (byte) 0x01; // System command telegram, response required
696  command[1] = (byte) 0x84; // CLOSE command
697  copyUBYTEValueToBytes(handle, command, 2);
698  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
699  evaluateStatus(functionName, returnPackage, command[1]);
700  }
701 
702  @SimpleFunction(description = "Delete a file on the robot.")
703  public void DeleteFile(String fileName) {
704  String functionName = "DeleteFile";
705  if (!checkBluetooth(functionName)) {
706  return;
707  }
708  if (fileName.length() == 0) {
709  form.dispatchErrorOccurredEvent(this, functionName,
711  return;
712  }
713 
714  byte[] command = new byte[22];
715  command[0] = (byte) 0x01; // System command telegram, response required
716  command[1] = (byte) 0x85; // DELETE command
717  copyStringValueToBytes(fileName, command, 2, 19);
718  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
719  evaluateStatus(functionName, returnPackage, command[1]);
720  }
721 
722  @SimpleFunction(description = "Returns a list containing the names of matching files found on " +
723  "the robot.")
724  public List<String> ListFiles(String wildcard) {
725  String functionName = "ListFiles";
726  if (!checkBluetooth(functionName)) {
727  return new ArrayList<String>();
728  }
729 
730  List<String> fileNames = new ArrayList<String>();
731 
732  if (wildcard.length() == 0) {
733  wildcard = "*.*";
734  }
735 
736  byte[] command = new byte[22];
737  command[0] = (byte) 0x01; // System command telegram, response required
738  command[1] = (byte) 0x86; // FIND FIRST command
739  copyStringValueToBytes(wildcard, command, 2, 19);
740  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
741  int status = getStatus(functionName, returnPackage, command[1]);
742  while (status == 0) {
743  int handle = getUBYTEValueFromBytes(returnPackage, 3);
744  String fileName = getStringValueFromBytes(returnPackage, 4);
745  fileNames.add(fileName);
746  command = new byte[3];
747  command[0] = (byte) 0x01; // System command telegram, response required
748  command[1] = (byte) 0x87; // FIND NEXT command
749  copyUBYTEValueToBytes(handle, command, 2);
750  returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
751  status = getStatus(functionName, returnPackage, command[1]);
752  }
753  return fileNames;
754  }
755 
756  @SimpleFunction(description = "Get the firmware and protocol version numbers for the robot as" +
757  " a list where the first element is the firmware version number and the second element is" +
758  " the protocol version number.")
759  public List<String> GetFirmwareVersion() {
760  String functionName = "GetFirmwareVersion";
761  if (!checkBluetooth(functionName)) {
762  return new ArrayList<String>();
763  }
764 
765  byte[] command = new byte[2];
766  command[0] = (byte) 0x01; // System command telegram, response required
767  command[1] = (byte) 0x88; // GET FIRMWARE VERSION command
768  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
769  if (evaluateStatus(functionName, returnPackage, command[1])) {
770  List<String> versions = new ArrayList<String>();
771  versions.add(returnPackage[6] + "." + returnPackage[5]); // firmware
772  versions.add(returnPackage[4] + "." + returnPackage[3]); // protocol
773  return versions;
774  }
775  return new ArrayList<String>();
776  }
777 
778  private Integer openWriteLinear(String functionName, String fileName, long fileSize) {
779  byte[] command = new byte[26];
780  command[0] = (byte) 0x01; // System command telegram, response required
781  command[1] = (byte) 0x89; // OPEN WRITE LINEAR command
782  copyStringValueToBytes(fileName, command, 2, 19);
783  copyULONGValueToBytes(fileSize, command, 22);
784  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
785  if (evaluateStatus(functionName, returnPackage, command[1])) {
786  if (returnPackage.length == 4) {
787  return getUBYTEValueFromBytes(returnPackage, 3);
788  } else {
789  Log.w(logTag, functionName + ": unexpected return package length " +
790  returnPackage.length + " (expected 4)");
791  }
792  }
793  return null;
794  }
795 
796  // SetBrickName will change the name of the NXT.
797  // The new name will appear on the NXT LCD immediately,
798  // but the AddressesAndNames property does not update until
799  // the app is restarted.
800  @SimpleFunction(description = "Set the brick name of the robot.")
801  public void SetBrickName(String name) {
802  String functionName = "SetBrickName";
803  if (!checkBluetooth(functionName)) {
804  return;
805  }
806 
807  byte[] command = new byte[18];
808  command[0] = (byte) 0x01; // System command telegram, response required
809  command[1] = (byte) 0x98; // SET BRICK NAME command
810  copyStringValueToBytes(name, command, 2, 15);
811  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
812  evaluateStatus(functionName, returnPackage, command[1]);
813  }
814 
815  @SimpleFunction(description = "Get the brick name of the robot.")
816  public String GetBrickName() {
817  String functionName = "GetBrickName";
818  if (!checkBluetooth(functionName)) {
819  return "";
820  }
821 
822  byte[] command = new byte[2];
823  command[0] = (byte) 0x01; // System command telegram, response required
824  command[1] = (byte) 0x9B; // GET DEVICE INFO command
825  byte[] returnPackage = sendCommandAndReceiveReturnPackage(functionName, command);
826  if (evaluateStatus(functionName, returnPackage, command[1])) {
827  return getStringValueFromBytes(returnPackage, 3);
828  }
829  return "";
830  }
831 }
com.google.appinventor.components.runtime.util.YailList
Definition: YailList.java:26
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_DATA_TOO_LARGE
static final int ERROR_NXT_DATA_TOO_LARGE
Definition: ErrorMessages.java:60
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_MESSAGE_TOO_LONG
static final int ERROR_NXT_MESSAGE_TOO_LONG
Definition: ErrorMessages.java:59
com.google.appinventor.components.runtime.NxtDirectCommands
Definition: NxtDirectCommands.java:47
com.google.appinventor.components.runtime.util.ErrorMessages
Definition: ErrorMessages.java:17
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.runtime.util.FileUtil
Definition: FileUtil.java:37
com.google.appinventor.components.runtime.NxtDirectCommands.NxtDirectCommands
NxtDirectCommands(ComponentContainer container)
Definition: NxtDirectCommands.java:52
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_INVALID_SENSOR_PORT
static final int ERROR_NXT_INVALID_SENSOR_PORT
Definition: ErrorMessages.java:57
com.google.appinventor.components
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_INVALID_MOTOR_PORT
static final int ERROR_NXT_INVALID_MOTOR_PORT
Definition: ErrorMessages.java:56
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_INVALID_SOURCE_ARGUMENT
static final int ERROR_NXT_INVALID_SOURCE_ARGUMENT
Definition: ErrorMessages.java:63
com.google.appinventor.components.runtime.util.MediaUtil
Definition: MediaUtil.java:53
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.runtime.File
Definition: File.java:53
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_INVALID_PROGRAM_NAME
static final int ERROR_NXT_INVALID_PROGRAM_NAME
Definition: ErrorMessages.java:54
com.google.appinventor.components.annotations.UsesPermissions
Definition: UsesPermissions.java:21
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_INVALID_MAILBOX
static final int ERROR_NXT_INVALID_MAILBOX
Definition: ErrorMessages.java:58
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_COULD_NOT_DECODE_ELEMENT
static final int ERROR_NXT_COULD_NOT_DECODE_ELEMENT
Definition: ErrorMessages.java:61
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_UNABLE_TO_DOWNLOAD_FILE
static final int ERROR_NXT_UNABLE_TO_DOWNLOAD_FILE
Definition: ErrorMessages.java:65
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.LegoMindstormsNxtBase
Definition: LegoMindstormsNxtBase.java:29
com.google.appinventor.components.runtime.util.MediaUtil.copyMediaToTempFile
static File copyMediaToTempFile(Form form, String mediaPath)
Definition: MediaUtil.java:352
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google
com
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_INVALID_FILE_NAME
static final int ERROR_NXT_INVALID_FILE_NAME
Definition: ErrorMessages.java:55
com.google.appinventor.components.runtime.util.FileUtil.openFile
static FileInputStream openFile(String fileName)
Definition: FileUtil.java:144
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_INVALID_DESTINATION_ARGUMENT
static final int ERROR_NXT_INVALID_DESTINATION_ARGUMENT
Definition: ErrorMessages.java:64
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_NXT_COULD_NOT_FIT_ELEMENT_IN_BYTE
static final int ERROR_NXT_COULD_NOT_FIT_ELEMENT_IN_BYTE
Definition: ErrorMessages.java:62
com.google.appinventor.components.annotations
com.google.appinventor