-
Notifications
You must be signed in to change notification settings - Fork 0
Guide to Java Level 2
Middle school FTC students who have completed Level 1: Java Foundations. This guide bridges pure Java knowledge into FTC robot programming.
Prerequisites: Java basics (Level 1 or equivalent — a class, self-taught, or prior experience)
Time to Complete: 2-3 weeks alongside robot build
┌─────────────────────────────────────────────────────────────┐
│ LEVEL 1: Java Foundations │
│ (Recommended if new to Java) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ LEVEL 2: Java for FTC │
│ (You are here) │
└─────────────────────────────────────────────────────────────┘
│
▼
Ready to program your robot!
Already know Java? Skip to Part 2 and start learning FTC-specific patterns.
TeamCode/
├── java/
│ └── org/firstinspires/ftc/teamcode/
│ ├── TeleOp/ ← Driver-controlled programs
│ │ └── MainTeleOp.java
│ ├── Autonomous/ ← Self-driving programs
│ │ └── RedLeft.java
│ └── Subsystems/ ← Reusable mechanism code (optional)
│ └── Intake.java
Every FTC program is an OpMode. There are two types:
| Type | When to Use | How It Works |
|---|---|---|
LinearOpMode |
Most programs | Runs top-to-bottom like normal code |
OpMode |
Advanced (iterative) | Runs in a loop like a game engine |
Start with LinearOpMode — it's easier to understand.
package org.firstinspires.ftc.teamcode;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.hardware.DcMotor;
@TeleOp(name = "My First TeleOp")
public class MyFirstTeleOp extends LinearOpMode {
// Declare hardware here
private DcMotor leftMotor;
private DcMotor rightMotor;
@Override
public void runOpMode() {
// INIT PHASE: Runs once when you press INIT
leftMotor = hardwareMap.get(DcMotor.class, "leftMotor");
rightMotor = hardwareMap.get(DcMotor.class, "rightMotor");
telemetry.addData("Status", "Initialized");
telemetry.update();
// Wait for driver to press START
waitForStart();
// RUN PHASE: Runs after START is pressed
while (opModeIsActive()) {
// Your teleop code goes here
// This loop runs over and over until STOP
double drive = -gamepad1.left_stick_y;
double turn = gamepad1.right_stick_x;
leftMotor.setPower(drive + turn);
rightMotor.setPower(drive - turn);
telemetry.addData("Drive", drive);
telemetry.addData("Turn", turn);
telemetry.update();
}
}
}1. Annotations tell FTC what kind of program this is:
@TeleOp(name = "My First TeleOp") // Shows up in TeleOp menu
// OR
@Autonomous(name = "Red Left Auto") // Shows up in Autonomous menu2. Hardware declaration (before runOpMode):
private DcMotor leftMotor; // Declare the variable3. Hardware initialization (inside runOpMode, before waitForStart):
leftMotor = hardwareMap.get(DcMotor.class, "leftMotor");
// The string "leftMotor" must match the name in your robot configuration!4. The main loop:
while (opModeIsActive()) {
// This runs over and over (about 50 times per second)
// Put your teleop control code here
}// Declaration
private DcMotor motor;
// Initialization
motor = hardwareMap.get(DcMotor.class, "motorName");
// Basic control
motor.setPower(0.5); // 50% forward
motor.setPower(-0.5); // 50% backward
motor.setPower(0); // Stop
// Direction
motor.setDirection(DcMotor.Direction.REVERSE); // Flip direction
// Braking behavior
motor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE); // Stops quickly
motor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.FLOAT); // Coasts to stop// Declaration
private Servo servo;
// Initialization
servo = hardwareMap.get(Servo.class, "servoName");
// Control (position 0.0 to 1.0)
servo.setPosition(0.0); // One extreme
servo.setPosition(0.5); // Middle
servo.setPosition(1.0); // Other extreme// Joysticks (return -1.0 to 1.0)
double leftY = gamepad1.left_stick_y; // Up = negative, Down = positive
double leftX = gamepad1.left_stick_x; // Left = negative, Right = positive
double rightY = gamepad1.right_stick_y;
double rightX = gamepad1.right_stick_x;
// Buttons (return true or false)
boolean aPressed = gamepad1.a;
boolean bPressed = gamepad1.b;
boolean xPressed = gamepad1.x;
boolean yPressed = gamepad1.y;
// Bumpers and triggers
boolean leftBumper = gamepad1.left_bumper;
boolean rightBumper = gamepad1.right_bumper;
float leftTrigger = gamepad1.left_trigger; // 0.0 to 1.0
float rightTrigger = gamepad1.right_trigger; // 0.0 to 1.0
// D-pad
boolean dpadUp = gamepad1.dpad_up;
boolean dpadDown = gamepad1.dpad_down;
boolean dpadLeft = gamepad1.dpad_left;
boolean dpadRight = gamepad1.dpad_right;// Add data to display
telemetry.addData("Label", value);
telemetry.addData("Motor Power", motor.getPower());
telemetry.addData("Button A", gamepad1.a);
// Must call update() to actually show it!
telemetry.update();Each joystick controls one side of the robot:
while (opModeIsActive()) {
double leftPower = -gamepad1.left_stick_y;
double rightPower = -gamepad1.right_stick_y;
leftMotor.setPower(leftPower);
rightMotor.setPower(rightPower);
}One joystick controls forward/back and turning:
while (opModeIsActive()) {
double drive = -gamepad1.left_stick_y; // Forward/back
double turn = gamepad1.right_stick_x; // Turning
leftMotor.setPower(drive + turn);
rightMotor.setPower(drive - turn);
}while (opModeIsActive()) {
if (gamepad1.a) {
intakeMotor.setPower(1.0); // Run intake while A held
} else if (gamepad1.b) {
intakeMotor.setPower(-1.0); // Reverse while B held
} else {
intakeMotor.setPower(0); // Stop when nothing pressed
}
}boolean clawOpen = false;
boolean lastButtonState = false;
while (opModeIsActive()) {
boolean currentButtonState = gamepad1.x;
// Detect when button is first pressed (not held)
if (currentButtonState && !lastButtonState) {
clawOpen = !clawOpen; // Toggle the state
}
lastButtonState = currentButtonState;
// Set servo based on state
if (clawOpen) {
clawServo.setPosition(0.7);
} else {
clawServo.setPosition(0.3);
}
}@Autonomous(name = "Simple Auto")
public class SimpleAuto extends LinearOpMode {
private DcMotor leftMotor;
private DcMotor rightMotor;
@Override
public void runOpMode() {
leftMotor = hardwareMap.get(DcMotor.class, "leftMotor");
rightMotor = hardwareMap.get(DcMotor.class, "rightMotor");
waitForStart();
// Drive forward for 2 seconds
leftMotor.setPower(0.5);
rightMotor.setPower(0.5);
sleep(2000); // Wait 2000 milliseconds (2 seconds)
// Stop
leftMotor.setPower(0);
rightMotor.setPower(0);
// Turn right for 1 second
leftMotor.setPower(0.5);
rightMotor.setPower(-0.5);
sleep(1000);
// Stop
leftMotor.setPower(0);
rightMotor.setPower(0);
}
}@Autonomous(name = "Better Auto")
public class BetterAuto extends LinearOpMode {
private DcMotor leftMotor;
private DcMotor rightMotor;
@Override
public void runOpMode() {
leftMotor = hardwareMap.get(DcMotor.class, "leftMotor");
rightMotor = hardwareMap.get(DcMotor.class, "rightMotor");
waitForStart();
// Now autonomous reads like a story!
driveForward(0.5, 2000);
turnRight(0.5, 1000);
driveForward(0.5, 1500);
stopMotors();
}
// Helper methods
private void driveForward(double power, long milliseconds) {
leftMotor.setPower(power);
rightMotor.setPower(power);
sleep(milliseconds);
}
private void turnRight(double power, long milliseconds) {
leftMotor.setPower(power);
rightMotor.setPower(-power);
sleep(milliseconds);
}
private void turnLeft(double power, long milliseconds) {
leftMotor.setPower(-power);
rightMotor.setPower(power);
sleep(milliseconds);
}
private void stopMotors() {
leftMotor.setPower(0);
rightMotor.setPower(0);
}
}As your robot gets more complex, organize mechanisms into their own classes:
// Intake.java
package org.firstinspires.ftc.teamcode.Subsystems;
import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.HardwareMap;
public class Intake {
private DcMotor intakeMotor;
// Constructor - sets up the hardware
public Intake(HardwareMap hardwareMap) {
intakeMotor = hardwareMap.get(DcMotor.class, "intake");
}
// Methods - what the intake can do
public void intake() {
intakeMotor.setPower(1.0);
}
public void outtake() {
intakeMotor.setPower(-1.0);
}
public void stop() {
intakeMotor.setPower(0);
}
}@TeleOp(name = "Organized TeleOp")
public class OrganizedTeleOp extends LinearOpMode {
private Intake intake; // Use your subsystem class
@Override
public void runOpMode() {
intake = new Intake(hardwareMap); // Create the subsystem
waitForStart();
while (opModeIsActive()) {
// Clean, readable code!
if (gamepad1.a) {
intake.intake();
} else if (gamepad1.b) {
intake.outtake();
} else {
intake.stop();
}
}
}
}// BAD - nothing shows on screen
telemetry.addData("Power", motor.getPower());
// GOOD - data actually displays
telemetry.addData("Power", motor.getPower());
telemetry.update();// If your config says "left_drive", use exactly that!
motor = hardwareMap.get(DcMotor.class, "left_drive"); // GOOD
motor = hardwareMap.get(DcMotor.class, "leftDrive"); // BAD - won't find it// BAD - only runs once
waitForStart();
leftMotor.setPower(gamepad1.left_stick_y);
// GOOD - runs continuously
waitForStart();
while (opModeIsActive()) {
leftMotor.setPower(gamepad1.left_stick_y);
}// Pushing forward gives NEGATIVE values!
double drive = -gamepad1.left_stick_y; // Negate to make forward = positive-
Basic TeleOp: Create a tank drive that uses both joysticks
-
Add an Intake: Add a motor controlled by the A and B buttons
-
Add a Claw: Add a servo controlled by X (open) and Y (close)
-
Create a Toggle: Make the claw toggle open/closed with one button
-
Simple Auto: Drive forward, turn 90°, drive forward again
-
Subsystem Practice: Create an
Armsubsystem class withraise(),lower(), andstop()methods
Before moving on, you should be able to:
- Create a new OpMode with the correct annotations
- Initialize motors and servos from the hardware map
- Read gamepad inputs (joysticks, buttons, triggers)
- Control motors based on joystick input
- Control servos based on button input
- Display information using telemetry
- Create a basic time-based autonomous
- Create helper methods to organize your code
- Create a simple subsystem class
Once you're comfortable with these basics:
- Learn about encoders — More precise autonomous movement
- Learn about sensors — Color sensors, distance sensors, touch sensors
- Learn about roadrunner/pedropathing — Advanced autonomous path following
And when you move to FRC in high school, you'll be ready for Level 3: Java Refresher for FRC, which shows you how these concepts translate to the Command-Based framework!
@TeleOp(name = "Name Here")
public class MyOpMode extends LinearOpMode {
private DcMotor motor;
@Override
public void runOpMode() {
motor = hardwareMap.get(DcMotor.class, "name");
waitForStart();
while (opModeIsActive()) {
// Your code here
}
}
}motor.setPower(0.5); // 50% forward
motor.setPower(-0.5); // 50% backward
motor.setPower(0); // Stopservo.setPosition(0.0); // One end
servo.setPosition(1.0); // Other endgamepad1.left_stick_y // -1 to 1 (inverted!)
gamepad1.right_stick_x // -1 to 1
gamepad1.a // true/false
gamepad1.left_trigger // 0 to 1telemetry.addData("Label", value);
telemetry.update(); // Don't forget this!