All pages
Powered by GitBook
1 of 16

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Part 1: Tackling the Basics

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.

During Hello Robot you will encounter sections called "Quick Check!" These pauses are intend to be moments to think deeper on a topic or to self-check your understanding as you progress. It's is expected that the completion of Hello Robot may take multiple days, meetings, or classes.

Quick Check!

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?

Why do you think testing might be important in robotics?

Testing before a design is put into use, or as it is being constructed, helps to proactively identify, isolate, and correct potential issues.

Think about it this way:

Imagine spending all day working on building an arm for your robot so it can climb. The design is a little complex, but after an hour or so you have it connected to your robot and everything program.

You go to turn it on for the first time and.... the motor does not move. You can't tell because of where the motor sits if it is damaged or if something is tightened too much preventing it from moving. The rest of the day is spent taking the arm back off to check and repair.

Now think about how things may have gone if we tested the arm before it was attached to the robot. We don't need everything else to move, just a test code to move its motor. Might save us some time right?

Building a Test Bed

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.

Remember when testing a component there may be multiple points of failure such as the port, wire, program, or device itself. Utilizing a test bed helps to narrow down those failure points by making it easier to test and compare in a system's simplest state.

Click to learn more about how a test bed may used in real world applications!

A test bed is a testing environment for hardware and software components, commonly used in the engineering world. Test bed applications includes a broad range of different equipment and measurement testing. In some cases a test bed is a piece of equipment for testing a specific product, in other cases it is a system of components that create a testing environment. Regardless, the end goal of a test bed is to ensure a component is working before it is used for its intended purpose.

To create our test bed for this tutorial you will need the following. The names we used in our configuration are included:

Component
Configuration Name

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.

Well a test bed is recommended, in the case of time restrictions, space, or other limitations, individual components may be added or removed during each section of Hello Robot. Make sure moving components, such as motors or servos are ALWAYS secured while running, even at low speeds.

Creating an OpMode - OnBot

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.

While there are standardized naming conventions in programming, at the end of the day you will want to pick something that makes sense to YOU or your team. This might include having your name, team name, a school class period, or similar in your name.

Your OpMode name should not be the same as a created variable name.

To start, let's take a look at the OnBot Java layout in the REV Hardware Client:

  1. Create New OpMode - The plus sign button opens up a window to create a new OpMode.

  2. Project Browser Pane - This pane shows all the Java project files on the Robot Controller.

  3. Source Code Editing Pane - This is the main code editing area

  4. 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

  5. Build Everything - Builds ALL of the .java files on a Robot Controller.

Selecting to "Build Everything" will attempt to compile ALL your OnBot Java files. Errors may appear for incomplete or test files along with the active file.

To temporarily disable an OpMode, right click the file name and select "Disable / Comment":

OpModes may be reenable the same way!

Select the Create New OpMode button. This will open the New File window.

The 10.3 version of the Robot Controller App/Driver Station App released in June 2025 introduces a new interface for creating an OnBot Java file. Both versions are available to reference in the tabs before, but double check your version number before proceeding!

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".

HelloRobot_TeleOp created with the Setup Code for Configured Hardware active
OnBot Java new file menu in 10.3
Hardware mapping in OnBot Java
Using a sample code in 10.3 OnBot Java

Programming Servos

What is a Servo?

This section is considering the Smart Robot Servo in its default mode. If your servo has been changed to function in continuous mode or with angular limits it will not behave the same using the code examples below. You can learn more about the or changing the Servo's mode via the by clicking the hyperlinks.

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!

Quick Check

Let's review quick our basic positions:

Programmed Position
Degrees

Based on what we've learned so far, think about the follow two questions:

  1. If we wanted our servo to move to -67.5° what position would we program it move to?

  2. If we have programmed our servo to move to position 0.7, what would that equal in degrees?

Click to reveal the answers
  1. Breaking it down we can see that -67.5° would be half of our movement between 0° and -135° . Therefore, we will set our position to halfway between 0 and 0.5 equaling 0.25.

  2. This second question requires a little more math. Let's think about how far our servo would move for each 0.1 of a position. Our total movement is 270° so if divided by 10 we know 0.1 = 27°. Going from there we can do the math starting from the 0° we know position 0.5 brings us to. Therefore, moving to position 0.7 should be 54°.

For Hello Robot we will only be programming using positions. Understanding their translation to degrees is still important, however, to help think through designing a mechanism. Degrees may also be preferred when using a direct to program the servo.

Let's get Programming!

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°

Programming Servo Basics

Using a Gamepad with a Servo

Programming Servo Telemetry

@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();

        }
    }
Smart Robot Servo
SRS Programmer
Smart Robot Servo
pulse input

Tackling the Basics Directory - OnBot

Quick Jump Back to a Section

Creating an OpMode - Blocks
Programming Essentials
Programming Servos
Programming Motors
Programming Touch Sensors
Programming Color Sensors

Programming Motor Basics

Spinning a Motor:

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.

Quick Check!

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?

