Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Now that we have our Control System all set up and ready to program it's time to get a full robot running, right?
While we will be getting motors moving and sensors sensing during this section, it's important that we first start small. In this section, we'll be working with a simple test bed as we breakdown how to program some of the components that can be connected to the Control Hub.
By tackling these components individually we'll be able to explore more of their capabilities, common uses, and discuss errors that may occur while working with a full robot.
As mentioned, during this section we will be focus first on the concept of testing. Why do you think testing might be important in robotics?
One of the best practices to get into the routine of is testing all your components individually when they are first received. That's where out test bed comes into play. For our test bed we will be sticking to the basics with our components connected directly to our Control Hub rather than something like a Servo Power Module or Expansion Hub. If desired, we could add some mechanical parts, such as a servo horn or wheel, to aid with visualizing our testing, but this is not required.
in this tutorial we'll be using our test bed to learn about programming basics, however it is highly encourage to maintain a test bed for future testing.
To create our test bed for this tutorial you will need the following. The names we used in our configuration are included:
1
Control Hub
2
Core Hex Motor
test_motor
3
Smart Robot Servo
test_servo
4
REV Touch Sensor
test_touch
5
Color Sensor V3
test_color
6
Battery
The design of a test bed depends on the use case and available resources. For instance, one of the design requirements for the test bed featured here was accessibility. Notice that the placement of the hardware components on the Extrusion allows for the actuators, sensors, and Control Hub to be removed or swapped out with ease.
Be sure to complete your configuration on the Driver Hub once you have assembled your test bed.
There are other minor, but important, design considerations to make for a test bed. For example, when adding an actuator to a test bed consider the following questions:
What level of constraint does the actuator need? One of the benefits of creating a test bed for motors, or other actuators, is that the motors can be properly constrained during the testing process. In this case providing basic motion support and constraint is valuable.
How will you be able to tell the behavior of the actuator? The example test bed uses a wheel with a zip tie to help users visualize the behavior of the motor. Tape or other markers can be used, as well.
The time has come to create our first OpMode. We want to make sure to choose a clear and unique name each time we make a program. This will help us to find it again later or to communicate with teammates who may also be driving the robot.
In the programming world, there are common naming conventions that have been established to denote variables, classes, functions, etc. OpModes share some similarities to classes, a program-code-template. Thus the naming convention for OpModes tends to follow the naming convention for classes, which has the first letter of every word is capitalized.
To start, let's take a look at the OnBot Java layout in the REV Hardware Client:
Create New OpMode - The plus sign button opens up a window to create a new OpMode.
Project Browser Pane - This pane shows all the Java project files on the Robot Controller.
Source Code Editing Pane - This is the main code editing area
Message Pane - This pane provides information on the success or failure of code builds, as well as where to check for errors after attemping a build
Build Everything - Builds ALL of the .java files on a Robot Controller.
Select the Create New OpMode button. This will open the New File window.
There are a lot of things we can modify while setting up our OpMode, such as the name, sample in use, and kind of OpMode. For Hello Robot, use the following settings for reference:
File Name: HelloRobot_TeleOp
Sample: BlankLinearOpMode
OpMode Type: TeleOp
Setup for Configured Hardware: on
Setup Code for Configured Hardware is an incredibly useful tool that allows for OnBot to help create the hardwareMap based on the current active configuration!
Your intended configuration file should be active BEFORE creating an OpMode using this feature.
Once the proper settings have been choose, select "OK" to create the OpMode!
There are a lot of things we can modify while setting up our OpMode, such as the name, sample in use, and kind of OpMode. For Hello Robot, use the following settings for reference:
File Location: org/firstinspires/ftc/teamcode
File Type: Blank OpMode - Linear OpMode
File Name: HelloRobot_TeleOp.java
OpMode Type: TeleOp
Add generated code for configured hardware: on
Note: There is a known bug in v10.3 preventing the "Add generated code for configured hardware" option from working as expected.
The following will need to be manually added to match Hello Robot's tutorial:
Sample Codes:
In the 10.3 version of the Robot Controller App, samples and blank templates have been separated to make navigation easier.
To use a sample code change "File Type" to "Example OpMode" then select the desired sample from the dropdown. This will autofill the "File Name" and "OpMode type", but will disable the option to "Add generated code for configured hardware".







