LearnJavaForFTC PDF
LearnJavaForFTC PDF
LearnJavaForFTC PDF
Alan G. Smith
FIRST Tech Challenge, and FTC are registered trademarks of For Inspi-
ration and Recognition of Science and Technology (FIRST) which does not
sponsor, authorize, or endorse this book.
REV, REV Control Hub, REV Expansion Hub, and Rev Robotics are trademarks
of Rev Robotics which does not sponsor, authorize, or endorse this book.
ISBN: 9798644009886
This book is dedicated to:
1. Introduction 1
1.1. Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1. Robot Controller . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.2. Programming Board . . . . . . . . . . . . . . . . . . . . . 1
1.1.3. Driver Station . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2. Our first OpMode . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.1. Some terminology . . . . . . . . . . . . . . . . . . . . . . 2
1.2.2. What is an OpMode? . . . . . . . . . . . . . . . . . . . . . 3
1.2.3. Parts of an OpMode . . . . . . . . . . . . . . . . . . . . . 3
1.2.4. Hello, World . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3. Now you try . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4. Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.5. Sending to the Robot Controller . . . . . . . . . . . . . . . . . . 10
1.6. Gotchas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.7. Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4. Making decisions 21
4.1. If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2. Else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.2.1. Else if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.3. Combinations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.4. While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
v
Contents
4.5. For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.6. Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7. Motors 49
7.1. Editing Configuration File . . . . . . . . . . . . . . . . . . . . . . 49
7.2. Mechanisms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
7.3. OpMode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
7.4. Motor as Sensor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
7.5. Motors and Sensors together . . . . . . . . . . . . . . . . . . . . 56
7.6. Motors and Gamepads . . . . . . . . . . . . . . . . . . . . . . . . 57
7.7. Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
8. Servos 61
8.1. Configuration File . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8.2. Mechanisms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8.3. OpMode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
8.4. Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
9. Analog Sensors 65
9.1. Configuration File . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
9.2. Mechanisms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
9.3. OpMode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
vi
Contents
9.4. Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
13. Arrays 95
13.1. ArrayList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
13.1.1. Making your own generic class . . . . . . . . . . . . . . . 97
13.2. Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
14. Inheritance 99
14.1. Isa vs. hasa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
14.2. So why in the world would you use this? . . . . . . . . . . . . . 101
14.3. Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
vii
Contents
B. LinearOpMode 125
B.1. What is it? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
B.2. Should you use it? . . . . . . . . . . . . . . . . . . . . . . . . . . 126
B.2.1. Benefits of LinearOpMode . . . . . . . . . . . . . . . . . . 126
B.2.2. Drawbacks of LinearOpMode . . . . . . . . . . . . . . . . 127
D. Credits 173
viii
1. Introduction
1.1. Hardware
The Robot Controller, often abbreviated “RC”, is the “brains” of your robot. It
is what our programs run on. The RC can be either an Android phone or a
REV Control Hub. When using an Android phone, it is connected to a REV
Expansion Hub over USB. The REV Expansion Hub is what all motors, servos,
and sensors connect to. A Rev Control Hub is new for the 2020-2021 FTC
Season and is basically an Android phone and Expansion Hub in the same
package instead of having them separate.
For this book, instead of a full robot we have made a simple Programming Board
(just the electrical components that we are using) that we can use throughout
the book so that we all have the same hardware. For directions on how to make
your own, see Appendix A.
1
Go Quantum Quacks - FTC #16072
1
1. Introduction
class In Java all code is grouped together in classes. We’ll discuss exactly what
classes are later in chapter 5. For now, just know that a class groups like
code together and in Java, each class is in its own file that is named the
same as the class with .java at the end.
method A method is a group of code within a class. Methods are the smallest
group of code that can be executed. It is like a function in some languages
or a MyBlock in EV3-G. We’ll talk more about this later in section 5.2.
package A directory in JAVA. It is where the code is located. Files in the same
package have special privileges with each other. We’ll talk about this in
section 5.3. And yes, a package can have packages within it.
2
1.2. Our first OpMode
In FTC, An OpMode2 is a program for our robot. We can have multiple Op-
Modes. They are all stored in the TeamCode package.
3. stop()- This is run once when the driver presses STOP pressed
STOP. stop()
3
1. Introduction
see something that doesn’t make sense, keep reading and hopefully it will be
cleared up.)
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class HelloWorld extends OpMode {
8 @Override
9 public void init() {
10 telemetry.addData("Hello","World");
11 }
12
13 @Override
14 public void loop() {
15
16 }
17 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
If you are working in Android Studio, you won’t have to enter any of these
lines as it will add them for you. Line 1 basically says where this file is located.
3 and 4 bring in code from the FTC SDK (Software Development Kit) so we can
use them.
6 @TeleOp()
This is CRITICAL. If you forget this line, it won’t show up on the Driver-
Station as an OpMode to select from. Any line that starts with an @ is called
an Annotation. You can choose from @Teleop() or @Autonomous(). You can op-
tionally give it a name and a group, but if you leave those off then it will use
your class name as the name. This works well enough, so we’ll typically leave
those pieces out. Another annotation that you’ll see commonly is @Disabled If
you have that, then your code will compile but it won’t be shown in the list of
4
1.2. Our first OpMode
OpModes.3
7 public class HelloWorld extends OpMode {
17 }
- means others can see it. Required for OpModes. We’ll discuss this
public
more in section 5.3.
class - means we are defining a class
HelloWorld - this is the name of the class. It must be the same as the filename
(except the filename has .java on it). By convention, it should be started with
a capital letter and each new word is a capital letter (Pascal case). We’ll talk
more about classes in chapter 5.
extends OpMode - This means the class is a child of OpMode . A child gets all
of the behavior of its parent and then can add (or replace) functionality. We’ll
talk about what this means in chapter 14.
a class is defined from the opening curly brace “{“ to the closing curly brace
“}”
8 @Override
9 public void init() {
10 telemetry.addData("Hello","World");
11 }
@Override tells the compiler that we are meaning to override (replace) func-
tionality in our parent class. We’ll talk more about this in chapter 14.
public means this method is callable from outside the class. We’ll discuss
this more in section 5.3
void means it doesn’t return anything. We’ll talk about return types in
subsection 5.2.1
init is the name of a method. We’ll talk more about methods in section 5.2
Inside of the parenthesis are any parameters passed in or none. (as in this
case) We’ll talk about parameters in subsection 5.2.2
The method is defined from the opening curly brace “{“ to the closing curly
brace “}”
telemetry.addData(caption, value); This is very cool because it sends data
to the driver station which lets us debug problems. In this case we sent back
a string (a group of characters - we’ll talk about strings in section 2.2), but
you can also send back numbers or variables. You’ll notice that this ends in a
3
Our team often does that for test code that we don’t want to distract us during a tournament
but is VERY helpful to have where we can make it available quickly.
5
1. Introduction
semi-colon “;” All statements in JAVA either end with a semi-colon or have a
set of curly braces attached.
13 @Override
14 public void loop() {
15
16 }
This looks much the same as our init() method, but there is no code in the
loop() method, so the program won’t do anything here. (We included it because
it is required.)
Before you do this, you need to have your phones ready to go and Android
Studio installed with a copy of the FTC SDK. For instructions, see the FTC
document Android Studio Guide 4 .
You’ll learn the best here if you type in the examples (and you’ll get faster
at Android Studio). While this may seem like it slows you down, it helps you
learn faster. This is the only time in the book I’ll mention “Now you try”. For
the rest, I suggest you type it in AFTER we have explained what it does and
then try it. To start with, change the project area to show “Android” (by using
the dropdown). If you are wondering why your Android Studio is white colored
while mine is Dark, that is because I use the built-in theme “Darcula”.5
4
https://www.firstinspires.org/sites/default/files/uploads/resource_library/ftc/android-
studio-guide.pdf
5
To change your theme click File > Settings from the menu bar (or Android Studio > Preferences
on macOS). Go to Appearance under Appearance and Behavior, and you’ll see Theme.
6
1.3. Now you try
3. (If you are using Android Studio 4.x, it will look like this....)
a) Fill in the name as HelloWorld
b) Press “OK”
c) If you get another dialog box with a bunch of blanks, leave them
blank and press “OK”
d) You’ll get a listing that will look like this
package org.firstinspires.ftc.teamcode;
4. (If you are using Android Studio 3.x, it will look like this...)
a) Fill in the name as HelloWorld
b) Fill in the Superclass as OpMode. (We’ll explain what this means in
chapter 14) As you type it in, it will show you the matches. When you
select it, it will fill in as com.qualcomm.robotcore.eventloop.opmode
7
1. Introduction
d) It will have a red squiggle line under the class declaration. That is
because you haven’t implemented the two required methods yet. You
haven’t done anything wrong.
Make yours look like the HelloWorld.java file in Listing 1.1 earlier. (You can
start at line 6 and you’ll watch it make the import statements as you type)
As you start typing, you’ll notice that Android Studio is giving suggestions.
You can either click on the one you want, or when it is at the top of the list
then press tab.
This is the same pattern you’ll follow for all OpModes in this book.
1.4. Comments
So far our programs have been only for the computer. But it turns out that
you can put things in them that are only for the human readers. You can (and
should) add comments to the program which the computer ignores and are for
human readers only. Comments should explain things that are not obvious
from the code such as why something is being done. In general, comments
should explain why and not what. Please don’t just put in a comment that
repeats the code.
Java supports two forms of comments:
1. A single line comment. It starts with a // and tells the computer to ignore
the rest of the line.
// This is a comment
8
1.4. Comments
/* So is this */
/*
* And
* this
* as
* well */
In addition, there is a subset of this type of comment called a javadoc that we’ll
talk about in chapter 15. This starts on a line with a /** and then goes until
it sees */. This is used for automatically creating documentation from your
comments.
/**
* This is a javadoc comment
*/
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class HelloWorldCommented extends OpMode {
8 /**
9 * This is called when the driver presses INIT
10 */
11 @Override
12 public void init() {
13 // this sends to the driver station
14 telemetry.addData("Hello","World");
15 }
16
17 /**
18 * This is called repeatedly while OpMode is playing
19 */
20 @Override
21 public void loop() {
22 // intentionally left blank
9
1. Introduction
23 }
24 }
3. Press the green play arrow next to the name of the device on the top
toolbar.
4. Wait until you hear the sound from the Robot Controller and the Driver
Station.
5. Now press the right arrow on the driver station to see the list of TeleOp
OpModes. (The arrow on the left shows the list of Autonomous OpModes)
7. You should see “Hello: World” in the area where the Telemetry data is
reported.
1.6. Gotchas
If your program won’t compile (or it doesn’t do what you expect), here are a few
things to check that often confuse people:
• Blocks of code are encapsulated with curly braces ’{’ and ’}’
• Every open parenthesis ’(’ must have a matching close parenthesis ’)’
10
1.7. Exercises
– Semicolons are not used when a code block follows - for example the
class or method declarations we have seen so far
– Semicolons (like everything) are ignored in comments
– Semicolons are not used after the end curly brace. ’}’
1.7. Exercises
After you have done the exercise, send it to the robot controller to make sure it
works.
There are sample solutions in Appendix C. However, you should struggle
with them first and only look there when you are stuck. If you end up looking
there, you should make up another exercise for yourself.
1. Change the code so that instead of saying “Hello: World” it says Hello and
then your name.
11
2. Variables and Data Types
The above variable types are int, double, and boolean (These are the three
you’ll use most often in FTC). We’ll discuss these and the other primitive
datatypes in the next section.
In Java, if you don’t assign a value to a variable when you create it then it
starts out being equal to 0. (or false for boolean)
To assign a value to a variable, you use the = operator like this:
teamNumber = 16072;
motorSpeed = 0.5;
touchSensorPressed = true;
You can assign a value to a variable multiple times and it will be equal to
what you assigned it to most recently.
It’s common to declare a variable and assign the value in one line!
For example, to assign 0.5 to a variable named motorSpeed of type double,
we write:
double motorSpeed = 0.5;
1
This is called camelCase because the upper case letters look like humps.
13
2. Variables and Data Types
5. long - this is a larger integer. You can use it when you are concerned
about running out of room in an int.3
7. double - this is for floating point numbers. It can hold numbers with
decimals.4
8. boolean - this can be either true or false. (Yes, it contains one or the other
of these values.)
In the code below, there are examples of the three most typical primitive types
for FTC.
2
It is also limited in the range from +2,147,483,647 to -2,147,483,648
3
It is limited in the range from +9,223,372,036,854,775,807 to -9,223,372,036,854,775,808
4
while technically it is limited, it is so large you can think of it as unlimited
14
2.2. String
19 @Override
20 public void loop() {
21
22 }
23 }
In the three lines below you’ll see them defined. Notice how they all follow
the same pattern:
10 int teamNumber = 16072;
11 double motorSpeed = 0.5;
12 boolean touchSensorPressed = true;
They are sent to the driver station using telemetry.addData. Again, you’ll
notice that they all follow the same pattern.
14 telemetry.addData("Team Number", teamNumber);
15 telemetry.addData("Motor Speed", motorSpeed);
16 telemetry.addData("Touch Sensor", touchSensorPressed);
2.2. String
A String is for holding text. You might be wondering why it is capitalized when
all of the other data types we have seen so far aren’t. This is because String
is really a class. By convention, class names start with a Capital letter and
then every other word is also capitalized. 5 We’ll talk more about classes in
chapter 5.
In the code below, there is an example of using a String data type.
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
5
This is called PascalCase because it was popularized by one of the lead designers of Turbo
Pascal.
15
2. Variables and Data Types
You’ll notice that the pattern here is similar with datatype variableName; or
datatype variableName = initialValue;
2.3. Scope
This may seem unimportant, but you’ll see why it matters later. A variable is
only usable within its scope. Its scope is from where it is declared until the end
of the block it is defined within. A block is defined as any set of open and close
curly braces. { }
A simple example:
public void loop(){
int x = 5;
// x is visible here
{
int y = 4;
// x and y are visible here
}
// only x is visible here
}
2.4. Exercises
1. Change the String to have your name instead of mine in the code in
section 2.2
2. Add a variable of type int that is called grade that has your grade in it.
Use telemetry to send that to the driver station.
16
3. Gamepad and basic math
We can access the gamepads connected to the driver station from our OpMode.
They are of the Gamepad class. We’ll talk more about classes in chapter 5.
Since there are two of them, they are called gamepad1 and gamepad2.1 The but-
tons on the gamepad are all boolean (true if they are pressed, false if they
aren’t). The d-pad is exposed as four buttons.2 The joysticks are double with
values between -1.0 and 1.0 (0.0 means in the center). There is one for each x
(side to side) and one for each y (up and down). The x is negative to the left and
positive to the right. For strange reasons, up is negative and down is positive.
The left trigger and right trigger are also double with values between 0.0 and
1.0 (0.0 means not pressed, 1.0 means fully pressed). To get to these we use
variableName.memberName Below, we show what the memberNames are for all of
the parts of the gamepad. In the image below, the ones that are bolded are
double (Sometimes we call these analog and the ones that are binary - digital)
17
3. Gamepad and basic math
1 package org.firstinspires.ftc.teamcode;
2
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class GamepadOpMode extends OpMode {
8 @Override
9 public void init() {
10 }
11
12 @Override
13 public void loop() {
14 telemetry.addData("Left stick x", gamepad1.left_stick_x);
15 telemetry.addData("Left stick y", gamepad1.left_stick_y);
16 telemetry.addData("A button", gamepad1.a);
17 }
18 }
In the last section, we talked about how to read a gamepad. You probably no-
ticed that reading the joystick gave us a number. Once something is a number,
we own it. We can do any kind of math to it to get what we wanted. Below are
some of the most common operators.
18
3.1. Basic Math
Math Meaning
Operator
= assignment operator
+ addition operator
- subtraction operator AND negative operator ( So
saying -x is the same thing as saying (0 - x) )
* multiplication operator
/ division operator - be aware that if you are using
integers only the whole part is kept. It is NOT rounded.
For example: 5 / 2 == 2 ( == is how we describe two
things are equal. We’ll talk about it in section 4.1. )
% modulo operator - This gives the remainder. For
example: 5 % 2 == 1
( and ) These are parenthesis and they allow you to specify
the order of operations just like in regular math. You
can use these to tell the difference between 3 * (4 +
2) or (3 * 4) + 2 While there is a well defined order of
operations, instead of memorizing that it makes more
sense to use parenthesis to be specific.
Below is an example of how we might set the speed forward we want to go
based off of the joystick. In this case we are limiting our speed from -0.5 to 0.5.
The joystick y-value is negative when you press it up and positive when you
press it down which is backwards of how most people want to drive the robot,
so we “negate” the value here to flip it (so negative is positive and vice versa)
12 @Override
13 public void loop() {
14 double speedForward = -gamepad1.left_stick_y / 2.0;
15 telemetry.addData("Left stick y", gamepad1.left_stick_y);
19
3. Gamepad and basic math
The first thing we do is create a new variable and assign to it from another
variable using math.
14 double speedForward = -gamepad1.left_stick_y / 2.0;
You’ll notice that then we can send that variable directly using telemetry
16 telemetry.addData("speed Forward", speedForward);
3.3. Exercises
1. Add telemetry to show the right stick of gamepad1.
3. Report to the user the difference between the left joystick y and the right
joystick y on gamepad1.
4. Report to the user the sum of the left and right triggers on gamepad1.
20
4. Making decisions
4.1. If
So far our programs have executed all of their code. Control structures allow
you to change which code is executed and even to execute code multiple times.
The if statement is the first control structure we’ll talk about. Here is an
example of a program using it:
12 @Override
13 public void loop() {
14 if(gamepad1.left_stick_y < 0){
15 telemetry.addData("Left stick", " is negative");
16 }
17
21
4. Making decisions
Operator Meaning
== is equal to
!= is not equal to
< is less than
> is greater than
<= is less than or equal to
>= is greater than or equal to
Not only can we use conditional operators, we can also use a boolean variable
to make the decision. Here is an example:
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class IfOpMode2 extends OpMode {
8 @Override
9 public void init() {
10 }
11
22
4.2. Else
12 @Override
13 public void loop() {
14 if(gamepad1.a){
15 telemetry.addData("A Button", "pressed");
16 }
17 }
18 }
4.2. Else
An if statement can have an else clause which handles what should be done
if the if expression is false. That sounds confusing, but here is an example:
Listing 4.3: IfElseOpMode.java
1 package org.firstinspires.ftc.teamcode;
2
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class IfElseOpMode extends OpMode {
8 @Override
9 public void init() {
10 }
11
12 @Override
13 public void loop() {
14 if(gamepad1.left_stick_y < 0){
15 telemetry.addData("Left stick", " is negative");
16 }
17 else{
18 telemetry.addData("Left stick", " is positive");
19 }
20
4.2.1. Else if
Since an else statement can have a single statement OR a block of code we can
chain them together like this:
23
4. Making decisions
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class IfElseIfOpMode extends OpMode {
8 @Override
9 public void init() {
10 }
11
12 @Override
13 public void loop() {
14 if (gamepad1.left_stick_y < -0.5) {
15 telemetry.addData("Left stick", " is negative and large");
16 }
17 else if (gamepad1.left_stick_y < 0){
18 telemetry.addData("Left stick", " is negative and small");
19 }
20 else if (gamepad1.left_stick_y < 0.5){
21 telemetry.addData("Left stick", " is positive and small");
22 }
23 else {
24 telemetry.addData("Left stick", " is positive and large");
25 }
26 telemetry.addData("Left stick y", gamepad1.left_stick_y);
27 }
28 }
4.3. Combinations
Sometimes you want to test for more than one thing. For example, you may
want to test if a variable is between two numbers. While you can use multiple
if statements, it is often more convenient and readable to use logical combi-
nations. There are four1 simple ways that you can combine logical conditions.
(and then you can combine these even further)
1
Technically there are three, but you can use the XOR bitwise operator in a similar manner.
Just be careful to make sure you are operating on boolean expressions and not ones that are
integers or you’ll get unexpected results.
24
4.4. While
One thing that might not be obvious is that you can use these to set a value
for a boolean variable. So for example:
boolean bVar;
bVar = !bVar;
When it is declared, bVar will be false. (Since all boolean variables are ini-
tialized to false by default.) After the line bVar =!bVar it will be equal to true.
4.4. While
A while loop is much like an if statement except for after it is done it goes back
to the beginning and checks the conditional again. What if we had the amount
the robot had turned, but we wanted its heading (between -180 and 180). We
could use code like this:
while(angle > 180){
25
4. Making decisions
angle -= 360;
}
while(angle < -180){
angle += 360;
}
The reason it takes two while clauses is because one takes care of the case
where we had turned more than 180 degrees in the positive direction, and the
other takes care of the case where we had turned more than 180 degrees in
the negative direction.2
There is also a do...while loop which executes once regardless and checks
the condition at the end instead of the beginning. This is pretty rare in Java
FTC code but is included here for completeness. A quick example:
do{
// code goes here
a++;
}while(a < 10)
4.5. For
There are two types of for loops. The traditional type looks like many program-
ming languages, for(start; conditional; update) The start is executed once
before we begin, the conditional is checked every time before we execute, the
end is done at the end of EVERY time through.
for(int i = 0; i < 4; i++){
// This code will happen 4 times
}
2
If we were doing this for real, we would do it in radians. But we used degrees here to make
the concept simpler.
26
4.6. Exercises
This is often used, but in many cases it is to go through an array and you
are better off using a for-each that we’ll talk about when we talk about arrays
in chapter 13.
4.6. Exercises
1. Make a “turbo button”. When gamepad1.a is not pressed, multiply the
joystick by 0.5 and when it is pressed multiply by 1 and report to the user
as Forward Speed.
27
5. Class Members and Methods
A class is a model of something. It can contain data (members) and functions
(methods). Whenever you create a class, it becomes a data type that people can
make variables of that type. You can think of a class like a blueprint that can
be used to make any number of identical things. (called “objects”) For example,
the String data type is a class but we can have multiple objects of type String
in our programs. Remember that we name classes starting with a Capital letter
and then every other word in the class name is also capitalized.1
5.1. Members
So far, we have had variables in our methods but we can also have them belong
to our class. To have them belong to our class, they need to be within the
class body but outside of every method body. By convention, they are at the
beginning of the class but they don’t have to be. If they are in our class, then
every method in our class can use them and when they get changed everyone
sees the new value. However, every object (copy) has its own member variables2
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class ClassMemberOpMode extends OpMode {
8 boolean initDone;
9
10 @Override
11 public void init() {
12 telemetry.addData("init Done", initDone);
13 initDone = true;
1
Remember this is called Pascal Case.
2
unless they are declared static which means they are shared between all objects of the class.
We’ll talk about this in section 5.5.
29
5. Class Members and Methods
14 }
15
16 @Override
17 public void loop() {
18 telemetry.addData("init Done", initDone);
19 }
20 }
Even though initDone gets updated in init(), nothing sends it to the driver
station until loop() gets called for the first time.
You can use the this keyword to unambiguously say you are referring to the
class member, but if there isn’t a variable with the same name in your method
then you can leave it off. That would look like this.initDone .
We can create new methods. A method has a return type (which is any data
type), a name, and can take 0 or more parameters. A parameter is a way you
can pass information into a method. Each parameter has a data type and a
name. Inside the method, it is just like you had a variable defined inside the
method with that data type and name. (but it received its value from whomever
called the class method.)
By convention we name methods starting with a lowercase letter and then
having each additional word in the name start with an uppercase letter3 After
its parameters, there is the method body which goes from the opening curly
bracket { to the close curly bracket }.
9 @Override
10 public void init() {
11 }
12
3
Remember this is called camelCase
30
5.2. Class Methods
The return type is simply the data type in front of the name. You can also
say that a method doesn’t return anything. In that case, instead of the data
type you put the keyword void before the name. To return the value you use
the return statement. It is simply return <value>; You can return a variable
or a constant (typed in number, string, etc.) You can see this done in the
example above. As soon as the return keyword is executed the method returns
to whomever called it.
If you have a class method that returns void, then you can either have a
return with nothing after it like return; or you can omit the return statement
and it will return at the end of the code.
5.2.2. Parameters
You probably noticed that the name had an open parenthesis ( after it. Then
each parameter is listed like a variable (except no default assignment allowed).
31
5. Class Members and Methods
If there is more than one parameter, they are separated by a comma , Then at
the end of the parameters is a close parenthesis )
So some examples of methods:
// returnDataType name(parameters)
double squareInputWithSign(double input){
double output = input * input;
if(input < 0){
output = output * -1;
}
return output;
}
void setMotorSpeed(double speed){
motor.set(speed);
}
boolean isSensorPressed(){
return touchSensor.isPressed();
}
double min(double x, double y){
if(x < y){
return x;
}
return y;
}
The min example may have surprised you because there isn’t an else. There
isn’t any need because if x < y, then it will return out of the method. So the
only way it will get to the return y; statement is if x >= y . So you could write
it with an else, but that isn’t necessary.
A constructor is a special method in a Java class that has the same name
as the class and it has no return type. It gets called whenever the class is
initialized. (created). In Java you can have multiple constructors where each
one has different parameters.
An example:
public class Point{
int x;
int y;
32
5.2. Class Methods
this.y = y;
}
}
In this case, we had to use the this keyword because the class member is
named the same as the parameter. Sometimes people will change the parame-
ter name instead - like this:
public class Point{
int x;
int y;
Or, people that are coming from other languages will sometimes start all
class members with m_ so it looks like this:
public class Point{
int m_x;
int m_y;
Personally, I prefer the first option, but it is a preference. All three are legal
options and will do the same thing.
All objects in Java have a method called toString() This is used whenever we
convert to a string (like when we send to telemetry.addData) The default has
the name of the class and its hash code (typically NOT useful.) This makes it
easier to debug when there are problems by showing what is inside the class.
So using our Point class example from above:
public class Point{
int x;
int y;
33
5. Class Members and Methods
You might be wondering why we use @Override when we are not extending
another class. It turns out in Java that all classes extend the base class Object
We are adding strings and numbers together here which may seem strange.
The String class redefines (overloads) the + operator to mean concatenate (join)
two strings together. It also overloads += to concatenate and then assign the
resultant string.4 If it comes across something that isn’t a string, it calls
its toString() method which works (mostly) as you would expect for primitive
types.
• private - It can only be seen with the class. It cannot be accessed from
outside the class.
• (default - none specified) - only that class and other classes in the same
package (directory) can see them
• protected - It can only be seen with the class, its children, and other
classes in the same package (We’ll talk about children in chapter 14)
• public - It can be seen from everywhere. (You have seen this on init()
and loop()in your OpModes)
34
5.4. Creating your own classes
But in this case we are going to name it RobotLocation and it will have no
Superclass so in Android Studio 3.x make sure the superclass is blank. (In
Android Studio 4.x there is no place to put in superclass)
21 @Override
22 public String toString(){
23 return "RobotLocation: angle (" + angle + ")";
24 }
25
35
5. Class Members and Methods
32 }
Here is an example of the class member we talked about in section 5.1 Since
it doesn’t have an access modifier, it is default which means it is only available
to this class and other classes in the same package.
6 public RobotLocation(double angle){
7 this.angle = angle;
8 }
This is a public class method that returns the heading (so it needs to be
within -180 and 180). This would be a great place for a comment describing
the method. We left comments out of most source in the book since the text of
the book comments them.
21 @Override
22 public String toString(){
23 return "RobotLocation: angle (" + angle + ")";
24 }
This is the special method toString() that we talked about in subsection 5.2.4.
26 public void turn(double angleChange){
27 angle += angleChange;
28 }
36
5.4. Creating your own classes
This is a public class method where we can specify how much the robot is
turning. You’ll notice that since the parameter is not the same as the class
member we are using that we don’t have to use the this keyword for the class
member. You’ll also notice that we use the add and assign operator += as a
shortcut.
29 public void setAngle(double angle){
30 this.angle = angle;
31 }
Here is another public class method where we can set the angle.
You might have noticed that there is no way to get the angle out. (We can
only get out the heading). We could absolutely add this method if we needed it.
Sometimes you’ll see programmers take the lazy way out and make class
members public so they don’t have to write “setter” or “getter” methods (also
called accessor methods). The problem with that is that it makes it hard for
you to change the internals later without affecting other parts of your code.
For example, if you wanted to change it to keep things in radians internally:
3
4 public class RobotLocationRadians {
5 double angleRadians;
6
7
37
5. Class Members and Methods
I used some methods in the Math class so I wouldn’t have to write the rou-
tines to convert from Degrees to Radians and back. We talk about the Math
class in section 17.2. 5
You’ll notice that the way your class is used doesn’t have to change (I only
renamed it so I could keep them in the same package. In practice, you wouldn’t
even rename your class.)
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp
7 public class UseRobotLocationOpMode extends OpMode {
8 RobotLocation robotLocation = new RobotLocation(0);
9
10 @Override
11 public void init() {
12 robotLocation.setAngle(0);
13 }
5
In general, using a routine in a library is better than writing it yourself.
38
5.4. Creating your own classes
14
15 @Override
16 public void loop() {
17 if(gamepad1.a){
18 robotLocation.turn(0.1);
19 }
20 else if(gamepad1.b){
21 robotLocation.turn(-0.1);
22 }
23 telemetry.addData("Location", robotLocation);
24 telemetry.addData("Heading", robotLocation.getHeading());
25 }
26 }
This is the OpMode that uses our new class. The first 7 lines are the same
so we’ll start after that.
8 RobotLocation robotLocation = new RobotLocation(0);
This is a data member in our OpMode. You’ll notice that it uses the new
keyword. We use this whenever we are creating an instance of a class (or
object). The new keyword tells the compiler to reserve room for it and call the
constructor that matches the parameters you gave it. (type only, the names are
ignored) Also by convention variables start with a lower case letter while the
class starts with an upper case letter. They don’t have to be named the same
but often are.
10 @Override
11 public void init() {
12 robotLocation.setAngle(0);
13 }
Inside our init() method, we call the setAngle() method of the robotLocation
object. The reason we call setAngle() here is in case we select the opMode, init
it, run it and then stop and press init again. If we don’t set it in init() then it
will keep its value from the last time it was modified.
As a best practice for FTC, your init()method should
set things back to their expected default state.
15 @Override
16 public void loop() {
17 if(gamepad1.a){
18 robotLocation.turn(0.1);
19 }
39
5. Class Members and Methods
20 else if(gamepad1.b){
21 robotLocation.turn(-0.1);
22 }
Obviously this doesn’t turn the robot (because we don’t have any motors
hooked up), so perhaps turn() was an unfortunate naming choice. Run this
and you’ll get a feel for how fast loop() is called. Also, we don’t allow the
user to turn positively and negatively at the same time (since that makes no
sense). Since it looks at gamepad1.a first, if they are both pressed then it will
turn positively.
5.5. static
The static keyword means that it belongs to the type instead of the object. This
can be used for methods (but then they can’t access any non-static members
or methods) or for class members.
For class members, it is used typically for constants when you want all in-
stances to share it.
For methods, it is often used when you want to let someone call a method
and they don’t need to have an object of that type first.
5.6. Exercises
1. Add a double getAngle() method to RobotLocation and then display it in
your opMode.
3. After you have done exercise 2, also add in support for y. Use
gamepad1.dpad_up for robotLocation.changeY(0.1)
and gamepad1.dpad_down for robotLocation.changeY(-0.1)
40
6. Our first hardware
Until this point, we have been in pure software that hasn’t used any of our
hardware. That is fine, but our robot will be pretty boring without any sensors,
motors, or servos. This (and following chapters) assume you have a program-
ming board setup like in Appendix A
This will feel like a lot of steps the first time, but soon it’ll become very natural
to run through them.
41
6. Our first hardware
42
6.2. Mechanisms
14. Press OK
6.2. Mechanisms
Until this point we have had everything in one package. At this point, we are
going to split things into two packages. One will hold our mechanisms (For
this book, we have one mechanism called the ProgrammingBoard.1 On our
real robot we would likely have multiple mechanisms.) The other will hold our
opModes.
So there are now two classes:
This one is in the mechanisms package. To create a package, right click in
the same place that we have to make a new class, but select new package and
type in “mechanisms”.
That will make the package. Then right click on the package and select
new class. This one should be “ProgrammingBoard1” and it should have no
superclass.
3 import com.qualcomm.robotcore.hardware.DigitalChannel;
4 import com.qualcomm.robotcore.hardware.HardwareMap;
5
1
To make writing the book easier, I added a number at the end so I could keep them all in one
package. Normally you would just add things to your existing class instead of making a new
one.
43
6. Our first hardware
13
This line says that we have a class member of type DigitalChannel with a
name of touchSensor. DigitalChannel comes from the FTC SDK. We’ll talk about
how to navigate the SDK to find out what is there in chapter 16. This needs
to be a class member since it is set in init() and used in other methods. We
set it to private to make sure only our class can interact directly with it. This
is a good practice for all hardware. Normally you would want to name it with
what the sensor does (like armInPositionTouchSensor, but since this is part of
a programming board it doesn’t have more of a purpose than being a Touch
Sensor.
9 public void init(HardwareMap hwMap) {
We have an init() method. We could have called it anything, but since we’ll
call it from our init() in our OpMode it seemed reasonable. While it might be
tempting to make this the constructor, that limits what we can and can’t do,
so it is easier to follow the same structure. You’ll notice that this takes one
parameter of type HardwareMap and it is called hwMap. We could have called it
hardwareMap but I am lazy so I took a shortcut. HardwareMap also comes from the
FTC SDK and it is how our programs get information from the configuration
file on the robot.
10 touchSensor = hwMap.get(DigitalChannel.class, "touch_sensor");
This assigns to the variable touchSensor the hardware that is in the config-
uration file of type DigitalChannel.class and with a name of touch_sensor. This
name has to match EXACTLY what is in the configuration file. It may seem
strange to you that you don’t have to use new here. That is because the get()
method of HardwareMap does it for you.
11 touchSensor.setMode(DigitalChannel.Mode.INPUT);
It turns out that you can set each DigitalChannel as either INPUT or OUTPUT.
Since we are reading from the touch sensor, we need to set it as an INPUT .
44
6.3. OpMode
We create a class method so that those outside of our class can read the state
of the touchSensor. This is better than making touchSensor public because
nobody can change how it is configured.
6.3. OpMode
This one is in the opmodes package
Listing 6.2: TouchSensorOpMode.java
1 package org.firstinspires.ftc.teamcode.opmodes;
2
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard1;
7
8 @TeleOp()
9 public class TouchSensorOpMode extends OpMode {
10 ProgrammingBoard1 board = new ProgrammingBoard1();
11 @Override
12 public void init() {
13 board.init(hardwareMap);
14 }
15
16 @Override
17 public void loop() {
18 telemetry.addData("Touch sensor", board.getTouchSensorState());
19 }
20 }
The first few lines of this should look amazingly familiar by now.
10 ProgrammingBoard1 board = new ProgrammingBoard1();
45
6. Our first hardware
14 }
Our init is very clean. It only calls the init of our board object. The vari-
able hardwareMap is part of the OpMode and it is how we see how the robot is
configured.
17 public void loop() {
18 telemetry.addData("Touch sensor", board.getTouchSensorState());
19 }
For the loop all we do is send to the telemetry the state of the touch sensor.
One of the huge advantages of splitting things out is that we can isolate hard-
ware “weirdness”. For example, you were probably surprised that pushing in
the touch sensor returns false and it not pushed in was true. So let’s change
that.
First, we’ll change our ProgrammingBoard class. The easiest way to do this
is right click on the file, select copy. Then select paste and give it the new file
name. Then you can just make the changes instead of typing everything in
again.
3 import com.qualcomm.robotcore.hardware.DigitalChannel;
4 import com.qualcomm.robotcore.hardware.HardwareMap;
5
46
6.4. Making changes
It turns out that doing it in one line does exactly the same thing.
Also, since we changed the name of the method, we have to change it in the
OpMode as well.
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard2;
7
8 @TeleOp()
9 public class TouchSensorOpMode2 extends OpMode {
10 ProgrammingBoard2 board = new ProgrammingBoard2();
11 @Override
12 public void init() {
13 board.init(hardwareMap);
14 }
15
16 @Override
17 public void loop() {
18 telemetry.addData("Touch pressed", board.isTouchSensorPressed());
19 }
20 }
47
6. Our first hardware
6.5. Exercises
1. Add a method isTouchSensorReleased() to the ProgrammingBoard2 class and
use it in your opMode
2. Have your opMode send “Pressed” and “Not Pressed” for the “Touch sen-
sor” instead of true or false. There are lots of ways to do this.
48
7. Motors
49
7. Motors
7.2. Mechanisms
50
7.2. Mechanisms
5 import com.qualcomm.robotcore.hardware.HardwareMap;
6
Most of this should look the same as our last file, so we’ll just talk about the
changes
Here we are adding a variable of type DcMotor with name motor. Normally you
would want to name the motor with what it does, but since this is part of a
programming board - we’ll just call in motor. DcMotor comes from the FTC SDK.
This assigns to the variable motor the hardware that is in the configuration
file of type DcMotor.class and with a name of motor. This name has to match
EXACTLY what is in the configuration file.
15 motor.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
This sets how we want to use the motor. The choices are:
51
7. Motors
RunMode Meaning
RUN_TO_POSITION The motor is to attempt to rotate in whatever
direction is necessary to cause the encoder
reading to advance or retreat from its current
setting to the setting which has been provided
through the setTargetPosition() method.
RUN_USING_ENCODER The motor is to do its best to run at targeted
velocity.
RUN_WITHOUT_ENCODER The motor is simply to run at whatever velocity
is achieved by applying a particular power
level to the motor.
STOP_AND_RESET_ENCODER The motor is to set the current encoder
position to zero.
We set it here to DcMotor.RunMode.RUN_USING_ENCODER which means that it uses
the encoder on the motor so that we are setting a speed and it figures out how
to modify power to get to that speed (if possible). We like this mode because if
you set two motors to the same speed then they have a better chance at being
at the same speed than in any other mode. (We have met teams that don’t even
plug in the encoders and they are having weird problems with the robot not
driving straight.)
While RUN_TO_POSITION can be very handy for single
motors, we recommend AGAINST using it in a drive
train because the different speeds for the different
wheels trying to get to a position can cause wacky side
effects. This is because each motor is trying to get to
its position irregardless of the other motors. So you
can have a robot “wiggle”. In a perfect world without
friction this would work the same. However, we don’t
live in that world. :-)
This is a class method so that code outside our class can set the speed of
the motor. This is better than exposing the motor as public because people
can’t accidentally change configuration. setPower()on a motor takes a double
between -1.0 and 1.0. -1.0 is full speed “backwards”, 0.0 is stopped, and 1.0
is full speed “forwards”.
52
7.3. OpMode
7.3. OpMode
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard4;
7
8 @TeleOp()
9 public class MotorOpMode extends OpMode {
10 ProgrammingBoard4 board = new ProgrammingBoard4();
11 @Override
12 public void init() {
13 board.init(hardwareMap);
14 }
15
16 @Override
17 public void loop() {
18 board.setMotorSpeed(0.5);
19 }
20 }
This has very little that is new, so we’ll only talk about that.
17 public void loop() {
18 board.setMotorSpeed(0.5);
19 }
The motor also has a rotation sensor built into it. We are using it when we say
RUN_USING_ENCODER, but we can also read it and use it in our code. It’ll
need a method in the ProgrammingBoard class so we can read it.
53
7. Motors
3 import com.qualcomm.robotcore.hardware.DcMotor;
4 import com.qualcomm.robotcore.hardware.DigitalChannel;
5 import com.qualcomm.robotcore.hardware.HardwareMap;
6
7 public class ProgrammingBoard4 {
8 private DigitalChannel touchSensor;
9 private DcMotor motor;
10 private double ticksPerRotation;
11
12 public void init(HardwareMap hwMap) {
13 touchSensor = hwMap.get(DigitalChannel.class, "touch_sensor");
14 touchSensor.setMode(DigitalChannel.Mode.INPUT);
15 motor = hwMap.get(DcMotor.class, "motor");
16 motor.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
17 ticksPerRotation = motor.getMotorType().getTicksPerRev();
18 }
19 public boolean isTouchSensorPressed() {
20 return !touchSensor.getState();
21 }
22
23 public void setMotorSpeed(double speed){
24 motor.setPower(speed);
25 }
26 public double getMotorRotations(){
27 return motor.getCurrentPosition() / ticksPerRotation;
28 }
29 }
Most of this is the same so we’ll just talk about the differences
10 private double ticksPerRotation;
This is a member variable where we will store the number of encoder ticks
per rotation. We do this to make things easier for the opModes.
17 ticksPerRotation = motor.getMotorType().getTicksPerRev();
If we set the exact motor we have in the configuration, then we can do this
to get the number of ticks per rev (revolution). I prefer to call them rotation
since our students come from FLL teams where they are more used to that
terminology. If you have additional gear changes after the motor, you’ll have
to calculate this. For example if you have a 2:1 gear reduction then you would
54
7.4. Motor as Sensor
simply multiply the number of ticks per rev at the motor by 2 to get the number
of revolutions of your mechanism.
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard4;
7
8 @TeleOp()
9 public class MotorOpMode2 extends OpMode {
10 ProgrammingBoard4 board = new ProgrammingBoard4();
11 @Override
12 public void init() {
13 board.init(hardwareMap);
14 }
15
16 @Override
17 public void loop() {
18 board.setMotorSpeed(0.5);
19 telemetry.addData("Motor rotations", board.getMotorRotations());
20 }
21 }
Here we are simply sending to telemetry what we are seeing from the motor
rotations.
55
7. Motors
We don’t need to make any change to our configuration file or our Program-
mingBoard file since they already have a motor and a sensor.
Remember that setting the motor speed to 0 makes it stop. You can
set for each motor what you would like it to do when set to zero by
calling setZeroBehavior() with either DcMotor.ZeroPowerBehavior.BRAKE or
DcMotor.ZeroPowerBehavior.FLOAT
56
7.6. Motors and Gamepads
So in this case, when the touch sensor is pressed we move the motor “for-
ward” at half speed. When it isn’t, we stop it.
You may end up in a circumstance where you want “forward” to be the
opposite direction of clockwise. (Like on the left hand side of your drive
train). To do this, you simply call the motor’s method setDirection() with
DcMotorSimple.Direction.REVERSE and if you want to change it back you call it
with DcMotorSimple.Direction.FORWARD. The motor remembers these settings.
So you might make your ProgrammingBoard class init() method look like
this:
...
motor = hwMap.get(DcMotor.class, "motor");
motor.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
motor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
motor.setDirection(DcMotorSimple.Direction.REVERSE);
ticksPerRotation = motor.getMotorType().getTicksPerRev();
...
And of course, we can use our Gamepad just like a sensor. (we are sensing
what the human is doing.)
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard4;
7
8 @TeleOp()
9 public class MotorGamepadOpMode extends OpMode {
10 ProgrammingBoard4 board = new ProgrammingBoard4();
11 @Override
12 public void init() {
13 board.init(hardwareMap);
14 }
15
16 @Override
17 public void loop() {
18 if(gamepad1.a) {
57
7. Motors
19 board.setMotorSpeed(0.5);
20 }
21 else{
22 board.setMotorSpeed(0.0);
23 }
24 telemetry.addData("Motor rotations", board.getMotorRotations());
25 }
26 }
This is exactly the same as before except for using gamepad1.a instead of the
touch sensor.
But we don’t have to be limited to just the buttons. We can make it finer
controlled by using an analog input from the gamepad
20 board.setMotorSpeed(motorSpeed);
21
22 telemetry.addData("Motor speed", motorSpeed);
23 telemetry.addData("Motor rotations", board.getMotorRotations());
24 }
25 }
58
7.7. Exercises
7.7. Exercises
1. Add a method to the ProgrammingBoard that allows you to change the
ZeroPowerBehavior of the motor, and then add to your OpMode where
pressing gamepad1.a sets it to BRAKE and gamepad1.b sets it to FLOAT.
2. Make the joystick less sensitive in the middle without losing range by
bringing in the squareInputWithSign() method from section 5.2 into your
opMode and using it.
59
8. Servos
8.2. Mechanisms
Let’s start with what we need to do to our ProgrammingBoard class.
Listing 8.1: ProgrammingBoard5.java
1 package org.firstinspires.ftc.teamcode.mechanisms;
2
3 import com.qualcomm.robotcore.hardware.DcMotor;
4 import com.qualcomm.robotcore.hardware.DigitalChannel;
5 import com.qualcomm.robotcore.hardware.HardwareMap;
6 import com.qualcomm.robotcore.hardware.Servo;
7
61
8. Servos
19 ticksPerRotation = motor.getMotorType().getTicksPerRev();
20 servo = hwMap.get(Servo.class, "servo");
21 }
22 public boolean isTouchSensorPressed() {
23 return !touchSensor.getState();
24 }
25
This is very similar to the ones before. We’ll just talk about the new parts.
12 private Servo servo;
Here we create a class member of type Servo named servo. The Servo class
comes from the FTC SDK. Again, we would use a more descriptive name on our
robot.
20 servo = hwMap.get(Servo.class, "servo");
This assigns to the variable servo the hardware that is in the configuration
file of type Servo.class and with a name of servo. This name has to match
EXACTLY what is in the configuration file.
32 public void setServoPosition(double position){
33 servo.setPosition(position);
34 }
This allows code outside of our class to set the servo position. Typically we
might expose a method for each position we want it to go to - for example
setClawOpen() and setClawClose()
servo.setPosition() takes a double which is a fraction between 0.0 and 1.0
saying where in that range to move. We can programmatically change what
that means with two methods:
62
8.3. OpMode
8.3. OpMode
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard5;
7
8 @TeleOp()
9 public class ServoGamepadOpMode extends OpMode {
10 ProgrammingBoard5 board = new ProgrammingBoard5();
11 @Override
12 public void init() {
13 board.init(hardwareMap);
14 }
15
16 @Override
17 public void loop() {
18 if(gamepad1.a) {
19 board.setServoPosition(1.0);
20 }
21 else if(gamepad1.b){
22 board.setServoPosition(0.0);
23 }
1
The min has to be less than the max, so you can’t use this to flip the direction.
63
8. Servos
24 else{
25 board.setServoPosition(0.5);
26 }
27 }
28 }
You’ll see that we are using chained if and else so that we only try to set the
servo position to one location. Otherwise we will confuse the servo and you’ll
likely see some jitter on it. (although the last one will likely win since there is
more time in between calls to loop() than within loop()
8.4. Exercises
1. Change the ProgrammingBoard class so that the servo is backwards and
only goes from the midpoint to far left.
64
9. Analog Sensors
We’ll be using a potentiometer here, but the same concepts work for all ana-
log sensors. It is very common to abbreviate potentiometer as “pot” because
potentiometer is hard to spell.
9.2. Mechanisms
First, lets add support to our ProgrammingBoard class.
Listing 9.1: ProgrammingBoard6.java
1 package org.firstinspires.ftc.teamcode.mechanisms;
2
3 import com.qualcomm.robotcore.hardware.AnalogInput;
4 import com.qualcomm.robotcore.hardware.DcMotor;
5 import com.qualcomm.robotcore.hardware.DigitalChannel;
6 import com.qualcomm.robotcore.hardware.HardwareMap;
7 import com.qualcomm.robotcore.hardware.Servo;
8 import com.qualcomm.robotcore.util.Range;
9
65
9. Analog Sensors
Most of this is the same, so we’ll just explain the new bits.
15 private AnalogInput pot;
We are declaring a class member of type AnalogInput with name pot. The
AnalogInput class comes from the FTC SDK.
24 pot = hwMap.get(AnalogInput.class, "pot");
This assigns to the variable pot the hardware that is in the configuration file
of type AnalogInput.class and with a name of pot. This name has to match
EXACTLY what is in the configuration file.
39 public double getPotAngle(){
40 return Range.scale(pot.getVoltage(), 0, pot.getMaxVoltage(), 0, 270);
41 }
66
9.3. OpMode
then it would figure out that the input (25) was 1/4 of the way between 0
and 100. It would then figure out what 1/4 between 0 and 1.0 is and would
set output to 0.25.
In this case we know that the lowest possible voltage that could be detected
is 0, the highest we can get by calling pot.getMaxVoltage(). We know our poten-
tiometer can be between 0 and 270 degrees. So we use Range.scale to convert
for us.
You might have noticed that you made a method call on a class instead of an
object (a variable of type class). That is because it is a static method. This is
an example of what we talked about in section 5.5.
9.3. OpMode
67
9. Analog Sensors
16
17 @Override
18 public void loop() {
19 telemetry.addData("Pot Angle", board.getPotAngle());
20 }
21 }
Since we are doing the conversion in our ProgrammingBoard class, this be-
comes trivial. We are simply reporting the angle. This can be used on our robot
to know what angle something is turned to.
9.4. Exercises
1. Make a class method for your ProgrammingBoard that exposes the pot in
the range [0.0..1.0]
2. Now make an OpMode that sets the servo to the position that the pot is
returning in that range. Then you can turn the pot and it will cause the
servo to “follow” it.
68
10. Color and Distance Sensors
10.2. Mechanisms
Let’s start by making a change to our ProgrammingBoard class.
Listing 10.1: ProgrammingBoard7.java
1 package org.firstinspires.ftc.teamcode.mechanisms;
2
3 import com.qualcomm.robotcore.hardware.AnalogInput;
4 import com.qualcomm.robotcore.hardware.ColorSensor;
5 import com.qualcomm.robotcore.hardware.DcMotor;
6 import com.qualcomm.robotcore.hardware.DigitalChannel;
7 import com.qualcomm.robotcore.hardware.DistanceSensor;
8 import com.qualcomm.robotcore.hardware.HardwareMap;
9 import com.qualcomm.robotcore.hardware.Servo;
10 import com.qualcomm.robotcore.util.Range;
11
12 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
13
14 public class ProgrammingBoard7 {
15 private DigitalChannel touchSensor;
16 private DcMotor motor;
17 private double ticksPerRotation;
18 private Servo servo;
69
10. Color and Distance Sensors
Most of this is similar so we’ll only talk about the new parts.
20 private ColorSensor colorSensor;
21 private DistanceSensor distanceSensor;
70
10.3. OpMode
This is a little different. A REV ColorSensor can act as both a color sensor
and a distance sensor.1 So we make two variables - one for the ColorSensor
class and one for the DistanceSensor class. Both of these classes are in the FTC
SDK.
32 colorSensor = hwMap.get(ColorSensor.class, "sensor_color_distance");
33 distanceSensor = hwMap.get(DistanceSensor.class, "sensor_color_distance");
Both of these follow the pattern we have seen before. The unusual part is
that they use the SAME string for the sensor. Again, it has to match EXACTLY
what is in the configuration file.
51 public int getAmountRed(){
52 return colorSensor.red();
53 }
This is a class method that returns the amount of red that the color sensor
sees (between 0 and 255) . The colorSensor class has several class methods
that are useful.
Method What it returns
red() Amount of red seen (0-255)
green() Amount of green seen (0-255)
blue() Amount of blue seen (0-255)
argb() An integer in the format #aarrggbb
(where a is alpha, r is red, g is green, b
is blue)
This uses a neat class included in the FTC SDK called DistanceUnit. It allows
us to decide what units we want to work in and hopefully keeps us from making
a NASA class mistake with units.2 This is a simple pass through so we’ll talk
more about DistanceUnit as we discuss the OpMode.
10.3. OpMode
And we need an OpMode that can use it.
1
Although the distance sensor part of a color sensor is much less accurate and over a smaller
range than a REV Distance sensor.
2
https://en.wikipedia.org/wiki/Mars_Climate_Orbiter
71
10. Color and Distance Sensors
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
7 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard7;
8
9 @TeleOp()
10 public class DistanceColorOpMode extends OpMode {
11 ProgrammingBoard7 board = new ProgrammingBoard7();
12 @Override
13 public void init() {
14 board.init(hardwareMap);
15 }
16
17 @Override
18 public void loop() {
19 telemetry.addData("Amount red", board.getAmountRed());
20 telemetry.addData("Distance (CM)", board.getDistance(DistanceUnit.CM));
21 telemetry.addData("Distance (IN)", board.getDistance(DistanceUnit.INCH));
22 }
23 }
This simply prints the amount of red seen by the color sensor
20 telemetry.addData("Distance (CM)", board.getDistance(DistanceUnit.CM));
21 telemetry.addData("Distance (IN)", board.getDistance(DistanceUnit.INCH));
Parameter Unit
DistanceUnit.MM millimeter
DistanceUnit.CM centimeter
DistanceUnit.INCH inch
DistanceUnit.METER meter
72
10.4. Exercises
If you are using this with your class, you’ll have to decide what unit you are
going to store things in (I typically recommend CM, but that is up to you.) Then
you can convert things like this:
public class Square{
double length_cm = 10;
10.4. Exercises
1. Add a method getAmountBlue()to the ProgrammingBoard and report it
back by changing the OpMode
2. Make the motor stop when the distance sensor sees something closer than
10cm and go at half speed when farther than that.
73
11. Gyro (IMU)
Unlike everything else, you don’t need to add it to the robot configuration be-
cause it is already there as “imu”. You can rename it or delete it.
11.2. Mechanisms
75
11. Gyro (IMU)
76
11.2. Mechanisms
71 angleUnit);
72 return angles.firstAngle;
73 }
74 }
The IMU (Inertial Measurement Unit) that is inside of every REV Expansion
Hub and REV Control Hub is based off of the BNO055IMU (say that 5 times
fast...) While it has a TON of capabilities, we are going to just barely tap into it
here.
27 private BNO055IMU imu;
We create a class member of type BNO055IMU (you guessed it from the FTC
SDK) with the name imu.
40 imu = hwMap.get(BNO055IMU.class, "imu");
First, we get the imu from the hardware map (just like we have done with
other pieces of hardware). If you didn’t change the name in your configuration
(and you shouldn’t), it will be “imu”.
41 BNO055IMU.Parameters params = new BNO055IMU.Parameters();
42 // change to default set of parameters go here
43 imu.initialize(params);
We are creating a class method so code outside of our class can get the
heading of the robot (actually REV hub). Much like we had DistanceUnit before,
there is also a class called AngleUnit. There are two angle units supported:
DEGREES and RADIANS.1 AngleUnit will make sure everything is normalized (that
means it will be within -180 and 180 degrees for DEGREES and between -Π and
Π for RADIANS.2
69 Orientation angles = imu.getAngularOrientation(AxesReference.INTRINSIC,
70 AxesOrder.ZYX,
71 angleUnit);
1
No love for gradians... - https://en.wikipedia.org/wiki/Gradian
2
Yes, this means our RobotLocation class could have been much simpler.
77
11. Gyro (IMU)
The first thing I want to point out is that you can use white space to make
the code more readable (like is done here.)
imu.getAngularOrientation takes three parameters:
2. AxesOrder - what order you want the Axes returned in. We are saying we
want them in the order ZYX. For reasons I don’t understand in addition to
XYZ, XZY, ZXY, ZYX, YXZ, YZX there is also XYX, XZX, YXY, YZY, ZXZ, ZYZ. If you
understand why, please contact me and tell me.
3. AngleUnit- What unit we want the angles in. This can be either DEGREES or
RADIANS.
72 return angles.firstAngle;
We return the firstAngle of the orientation (which will be the Z Axis since we
asked for ZYX.)
This may seem really confusing, but the good news is that you only have to
write it once and make sure it works. Then after that you can forget all of the
complication and just call our class method getHeading().
11.3. OpMode
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
7 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
8
9 @TeleOp()
10 public class GyroOpMode extends OpMode {
11 ProgrammingBoard8 board = new ProgrammingBoard8();
12 @Override
13 public void init() {
78
11.4. Exercises
14 board.init(hardwareMap);
15 }
16
17 @Override
18 public void loop() {
19 telemetry.addData("Our Heading", board.getHeading(AngleUnit.DEGREES));
20 }
21 }
Really the only thing that is new here is our telemetry in line 19. Put it on
the programming board and turn it around and watch the telemetry change.
11.4. Exercises
1. Change the OpMode to also show the heading in RADIANS as well as
DEGREES
2. Make the motor stopped when our heading is 0, go negative when our
heading is negative, and positive when our heading is positive.
79
12. Dealing with State
State is where you remember what you have done and do something different
because of what you have done in the past.
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
7
8 @TeleOp()
9 public class ToggleOpMode extends OpMode {
10 ProgrammingBoard8 board = new ProgrammingBoard8();
11 boolean aAlreadyPressed;
12 boolean motorOn;
13 @Override
14 public void init() {
15 board.init(hardwareMap);
16 }
17
18 @Override
19 public void loop() {
20 if(gamepad1.a && !aAlreadyPressed){
21 motorOn = !motorOn;
22 telemetry.addData("Motor", motorOn);
23 if (motorOn) {
24 board.setMotorSpeed(0.5);
25 } else {
81
12. Dealing with State
26 board.setMotorSpeed(0.0);
27 }
28 }
29 aAlreadyPressed = gamepad1.a;
30 }
31 }
Here we define two more class members. Since we don’t initialize them and
they are boolean they start out as false.
20 if(gamepad1.a && !aAlreadyPressed){
Normally, I like to avoid shortcuts but in this case it is so common that most
programmers would prefer the way it is done in the example.
23 if (motorOn) {
24 board.setMotorSpeed(0.5);
25 } else {
26 board.setMotorSpeed(0.0);
27 }
28 }
This actually turns on (or off) the motor. More than one programmer has
forgotten this piece and been puzzled when changing the value of a variable
called motorOn did not actually change the motor.
30 }
82
12.2. Autonomous state - Example
When writing autonomous code, you want to write it as separate steps. This
allows you to test out parts of it separately.
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
7
8 @Autonomous()
9 public class AutoState1 extends OpMode {
10 ProgrammingBoard8 board = new ProgrammingBoard8();
11 int state;
12
13 @Override
14 public void init() {
15 board.init(hardwareMap);
16 }
17
18 @Override
19 public void start() {
20 state = 0;
21 }
22
23 @Override
24 public void loop() {
25 telemetry.addData("State", state);
83
12. Dealing with State
26 if (state == 0) {
27 board.setServoPosition(0.5);
28 if (board.isTouchSensorPressed()) {
29 state = 1;
30 }
31 } else if (state == 1) {
32 board.setServoPosition(0.0);
33 if (!board.isTouchSensorPressed()) {
34 state = 2;
35 }
36 } else if (state == 2) {
37 board.setServoPosition(1.0);
38 board.setMotorSpeed(0.5);
39 if (board.getPotAngle() > 90) {
40 state = 3;
41 }
42 } else if (state == 3) {
43 board.setMotorSpeed(0.0);
44 state = 4;
45 } else {
46 telemetry.addData("Auto", "Finished");
47 }
48 }
49 }
Here we create our state variable to hold which state we are in. If we don’t
assign an initial value it is zero.
18 @Override
19 public void start() {
20 state = 0;
21 }
It is very helpful for debugging to send to the driver station what step in your
auto program you are so you can figure out what is going on.
26 if (state == 0) {
84
12.2. Autonomous state - Example
27 board.setServoPosition(0.5);
28 if (board.isTouchSensorPressed()) {
29 state = 1;
30 }
31 } else if (state == 1) {
You can see here an example of using if/else chaining. Also, you’ll notice
that when the touch sensor is pressed, we change the value of state. So the
next time through we’ll go to the next chain.
But there is another way...
In Java, if you are comparing for a number of options you can use a switch
statement. Here is the same program rewritten with a switch statement.
3 import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
4 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
5
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
7
8 @Autonomous()
9 public class AutoState2 extends OpMode {
10 ProgrammingBoard8 board = new ProgrammingBoard8();
11 int state;
12
13 @Override
14 public void init() {
15 board.init(hardwareMap);
16 }
17
18 @Override
19 public void start() {
20 state = 0;
21 }
22
23 @Override
24 public void loop() {
25 telemetry.addData("State", state);
26 switch (state) {
27 case 0:
85
12. Dealing with State
28 board.setServoPosition(0.5);
29 if (board.isTouchSensorPressed()) {
30 state = 1;
31 }
32 break;
33 case 1:
34 board.setServoPosition(0.0);
35 if (!board.isTouchSensorPressed()) {
36 state = 2;
37 }
38 break;
39 case 2:
40 board.setServoPosition(1.0);
41 board.setMotorSpeed(0.5);
42 if (board.getPotAngle() > 90) {
43 state = 3;
44 }
45 break;
46 case 3:
47 board.setMotorSpeed(0.0);
48 state = 4;
49 break;
50 default:
51 telemetry.addData("Auto", "Finished");
52 }
53 }
54 }
You may think that since this is more lines that it is worse, but let’s look at
it anyway. (It is personal preference based on which you feel is more readable
and you can do things with if/else chaining that you can’t do with a switch
statement)
26 switch (state) {
Each case starts with the case keyword followed by the constant followed by
a colon :
32 break;
All code is executed until it hits the break statement. At this point, it jumps
to the closing brace of the switch statement.
86
12.2. Autonomous state - Example
50 default:
You can (but don’t have to) have a default: clause. This will be executed if
none of the other cases were a match.
But a problem with these two programs is that if you have to put one in the
middle, you have to make lots of changes. We can do better....
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
7
8 @Autonomous()
9 public class AutoState3 extends OpMode {
10 ProgrammingBoard8 board = new ProgrammingBoard8();
11 String state = "START";
12
13 @Override
14 public void init() {
15 board.init(hardwareMap);
16 }
17
18 @Override
19 public void start() {
20 state = "START";
21 }
22
23 @Override
24 public void loop() {
25 telemetry.addData("State", state);
87
12. Dealing with State
26 switch (state) {
27 case "START":
28 board.setServoPosition(0.5);
29 if (board.isTouchSensorPressed()) {
30 state = "WAIT_FOR_SENSOR_RELEASE";
31 }
32 break;
33 case "WAIT_FOR_SENSOR_RELEASE":
34 board.setServoPosition(0.0);
35 if (!board.isTouchSensorPressed()) {
36 state = "WAIT_FOR_POT_TURN";
37 }
38 break;
39 case "WAIT_FOR_POT_TURN":
40 board.setServoPosition(1.0);
41 board.setMotorSpeed(0.5);
42 if (board.getPotAngle() > 90) {
43 state = "STOP";
44 }
45 break;
46 case "STOP":
47 board.setMotorSpeed(0.0);
48 state = "DONE";
49 break;
50 default:
51 telemetry.addData("Auto", "Finished");
52 }
53 }
54 }
Really all we have done is change state from an integer to a String. Now
our code is easier to read (called self-documenting) and it is easier to add in
another state. (Win-win!!)
But now if we have a typo in a string the compiler won’t catch it, and we’ll
have a problem in our code. What if we could have the readability of strings,
but have the compiler catch typos. We can....
3 import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
88
12.2. Autonomous state - Example
4 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
5
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
7
8 @Autonomous()
9 public class AutoState4 extends OpMode {
10 enum State {
11 START,
12 WAIT_FOR_SENSOR_RELEASE,
13 WAIT_FOR_POT_TURN,
14 STOP,
15 DONE
16 }
17
21 @Override
22 public void init() {
23 board.init(hardwareMap);
24 }
25
26 @Override
27 public void start() {
28 state = State.START;
29 }
30
31 @Override
32 public void loop() {
33 telemetry.addData("State", state);
34 switch (state) {
35 case START:
36 board.setServoPosition(0.5);
37 if (board.isTouchSensorPressed()) {
38 state = State.WAIT_FOR_SENSOR_RELEASE;
39 }
40 break;
41 case WAIT_FOR_SENSOR_RELEASE:
42 board.setServoPosition(0.0);
43 if (!board.isTouchSensorPressed()) {
44 state = State.WAIT_FOR_POT_TURN;
45 }
46 break;
47 case WAIT_FOR_POT_TURN:
48 board.setServoPosition(1.0);
89
12. Dealing with State
49 board.setMotorSpeed(0.5);
50 if (board.getPotAngle() > 90) {
51 state = State.STOP;
52 }
53 break;
54 case STOP:
55 board.setMotorSpeed(0.0);
56 state = State.DONE;
57 break;
58 default:
59 telemetry.addData("Auto", "Finished");
60 }
61 }
62 }
Let’s talk through some of this. This actually works exactly the same as
our first switch statement except now it is more readable (and we can’t assign
values to it that we aren’t expecting)
10 enum State {
11 START,
12 WAIT_FOR_SENSOR_RELEASE,
13 WAIT_FOR_POT_TURN,
14 STOP,
15 DONE
16 }
90
12.3. It’s all relative
33 telemetry.addData("State", state);
One of the really cool things about enum is that they implement toString
automagically so when you print them you get human readable descriptions.
For a lot of autonomous programs, you may want things to occur for an amount
of time or an amount of encoder ticks. To do this we need to save off the time
or ticks when we started.
To get the time, all opModes have access to getRuntime() which returns a
double that is the number of seconds since the opMode was created. This
isn’t very useful by itself because we don’t know how long ago that was be-
fore “START” was pushed. There is also a resetStartTime() which makes the
current time zero. (We often put this in our start() method)
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
7
8 @Autonomous()
9 public class AutoTime extends OpMode {
10 enum State {
11 START,
12 SECOND_STEP,
13 DONE
14 }
15
16 ProgrammingBoard8 board = new ProgrammingBoard8();
17 State state = State.START;
18 double lastTime;
19
20 @Override
21 public void init() {
22 board.init(hardwareMap);
23 }
24
25 @Override
91
12. Dealing with State
32 @Override
33 public void loop() {
34 telemetry.addData("State", state);
35 telemetry.addData("Rumtime", getRuntime());
36 telemetry.addData("Time in State", getRuntime() - lastTime);
37 switch (state) {
38 case START:
39 if (getRuntime() >= 3.0) {
40 state = State.SECOND_STEP;
41 lastTime = getRuntime();
42 }
43 break;
44 case SECOND_STEP:
45 if (getRuntime() >= lastTime + 3.0) {
46 state = State.DONE;
47 lastTime = getRuntime();
48 }
49 break;
50 default:
51 telemetry.addData("Auto", "Finished");
52 }
53 }
54 }
92
12.4. Exercises
35 telemetry.addData("Rumtime", getRuntime());
36 telemetry.addData("Time in State", getRuntime() - lastTime);
In the first one, we are showing our total runtime. In the second, we show
our relative. This is done by keeping track of when we went into a state and
then showing the difference.
39 if (getRuntime() >= 3.0) {
12.4. Exercises
1. Make a program that ramps your motor to full speed (.25 for 250ms, .50
for 250ms, .75 for 250ms, 1.0) and goes at full speed until the touch
sensor is pressed.
2. Make a program that turns the motor until the distance sensor is less
than 10cm OR 5 seconds has passed and then turns the servo.
93
13. Arrays
An array can hold a fixed number of values of one type. Imagine that we had
four motors on our drive train. Instead of code like:
DcMotor motor1;
DcMotor motor2;
DcMotor motor3;
DcMotor motor4;
we could have:
DcMotor[] motors = new DcMotor[4]
This may seem interesting, but not all that useful until you start using other
things you have learned
void stopAllMotors(){
for(int i = 0; i < 4; i++){
motors[i].setPower(0.0);
}
}
This is done so often that Java has a cool shortcut for it. This is called the
for..each
void stopAllMotors(){
for(DcMotor motor : motors){
motor.setPower(0.0);
}
}
95
13. Arrays
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class ArrayOpMode extends OpMode {
8 String[] words = {"Zeroth", "First", "Second", "Third", "Fourth", "Fifth", "←֓
֒→ Infinity"};
9 int wordIndex;
10 double DELAY_SECS = 0.5;
11
12 double nextTime;
13
14 @Override
15 public void init() {
16 wordIndex = 0;
17 }
18
19 @Override
20 public void loop() {
21 if (nextTime < getRuntime()) {
22 wordIndex++;
23 if (wordIndex >= words.length) {
24 wordIndex = words.length - 1;
25 }
26 nextTime = getRuntime() + DELAY_SECS;
27 }
28 telemetry.addLine(words[wordIndex]);
29 }
30 }
13.1. ArrayList
This is all great, but an array can’t grow or shrink in size. For that there is
ArrayList.
96
13.2. Exercises
The angle brackets are new. That means the type is a “Generic”. What
that means is that you specify what type the class uses when you define your
object. So this is creating an ArrayList that holds integers. (It could be any
type including classes)
A few common methods:
items.add(4); // this adds this element to the end of the list
items.get(index); // returns the element at the index of the list (starts at 0)
items.clear(); // removes all items from list
items.size(); // returns the number of elements in the list
13.2. Exercises
1. Modify the opMode to send the chorus of a song you know at a fixed rate
on telemetry. Once it gets to the end, it should send it again.
97
14. Inheritance
In Java, when you create a class it always “inherits” from a class. If you don’t
use the extends keyword then it is inheriting from the Object class in Java. So
what does this really do?
Let’s start with a simple example and then we’ll show how it can be useful
in FTC. (We are going to put all of these in the org.firstinspires.ftc.teamcode
package (directory))
99
14. Inheritance
1 package org.firstinspires.ftc.teamcode;
2
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class SimpleInheritance extends OpMode {
8 SuperClass super_obj = new SuperClass();
9 ChildClass child_obj = new ChildClass();
10
11 @Override
12 public void init() {
13 telemetry.addData("Parent a", super_obj.a());
14 telemetry.addData("Parent b", super_obj.b());
15 telemetry.addData("Child a", child_obj.a());
16 telemetry.addData("Child b", child_obj.b());
17 telemetry.addData("Child c", child_obj.c());
18 }
19
20 @Override
21 public void loop() {
22
23 }
24 }
Can you guess what will show up on the telemetry screen? Try it. Were you
right?
You can think about inheritance as your new class con- ChildClass
taining all of the super class (often called “parent”) plus
its new stuff. This is shown in the diagram on the right.
If you have a class method with the exact same name
SuperClass
and parameters, then it will replace it. You should put
an @Override annotation on it so that everyone knows Object
that was intentional. (You actually don’t have to but it
is good practice to do it.)
So now there is a question. If you can get the contents of another class by
either deriving from it or having it as a class member, which should you do?
100
14.2. So why in the world would you use this?
This is typically called “isa” vs “hasa” (short for is a and has a) So you should
derive from it if your class is of that type, but include it if you simply have it
as a class member if it just just one of the things you have. Generally I like to
start having it as a class member and only derive from another class if that is
really clearly what I need to do.
It is time for the largest word in this book - polymorphism - that is. When you
are derived from another class you can be treated either as your class or your
superclass. This will be the longest example in the book (5 files!!), but I hope it
will help you take your programming to the next level.
We are going to make an OpMode that we can use to test out our wiring. (I
HIGHLY recommend this for your robot. Once you have it, you’ll find out how
useful it is over and over again to determine whether something is a software
or electrical/mechanical problem.
There is really only one new thing in this file but it shows up twice. It is the
keyword abstract.
5 abstract public class TestItem {
101
14. Inheritance
When abstract is before a class it means that no objects can be made of the
type of this class. (In other words it is only meant to have other classes derive
from it.)
16 abstract public void run(boolean on, Telemetry telemetry);
3 import com.qualcomm.robotcore.hardware.DcMotor;
4
5 import org.firstinspires.ftc.robotcore.external.Telemetry;
6
7 public class TestMotor extends TestItem {
8 private double speed;
9 private DcMotor motor;
10
102
14.2. So why in the world would you use this?
You’ll see here that this extends the TestItem class we made earlier.
12 super(description);
The super keyword refers to the class we derived from. Since this calls super()
that is calling our superclass constructor. This is considered the correct way
to implement a constructor in a child class.
Everything else in this file we have seen before
6 import org.firstinspires.ftc.robotcore.external.Telemetry;
7
8 public class TestAnalogInput extends TestItem {
9 private AnalogInput analogInput;
10 private double min;
11 private double max;
12
13 public TestAnalogInput(String description, AnalogInput analogInput, double min, ←֓
֒→ double max) {
14 super(description);
15 this.analogInput = analogInput;
16 this.min = min;
17 this.max = max;
18 }
19
20 @Override
21 public void run(boolean on, Telemetry telemetry) {
22 telemetry.addData("Voltage: ", analogInput.getVoltage());
23 telemetry.addData("In Range:",
24 Range.scale(analogInput.getVoltage(),
25 0, analogInput.getMaxVoltage(),
26 min, max));
27 }
28 }
This class should look very much like TestMotor to you. The one difference is
we always read from the analogInput instead of using an if statement. (A rule
103
14. Inheritance
I follow is you should only have to tell it to run a test if it causes something to
change)
Listing 14.7: ProgrammingBoard9.java
1 package org.firstinspires.ftc.teamcode.mechanisms;
2
3 import com.qualcomm.hardware.bosch.BNO055IMU;
4 import com.qualcomm.robotcore.hardware.AnalogInput;
5 import com.qualcomm.robotcore.hardware.ColorSensor;
6 import com.qualcomm.robotcore.hardware.DcMotor;
7 import com.qualcomm.robotcore.hardware.DigitalChannel;
8 import com.qualcomm.robotcore.hardware.DistanceSensor;
9 import com.qualcomm.robotcore.hardware.HardwareMap;
10 import com.qualcomm.robotcore.hardware.ServoImplEx;
11 import com.qualcomm.robotcore.util.Range;
12
13 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
14 import org.firstinspires.ftc.robotcore.external.navigation.AxesOrder;
15 import org.firstinspires.ftc.robotcore.external.navigation.AxesReference;
16 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
17 import org.firstinspires.ftc.robotcore.external.navigation.Orientation;
18
19 import java.util.ArrayList;
20
21 public class ProgrammingBoard9 {
22 private DigitalChannel touchSensor;
23 private DcMotor motor;
24 private double ticksPerRotation;
25 private ServoImplEx servo;
26 private AnalogInput pot;
27 private ColorSensor colorSensor;
28 private DistanceSensor distanceSensor;
29 private BNO055IMU imu;
30
104
14.2. So why in the world would you use this?
105
14. Inheritance
You’ll notice that this has a new method at the end of it.
84 public ArrayList<TestItem> getTests() {
Here we add our two new tests to it. Note that we had to have the new keyword
and this calls their constructor. Also note that if we had three motors, we
wouldn’t need 3 classes - we would just have 3 copies of the line tests.add(new
TestMotor.... with a different description, speed, and motor variable.
88 return tests;
106
14.2. So why in the world would you use this?
16 int testNum;
17
18 @Override
19 public void init() {
20 board.init(hardwareMap);
21 tests = board.getTests();
22 }
23
24 @Override
25 public void loop() {
26 // move up in the list of test
27 if (gamepad1.dpad_up && !wasUp) {
28 testNum--;
29 if (testNum < 0) {
30 testNum = tests.size() - 1;
31 }
32 }
33 wasUp = gamepad1.dpad_up;
34
35 // move down in the list of tests
36 if (gamepad1.dpad_down && !wasDown) {
37 testNum++;
38 if (testNum >= tests.size()) {
39 testNum = 0;
40 }
41 }
42 wasDown = gamepad1.dpad_down;
43
A few things to point out here that I hope will inspire you.
14 ArrayList<TestItem> tests;
15 boolean wasDown, wasUp;
16 int testNum;
107
14. Inheritance
Our list of tests as a member variable, wasDown and wasUp (like in section 12.1)
and testNum to keep track of which test number we are on. For wasDown and
wasUp, you see a shortcut where if you have multiple variables of the same type
you can define them together with a comma.
26 // move up in the list of test
27 if (gamepad1.dpad_up && !wasUp) {
28 testNum--;
29 if (testNum < 0) {
30 testNum = tests.size() - 1;
31 }
32 }
33 wasUp = gamepad1.dpad_up;
34
35 // move down in the list of tests
36 if (gamepad1.dpad_down && !wasDown) {
37 testNum++;
38 if (testNum >= tests.size()) {
39 testNum = 0;
40 }
41 }
42 wasDown = gamepad1.dpad_down;
This gets the test and then sends its description after “Test” with telemetry
so the driver station can see what test they will be running.
51 currTest.run(gamepad1.a, telemetry);
run() takes a boolean for whether to run the test or not. We just pass in
gamepad1.a directly here.
108
14.3. Exercises
14.3. Exercises
1. Add a test for the touchSensor. you’ll need a TestDigitalChannel class and
add it to the getTests() method in ProgrammingBoard. (No change needed
to OpMode)
2. Add a test for the servo, you’ll need a TestServo class - hint your construc-
tor probably needs an “on” value and an “off” value for the servo. You’ll
also need to add it to the getTests()
2
The reason we didn’t do this in the book is that you would likely only have the most recent
version of a mechanism in your code instead of multiple versions.
109
15. Javadoc
We talked earlier about a special kind of comment called a Javadoc. There are
several huge benefits from commenting this way. The FTC SDK is commented
in this way and that is what generates the documentation.
1. Android Studio will pick it up and give help to people using your classes
2. Autogenerating documentation that will amaze the judges
There are 3 places you can put a Javadoc comment.
1. Before your class
2. Before each class member
3. Before each class method
A Javadoc comment looks like this:
/**
* This is a javadoc comment
*/
If you write your class method declaration first, and then type in a /** above
it then it will automatically put @param for each parameter you have and a
@return if your method returns anything.
/**
* gets our imu heading
*
* @param angleUnit this determines the angle unit (degrees/radians) that it will ←֓
֒→ return in
* @return returns the current angle with the offset in the angleUnit specified
*/
private double getHeading(AngleUnit angleUnit) {
Orientation angles;
angles = imu.getAngularOrientation(AxesReference.INTRINSIC,
AxesOrder.ZYX,
angleUnit);
return angles.firstAngle;
}
111
15. Javadoc
/**
* This is the ProgrammingBoard class
*/
public class ProgrammingBoard{
...
After you have done this, in Android Studio go to Tools... Generate JavaDoc...
and you’ll see a dialog like this:
112
15.1. Exercises
3. Make sure you put it in its own directory because it creates a lot of files
15.1. Exercises
1. Add Javadoc comments to your ProgrammingBoard class
2. Add Javadoc comments to your TestMotor class (Because once you have
TestWiring all working for your robot you’ll want to show it to judges)
113
16. Finding things in FTC SDK
So far, I have told you about things that are in the FTC SDK. But there is lots
more that we haven’t looked at. So now let’s teach you how to go looking for
yourself.
Change the Project part to “Project” (It has been “An-
droid” and you’ll probably want to change it back after
this) Look under doc>javadoc and right click on “in-
dex.html”
You’ll probably notice that this looks just like the Javadoc you created in
chapter 15. Sure enough, that is what they use to create the documentation
for the FTC SDK as well
For example - Look through the All Classes until you get to Telemetry in the
lower left portion of the screen. Click on it. Then the main part of the browser
will have more information out about our old friend. Wait did you see that
there is a speak() method??
16.1. Exercise
1. Write an opMode that uses the telemetry.speak() method
115
16. Finding things in FTC SDK
116
17. A few other topics
This is a place for a few other topics that I thought were important to mention
but didn’t really fit anywhere else
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class MoreTelemetry extends OpMode {
8 @Override
9 public void init() {
10 telemetry.addData("Run time", "%0.2f", getRuntime());
11 }
12
13 @Override
14 public void loop() {
15 telemetry.addData("Right Joystick",
16 "x:%+0.2f y:% 0.2f", gamepad1.right_stick_x,
17 gamepad1.right_stick_y);
18
117
17. A few other topics
Here is our old friend addData but this time the string looks weird and there
is another parameter. The string is called a format string. It can have text and
values. Every value is started with a % sign and has how to show the number.
Below are the most common ones for FTC.
Conversion Description
’b’, ’B’ boolean - if the argument isn’t a
boolean, then it will show true unless
null
’d’ decimal - for integers
’f’ decimal number - for floating point
(float and double)
% show a literal ’%’ character
For f, you can give it a precision after the . which is the maximum number
of digits to show. If you want it to always show that number of digits (zero pad)
then put 0.2 for example.
15 telemetry.addData("Right Joystick",
16 "x:%+0.2f y:% 0.2f", gamepad1.right_stick_x,
17 gamepad1.right_stick_y);
Here is an example where we are showing more than one value on the same
line. The +is a flag saying to always show the sign (without it only shows the
sign if it is negative.) The space on the second one says to have a space if
positive instead of the positive sign. (The reason you might want this is so that
the numbers don’t jump as the negative sign comes in place.)
19 telemetry.addLine("Left joystick | ")
20 .addData("x", gamepad1.left_stick_x)
21 .addData("y", gamepad1.left_stick_y);
This has another way of showing multiple things per line by putting multiple
addData after an addLine
118
17.2. Math class
1 package org.firstinspires.ftc.teamcode;
2
3 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
4
You’ll notice that we have a constructor that takes in x and y and converts it
to polar coordinates.
The method called getAngle uses the AngleUnit to convert. As a bonus An-
gleUnit guarantees results to be normalized.
Some useful methods in this class: (all trig functions are in radians)
Math.abs(a) // take the absolute value
Math.acos(a) // take the arc cosine
Math.asin(a) // take the arc sin
Math.atan(a) // take the arc tan
Math.atan2(x, y) // This returns the angle theta from conversion of rectangular (x,y)←֓
֒→ to polar (r, theta)
Math.copySign(magnitude, sign) // return s the first argument with the sign (←֓
֒→ positive or negative) of the second
Math.cos(a) // take the cos
Math.hypot(x, y) // return the sqrt(x^2 + y^2)
Math.max(a, b) // returns the greater of a and b
Math.min(a, b) // returns the smaller of a and b
Math.random() // returns a double value with a positive sign greater than or equal ←֓
֒→ to 0.0 and less than 1.0
Math.signum(d) // returns -1.0 if d < 0, 0.0 if d == 0, 1.0 if d > 0
Math.sin(a) // take the sin
Math.sqrt(a) // take the square root
119
17. A few other topics
If your class says it implements an interface, but it doesn’t have all of the
methods in it then the compiler will give an error.
120
17.4. final
Another way to think about it is that a class can only inherit from one class,
but it can implement multiple interfaces.
My personal opinion is that you are probably better off using simple inheri-
tance unless you have something you need to do that requires multiple inter-
faces.
17.4. final
final is a keyword that can be applied to a variable, a method or a class.
final int THRESHOLD = 5;
• final applied to a method means that even if a new class extends this
class, this method cannot be overridden
final class A{
// methods and members
}
• final applied to a class means that no class can extend this one.
17.5. Exercises
1. Use the Polar class to make a new OpMode that reports the joysticks on
the gamepad in polar coordinates - show the angle in degree
121
17. A few other topics
2. Use formatting so the telemetry for exercise 1 always shows the positive
or negative sign and no decimals
3. Add the final keyword to various places to cause compiler errors so you
can see what they look like
122
A. Making your own Programming Board
1
Thanks, Eric!!
123
A. Making your own Programming Board
124
B. LinearOpMode
9 @Override
10 public void runOpMode(){
11 telemetry.addData("Hello","World");
12 telemetry.update();
13 waitForStart();
14 while (opModeIsActive()) {
15 }
16 }
17 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
125
B. LinearOpMode
6 @TeleOp()
7 public class HelloWorld extends OpMode {
8 @Override
9 public void init() {
10 telemetry.addData("Hello","World");
11 }
12
13 @Override
14 public void loop() {
15
16 }
17 }
126
B.2. Should you use it?
if(board.touchSensorPressed){
state = State.STOP;
}
break;
case State.STOP:
board.setMotorSpeed(0.0);
break;
...
The other large benefit is much of the sample code available online is written
this way.
2. Your code is all in one main control method instead of being broken out
into logical methods for the five methods in the OpMode. For both Op-
Mode and LinearOpMode you should use class methods to break your
code out into logical pieces to make it easier to read and maintain. Many
professional programmers get nervous whenever a method is longer than
fits on one screen.
3. You also are no longer protected from a loop taking too long so you don’t
respond in time to the driver station.
127
C. Sample Solutions
These are here if you get stuck, but they are not the only way to solve the
exercises.
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp
7 public class Exercise_1_1 extends OpMode {
8 @Override
9 public void init() {
10 telemetry.addData("Hello", "Alan");
11 }
12
13 @Override
14 public void loop() {
15
16 }
17 }
6 @Autonomous
7 public class Exercise_1_2 extends OpMode {
8 @Override
9 public void init() {
129
C. Sample Solutions
10 telemetry.addData("Hello", "Alan");
11 }
12
13 @Override
14 public void loop() {
15
16 }
17 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class Exercise_2_1 extends OpMode {
8 @Override
9 public void init() {
10 String myName = "Your Name";
11
12 telemetry.addData("Hello", myName);
13 }
14
15 @Override
16 public void loop() {
17
18 }
19 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class Exercise_2_2 extends OpMode {
8 @Override
130
C.3. Chapter 3 Solutions
13 telemetry.addData("Hello", myName);
14 telemetry.addData("Grade", grade);
15 }
16
17 @Override
18 public void loop() {
19
20 }
21 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class Exercise_3_1 extends OpMode {
8 @Override
9 public void init() {
10 }
11
12 @Override
13 public void loop() {
14 telemetry.addData("Right stick x", gamepad1.right_stick_x);
15 telemetry.addData("Right stick y", gamepad1.right_stick_y);
16 }
17 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
131
C. Sample Solutions
6 @TeleOp()
7 public class Exercise_3_2 extends OpMode {
8 @Override
9 public void init() {
10 }
11
12 @Override
13 public void loop() {
14 telemetry.addData("B button", gamepad1.b);
15 }
16 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class Exercise_3_3 extends OpMode {
8 @Override
9 public void init() {
10 }
11
12 @Override
13 public void loop() {
14 telemetry.addData("Diff left y and right y",
15 gamepad1.left_stick_y - gamepad1.right_stick_y);
16 }
17 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class Exercise_3_4 extends OpMode {
8 @Override
9 public void init() {
10 }
11
132
C.4. Chapter 4 Solutions
12 @Override
13 public void loop() {
14 telemetry.addData("sum triggers",
15 gamepad1.left_trigger + gamepad1.right_trigger);
16 }
17 }
13 @Override
14 public void loop() {
15 double fwdSpeed = gamepad1.left_stick_y;
16
17 if (!gamepad1.a) {
18 fwdSpeed *= 0.5;
19 }
20 telemetry.addData("Forward Speed", fwdSpeed);
21 }
22 }
133
C. Sample Solutions
8 @Override
9 public void init() {
10
11 }
12
13 @Override
14 public void loop() {
15 double ySpeed = gamepad1.left_stick_y;
16 double xSpeed = gamepad1.left_stick_x;
17
22 }
23 telemetry.addData("X Speed", xSpeed);
24 telemetry.addData("Y Speed", ySpeed);
25 }
26 }
134
C.5. Chapter 5 Solutions
20
21 @Override
22 public String toString() {
23 return "RobotLocation: angle (" + angle + ")";
24 }
25
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6
7 @TeleOp
8 public class UseRobotLocationOpMode_5_1 extends OpMode {
9 RobotLocation_5_1 robotLocation = new RobotLocation_5_1(0);
10
11 @Override
12 public void init() {
13 robotLocation.setAngle(0);
14 }
15
16 @Override
17 public void loop() {
18 if (gamepad1.a) {
19 robotLocation.turn(0.1);
20 } else if (gamepad1.b) {
21 robotLocation.turn(-0.1);
22 }
23 telemetry.addData("Location", robotLocation);
24 telemetry.addData("Heading", robotLocation.getHeading());
135
C. Sample Solutions
25 telemetry.addData("Angle", robotLocation.getAngle());
26 }
27 }
136
C.5. Chapter 5 Solutions
40 return x;
41 }
42
43 public void changeX(double change) {
44 x += change;
45 }
46
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6
7 @TeleOp
8 public class UseRobotLocationOpMode_5_2 extends OpMode {
9 RobotLocation_5_2 robotLocation = new RobotLocation_5_2(0);
10
11 @Override
12 public void init() {
13 robotLocation.setAngle(0);
14 }
15
16 @Override
17 public void loop() {
18 if (gamepad1.a) {
19 robotLocation.turn(0.1);
20 } else if (gamepad1.b) {
21 robotLocation.turn(-0.1);
22 }
23 if (gamepad1.dpad_left) {
24 robotLocation.changeX(-0.1);
25 } else if (gamepad1.dpad_right) {
26 robotLocation.changeX(0.1);
27 }
28 telemetry.addData("Location", robotLocation);
29 telemetry.addData("Heading", robotLocation.getHeading());
30 }
31 }
137
C. Sample Solutions
22 @Override
23 public String toString() {
24 return "RobotLocation: angle (" + angle + ") pos (" + x + "," + y + ")";
25 }
26
138
C.5. Chapter 5 Solutions
44 x += change;
45 }
46
47 public void setX(double x) {
48 this.x = x;
49 }
50
64 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6
7 @TeleOp
8 public class UseRobotLocationOpMode_5_3 extends OpMode {
9 RobotLocation_5_3 robotLocation = new RobotLocation_5_3(0);
10
11 @Override
12 public void init() {
13 robotLocation.setAngle(0);
14 }
15
16 @Override
17 public void loop() {
18 if (gamepad1.a) {
19 robotLocation.turn(0.1);
20 } else if (gamepad1.b) {
21 robotLocation.turn(-0.1);
139
C. Sample Solutions
22 }
23 if (gamepad1.dpad_left) {
24 robotLocation.changeX(-0.1);
25 } else if (gamepad1.dpad_right) {
26 robotLocation.changeX(0.1);
27 }
28 if (gamepad1.dpad_up) {
29 robotLocation.changeY(-0.1);
30 } else if (gamepad1.dpad_down) {
31 robotLocation.changeY(0.1);
32 }
33 telemetry.addData("Location", robotLocation);
34 telemetry.addData("Heading", robotLocation.getHeading());
35 }
36 }
140
C.6. Chapter 6 Solutions
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class TouchSensorOpMode_6_1 extends OpMode {
8 ProgrammingBoard_6_1 board = new ProgrammingBoard_6_1();
9
10 @Override
11 public void init() {
12 board.init(hardwareMap);
13 }
14
15 @Override
16 public void loop() {
17 telemetry.addData("Touch sensor Released", board.isTouchSensorReleased());
18 }
19 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class TouchSensorOpMode_6_2 extends OpMode {
8 ProgrammingBoard_6_1 board = new ProgrammingBoard_6_1();
9
10 @Override
11 public void init() {
12 board.init(hardwareMap);
13 }
14
15 @Override
16 public void loop() {
17 String touchSensorString = "Not Pressed";
18 if (board.isTouchSensorPressed()) {
19 touchSensorString = "Pressed";
20 }
21 telemetry.addData("Touch sensor", touchSensorString);
22 }
141
C. Sample Solutions
23 }
142
C.7. Chapter 7 Solutions
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5 import com.qualcomm.robotcore.hardware.DcMotor;
6
7 @TeleOp()
8 public class MotorOpMode_7_1 extends OpMode {
9 ProgrammingBoard_7_1 board = new ProgrammingBoard_7_1();
10
11 @Override
12 public void init() {
13 board.init(hardwareMap);
14 }
15
16 @Override
17 public void loop() {
18 double motorSpeed = gamepad1.left_stick_y;
19
20 board.setMotorSpeed(motorSpeed);
21 telemetry.addData("speed", motorSpeed);
22 if (gamepad1.a) {
23 board.setMotorZeroBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
24 telemetry.addData("Zero", "Brake");
25 } else if (gamepad1.b) {
26 board.setMotorZeroBehavior(DcMotor.ZeroPowerBehavior.FLOAT);
27 telemetry.addData("Zero", "Float");
28 }
29 }
30 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class MotorOpMode_7_2 extends OpMode {
8 ProgrammingBoard_7_1 board = new ProgrammingBoard_7_1();
9
10 @Override
11 public void init() {
143
C. Sample Solutions
12 board.init(hardwareMap);
13 }
14
15 double squareInputWithSign(double input) {
16 double output = input * input;
17 if (input < 0) {
18 output = output * -1;
19 }
20 return output;
21 }
22
23 @Override
24 public void loop() {
25 double motorSpeed = squareInputWithSign(gamepad1.left_stick_y);
26
27 board.setMotorSpeed(motorSpeed);
28 }
29 }
144
C.8. Chapter 8 Solutions
21 servo.setDirection(Servo.Direction.REVERSE);
22 servo.scaleRange(0.5, 1.0);
23 }
24
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard5;
7
8 @TeleOp()
9 public class ServoGamepadOpMode_8_2 extends OpMode {
10 ProgrammingBoard5 board = new ProgrammingBoard5();
11
12 @Override
13 public void init() {
14 board.init(hardwareMap);
15 }
16
17 @Override
18 public void loop() {
19 board.setServoPosition(gamepad1.left_trigger);
20 }
21 }
145
C. Sample Solutions
146
C.10. Chapter 10 Solutions
42
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class Exercise_9_2 extends OpMode {
8 ProgrammingBoard_9_1 board = new ProgrammingBoard_9_1();
9
10 @Override
11 public void init() {
12 board.init(hardwareMap);
13 }
14
15 @Override
16 public void loop() {
17 double potValue = board.getPotRange();
18
21 board.setServoPosition(potValue);
22 }
23 }
3 import com.qualcomm.robotcore.hardware.AnalogInput;
147
C. Sample Solutions
4 import com.qualcomm.robotcore.hardware.ColorSensor;
5 import com.qualcomm.robotcore.hardware.DcMotor;
6 import com.qualcomm.robotcore.hardware.DigitalChannel;
7 import com.qualcomm.robotcore.hardware.DistanceSensor;
8 import com.qualcomm.robotcore.hardware.HardwareMap;
9 import com.qualcomm.robotcore.hardware.Servo;
10 import com.qualcomm.robotcore.util.Range;
11
12 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
13
148
C.10. Chapter 10 Solutions
49 servo.setPosition(position);
50 }
51
52 public double getPotAngle() {
53 return Range.scale(pot.getVoltage(), 0, pot.getMaxVoltage(), 0, 270);
54 }
55
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
7
8 @TeleOp()
9 public class Exercise_10_1_OpMode extends OpMode {
10 ProgrammingBoard_10_1 board = new ProgrammingBoard_10_1();
11
12 @Override
13 public void init() {
14 board.init(hardwareMap);
15 }
16
17 @Override
18 public void loop() {
19 telemetry.addData("Amount blue", board.getAmountBlue());
20 telemetry.addData("Distance (CM)", board.getDistance(DistanceUnit.CM));
21 telemetry.addData("Distance (IN)", board.getDistance(DistanceUnit.INCH));
22 }
23 }
149
C. Sample Solutions
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
7
8 @TeleOp()
9 public class Exercise_10_2_OpMode extends OpMode {
10 ProgrammingBoard_10_1 board = new ProgrammingBoard_10_1();
11
12 @Override
13 public void init() {
14 board.init(hardwareMap);
15 }
16
17 @Override
18 public void loop() {
19 double distanceCM = board.getDistance(DistanceUnit.CM);
20 if (distanceCM < 10.0) {
21 board.setMotorSpeed(0.0);
22 } else {
23 board.setMotorSpeed(0.5);
24 }
25 telemetry.addData("Distance (CM)", distanceCM);
26 }
27 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
7 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
8
9 @TeleOp()
10 public class Exercise_11_1 extends OpMode {
150
C.11. Chapter 11 Solutions
13 @Override
14 public void init() {
15 board.init(hardwareMap);
16 }
17
18 @Override
19 public void loop() {
20 telemetry.addData("Our Heading (DEG)", board.getHeading(AngleUnit.DEGREES));
21 telemetry.addData("Our Heading (RAD)", board.getHeading(AngleUnit.RADIANS));
22 }
23 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5 import com.qualcomm.robotcore.util.Range;
6
7 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
8 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
9
10 @TeleOp()
11 public class Exercise_11_2 extends OpMode {
12 ProgrammingBoard8 board = new ProgrammingBoard8();
13
14 @Override
15 public void init() {
16 board.init(hardwareMap);
17 }
18
19 @Override
20 public void loop() {
21 double headingDegrees = board.getHeading(AngleUnit.DEGREES);
22 double motorSpeed = Range.scale(headingDegrees, -180, 180, -1.0, 1.0);
23
27 board.setMotorSpeed(motorSpeed);
28 }
29 }
151
C. Sample Solutions
6 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
7
8 @Autonomous()
9 public class Exercise_12_1 extends OpMode {
10 enum State {
11 START,
12 QUARTER_SPEED,
13 HALF_SPEED,
14 THREE_QUARTERS_SPEED,
15 FULL_SPEED,
16 DONE
17 }
18
19 ProgrammingBoard8 board = new ProgrammingBoard8();
20 State state = State.START;
21 double lastStepTime;
22
23 @Override
24 public void init() {
25 board.init(hardwareMap);
26 }
27
28 @Override
29 public void start() {
30 state = State.START;
31 }
32
33 @Override
34 public void loop() {
35 telemetry.addData("State", state);
36 switch (state) {
37 case START:
38 board.setMotorSpeed(0.250);
39 state = State.QUARTER_SPEED;
40 lastStepTime = getRuntime();
41 break;
152
C.12. Chapter 12 Solutions
42 case QUARTER_SPEED:
43 if (getRuntime() > lastStepTime + .250) {
44 board.setMotorSpeed(0.500);
45 state = State.HALF_SPEED;
46 lastStepTime = getRuntime();
47 }
48 break;
49 case HALF_SPEED:
50 if (getRuntime() > lastStepTime + .250) {
51 board.setMotorSpeed(0.750);
52 state = State.THREE_QUARTERS_SPEED;
53 lastStepTime = getRuntime();
54 }
55 break;
56 case THREE_QUARTERS_SPEED:
57 if (getRuntime() > lastStepTime + .250) {
58 board.setMotorSpeed(1.00);
59 state = State.FULL_SPEED;
60 }
61 break;
62 case FULL_SPEED:
63 if (board.isTouchSensorPressed()) {
64 board.setMotorSpeed(0.0);
65 state = State.DONE;
66 }
67 break;
68 default:
69 telemetry.addData("Auto", "Finished");
70 }
71 }
72 }
3 import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
4 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
5
6 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
7 import org.firstinspires.ftc.teamcode.mechanisms.ProgrammingBoard8;
8
9 @Autonomous()
10 public class Exercise_12_2 extends OpMode {
11 enum State {
153
C. Sample Solutions
12 START,
13 GO_UNTIL_DISTANCE,
14 TURN_SERVO,
15 DONE
16 }
17
22 @Override
23 public void init() {
24 board.init(hardwareMap);
25 state = State.START;
26 }
27
28 @Override
29 public void loop() {
30 telemetry.addData("State", state);
31 switch (state) {
32 case START:
33 board.setMotorSpeed(0.5);
34 board.setServoPosition(0.0);
35 resetStartTime();
36 state = State.GO_UNTIL_DISTANCE;
37 break;
38 case GO_UNTIL_DISTANCE:
39 if ((board.getDistance(DistanceUnit.CM) < 10) || (getRuntime() > 5.0)←֓
֒→ ) {
40 board.setMotorSpeed(0.0);
41 state = State.TURN_SERVO;
42 }
43 break;
44 case TURN_SERVO:
45 board.setServoPosition(0.5);
46 state = State.DONE;
47 break;
48 default:
49 telemetry.addData("Auto", "Finished");
50 }
51 }
52 }
154
C.13. Chapter 13 Solutions
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class Exercise_13_1 extends OpMode {
8 String[] lines = {
9 "Then he waddled away",
10 "(Waddle waddle)",
11 "Then he waddled away",
12 "(Waddle waddle waddle)",
13 "Then he waddled away",
14 "(Waddle waddle)",
15 "’Til the very next day",
16 "(Bum bum bum bum bum ba-dum)"};
17 int lineIndex;
18 double DELAY_SECS = 0.5;
19
20 double nextTime;
21
22 @Override
23 public void init() {
24 lineIndex = 0;
25 }
26
27 @Override
28 public void loop() {
29 if (nextTime < getRuntime()) {
30 lineIndex++;
31 if (lineIndex >= lines.length) {
32 lineIndex = lines.length - 1;
33 }
34 nextTime = getRuntime() + DELAY_SECS;
35 }
36 telemetry.addLine(lines[lineIndex]);
37 }
38 }
155
C. Sample Solutions
1 package org.firstinspires.ftc.teamcode.solutions;
2
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import java.util.ArrayList;
7
8 @TeleOp()
9 public class Exercise_13_2 extends OpMode {
10 ArrayList<String> lines = new ArrayList<>();
11
12 int lineIndex;
13 double DELAY_SECS = 0.5;
14
15 double nextTime;
16
17 @Override
18 public void init() {
19 lineIndex = 0;
20 lines.clear();
21 lines.add("Then he waddled away");
22 lines.add("(Waddle waddle)");
23 lines.add("Then he waddled away");
24 lines.add("(Waddle waddle waddle)");
25 lines.add("Then he waddled away");
26 lines.add("(Waddle waddle)");
27 lines.add("’Til the very next day");
28 lines.add("(Bum bum bum bum bum ba-dum)");
29 lines.add("");
30 }
31
32 @Override
33 public void loop() {
34 if (nextTime < getRuntime()) {
35 lineIndex++;
36 if (lineIndex >= lines.size()) {
37 lineIndex = 0;
38 }
39 nextTime = getRuntime() + DELAY_SECS;
40 }
41 telemetry.addLine(lines.get(lineIndex));
42 }
43 }
156
C.14. Chapter 14 Solutions
3 import com.qualcomm.hardware.bosch.BNO055IMU;
4 import com.qualcomm.robotcore.hardware.AnalogInput;
5 import com.qualcomm.robotcore.hardware.ColorSensor;
6 import com.qualcomm.robotcore.hardware.DcMotor;
7 import com.qualcomm.robotcore.hardware.DigitalChannel;
8 import com.qualcomm.robotcore.hardware.DistanceSensor;
9 import com.qualcomm.robotcore.hardware.HardwareMap;
10 import com.qualcomm.robotcore.hardware.Servo;
11 import com.qualcomm.robotcore.util.Range;
12
13 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
14 import org.firstinspires.ftc.robotcore.external.navigation.AxesOrder;
15 import org.firstinspires.ftc.robotcore.external.navigation.AxesReference;
16 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
17 import org.firstinspires.ftc.robotcore.external.navigation.Orientation;
18 import org.firstinspires.ftc.teamcode.mechanisms.TestAnalogInput;
157
C. Sample Solutions
19 import org.firstinspires.ftc.teamcode.mechanisms.TestItem;
20 import org.firstinspires.ftc.teamcode.mechanisms.TestMotor;
21
22 import java.util.ArrayList;
23
24 public class ProgrammingBoard_14_1 {
25 private DigitalChannel touchSensor;
26 private DcMotor motor;
27 private double ticksPerRotation;
28 private Servo servo;
29 private AnalogInput pot;
30 private ColorSensor colorSensor;
31 private DistanceSensor distanceSensor;
32 private BNO055IMU imu;
33
158
C.14. Chapter 14 Solutions
64 servo.setPosition(position);
65 }
66
67 public double getPotAngle() {
68 return Range.scale(pot.getVoltage(), 0, pot.getMaxVoltage(), 0, 270);
69 }
70
92 return tests;
93 }
94 }
3 import com.qualcomm.robotcore.hardware.Servo;
4
5 import org.firstinspires.ftc.robotcore.external.Telemetry;
6 import org.firstinspires.ftc.teamcode.mechanisms.TestItem;
7
159
C. Sample Solutions
12
20 @Override
21 public void run(boolean on, Telemetry telemetry) {
22 if (on) {
23 servo.setPosition(onValue);
24 } else {
25 servo.setPosition(offValue);
26 }
27 }
28 }
3 import com.qualcomm.hardware.bosch.BNO055IMU;
4 import com.qualcomm.robotcore.hardware.AnalogInput;
5 import com.qualcomm.robotcore.hardware.ColorSensor;
6 import com.qualcomm.robotcore.hardware.DcMotor;
7 import com.qualcomm.robotcore.hardware.DigitalChannel;
8 import com.qualcomm.robotcore.hardware.DistanceSensor;
9 import com.qualcomm.robotcore.hardware.HardwareMap;
10 import com.qualcomm.robotcore.hardware.Servo;
11 import com.qualcomm.robotcore.util.Range;
12
13 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
14 import org.firstinspires.ftc.robotcore.external.navigation.AxesOrder;
15 import org.firstinspires.ftc.robotcore.external.navigation.AxesReference;
16 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
17 import org.firstinspires.ftc.robotcore.external.navigation.Orientation;
18 import org.firstinspires.ftc.teamcode.mechanisms.TestAnalogInput;
19 import org.firstinspires.ftc.teamcode.mechanisms.TestItem;
20 import org.firstinspires.ftc.teamcode.mechanisms.TestMotor;
21
22 import java.util.ArrayList;
23
160
C.14. Chapter 14 Solutions
161
C. Sample Solutions
70
93 return tests;
94 }
95 }
3 import com.qualcomm.robotcore.hardware.DigitalChannel;
4 import com.qualcomm.robotcore.hardware.HardwareMap;
5
162
C.14. Chapter 14 Solutions
17 }
3 import com.qualcomm.robotcore.hardware.DcMotor;
4 import com.qualcomm.robotcore.hardware.HardwareMap;
5
3 import com.qualcomm.robotcore.hardware.HardwareMap;
4
163
C. Sample Solutions
11 }
12
3 import com.qualcomm.robotcore.hardware.HardwareMap;
4 import com.qualcomm.robotcore.hardware.Servo;
5
3 import com.qualcomm.robotcore.hardware.AnalogInput;
4 import com.qualcomm.robotcore.hardware.HardwareMap;
5 import com.qualcomm.robotcore.util.Range;
6
7 public class ProgrammingBoard_14_3_6 extends ProgrammingBoard_14_3_5 {
8 protected AnalogInput pot;
9
10 public void init(HardwareMap hwMap) {
11 pot = hwMap.get(AnalogInput.class, "pot");
12 }
13
164
C.14. Chapter 14 Solutions
17 }
3 import com.qualcomm.robotcore.hardware.ColorSensor;
4 import com.qualcomm.robotcore.hardware.DistanceSensor;
5 import com.qualcomm.robotcore.hardware.HardwareMap;
6
7 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
8
9 public class ProgrammingBoard_14_3_7 extends ProgrammingBoard_14_3_6 {
10 protected ColorSensor colorSensor;
11 protected DistanceSensor distanceSensor;
12
165
C. Sample Solutions
13
3 import org.firstinspires.ftc.teamcode.mechanisms.TestAnalogInput;
4 import org.firstinspires.ftc.teamcode.mechanisms.TestItem;
5 import org.firstinspires.ftc.teamcode.mechanisms.TestMotor;
6
7 import java.util.ArrayList;
8
9 public class ProgrammingBoard_14_3_9 extends ProgrammingBoard_14_3_8 {
10 public ArrayList<TestItem> getTests() {
11 ArrayList<TestItem> tests = new ArrayList<>();
12 tests.add(new TestMotor("PB Motor", 0.5, motor));
13 tests.add(new TestAnalogInput("PB Pot", pot, 0, 270));
14 return tests;
15 }
16 }
166
C.15. Chapter 15 Solutions
3 import com.qualcomm.hardware.bosch.BNO055IMU;
4 import com.qualcomm.robotcore.hardware.AnalogInput;
5 import com.qualcomm.robotcore.hardware.ColorSensor;
6 import com.qualcomm.robotcore.hardware.DcMotor;
7 import com.qualcomm.robotcore.hardware.DigitalChannel;
8 import com.qualcomm.robotcore.hardware.DistanceSensor;
9 import com.qualcomm.robotcore.hardware.HardwareMap;
10 import com.qualcomm.robotcore.hardware.Servo;
11 import com.qualcomm.robotcore.util.Range;
12
13 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
14 import org.firstinspires.ftc.robotcore.external.navigation.AxesOrder;
15 import org.firstinspires.ftc.robotcore.external.navigation.AxesReference;
16 import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
17 import org.firstinspires.ftc.robotcore.external.navigation.Orientation;
18 import org.firstinspires.ftc.teamcode.mechanisms.TestAnalogInput;
19 import org.firstinspires.ftc.teamcode.mechanisms.TestItem;
20 import org.firstinspires.ftc.teamcode.mechanisms.TestMotor;
21
22 import java.util.ArrayList;
23
24 public class ProgrammingBoard_15_1 {
25 private DigitalChannel touchSensor;
26 private DcMotor motor;
27 private double ticksPerRotation;
28 private Servo servo;
29 private AnalogInput pot;
30 private ColorSensor colorSensor;
31 private DistanceSensor distanceSensor;
32 private BNO055IMU imu;
33
34 /**
35 * This initializes our programming board and gets it ready for use.
36 * It MUST be called before any of the other methods
37 *
38 * @param hwMap the hardware map from the opMode
39 */
40 public void init(HardwareMap hwMap) {
41 touchSensor = hwMap.get(DigitalChannel.class, "touch_sensor");
42 touchSensor.setMode(DigitalChannel.Mode.INPUT);
43 motor = hwMap.get(DcMotor.class, "motor");
44 motor.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
45 ticksPerRotation = motor.getMotorType().getTicksPerRev();
46 servo = hwMap.get(Servo.class, "servo");
47 pot = hwMap.get(AnalogInput.class, "pot");
167
C. Sample Solutions
48
57 /**
58 * @return whether the touch sensor is pressed or not
59 */
60 public boolean isTouchSensorPressed() {
61 return !touchSensor.getState();
62 }
63
64 /**
65 * @param speed the speed (-1.0 to 1.0) where negative is backwards
66 */
67 public void setMotorSpeed(double speed) {
68 motor.setPower(speed);
69 }
70
71 /**
72 * @return returns the number of rotations from the encoder
73 */
74 public double getMotorRotations() {
75 return motor.getCurrentPosition() / ticksPerRotation;
76 }
77
78 /**
79 * @param position the position (0.0-1.0) for the servo
80 */
81 public void setServoPosition(double position) {
82 servo.setPosition(position);
83 }
84
85 /**
86 * @return the angle (0 - 270) the potentiometer is pointed to
87 */
88 public double getPotAngle() {
89 return Range.scale(pot.getVoltage(), 0, pot.getMaxVoltage(), 0, 270);
90 }
91
92 /**
168
C.15. Chapter 15 Solutions
100 /**
101 * @param du what units to return distance in
102 * @return distance seen by distance sensor
103 */
104
169
C. Sample Solutions
5 import org.firstinspires.ftc.robotcore.external.Telemetry;
6 import org.firstinspires.ftc.teamcode.mechanisms.TestItem;
7
8
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
170
C.17. Chapter 17 Solutions
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 @TeleOp()
7 public class Exercise_16_1 extends OpMode {
8 boolean wasA;
9
10 @Override
11 public void init() {
12 telemetry.speak("Initialized");
13 }
14
15 @Override
16 public void loop() {
17 if (gamepad1.a && !wasA) {
18 telemetry.speak("A button pressed");
19 }
20 wasA = gamepad1.a;
21 }
22 }
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
7 import org.firstinspires.ftc.teamcode.Polar;
8
9 @TeleOp()
10 public class Exercise_17_1 extends OpMode {
11 @Override
12 public void init() {
13
14 }
15
16 @Override
17 public void loop() {
18 Polar leftStick = new Polar(gamepad1.left_stick_x, -gamepad1.left_stick_y);
171
C. Sample Solutions
3 import com.qualcomm.robotcore.eventloop.opmode.OpMode;
4 import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
5
6 import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
7 import org.firstinspires.ftc.teamcode.Polar;
8
9 @TeleOp()
10 public class Exercise_17_2 extends OpMode {
11 @Override
12 public void init() {
13
14 }
15
16 @Override
17 public void loop() {
18 Polar leftStick = new Polar(gamepad1.left_stick_x, -gamepad1.left_stick_y);
19 Polar rightStick = new Polar(gamepad1.right_stick_x, -gamepad1.right_stick_y)←֓
֒→ ;
20
172
D. Credits
Thanks to the following people that provided feedback on earlier versions of
the book to make it better. If you have comments, please put them in at
https://github.com/alan412/LearnJavaForFTC/issues/new/choose
• Joshua
173