Setting Direction and Power:

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.

The direction a motor spins may be determined by the power OR may be designated during the initialization process.

Color Sensor Telemetry

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.

Adding Hue, Saturation, and Value Telemetry

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!

Quick Check!

Save your OpMode and give it a try! How do the values change depending on what color object the sensor is looking at?

This feature requires the Color Sensor's LED to be switched on!

Alpha Telemetry

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.

Code Checkpoint

Here is how our OnBot Java code should look after setting up our color sensor to collect all the information!

Programming Motors

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.

What is a Motor?

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.

For this tutorial, either a Core Hex Motor or HD Hex Motor may be used as long as they have been properly configured on the Driver Hub.

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!

Let's get Programming!

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:

Programming Motor Telemetry

Motors and Telemetry

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()

Full Code:

Programming Color Sensors

It is recommended to create a new OpMode while following this tutorial. Ours is named HelloRobot_ColorSensor!

Color Sensor Basics:

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.

But what makes up a color?

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!

Detecting Light vs. Dark

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.

Quick Check!

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?

What happened?

Likely, the numbers and differences you saw while testing are different than those we'd see ourselves. There are many factors that might change the color sensor's readings including the lighting in the room and surface material.

However, one thing that is the same is that 1 should be the least amount of light, such as when your hand is covering the sensor, and 0 is the most amount of light being seen.

Normalized Colors

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!

Programming a Motor with a Gamepad

Driving Motors with the Gamepad

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!

Quick Check!

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.

Adjusting Y-axis Direction

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();
        }
    }
}

Programming Motor Basics

Programming a Motor with a Gamepad

Programming Motor Telemetry

    @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();
        }
OpticalDistanceSensor
Setup Code for Configured Hardware

Programming Servo Telemetry

What is Telemetry?

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();

        }

Programming Servo Basics

Programming Position Movements

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.

Quick Check!

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?

Running our program a second time

Likely, on a second run our servo did not move since it is already at the correct position. Now check what happens if you first manually rotate the servo while the robot is disabled. Once the code is activated again by pressing play we should see it move again!

Note: Servos are designed to maintain their position so long as the robot's program is enabled. Trying to forcibly move the servo while ON may damage it and is not recommended.

If your servo did not move as expected, double check your wiring and port are correct compared to your configuration.

Resetting Back to Zero

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!

Programming Touch Sensors

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.

Touch Sensor Basics

The following example code's file name is: HelloRobot_TouchSensor

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

Adding Telemetry

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();
}

Touch Sensor as a Limit Switch

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!

Click to view full code!
import com.qualcomm.robotcore.eventloop.opmode.Disabled;
import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.Servo;
import com.qualcomm.robotcore.hardware.TouchSensor;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;

@TeleOp

public class HelloRobot_TouchSensor extends LinearOpMode {
    TouchSensor test_touch;  // Touch sensor Object
    private DcMotor test_motor = null;
    private Servo test_servo = null;
    
    @Override
public void runOpMode() {
        test_motor = hardwareMap.get(DcMotor.class, "test_motor");
        test_servo = hardwareMap.get(Servo.class, "test_servo");
        test_touch = hardwareMap.get(TouchSensor.class, "test_touch");
        
        // 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.
                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");
                        }
        telemetry.update();
        }
    }
}

Reversing it

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!

Using a Gamepad with a Servo

Programming a Servo with a Gamepad

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:

If you are using a PS4 Controller, selecting the appropriate button from the dropdown in Blocks may be easier to follow when looking back at your code. The buttons are also interchangeable when programming in Blocks. (ex: Y in code = Triangle pressed on controller)

Introducing If/Else Statements

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.

Quick Check!

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)?

What will happen when the answer is FALSE?

At the moment, we have not asked our robot to do anything specific when our statement is false. This means for now our servo will not move or change while our Y button is not pressed.

If/Else If Statements:

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.

Quick Check!

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?

Reveal the answer!

Programming our servo to move to position 1 when A is pressed will look very similar to our existing code:

Now our statement reads: "If the Y button is pressed then move the servo to position 0, else if the A button is pressed then move the servo to position 1."

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?

Adding Logic Operators:

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.

Full Program:

Detecting Color

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!

Adding a Hue Variable

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:

Detecting Common Colors

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!

Full Color Sensor Program

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 code
while (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();
        }
    }
}
if/else statements while working with the touch sensor.

Programming Essentials

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.

If using Robot Controller App v10.3 or newer the way templates are used has changed. More information is available here.

/*
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().

When utilizing the samples provided in this tutorial, double check that the correct number of brackets and file names are added to your final program!

Let's take a quick tour of this template!

Initialization:

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!

waitForStart();

        // 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.

opModeIsActive()

        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.

Adding Comments

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)

Common Errors

Bracket Mismatch

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:

A bracket appears to be missing from the program!
There appears to be an extra bracket in the program!

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:

Mismatched File/Class Names

Click to enlarge error image

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:

Always remember to Build your program after making major changes and before testing with your robot!