A servo is a form of actuator, or a device designed for moving. With a typical servo, you can specify a target position. The servo will turn its motor shaft to move to the target position, and then maintain that position, even if moderate forces are applied to try and disturb its position.
For Hello Robot we will be using the , which is able to switch between a continuous and angular mode.
Continuous mode allows for the servo to rotate a full 360°, either direction, indefinitely similar to a standard motor.
Angular mode sets the servo to move to specified positions within a 270° range of motion.
Let's take a look at how to program our servo while it is on angular mode:
While most common servos have a range of 180° for motion, the Smart Robot Servo has a range of 270° due to its ability to switch between modes. When programming this means our 0 and 1 position might be a little different than what you'd expect.
Looking at the image above we can see that on default when asking our servo to move to its position 0 it will be at -135° . On the opposite end, moving to its position 1 takes our servo to +135° . Therefore if we wanted to return to 0° we would need to program it to move to its position 0.5.
A servo horn attachment connected to your Smart Robot Servo may effect where 0° appears. We recommend using a SRS programmer to set the servo to zero before adding attachments. This may also be done using the code learned in this section!
Let's review quick our basic positions:
Based on what we've learned so far, think about the follow two questions:
If we wanted our servo to move to -67.5° what position would we program it move to?
If we have programmed our servo to move to position 0.7, what would that equal in degrees?
In the next few sections, we will be learning to program our servo to first move automatically to different requested positions then in response to our gamepad's input.
Below is a sneak peek of our final full code:
0
-135°
0.5
0°
1
135°
@TeleOp
public class HelloRobot_TeleOp extends LinearOpMode {
private Blinker control_Hub;
private DcMotor test_motor;
private Servo test_servo;
private TouchSensor test_touch;
@Override
public void runOpMode() {
control_Hub = hardwareMap.get(Blinker.class, "Control Hub");
test_motor = hardwareMap.get(DcMotor.class, "test_motor");
test_servo = hardwareMap.get(Servo.class, "test_servo");
test_touch = hardwareMap.get(TouchSensor.class, "test_touch");
test_servo.setPosition(0);
telemetry.addData("Status", "Initialized");
telemetry.update();
// Wait for the game to start (driver presses PLAY)
waitForStart();
// run until the end of the match (driver presses STOP)
while (opModeIsActive()) {
if (gamepad1.y){
//move to position 0
test_servo.setPosition(0);
} else if (gamepad1.x || gamepad1.b) {
//move to position 0.5
test_servo.setPosition(0.5);
} else if (gamepad1.a) {
//move to position 1
test_servo.setPosition(1);
}
telemetry.addData("Servo Position", test_servo.getPosition());
telemetry.addData("Status", "Running");
telemetry.update();
}
}
Let's start by getting a motor spinning by adding the line test_motor.setPower(1);
while (opModeIsActive()) {
test_motor.setPower(1);
telemetry.addData("Status", "Running");
telemetry.update();
}Select Build Everything to build the code.
Try running this OpMode on the test bed and consider the following questions:
How fast is the motor running?
What happens if you change the power from 1 to 5? What about 100?
What happens if you change the power from 1 to 0.3?
This is a good time to experiment with different values to see how our motor reacts. You might notice that setting our power to 5 or even 100 does not make the motor spin any fast than when set to 1. But setting our power to 0.3 significantly slows our motor's speed, right?
Now what happens if you change the power from 1 to -1?
From our perspective, a power level of 1 probably doesn't sound very strong. However, to our robot the power being set to 1 translates to the motor running at 100%. That would mean setting the power to 0.3 requests the motor to spin at 30% of power.
When we set our power to a negative power, the motor is told to reverse direction while maintaining that power. So if we set our power to -1 then our motor will still run at 100%, but in the opposite direction than when set to 1.
We are going to add several telemetry sections within our program, but let's start by having our robot tell us how much red, green, or blue it sees when looking at an object with our Color Sensor.
For each telemetry line we are specifying first the label to be displayed on the Driver Hub's screen, "Red", "Green", or "Blue". Then we are setting our output to only be 3 decimal places using "%.3f".
Finally we ask the color sensor to report the amount seen of each color using colors.red, colors.green, or colors.blue.
To add telemetry for hue, saturation, and value we will be using the class JavaUtil and matching method for the information we wish to add: colorToHue, colorToSaturation, or colorToValue.
Because we are using NormalizedRGBA for our class of normalized color values, we need to take an extra step to be able to collect HSV components. By calling toColor() we are converting from a "FTC SDK RGBA Color Object" to an "Android HSV Color Object".
Similarly to our individual colors, we use "%.3f" to show 3 decimal places for the value reported. While we can add this to Hue as well, this value should report as a much larger whole number. We'll look at this more closely in the next section!
Save your OpMode and give it a try! How do the values change depending on what color object the sensor is looking at?
While working on your code, you may have noticed something called "alpha". The alpha value of a surface tells how transparent or opaque it may be.
Using a similar method as before, we can add a telemetry call for the alpha value to see on our Driver Hub.
Here is how our OnBot Java code should look after setting up our color sensor to collect all the information!
Modify your OpMode to add the motor related code. For now your completed servo code can be commented out. You may alternatively choose to create a second program.
Just like servos, a motor is a form of actuator. You may picture a dozen different things when you think of a motor, from those used to spin the wheels of a car to the large turbines that allow a plane to fly. For our robots, we will be focusing on DC motors. These are a type of electrical motor that use direct current, or DC, to rotate and produce the mechanical force needed to move an attached mechanism.
Most standard motors are able to continuously rotate in either direction with an adjustable speed or power. Some motors may also include a built in encoder, which allows them to move to a specified position, similar to a servo, or to collect data like the number of completed rotations!
In the next few sections, we will be learning to program our motor to first move automatically in different directions then in response to our gamepad inputs. In our final section we will take a look at using telemetry with our motor's built in encoder.
Below is a sneak peek of our full code:
Recall that telemetry is the process of collecting and transmitting data. There is a lot of useful information our motors could send back to us, but to start let's have it output the power based on the joystick's movement.
Similar to our servo program, let's add a new line of telemetry.addData();.
This time we are going to set our string as "Motor Power" and after our comma will use test_motor.getPower()
While a touch sensor features a physical switch to gather information, a color sensor makes use of reflected light. By doing so it collects different data to determine how much light it is seeing, the distance to a surface, and of course what color is in front of it.
For our robot we're going to focus on a few key components: hue, saturation, and value. With these we can use something known as the HSV color model to have the robot translate what its seeing into a recognizable color.
HSV is a form of a cylindrical RGB color model used to do things like create color pickers for digital painting programs, to edit photos, and for programming vision code.
Hue, saturation, and value all will play a part in helping our robot tell us what color it detects and allow us to make adjustments for something like a uniquely colored game piece!
Before we tackle colors, let's start with having our robot use the color sensor to tell us how much light is being reflected.
To do this we need to ask our color sensor to act as a light sensor, specifically an .
For this use case, we will use getLightDetected() to have the sensor report the amount of light detected in a range of 0-1.
Time to test your program to see what your color sensor detects! While testing think about the following questions:
Is the number higher when less or more light is detected?
What happens when the color sensor looks at different color surfaces?
Does the value change when turning the color sensor's LED light on or off?
Does the value change if there is a shadow or if the lighting in the room changes?
When using the option to "" while creating a new OpMode, the color sensor will be established similar to the following:
However, for this tutorial we want to set our color sensor up as a NormalizedColorSensor.
Color Normalization is another technique within vision programming intended to help compensate for differences caused by lighting and shadows when looking at colors. This also affects shades of a color. For example, there are a ton of different shades of blue, such as cyan, navy, and aquamarine, but to our robot these will all be referenced as blue.
With our color sensor now set for normalized colors, we'll add a call for NormalizedRGBA colors before we add telemetry for each individually.
Now we're ready to collect more data from our color sensor!
In the previous section you learned how to set the motor to run at a specific power level in a specific direction. However, in most applications, it will be necessary to control the motor with a gamepad, to easily change the direction or power level of a mechanism.
We are able to use a button to set the motor to a specific power or we can program it so it changes based on the direction of one of the gamepad's joysticks!
Using our existing test_motor.setPower(1); we can replace the value of 1 with this.gamepad1.left_stick_y;
Now the power of our motor will be set in response to the position of the gamepad's left joystick as it along the y-axis!
Build your OpMode and test it out with your gamepad! Think about the following questions while testing:
What happens when you move the left joystick up a small amount versus a large amount?
What happens if you move the joystick to the left or right along the X-axis?
What happens if you move the joystick at a diagonal or rotate it 360 degrees?
You may notice that when moving along the X-axis nothing happens at the moment. However, once the joystick hits an angle near the Y-axis vertices the motor may start to jitter and spin.
When you tested your program, did the motor spin the expected direction while moving the joystick up or down?
In the FTC SDK for most controllers the Y value of a joystick ranges from -1 when a joystick is in its topmost position, to +1 when a joystick is in its bottommost position.
That may be a little confusing to control, but we can add a negative symbol, or negation operator, to the line of code to change the direction of the motor in relation to the gamepad.
telemetry.addData("Red", "%.3f", colors.red);
telemetry.addData("Green", "%.3f", colors.green);
telemetry.addData("Blue", "%.3f", colors.blue);telemetry.addData("Hue", JavaUtil.colorToHue(colors.toColor()));
telemetry.addData("Saturation", "%.3f", JavaUtil.colorToSaturation(colors.toColor()));
telemetry.addData("Value", "%.3f", JavaUtil.colorToValue(colors.toColor()));telemetry.addData("Alpha", "%.3f", colors.alpha);@TeleOp
public class HelloRobot_ColorSensor extends LinearOpMode {
private NormalizedColorSensor test_color;
@Override
public void runOpMode() {
test_color = hardwareMap.get(NormalizedColorSensor.class, "test_color");
waitForStart();
while (opModeIsActive()) {
telemetry.addData("Light Detected", ((OpticalDistanceSensor) test_color).getLightDetected());
NormalizedRGBA colors = test_color.getNormalizedColors();
//Determining the amount of red, green, and blue
telemetry.addData("Red", "%.3f", colors.red);
telemetry.addData("Green", "%.3f", colors.green);
telemetry.addData("Blue", "%.3f", colors.blue);
//Determining HSV and alpha
telemetry.addData("Hue", JavaUtil.colorToHue(colors.toColor()));
telemetry.addData("Saturation", "%.3f", JavaUtil.colorToSaturation(colors.toColor()));
telemetry.addData("Value", "%.3f", JavaUtil.colorToValue(colors.toColor()));
telemetry.addData("Alpha", "%.3f", colors.alpha);
telemetry.update();
}
}
} @Override
public void runOpMode() {
control_Hub = hardwareMap.get(Blinker.class, "Control Hub");
test_motor = hardwareMap.get(DcMotor.class, "test_motor");
test_servo = hardwareMap.get(Servo.class, "test_servo");
test_touch = hardwareMap.get(TouchSensor.class, "test_touch");
test_servo.setPosition(0);
telemetry.addData("Status", "Initialized");
telemetry.update();
// Wait for the game to start (driver presses PLAY)
waitForStart();
// run until the end of the match (driver presses STOP)
while (opModeIsActive()) {
test_motor.setPower(-this.gamepad1.left_stick_y);
telemetry.addData("Motor Power", test_motor.getPower());
telemetry.addData("Status", "Running");
telemetry.update();
}
}while (opModeIsActive()) {
test_motor.setPower(- this.gamepad1.left_stick_y);
telemetry.addData();
telemetry.addData("Status", "Running");
telemetry.update();
}while (opModeIsActive()) {
test_motor.setPower(- this.gamepad1.left_stick_y);
telemetry.addData("Motor Power", test_motor.getPower());
telemetry.addData("Status", "Running");
telemetry.update();
} @Override
public void runOpMode() {
control_Hub = hardwareMap.get(Blinker.class, "Control Hub");
test_motor = hardwareMap.get(DcMotor.class, "test_motor");
test_servo = hardwareMap.get(Servo.class, "test_servo");
test_touch = hardwareMap.get(TouchSensor.class, "test_touch");
test_servo.setPosition(0);
telemetry.addData("Status", "Initialized");
telemetry.update();
// Wait for the game to start (driver presses PLAY)
waitForStart();
// run until the end of the match (driver presses STOP)
while (opModeIsActive()) {
test_motor.setPower(-this.gamepad1.left_stick_y);
telemetry.addData("Motor Power", test_motor.getPower());
telemetry.addData("Status", "Running");
telemetry.update();
}
}// run until the end of the match (driver presses STOP)
while (opModeIsActive()) {
test_motor.setPower(this.gamepad1.left_stick_y);
telemetry.addData("Status", "Running");
telemetry.update();
}// run until the end of the match (driver presses STOP)
while (opModeIsActive()) {
test_motor.setPower(-this.gamepad1.left_stick_y);
telemetry.addData("Status", "Running");
telemetry.update();
} while (opModeIsActive()) {
telemetry.addData("Light Detected", ((OpticalDistanceSensor) test_color).getLightDetected());
telemetry.update();
}public class HelloRobot_ColorSensor extends LinearOpMode {
private ColorSensor test_color;
@Override
public void runOpMode() {
test_color = hardwareMap.get(ColorSensor.class, "test_color");public class HelloRobot_ColorSensor extends LinearOpMode {
private NormalizedColorSensor test_color;
@Override
public void runOpMode() {
test_color = hardwareMap.get(NormalizedColorSensor.class, "test_color");while (opModeIsActive()) {
telemetry.addData("Light Detected", ((OpticalDistanceSensor) test_color).getLightDetected());
NormalizedRGBA colors = test_color.getNormalizedColors();
telemetry.update();
}Telemetry is the process of collecting and transmitting data. In robotics ,telemetry is used to output internal data from the actuators and sensors to the Driver Hub. It is a way for the robot to communicate back to you the programmer what the robot thinks its doing or seeing. This information can then be used to improve your code, make adjustments to a mechanism, or to strategize when driving around the field if competing.
For OnBot Java we can use the following to request the servo's position:
test_servo.getPosition();We already have a couple lines of telemetry within our code to let us know what state our program is in: "Initialized" or "Running". Below our if/else if statement we can add another telemetry.addData();.
First, we need to add a "string" or the text that we want to appear on our Driver Hub. For now we will enter this as "Servo Position". After our comma, we will add the data we want outputted to the Driver Hub, which is test_servo.getPosition(); .
while (opModeIsActive()) {
if (gamepad1.y){
//move to position 0
test_servo.setPosition(0);
} else if (gamepad1.x || gamepad1.b) {
//move to position 0.5
test_servo.setPosition(0.5);
} else if (gamepad1.a) {
//move to position 1
test_servo.setPosition(1);
telemetry.addData("Servo Position", test_servo.getPosition());
telemetry.addData("Status", "Running");
telemetry.update();
}Add the line test_servo.setPosition(1); to the OpMode while loop.
while (opModeIsActive()) {
test_servo.setPosition(1);
telemetry.addData("Status", "Running");
telemetry.update();
}Select Build Everything to build the code.
Let's give our program a try. Take a moment to observe what happens.
When running our program for the first time, we should have seen our servo move itself to position 1 and maintain that position. But what happens if we run it again? Does the servo move?
The intent of thetest_servo.setPosition();is to set the position of the servo. If the servo is already in the set position when a code is run, it will not change positions. Lets try adding the line test_servo.setPosition(0).
In this case, we do not want our servo to reset to 0 every time our code repeats. Because of this where do you think we would add this line?
Recall when we discussed the different sections of our OpMode during Programming Essentials. Since we only want our servo to reset ONCE we will request it do so during the initialization process when the code is first activated, but before play is pressed.
public void runOpMode() {
control_Hub = hardwareMap.get(Blinker.class, "Control Hub");
test_motor = hardwareMap.get(DcMotor.class, "test_motor");
test_servo = hardwareMap.get(Servo.class, "test_servo");
test_touch = hardwareMap.get(TouchSensor.class, "test_touch");
test_servo.setPosition(0);
telemetry.addData("Status", "Initialized");
telemetry.update();
// Wait for the game to start (driver presses PLAY)
waitForStart();
// run until the end of the match (driver presses STOP)
while (opModeIsActive()) {
test_servo.setPosition(1);
telemetry.addData("Status", "Running");
telemetry.update();
}
}
}Try running this op mode on the test bed and consider the following question:
What is different from the previous run?
In many applications starting the servo in a known state, like at position zero, is beneficial to the operation of a mechanism. Setting the servo to the known state in the initialization ensures it is in the correct position when the OpMode runs.
Take a moment to think about where setting the servo to a known state during initialization may be helpful before moving to the next section!
This section applies to the use of the REV Touch Sensor or Limit Switch. Requirements may vary when using other 3rd party touch sensors.
The REV Touch Sensor must be configured to digital port 1, 3, 5, or 7.
Let's start by breaking down how a touch sensor works at its core!
The information collected by a touch sensor comes in two states, also known as binary states. This information is perfect to use with a conditional statement like an if/else statement.
The line test_touch.isPressed(); collects the binary TRUE/FALSE state from the touch sensor and acts as the condition for the if/else statement.
if (test_touch.isPressed()){
//Touch Sensor is pressed
} else {
//Touch Sensor is not pressed
}The code above highlights the basics structure of the if/elsestatement for a touch sensor. We could read this line of code as "If the touch sensor is pressed do ____, else if the touch sensor is not pressed do _____."
So with this in mind:
Touch sensor pressed = true
Touch sensor NOT pressed = false
It's always helpful for us to be able to see what the robot thinks its doing on our Driver Hub's screen. To do this let's request the robot shares some telemetry data while our program is active.
Within our if/else statement we'll add a telemetry.addData for whether the touch sensor is pressed or not.
if (test_touch.isPressed()){
//Touch Sensor is pressed.
telemetry.addData("Touch Sensor", "Is Pressed");
} else {
//Touch Sensor is not pressed
telemetry.addData("Touch Sensor", "Is Not Pressed");
}How our robot displays this data back to our Driver Hub is up to us to define. In this case we would see something similar to the following when the touch sensor is pressed:
The blue text within our quotation marks controls what will show on the Driver Hub. With that in mind we could have the robot say "Hello World!" when the button is pressed:
To finish our program before testing we need to add a telemetry.update(); after our if/else statement to request our robot updates the telemetry each time it loops:
// Wait for the game to start (driver presses PLAY)
waitForStart();
// run until the end of the match (driver presses STOP)
while (opModeIsActive()) {
if (test_touch.isPressed()){
//Touch Sensor is pressed.
telemetry.addData("Touch Sensor", "Is Pressed");
} else {
//Touch Sensor is not pressed
telemetry.addData("Touch Sensor", "Is Not Pressed");
}
telemetry.update();
}At the moment, our robot does not have any senses to help navigate the world around it like you might. However, that's the key advantage to adding sensors to our design.
For the touch sensor, one of the most common uses is for it to act as a limit switch. This will help the robot know when it needs to halt the movement of a mechanism, like an arm or lift, that's at its limit similar to how your nerves help to tell your brain to do the same.
We can test this idea by adding on to our existing if/else statement. This time we are going to ask our motor to move until our sensor is pressed:
if (test_touch.isPressed()){
//Touch Sensor is pressed
test_motor.setPower(0);
telemetry.addData("Touch Sensor", "Is Pressed");
} else {
//Touch Sensor is not pressed
test_motor.setPower(0.3);
telemetry.addData("Touch Sensor", "Is Not Pressed");
}Test it out! What happens when you test your program?
You'll learn more about how to use this with a completed arm in Part 2: Robot Control!
In the above example the if/else is checking first for if the touch sensor is pressed. The full statement could be read as "If the touch sensor is pressed set the motor's power to 0 else, if it is not pressed, set the power to 0.3". This statement can be reversed by adding a ! before test_touch.isPressed().
if (!test_touch.isPressed()){
//Touch Sensor is not pressed
test_motor.setPower(0.3);
telemetry.addData("Touch Sensor", "Is Not Pressed");
} else {
//Touch Sensor is pressed
test_motor.setPower(0);
telemetry.addData("Touch Sensor", "Is Pressed");
}In OnBot Java the operator ! tells the code to look for the opposite or to "not" be what is being called. So in this instance our if/else statement is checking if the touch sensor is NOT pressed first.
Give it a try!
Having our robot able to rotate the servo automatically can be incredibly useful, especially when writing an autonomous program, but what if I want to control the positions with my gamepad?
Let's take a look at how we can add input commands to our code!
For this example the known state will stay at position 0, so that after initialization the servo will be a the -135 degree position of the servo range. The following list shows what buttons correspond with which servo position:
One of the most common logic statements used in programming is an if/else statement, also known as an if/then statement. In its most simple format we will be asking our robot to check IF something is happening and if the answer is yes, or true in our robot's mind, THEN it will DO what has been asked.
During this section we are going to be asking "If the Y button is pressed on our controller then move our servo to position 0."
If our servo will move to position 0 when the previous statement is TRUE, what do expect to happen when the answer is FALSE (or the Y button is not pressed)?
An if/else if statement takes in multiple different conditional statements. If the first conditional statement is found to be false then the second conditional state is analyzed.
Let's add to our existing logic statement the ability to move our servo to position 1 when A is pressed on our controller. Give it a try first before revealing the answer below!
How would our full logic statement be read once our new blocks are added?
If you have not already, test the code we have written thus far! The previous test_servo.setPosition(1);should be removed if it has not already.
What happens when both buttons are pressed at the same time?
We've previously added our ability to move to position 0 and 1, but what about 0.5?
You may have noticed in our gamepad chart at the beginning of this section that we are going to have two buttons able to move our servo to position 0.5. This is so we can practice using a logical operator.
In OnBot Java, || means "or" allowing the robot to check if one of two buttons things are true. In this case, it will check if the x OR b button are pressed on the gamepad.
Click to Build Everything and give your program a try!
There are three different paths in this if/else if statement. If the first conditional statement is true (the Y button is pressed) the servo moves to code position 0 and the other conditional statements are ignored.
If the first condition is false (the Y button is not pressed) the second condition is analyzed. This means the order we add our pathways DOES matter. If X and A are pressed at the same time, the robot will will try to prioritize the X button first.
We've asked our robot to gather a lot of data with the color sensor. Now let's have it use that information to output an actual color name rather than just a value!
To determine the final color we will be using the reported hue value from JavaUtil.colorToHue(colors.toColor()). Because we'll be using hue repeatedly, let's establish it as a variable to help keep our code clean.
First we will set up "hue" as a number data type during initialization:
Next, we'll define "hue" while our opModeIsActive using the same method as before:
With our variable added, our current code should appear as follows:
Recall when we learned about using
Let's first set up the skeleton of our if/else statement for determining different colors:
Each check will be for a certain color that is within the specified range. For example, a color that's hue is between 90-149 should appear as green.
Within each statement let's add a telemetry.addData and the color for that range:
The exact hue values may need to be adjusted slightly, but those used above are based on the default conversion of HSV to RGB when using hue to identify color.
You'll notice that "red" is detected for values under 30 and above 350. This is intentional as red is the beginning and end of the RGB spectrum!
Build your OpMode and give it a try! You can adjust the values as you need to better reflect the colors available or changes due to lighting in the room.
Button
Degree Position
Code Position
Y/Triangle
-135
0
X/Square
0
0.5
B/Circle
0
0.5
A/Cross
135
1
if (gamepad1.y){
test_servo.setPosition(0);
}if (gamepad1.y){
test_servo.setPosition(0);
} else if () {
}if (gamepad1.y){
test_servo.setPosition(0);
} else if (gamepad1.a) {
test_servo.setPosition(1);
}if (gamepad1.y){
//move to position 0
test_servo.setPosition(0);
} else if (gamepad1.x || gamepad1.b) {
//move to position 0.5
test_servo.setPosition(0.5);
} else if (gamepad1.a) {
//move to position 1
test_servo.setPosition(1);
}public void runOpMode() {
control_Hub = hardwareMap.get(Blinker.class, "Control Hub");
test_motor = hardwareMap.get(DcMotor.class, "test_motor");
test_servo = hardwareMap.get(Servo.class, "test_servo");
test_touch = hardwareMap.get(TouchSensor.class, "test_touch");
test_servo.setPosition(0);
telemetry.addData("Status", "Initialized");
telemetry.update();
// Wait for the game to start (driver presses PLAY)
waitForStart();
// run until the end of the match (driver presses STOP)
while (opModeIsActive()) {
if (gamepad1.y){
//move to position 0
test_servo.setPosition(0);
} else if (gamepad1.x || gamepad1.b) {
//move to position 0.5
test_servo.setPosition(0.5);
} else if (gamepad1.a) {
//move to position 1
test_servo.setPosition(1);
}
telemetry.addData("Status", "Running");
telemetry.update();
}
}
}@TeleOp
public class HelloRobot_ColorSensor extends LinearOpMode {
private NormalizedColorSensor test_color;
double hue; // <------ New codewhile (opModeIsActive()) {
telemetry.addData("Light Detected", ((OpticalDistanceSensor) test_color).getLightDetected());
NormalizedRGBA colors = test_color.getNormalizedColors();
hue = JavaUtil.colorToHue(colors.toColor()); // <------ New code@TeleOp
public class HelloRobot_ColorSensor extends LinearOpMode {
private NormalizedColorSensor test_color;
double hue; // <------ New code
@Override
public void runOpMode() {
test_color = hardwareMap.get(NormalizedColorSensor.class, "test_color");
waitForStart();
while (opModeIsActive()) {
telemetry.addData("Light Detected", ((OpticalDistanceSensor) test_color).getLightDetected());
NormalizedRGBA colors = test_color.getNormalizedColors();
hue = JavaUtil.colorToHue(colors.toColor()); // <------ New code
//Determining the amount of red, green, and blue
telemetry.addData("Red", "%.3f", colors.red);
telemetry.addData("Green", "%.3f", colors.green);
telemetry.addData("Blue", "%.3f", colors.blue);
//Determining HSV and alpha
telemetry.addData("Hue", JavaUtil.colorToHue(colors.toColor()));
telemetry.addData("Saturation", "%.3f", JavaUtil.colorToSaturation(colors.toColor()));
telemetry.addData("Value", "%.3f", JavaUtil.colorToValue(colors.toColor()));
telemetry.addData("Alpha", "%.3f", colors.alpha);
telemetry.update();
}
}
}if(hue < 30){
}
else if (hue < 60) {
}
else if (hue < 90){
}
else if (hue < 150){
}
else if (hue < 225){
}
else if (hue < 350){
}
else{
} if(hue < 30){
telemetry.addData("Color", "Red");
}
else if (hue < 60) {
telemetry.addData("Color", "Orange");
}
else if (hue < 90){
telemetry.addData("Color", "Yellow");
}
else if (hue < 150){
telemetry.addData("Color", "Green");
}
else if (hue < 225){
telemetry.addData("Color", "Blue");
}
else if (hue < 350){
telemetry.addData("Color", "Purple");
}
else{
telemetry.addData("Color", "Red");
}@TeleOp
public class HelloRobot_ColorSensor extends LinearOpMode {
private NormalizedColorSensor test_color;
double hue;
@Override
public void runOpMode() {
test_color = hardwareMap.get(NormalizedColorSensor.class, "test_color");
waitForStart();
while (opModeIsActive()) {
telemetry.addData("Light Detected", ((OpticalDistanceSensor) test_color).getLightDetected());
NormalizedRGBA colors = test_color.getNormalizedColors();
hue = JavaUtil.colorToHue(colors.toColor());
//Determining the amount of red, green, and blue
telemetry.addData("Red", "%.3f", colors.red);
telemetry.addData("Green", "%.3f", colors.green);
telemetry.addData("Blue", "%.3f", colors.blue);
//Determining HSV and alpha
telemetry.addData("Hue", JavaUtil.colorToHue(colors.toColor()));
telemetry.addData("Saturation", "%.3f", JavaUtil.colorToSaturation(colors.toColor()));
telemetry.addData("Value", "%.3f", JavaUtil.colorToValue(colors.toColor()));
telemetry.addData("Alpha", "%.3f", colors.alpha);
//Using hue to detect color
if(hue < 30){
telemetry.addData("Color", "Red");
}
else if (hue < 60) {
telemetry.addData("Color", "Orange");
}
else if (hue < 90){
telemetry.addData("Color", "Yellow");
}
else if (hue < 150){
telemetry.addData("Color", "Green");
}
else if (hue < 225){
telemetry.addData("Color", "Blue");
}
else if (hue < 350){
telemetry.addData("Color", "Purple");
}
else{
telemetry.addData("Color", "Red");
}
telemetry.update();
}
}
}

During the process of creating an OpMode the OnBot Java tool prompted the selection of a sample code. In OnBot these samples act as templates; providing the outline and logical structure for different robotics use cases. In the previous section the sample code BlankLinearOpMode was selected. This sample code, seen in below, is the structural shell needed in order to have a working Linear OpMode.
/*
Copyright 2024
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.firstinspires.ftc.teamcode;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.hardware.Blinker;
import com.qualcomm.robotcore.hardware.Servo;
import com.qualcomm.robotcore.hardware.TouchSensor;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.eventloop.opmode.Disabled;
import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.DcMotorSimple;
import com.qualcomm.robotcore.util.ElapsedTime;
@TeleOp
public class HelloRobot_TeleOp extends LinearOpMode {
private Blinker control_Hub;
private DcMotor test_motor;
private Servo test_servo;
private TouchSensor test_touch;
@Override
public void runOpMode() {
control_Hub = hardwareMap.get(Blinker.class, "Control Hub");
test_motor = hardwareMap.get(DcMotor.class, "test_motor");
test_servo = hardwareMap.get(Servo.class, "test_servo");
test_touch = hardwareMap.get(TouchSensor.class, "test_touch");
telemetry.addData("Status", "Initialized");
telemetry.update();
// Wait for the game to start (driver presses PLAY)
waitForStart();
// run until the end of the match (driver presses STOP)
while (opModeIsActive()) {
telemetry.addData("Status", "Running");
telemetry.update();
}
}
}Throughout Hello Robot we will primarily be focusing on modifying the code found during our initialization process and while loop that runs when the Play button is pressed on the Driver Hub. As such, most examples will begin at public void runOpMode().
Let's take a quick tour of this template!
Our first section of code is our hardwareMap. This is where our attached components are called and defined between the program and the configuration file
control_Hub = hardwareMap.get(Blinker.class, "Control Hub");
test_motor = hardwareMap.get(DcMotor.class, "test_motor");
test_servo = hardwareMap.get(Servo.class, "test_servo");
test_touch = hardwareMap.get(TouchSensor.class, "test_touch");Within this area of our OpMode is anything we want to run BEFORE we press Play on the Driver Hub, but AFTER we press to initialize. This might include defining variables, motor directions, or servo positions!
// Wait for the game to start (driver presses PLAY)
waitForStart();Any code following this our waitForStart(); but before our while loop begins will be read ONCE when our play button is pressed! This might be used for resetting timers.
while (opModeIsActive()) {
telemetry.addData("Status", "Running");
telemetry.update();
}Last is our while loop! This is where any code we want to actively run and/or repeat until we press STOP is entered.
You will complete the majority of your program here.
Comments are lines of code intended to help you the programmer.
They can be used to explain the function of a section of code. This is especially helpful in collaborative programming environments. If code is handed from one programmer to another, comments communicate the intent of the code to the other programmer.
You can see a few premade comments already in our template written by the FIRST Tech Team to help get started!
To create a comment add // before the comment to be made. This can also be used to temporarily remove a line of code as the robot will not read comments!
// Wait for the game to start (driver presses PLAY)
// run until the end of the match (driver presses STOP)OnBot Java will attempt to notify you if there are either TOO MANY brackets within your code or NOT ENOUGH brackets by highlighting the final line in red as seen below:
The build errors may appear as below:
Keep in mind while checking your brackets that the error may be on a different line than the one reported! Take a look at the following example:
In this case, the missing bracket is on line 75 where my loop begins and should match with line 84's bracket. Once I have this corrected I can see the error clears:
While building your program you may encounter an error stating the class name is public and needs to be declared. This error can be common while copying and pasting from an example or tutorial and is the result of a mismatched name between the file name and public class.
Looking at the above example I can see my file is named "HelloRobot_TeleOp.java", but my class is "HelloWorld_TeleOp"!
To remedy this error, I could change my class name to match OR my file name. Which option is more ideal can be dependent on your end goal and how all your programs interact.
To change the name of a file, right click it on the list:
When renaming an OnBot Java file, the name is case sensitive and requires .java to be added to the end:
Alternatively, as previously mentioned, the public class name can be updated before building again to clear the error:









