FTC Java Basics

Prerequisites

If you are using OnBot Java as your editor, this guide will start with the program that we generated in the last guide.

If you are using Android Studio as your editor, you can simply create a file called “BasicDrive.java” in your teamcode folder and copy paste the starting code in this guide.

If you are new to the Java programming language, it is also recommended that you read the Java Syntax Basics guide. Even if you don’t understand everything in that guide (it is fairly technical), it will provide good background knowledge for this guide.

The Linear OpMode Structure

In this guide, ... will be used to indicate a section of code that was removed from the example. Note that the ... is not actually part of the code.

Here is the code generated by the BlankLinearOpMode sample:

/*
Copyright 2020 FIRST Tech Challenge Team XYZW
...
*/
package org.firstinspires.ftc.teamcode;

import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
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;

/**
 * This file contains an minimal example of a Linear "OpMode". 
...
 */
@TeleOp
public class BasicDrive extends LinearOpMode {


    @Override
    public void runOpMode() {

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

        }
    }
}

Let’s break it down into sections to learn how it works.

The Top of the File

/*
Copyright 2020 FIRST Tech Challenge Team XYZW
...
*/
package org.firstinspires.ftc.teamcode;

import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
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;

/**
 * This file contains an minimal example of a Linear "OpMode". 
...
 */

At the top, we have some automatically generated comments (enclosed in /* and */). There is also a package statement and some import statements. These statements will be automatically generated when you create the program and as you write code, therefore understanding them is not critical. Just know that the package statement defines the file’s location, and the import statements “import” FTC specific features (such as motors) into our program for us to use.

The Class

Next we have the class definition:

...

@TeleOp
public class BasicDrive extends LinearOpMode {
    ...
}

The public class BasicDrive indicates that we are defining a class (program) named “BasicDrive”. The extends LinearOpMode portion indicates that this class will inherit variables (values) and methods (behaviors) from a parent class called “LinearOpMode”, which is a built-in FTC class. In other words, this class will act like an OpMode rather than a regular Java program.

The @TeleOp line before the class definition is an annotation of the class. This annotation tell’s the Driver Station phone that this is a TeleOp OpMode, and therefore should be listed in that section of the app. You can also use @Autonomous and @Disabled annotations.

After the class is defined, the brackets ({}) will contain all of our code for the program.

The runOpMode Method

Inside of the class’ brackets, we have a method called runOpMode. The definition of the method looks like this:

...
    @Override
    public void runOpMode() {
        ...
    }
...

The method is public (usable anywhere), returns void (does not output a value), is named runOpMode, and has no parameters (the parenthesis () are empty).

This method is NOT unique to our class; rather, it is a method inherited from the parent class (LinearOpMode). Thus, it is important that the name of the method is exactly runOpMode. There is also an @Override annotation before the method, which indicates that this method already exists in the parent class, and we are simply overriding that method with our own code.

The robot will start running the code within this method’s brackets when the INIT button is pressed on the Driver Station phone.

Sectioning the runOpMode Method for TeleOp

The default code inside of the runOpMode section is set up so that the method is divided into various TeleOp sections:

  • init – code in the init section is ran once as soon as the INIT button is pressed. The init section is good for writing one-time setup code, such as mapping hardware devices (more on that later in this guide), setting the directions of motors, and resetting sensors.
  • loop – code in the loop section will run continuously until the OpMode is no longer active (the STOP button is pressed). The loop will contain all of our control code that needs to constantly run, such as updating drive motor speeds based on joysticks or updating a claw’s position based on button presses.

I added comments (highlighted in yellow) to indicate where code for the init and loop sections would go:

...
    @Override
    public void runOpMode() {
        // Init section
        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()) {
            // Loop section
            telemetry.addData("Status", "Running");
            telemetry.update();
        }
    }
...

In this code, we can see that init code would go at the very beginning of the method.

After the init code is two telemetry statements. Telemetry statements prepare (addData) and display (update) information on the Driver Station phone. These are not necessary but they are part of the BlankLinearOpMode template. The two statements after the init section simply let the driver know that the robot has finished initializing.

Next is a line with waitForStart();. As the name implies, the program will pause at this line untill the START button is pressed on the Driver Station.

d while (opModeIsActive()) { }. This is a while loop that will continuously run the code in the brackets while the condition in the parenthesis is true. In this case, the condition is the result of invoking the method opModeIsActive(), a method which returns true if the OpMode is running and false if it isn’t. Thus, the code in the brackets will keep running until the OpMode is stopped, making that the code in the brackets is the loop code.

Once again, there are also two telemetry statements in the loop code simply indicating to the driver that the robot is now running.

Adding Our Own Code

Now let’s try to add some of our own code to this program to run a DC motor. Let’s say that we named this motor “left” in our hardware configuration. In order to start using a device, we first have to create a variable for it. We will define this variable as an instance variable (outside of any methods) so that the entire class can use it:

...
@TeleOp
public class BasicDrive extends LinearOpMode {
    private DcMotor left;

    @Override
    public void runOpMode() {
        
        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();
        }
    }
}

I named this variable left. This is the same name as in the hardware configuration, but note that it doesn’t have to be; it is just generally good practice.

We have now defined a variable to store our DC motor in. However, currently this variable is empty. In order to use the motor, we have to map the variable to the device in our hardware configuration. FTC provides the method hardwareMap.dcMotor.get(deviceName) to accomplish this, where deviceName is a string (text) indicating exactly what we named the device in our hardware configuration.

In the init section of our code, let’s invoke this method and assign (set) it’s returned value to the left variable.

...
@TeleOp
public class BasicDrive extends LinearOpMode {
    private DcMotor left;

    @Override
    public void runOpMode() {
        left = hardwareMap.dcMotor.get("left");
        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();
        }
    }
}

Notice that we surrounded the device name string in quotes (" "). String values must always be surrounded in quotes so that the computer can differentiate them from variables.

The code we just wrote is our hardware configuration code. When you read the next guides, the hardware configuration code you will need for that guide will always be provided at the top for your convenience.

Now that we have assigned our left variable, we can use it to control the corresponding motor on our robot. left is an object variable, meaning that it contains various methods that we can access using the . operator and invoke. In our loop code, let’s invoke the motor’s setPower(power) method with a power value of 1.

...
@TeleOp
public class BasicDrive extends LinearOpMode {
    private DcMotor left;

    @Override
    public void runOpMode() {
        left = hardwareMap.dcMotor.get("left");
        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()) {
            left.setPower(1);
            telemetry.addData("Status", "Running");
            telemetry.update();
        }
    }
}

Now we have created a simple linear TeleOp OpMode that maps and controls a device on our robot. The next guides on the FTC Programming Resources page walk through various ways that devices can be used in order to control the robot.