6 package com.google.appinventor.components.runtime;
21 import android.os.Handler;
32 @DesignerComponent(version = YaVersion.EV3_MOTORS_COMPONENT_VERSION,
33 description =
"A component that provides both high- and low-level interfaces to a LEGO MINDSTORMS EV3 " +
34 "robot, with functions that can control the motors.",
35 category = ComponentCategory.LEGOMINDSTORMS,
37 iconName =
"images/legoMindstormsEv3.png")
40 private static final int DELAY_MILLISECONDS = 50;
41 private static final String DEFAULT_MOTOR_PORTS =
"ABC";
42 private static final double DEFAULT_WHEEL_DIAMETER = 4.32;
44 private int motorPortBitField = 1;
45 private double wheelDiameter = DEFAULT_WHEEL_DIAMETER;
46 private boolean directionReversed =
false;
47 private boolean regulationEnabled =
true;
48 private boolean stopBeforeDisconnect =
true;
49 private boolean tachoCountChangedEventEnabled =
false;
50 private final Runnable sensorValueChecker;
51 private Handler eventHandler;
52 private int previousValue = 0;
53 private boolean ifReset =
false;
59 super(container,
"Ev3Motors");
60 eventHandler =
new Handler();
61 sensorValueChecker =
new Runnable() {
63 String functionName =
"";
66 int sensorValue = getOutputCount(functionName, 0, motorPortBitField);
69 if (sensorValue != previousValue && tachoCountChangedEventEnabled) {
75 previousValue = sensorValue;
78 eventHandler.postDelayed(
this, DELAY_MILLISECONDS);
82 eventHandler.post(sensorValueChecker);
95 @
SimpleProperty(description =
"The motor ports that the motors are connected to. The ports are specified by a sequence of port letters.",
106 defaultValue = DEFAULT_MOTOR_PORTS)
109 String functionName =
"MotorPorts";
112 }
catch (IllegalArgumentException e) {
121 defaultValue =
"" + DEFAULT_WHEEL_DIAMETER)
124 wheelDiameter = diameter;
130 @
SimpleProperty(description =
"The diameter of the wheels attached on the motors in centimeters.",
134 return wheelDiameter;
141 defaultValue =
"False")
144 String functionName =
"ReverseDirection";
146 setOutputDirection(functionName, 0, motorPortBitField, reversed ? -1 : 1);
147 directionReversed = reversed;
148 }
catch (IllegalArgumentException e) {
156 @
SimpleProperty(description =
"It specifies if the direction of the motors is reversed.",
159 return directionReversed;
166 defaultValue =
"True")
169 regulationEnabled = enabled;
175 @
SimpleProperty(description =
"The robot adjusts the power to maintain the speed if speed regulation is enabled.",
178 return regulationEnabled;
184 @
SimpleProperty(description =
"Whether to stop the motor before disconnecting.",
187 return stopBeforeDisconnect;
196 defaultValue =
"True")
199 this.stopBeforeDisconnect = stopBeforeDisconnect;
205 @
SimpleProperty(description =
"Whether the TachoCountChanged event should fire when the angle is changed.",
208 return tachoCountChangedEventEnabled;
215 defaultValue =
"False")
218 tachoCountChangedEventEnabled = enabled;
226 String functionName =
"RotateIndefinitely";
228 if (regulationEnabled)
229 setOutputPower(functionName, 0, motorPortBitField, power);
231 setOutputSpeed(functionName, 0, motorPortBitField, power);
233 startOutput(functionName, 0, motorPortBitField);
234 }
catch (IllegalArgumentException e) {
242 @
SimpleFunction(description =
"Rotate the motors in a number of tacho counts.")
244 String functionName =
"RotateInTachoCounts";
246 if (regulationEnabled)
247 outputStepSpeed(functionName, 0, motorPortBitField, power, 0, tachoCounts, 0, useBrake);
249 outputStepPower(functionName, 0, motorPortBitField, power, 0, tachoCounts, 0, useBrake);
250 }
catch (IllegalArgumentException e) {
258 @
SimpleFunction(description =
"Rotate the motors in a period of time.")
260 String functionName =
"RotateInDuration";
262 if (regulationEnabled)
263 outputTimeSpeed(functionName, 0, motorPortBitField, power, 0, milliseconds, 0, useBrake);
265 outputTimePower(functionName, 0, motorPortBitField, power, 0, milliseconds, 0, useBrake);
266 }
catch (IllegalArgumentException e) {
276 String functionName =
"RotateInDistance";
277 int tachoCounts = (int) (distance * 360.0 / wheelDiameter / Math.PI);
280 if (regulationEnabled)
281 outputStepSpeed(functionName, 0, motorPortBitField, power, 0, tachoCounts, 0, useBrake);
283 outputStepPower(functionName, 0, motorPortBitField, power, 0, tachoCounts, 0, useBrake);
284 }
catch (IllegalArgumentException e) {
292 @
SimpleFunction(description =
"Start to rotate the motors at the same speed.")
294 String functionName =
"RotateSyncIndefinitely";
298 if (motorPortBitField != 0) {
299 if (isOneShotInteger(motorPortBitField))
300 setOutputSpeed(functionName, 0, motorPortBitField, power);
302 outputStepSync(functionName, 0, motorPortBitField, power, turnRatio, 0,
true);
305 }
catch (IllegalArgumentException e) {
313 @
SimpleFunction(description =
"Rotate the motors at the same speed for a distance in cm.")
315 String functionName =
"RotateSyncInDuration";
316 int tachoCounts = (int) (distance * 360.0 / wheelDiameter / Math.PI);
320 if (motorPortBitField != 0) {
321 if (isOneShotInteger(motorPortBitField))
322 outputStepSpeed(functionName, 0, motorPortBitField, power, 0, tachoCounts, 0, useBrake);
324 outputStepSync(functionName, 0, motorPortBitField, power, turnRatio, tachoCounts, useBrake);
327 }
catch (IllegalArgumentException e) {
335 @
SimpleFunction(description =
"Rotate the motors at the same speed in a period of time.")
337 String functionName =
"RotateSyncInDuration";
341 if (motorPortBitField != 0) {
342 if (isOneShotInteger(motorPortBitField))
343 outputTimeSpeed(functionName, 0, motorPortBitField, power, 0, milliseconds, 0, useBrake);
345 outputTimeSync(functionName, 0, motorPortBitField, power, turnRatio, milliseconds, useBrake);
348 }
catch (IllegalArgumentException e) {
356 @
SimpleFunction(description =
"Rotate the motors at the same speed in a number of tacho counts.")
358 String functionName =
"RotateSyncInTachoCounts";
362 if (motorPortBitField != 0) {
363 if (isOneShotInteger(motorPortBitField))
364 outputStepSpeed(functionName, 0, motorPortBitField, power, 0, tachoCounts, 0, useBrake);
366 outputStepSync(functionName, 0, motorPortBitField, power, turnRatio, tachoCounts, useBrake);
369 }
catch (IllegalArgumentException e) {
378 public
void Stop(
boolean useBrake) {
379 String functionName =
"Stop";
381 stopOutput(functionName, 0, motorPortBitField, useBrake);
382 }
catch (IllegalArgumentException e) {
392 String functionName =
"ToggleDirection";
394 setOutputDirection(functionName, 0, motorPortBitField, 0);
395 directionReversed = !directionReversed;
396 }
catch (IllegalArgumentException e) {
404 @
SimpleFunction(description =
"Set the current tacho count to zero.")
406 String functionName =
"ResetTachoCount";
408 clearOutputCount(functionName, 0, motorPortBitField);
409 }
catch (IllegalArgumentException e) {
419 String functionName =
"GetTachoCount";
421 return getOutputCount(functionName, 0, motorPortBitField);
422 }
catch (IllegalArgumentException e) {
431 @
SimpleEvent(description =
"Called when the tacho count has changed.")
436 private int roundValue(
int value,
int minimum,
int maximum) {
437 return value < minimum ? minimum : (value > maximum ? maximum : value);
440 private boolean isOneShotInteger(
int value) {
441 return (value != 0) && ((value & ~(value ^ (value - 1))) == 0);
444 private void resetOutput(String functionName,
int layer,
int nos) {
445 if (layer < 0 || layer > 3 || nos < 0 || nos > 15)
446 throw new IllegalArgumentException();
448 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_RESET,
458 private void startOutput(String functionName,
int layer,
int nos) {
459 if (layer < 0 || layer > 3 || nos < 0 || nos > 15)
460 throw new IllegalArgumentException();
462 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_START,
472 private void stopOutput(String functionName,
int layer,
int nos,
boolean useBrake) {
473 if (layer < 0 || layer > 3 || nos < 0 || nos > 15)
474 throw new IllegalArgumentException();
476 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_STOP,
483 useBrake ? (
byte) 1 : (
byte) 0);
487 private void outputStepPower(String functionName,
int layer,
int nos,
int power,
int step1,
int step2,
int step3,
boolean brake) {
488 if (layer < 0 || layer > 3 || nos < 0 || nos > 15 || step1 < 0 || step2 < 0 || step3 < 0)
489 throw new IllegalArgumentException();
491 power = roundValue(power, -100, 100);
493 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_STEP_POWER,
504 (
byte) (brake ? 1 : 0));
508 private void outputStepSpeed(String functionName,
int layer,
int nos,
int speed,
int step1,
int step2,
int step3,
boolean brake) {
509 if (layer < 0 || layer > 3 || nos < 0 || nos > 15 || step1 < 0 || step2 < 0 || step3 < 0)
510 throw new IllegalArgumentException();
512 speed = roundValue(speed, -100, 100);
514 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_STEP_SPEED,
525 (
byte) (brake ? 1 : 0));
529 private void outputStepSync(String functionName,
int layer,
int nos,
int speed,
int turnRatio,
int tachoCounts,
boolean brake) {
530 if (layer < 0 || layer > 3 || nos < 0 || nos > 15 || turnRatio < -200 || turnRatio > 200)
531 throw new IllegalArgumentException();
533 speed = roundValue(speed, -100, 100);
535 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_STEP_SYNC,
545 (
byte) (brake ? 1 : 0));
549 private void outputTimePower(String functionName,
int layer,
int nos,
int power,
int step1,
int step2,
int step3,
boolean brake) {
550 if (layer < 0 || layer > 3 || nos < 0 || nos > 15 || step1 < 0 || step2 < 0 || step3 < 0)
551 throw new IllegalArgumentException();
553 power = roundValue(power, -100, 100);
555 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_TIME_POWER,
566 (
byte) (brake ? 1 : 0));
570 private void outputTimeSpeed(String functionName,
int layer,
int nos,
int speed,
int step1,
int step2,
int step3,
boolean brake) {
571 if (layer < 0 || layer > 3 || nos < 0 || nos > 15 || step1 < 0 || step2 < 0 || step3 < 0)
572 throw new IllegalArgumentException();
574 speed = roundValue(speed, -100, 100);
576 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_TIME_SPEED,
587 (
byte) (brake ? 1 : 0));
591 private void outputTimeSync(String functionName,
int layer,
int nos,
int speed,
int turnRatio,
int milliseconds,
boolean brake) {
592 if (layer < 0 || layer > 3 || nos < 0 || nos > 15 || milliseconds < 0)
593 throw new IllegalArgumentException();
595 speed = roundValue(speed, -100, 100);
597 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_TIME_SYNC,
607 (
byte) (brake ? 1 : 0));
611 private void setOutputDirection(String functionName,
int layer,
int nos,
int direction) {
612 if (layer < 0 || layer > 3 || nos < 0 || nos > 15 || direction < -1 || direction > 1)
613 throw new IllegalArgumentException();
615 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_POLARITY,
626 private void setOutputSpeed(String functionName,
int layer,
int nos,
int speed) {
627 if (layer < 0 || layer > 3 || nos < 0 || nos > 15)
628 throw new IllegalArgumentException();
630 speed = roundValue(speed, -100, 100);
632 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_SPEED,
643 private void setOutputPower(String functionName,
int layer,
int nos,
int power) {
644 if (layer < 0 || layer > 3 || nos < 0 || nos > 15)
645 throw new IllegalArgumentException();
647 power = roundValue(power, -100, 100);
649 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_POWER,
660 private int getOutputCount(String functionName,
int layer,
int nos) {
661 if (layer < 0 || layer > 3 || nos < 0 || nos > 15)
662 throw new IllegalArgumentException();
665 nos = ((nos ^ (nos - 1)) + 1 >>> 1);
686 throw new IllegalArgumentException();
689 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_GET_COUNT,
697 byte[] reply =
sendCommand(functionName, command,
true);
699 if (reply !=
null && reply.length == 5 && reply[0] == Ev3Constants.DirectReplyType.DIRECT_REPLY) {
700 Object[] values = Ev3BinaryParser.unpack(
"xi", reply);
701 return (Integer) values[0];
707 private void clearOutputCount(String functionName,
int layer,
int nos) {
708 if (layer < 0 || layer > 3 || nos < 0 || nos > 15)
709 throw new IllegalArgumentException();
711 byte[] command = Ev3BinaryParser.encodeDirectCommand(Ev3Constants.Opcode.OUTPUT_CLR_COUNT,
723 String functionName =
"beforeDisconnect";
724 if (stopBeforeDisconnect) {
726 stopOutput(functionName, 0, motorPortBitField,
true);
727 }
catch (IllegalArgumentException e) {