Cookbook: "How do I...?"
Short answers to the questions students ask most. Each recipe is one question, a few lines of code to copy, one sentence on why, and links to the lesson that explains more.
Find your question fast: press Ctrl+F (on a Mac: Cmd+F) and type a word, like lift,
double-tap or park. Or use the list just below.
The code always works. Every code block on this page is copied from real Java files in
examples/.../cookbook. Those
files are built and run in the simulator each time the library changes, and a check
(tools/check_docs.py) makes sure this page and the files say the same thing.
New here? Do the Quick start first: it puts the five files in your robot project. These recipes go into those files. Never coded before? Read Words you will meet first.
The questions
Section titled “The questions”Start here
- Coming from Blocks: where did my blocks go? (if, loop, sleep, REVERSE, variables)
- Where does each line go? The files, whole
- Words you will meet
Driving
- How do I set up a mecanum drive?
- How do I set up a tank (standard) drive?
- A wheel turns the wrong way. How do I fix it?
- How do I tell the robot how the Control Hub is mounted?
- How do I make the robot slower (or faster)?
- How do I let one driver do everything?
- How do I put slow mode on a trigger?
- Driving that is already built in (field-centric, pause, “forward”)
Buttons and gestures
- How do I make a button do something once?
- How do I run something only while a button is held?
- How do I switch something on and off with one button?
- How do I put a tap, a hold and a double-tap on one button?
- How do I make a button safe (“hold for 1 second”)?
- How do I use two buttons together (like a shift key)?
- How do I make the gamepad buzz?
- How do I control a part with a stick?
- How do I move a part’s button to another button?
- How do I turn off all the standard buttons?
- How do I change the double-tap and hold times?
- Buttons that are already built in (button map, pause)
Mechanisms
- How do I add an intake on a motor?
- How do I add an intake on CR servos?
- How do I make the intake run from the start?
- How do I add a lift with set heights?
- My lift goes down when it should go up. How do I fix it?
- How do I add an arm that moves in degrees?
- How do I add a claw?
- How do I add a wrist?
- How do I add any servo with named positions (a gate, a fold-out)?
- How do I send a part to a position from a button?
- How do I add a launcher (flywheel and feeder)?
- How do I add a turret that follows the goal?
- How do I fire only when aimed, at the right speed?
Actions (several steps or parts together)
- How do I do several things one after the other?
- How do I repeat steps several times?
- How do I move two parts at the same time?
- How do I wait half a second (like sleep in Blocks)?
- How do I wait until something is true?
- How do I start an action from a button?
- How do I stop every running action?
- How do I run an action right after START?
- How do I use the same action in an autonomous?
Autonomous
- How do I make my first autonomous (drive 2 feet)?
- How do I drive forward, sideways and turn?
- Does my autonomous need a RED version?
- How do I let the drivers choose before the match?
- How do I make sure there is time to park?
- How do I run a mechanism while the robot drives?
- How do I wait for my alliance partner?
- How do I write a step the list cannot say?
- How do I change a distance without building the code again?
- How do I share the same first steps in several autos?
Odometry and driveTo (where is the robot on the field?)
- How do I add a goBILDA Pinpoint?
- How do I add a SparkFun OTOS?
- How do I add dead wheels?
- How do I tune the odometry?
- How do I drive to a point on the field?
- How do I show where the robot is?
- How do I use the robot’s position in an action?
Pedro paths (smooth curves, mecanum only)
- How do I add Pedro Pathing to Robot.java?
- How do I make a straight path and a curve?
- How do I follow a path in an autonomous?
- How do I drive two paths with no stop, slow down, or hold at the end?
- How do I keep facing the goal along a path?
Telemetry (lines on the Driver Station)
- How do I show a value on the Driver Station screen?
- How do I round a number on the Driver Station?
- How do I show lines from my own autonomous step?
Tuning and finders (getting the numbers right)
- Where do the numbers go?
- How do I find a servo’s open and closed numbers?
- How do I find a lift’s limits and heights?
- How do I change a number while the robot runs?
- How do I tune the drive for autonomous?
- How do I set the flywheel numbers?
- How do I build a shot table (speed for each distance)?
Simulator (try your code with no robot)
- How do I start the simulator?
- How do I see my own robot in the simulator?
- How do I test my code with a script?
Coming from Blocks: where did my blocks go?
Section titled “Coming from Blocks: where did my blocks go?”| In Blocks you used | Here you write | In which file |
|---|---|---|
a repeat while opModeIsActive loop |
nothing: the library runs the loop for you, many times a second | |
| drive forward, turn (in autonomous) | .move("Forward", 24, 0, 0.5), .turn("Left", 90, 0.4) (moves) |
a file in autos/ |
if a sensor or part says something, then … |
.waitUntil(...): wait until it is true, then go on (wait until) |
Actions.java or an autonomous |
if the drivers chose something before the match |
onlyIf(...) with a menu choice (choices) |
an autonomous |
repeat 3 times |
a for loop that adds the steps 3 times to an action (repeat) |
Actions.java |
| set servo position | a named position: robot.claw.open(), robot.intakeDeploy.goTo("deployed") (servo positions) |
Controls.java, Actions.java |
waitForStart |
nothing: the library waits for START. For something right after START: atStart(...) (at START) |
Robot.java |
if gamepad2.y then ... inside the loop |
one line, once: g2.y.press(...) (buttons) |
Controls.java |
if the button is held |
g1.a.hold(...) (hold) |
Controls.java |
sleep 500 |
.waitSeconds(0.5) (wait) |
Actions.java or an autonomous |
| a loop that waits until something is true | .waitUntil(...) (wait until) |
Actions.java or an autonomous |
| set motor direction REVERSE | drive motors: .reversed("front_right", ...) (drive); a lift: .reversed() (lift) |
Robot.java |
| set motor power | a command of the part: robot.intake.in(), robot.lift.goTo("high") (parts) |
Controls.java, Actions.java |
| a variable for a number (a speed, a position) | a number in Tunables.java (numbers) |
Tunables.java |
telemetry.addData |
show("Label", ...) (show) |
Controls.java |
| drive blocks (sticks to motor powers) | nothing: driving is built in (driving) |
Why no loop? In Blocks your code ran again and again in the loop. Here buttons(...) runs once,
at INIT: each line plugs a job into a button. The library’s loop then runs the job each time the button
is pressed. So you never write if, while or sleep for buttons. More: From Blocks to Java.
Where does each line go? The files, whole
Section titled “Where does each line go? The files, whole”Each recipe gives a few lines. They go INSIDE one of these files, at the spot the comment shows. Keep
every other line of the file. Your files also start with a package line and some import lines (not
shown here; they stay at the very top, above public class). An import tells Java where a word like
Lift comes from: when a word is red in Android Studio, click it and press Alt+Enter (Mac:
Option+Enter) to add its import.
Robot.java: the parts. Fields at the top, one line per part inside define():
public class Robot extends RobotDefinition { public Intake intake; // one field per part: "public", the kind of part, a name public Lift lift; public Claw claw; public Camera camera; public Launcher launcher;
@Override protected void define() { // the drive line, then one line per part drive(Drive.mecanum("front_left", "front_right", "back_left", "back_right") .imu("imu", LogoFacingDirection.UP, UsbFacingDirection.FORWARD)); odometry(Odometry.pinpoint("pinpoint").offsetsMm(-84, -168)); intake = add(Intake.motor("intake")); lift = add(Lift.slide("lift").bottomSwitch("lift_bottom")); claw = add(Claw.servo("claw")); camera = add(Camera.limelight("limelight").goal("BLUE SCORING", "RED SCORING")); launcher = add(Launcher.flywheel("launcher", "gobilda-5203-6000").feeder(Indexer.servo("indexer")) .speedFromDistance(camera)); }}Controls.java: the buttons, and your TeleOp. Button lines go inside buttons(...):
@TeleOp(name = "Cookbook TeleOp", group = "Cookbook") // the name on the Driver Station@Disabled // delete this line in your copypublic class Controls extends ControlMap<Robot> { public static boolean oneDriver = false;
@Override protected void buttons(Robot robot, Pad g1, Pad g2) { // robot = your Robot.java, g1 = gamepad 1, g2 = gamepad 2. Button lines go here: g2.y.press(Actions.scoreHigh(robot)).says("score high"); show("Lift ticks", robot.lift::position); }}Actions.java: one method per action:
public class Actions { // One method per action. r = your robot. Use it in Controls.java and in autos. public static Action scoreHigh(Robot r) { return Action.named("Score high") .then(() -> r.lift.goTo("high")) .waitUntil("lift at the top", r.lift::isAtTarget, 2.0) .then(r.claw::open) .waitSeconds(0.5) // wait half a second (this is "sleep", done safely) .then(() -> r.lift.goTo("down")); }}An autonomous file: see your first autonomous. The numbers file: see Tunables.java.
Words you will meet
Section titled “Words you will meet”| Word | Means |
|---|---|
Robot.java |
the file that says WHAT is on the robot: the drive and one line per part, inside define() |
Tunables.java |
the file with every number (speeds, positions), one section per part |
Controls.java |
the file that says which button does what, inside buttons(...). It is also your TeleOp |
Actions.java |
moves that use several steps or parts (score, grab) |
autos/ |
the folder with your autonomous programs, one file each |
| configuration name | the name of a motor or servo you typed on the Driver Station ("lift"). The code must use the same name |
robot.lift::position |
:: hands over a method without running it. The library runs it later (when the button is pressed) |
() -> robot.lift.goTo("high") |
a lambda: a small piece of code the library runs later. Use :: when the job is one method with nothing in its brackets (robot.claw::open); use () -> when the brackets hold something (goTo("high")) or for several commands |
g1, g2 |
gamepad 1 (the driver) and gamepad 2 (the operator) |
| operator | the second driver, on gamepad 2: runs the mechanisms while the driver drives |
| GUIDE | the centre button of the gamepad. Tap: the button map (what each button does). Hold 1 s (PRACTICE profile): the tuning menu |
| “every loop” | the library’s loop runs many times a second: it reads the gamepads, runs the jobs and updates every part. You never write it |
| power | from 0 (stopped) to 1 (full); a negative power runs the other way |
robot, g1, g2, s |
names the library hands you: buttons(Robot robot, Pad g1, Pad g2) and steps(Steps s, Robot robot) (see the files, whole) |
; |
ends a line of Java. A chain of steps (.move(...) then .turn(...)) is ONE line split over several lines: only its last line has ; |
"lift" (in quotes) |
a name. Capitals and spelling must match exactly |
| class | one Java file’s main block: public class Robot ... { ... }. “Inside the class” = between its { and } |
| method | a named piece of code, like define() or scoreHigh(...). Its lines go between its { and } |
@TeleOp(...), @Autonomous(...) |
put the program in the Driver Station’s list, with this name |
@Disabled |
hides the program from the list. The cookbook’s programs have it; delete it in your copy |
@Override, public, static, protected |
Java words the file needs. Copy them as they are |
| INIT, START | the Driver Station’s two buttons: INIT gets ready (nothing may move), START runs |
| PRACTICE, COMPETITION | the two profiles (ways to run): PRACTICE is slower and has the tuning menu |
| ticks (counts) | how far a motor has turned, from its encoder (a sensor in the motor) |
kP, kI, kD, kF |
the PID numbers: how hard a lift pushes toward its target. Start with the defaults |
catalog id ("gobilda-5203-312") |
a motor’s or servo’s name in the parts catalog |
| tuning menu | change numbers while the robot runs (not the INIT choice menu of an autonomous) |
| terminal | a window where you type commands. In Android Studio: the Terminal tab at the bottom (how) |
One rule for every recipe: a job never waits. No sleep(...) and no while loop in a button job
or an action: they freeze the whole robot. To wait, see
how do I wait half a second (like sleep)?.
Driving
Section titled “Driving”How do I set up a mecanum drive?
Section titled “How do I set up a mecanum drive?”Where: Robot.java, inside define().
// The four motor names, in this order: front left, front right, back left, back right.drive(Drive.mecanum("front_left", "front_right", "back_left", "back_right") .motor("gobilda-5203-312").wheelMm(96) // the motor (catalog id) and wheel size .imu("imu", LogoFacingDirection.UP, UsbFacingDirection.FORWARD));Why: the names must match the configuration, and the wheel size makes autonomous distances right. More: Lesson 2: Drive, Drive.
How do I set up a tank (standard) drive?
Section titled “How do I set up a tank (standard) drive?”Where: Robot.java, inside define() (in place of the mecanum line).
drive(Drive.tank("left_drive", "right_drive") .motor("gobilda-5203-312").wheelMm(96) .reversed("right_drive") // only the motors that run backward .imu("imu", LogoFacingDirection.UP, UsbFacingDirection.FORWARD));Why: the motors on the two sides face opposite ways, so one side runs backward: list that side’s
motors in .reversed(...). Two motors per side? Name all four: Drive.tank("front_left", "front_right", "back_left", "back_right").
More: Lesson 2, Drive.
A wheel turns the wrong way. How do I fix it?
Section titled “A wheel turns the wrong way. How do I fix it?”Where: Robot.java, in the drive line, before .imu(...).
drive(Drive.mecanum("front_left", "front_right", "back_left", "back_right") .motor("gobilda-5203-312").wheelMm(96) // List EVERY reversed drive motor. Without this line the left side is reversed. .reversed("front_right", "back_right") .imu("imu", LogoFacingDirection.UP, UsbFacingDirection.FORWARD));Why: motors on the two sides face opposite ways, so one side must run reversed for the robot to go straight. More: Lesson 2: check the motor directions, Troubleshooting.
How do I tell the robot how the Control Hub is mounted?
Section titled “How do I tell the robot how the Control Hub is mounted?”Where: Robot.java, the .imu(...) part of the drive line.
drive(Drive.mecanum("front_left", "front_right", "back_left", "back_right") // Which way the hub's LOGO faces, then which way its USB PORTS face, on the robot. .imu("imu", LogoFacingDirection.LEFT, UsbFacingDirection.UP));Why: the IMU (the hub’s turn sensor) must know which way is up, or turns and field-centric driving go wrong. More: Lesson 2: the IMU.
How do I make the robot slower (or faster)?
Section titled “How do I make the robot slower (or faster)?”Where: Tunables.java, section Drive.
public static class Drive { // Keep it slow for the first drives; raise maxPower when everything works. public static double maxPower = 0.6, practiceMaxPower = 0.4, slowSpeed = 0.3; public static double strafeCorrection = 1.15, turnKp = 0.012, turnKd = 0.003, moveMinPower = 0.30; // false until the Drive Tuner (lesson 6): until then no autonomous drives the robot. public static boolean calibrated = false;}Why: practiceMaxPower is used in the PRACTICE profile and maxPower in COMPETITION, so practice
is safe and matches are fast.
More: Lesson 2: the numbers, Profiles.
How do I let one driver do everything?
Section titled “How do I let one driver do everything?”Where: Controls.java, at the top of the class.
// true: every button is on gamepad 1 (the operator's buttons move there).public static boolean oneDriver = true;Why: with one driver, the operator’s buttons move to gamepad 1, and the stick clicks switch slow mode and field-centric. More: Lesson 3: one driver or two.
How do I put slow mode on a trigger?
Section titled “How do I put slow mode on a trigger?”Where: Controls.java, inside buttons(...). For one driver (with two drivers, LT on gamepad 1 is already slow).
// One driver: slow on LT. The further it is pulled, the slower (down to slowSpeed).g1.lt.slow().says("slow");Why: a trigger gives fine control: a little squeeze is a little slower. More: ControlMap: one driver.
Driving that is already built in
Section titled “Driving that is already built in”No code needed. With Controls.java (two drivers):
- Drive: gamepad 1 left stick drives (and strafes), right stick X turns. LT: slow (the further, the slower).
- Field-centric (mecanum): gamepad 1 Y switches it on/off. “Forward” is then away from the drivers.
- Pause: tap gamepad 1 BACK: everything stops on brake. Tap again to go on.
- Reset “forward”: hold gamepad 1 BACK for 1 second with the robot facing away from you.
More: Lesson 2: the driving you get for free, the standard map.
Buttons and gestures
Section titled “Buttons and gestures”All button recipes go in Controls.java, inside buttons(...). That method runs once, at INIT: each
line plugs a job into a button. Every line ends with ;. .says("...") is the text on the button map.
Pick a free button. Each part already has standard buttons (the standard map):
for example the claw uses operator B, the lift A, X, Y. A job you write on one of them
takes that button from the part (INIT says so). Two jobs that YOU write on one button stop INIT with the
button’s name. INIT lists the free buttons. To empty a button first: g2.b.free();
(move a button).
How do I make a button do something once?
Section titled “How do I make a button do something once?”Where: Controls.java, inside buttons(...).
// Once per press. "::" hands over the method; the button runs it later.g2.y.press(robot.claw::open).says("open the claw");// Several commands: "() ->" starts a small piece of code (a lambda).g2.x.press(() -> robot.lift.goTo("low")).says("lift to low");Why: press runs the job once per press, the moment the button goes down.
More: Lesson 3: add your own jobs, The button menu.
How do I run something only while a button is held?
Section titled “How do I run something only while a button is held?”Where: Controls.java, inside buttons(...).
// Runs every loop while held; the second job runs once when it is let go.g1.a.hold(robot.intake::in, robot.intake::stop).says("intake in (hold)");Why: without the second job, the intake would keep running after you let go. More: The button menu.
How do I switch something on and off with one button?
Section titled “How do I switch something on and off with one button?”Where: Controls.java, inside buttons(...).
// Each press switches the intake on or off. rumble(): 2 buzzes = on, 1 buzz = off.g1.x.toggle(robot.intake).rumble().says("intake on/off");Why: toggle remembers on or off for you, and the buzz tells the driver which one without looking.
More: The button menu.
How do I put a tap, a hold and a double-tap on one button?
Section titled “How do I put a tap, a hold and a double-tap on one button?”Where: Controls.java, inside buttons(...).
// Three gestures on one button: tap, hold, double-tap.g2.rb.tap(robot.launcher::fire).says("fire one") .hold(robot.launcher::fireWhileHeld, robot.launcher::ceaseFire).says("keep firing") .doubleTap(() -> robot.launcher.fire(3)).says("fire three");Why: one button can do several jobs, so the drivers need fewer buttons to remember. More: The button menu: gestures.
How do I make a button safe (“hold for 1 second”)?
Section titled “How do I make a button safe (“hold for 1 second”)?”Where: Controls.java, inside buttons(...).
// Runs once, after the button is held for 1 second: a safe "are you sure?".g2.dpadLeft.heldFor(1.0, robot.launcher::stop).says("hold 1 s: flywheel off");Why: a quick bump of the button does nothing, so a big job cannot start by accident. More: The button menu: gestures.
How do I use two buttons together (like a shift key)?
Section titled “How do I use two buttons together (like a shift key)?”Where: Controls.java, inside buttons(...).
// Hold RB (the "shift key"), then press D-pad up. For big jobs that must not run by accident.g1.rb.with(g1.dpadUp, () -> robot.lift.goTo("high")).says("lift to the top");Why: a pair is a safe place for rare, big jobs (a climb); D-pad up alone still does its own job. More: The button menu: two buttons together.
How do I make the gamepad buzz?
Section titled “How do I make the gamepad buzz?”Where: Controls.java, inside buttons(...).
Add .rumble() after a job (see the toggle recipe),
or buzz when the robot gets ready:
// A buzz on gamepad 2 each time the flywheel gets to its speed.g2.rumbleWhen(robot.launcher::isReady);Why: the drivers watch the robot, not the Driver Station, so a buzz says “ready” without looking away. More: The button menu: feedback.
How do I control a part with a stick?
Section titled “How do I control a part with a stick?”Where: Controls.java, inside buttons(...).
// A stick is a number from -1 to 1 (up and right are +). Its value goes to the job every loop.g2.rightStickY.use(robot.lift::manual).says("lift by hand");Why: a stick gives a number every loop, so the part moves as far as you push it (and holds when you let go). More: The button menu: sticks.
How do I move a part’s button to another button?
Section titled “How do I move a part’s button to another button?”Where: Controls.java, inside buttons(...).
Each part brings standard buttons (the intake: operator RT and LT). To move them:
g2.rt.free(); // free(): no job on it, even the one the intake suggestsg2.lt.free();g2.lb.hold(robot.intake::in, robot.intake::stop).says("intake in (hold)");g2.rb.hold(robot.intake::out, robot.intake::stop).says("intake out (hold)");Why: free() takes the standard job off a button; then you give the new buttons their jobs.
More: Lesson 3: the buttons the parts bring.
How do I turn off all the standard buttons?
Section titled “How do I turn off all the standard buttons?”Where: Controls.java, inside the class (next to buttons(...)).
// false: the parts bring no buttons of their own. Only the jobs in buttons(...) are used.@Overrideprotected boolean useStandardButtons() { return false;}Why: some teams want every button written in one place, with no surprises. More: ControlMap.
How do I change the double-tap and hold times?
Section titled “How do I change the double-tap and hold times?”Where: Tunables.java (optional: leave the section out for these defaults).
/** Optional: the gesture times. Leave this section out to use these defaults. */public static class Buttons { public static double tapGapSec = 0.35; // the most time between the taps of a double-tap public static double holdSec = 0.3; // a press this long is a hold, not a tap}Why: some drivers tap slower; these times are in the tuning menu too. A value you tune there,
copy back into Tunables.java (the file is the record).
More: The button menu.
Buttons that are already built in
Section titled “Buttons that are already built in”- The button map: INIT shows every button and what it does. During the match, tap GUIDE.
- The tuning menu: hold GUIDE for 1 second (PRACTICE profile). A tap shows the map, a hold opens the menu.
- Two jobs on one button stop INIT with the button’s name, so a mistake is found before the match.
- Pause: tap gamepad 1 BACK.
More: Lesson 3: the button map.
Mechanisms
Section titled “Mechanisms”Each part is one line in Robot.java’s define(), plus a field for it at the top of the class (for
example public Lift lift;) and a section with its numbers in Tunables.java. The section’s name is
the configuration name in CamelCase (each word starts with a capital letter, no spaces or _): intake_deploy reads IntakeDeploy. Its standard buttons come with it.
How do I add an intake on a motor?
Section titled “How do I add an intake on a motor?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// At the top of the class: public Intake intake; Here in define():intake = add(Intake.motor("intake")); // Tunables.Intake: inPower, outPowerWhy: the intake brings its buttons (operator RT in, LT out) and its numbers (Tunables.Intake).
More: Lesson 4, Intake.
How do I add an intake on CR servos?
Section titled “How do I add an intake on CR servos?”A CR (continuous rotation) servo spins all the way round, like a small motor, instead of going to a position.
Where: Robot.java, inside define() (and Tunables.java for the numbers).
// At the top of the class: public Intake intake; Here in define():// two continuous-rotation (CR) servos. The one that spins the wrong way is reversed.intake = add(Intake.crServos("left_intake", "right_intake").reversed("right_intake"));Why: two servos that face each other must turn opposite ways to pull a piece in. The numbers are in Tunables.Intake too (inPower, outPower).
More: Intake.
How do I make the intake run from the start?
Section titled “How do I make the intake run from the start?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// Runs in from START until stopped. Its button (operator RT) then switches it on/off.add(Intake.motor("intake").onAtStart());Why: many robots collect all match, so the driver should not have to switch it on. More: Intake.
How do I add a lift with set heights?
Section titled “How do I add a lift with set heights?”Where: Robot.java (the part) and Tunables.java (its numbers).
// At the top of the class: public Lift lift; Here in define():// a slide on a motor with a switch at the bottom (the switch sets 0 each time it is pressed).// .part(...) is optional: the motor's catalog id, so the simulator moves it like the real// motor (without it the robot works the same). No switch? Delete .bottomSwitch(...).lift = add(Lift.slide("lift").part("gobilda-5203-435").bottomSwitch("lift_bottom"));public static class Lift { public static double min = 0, max = 2600; // the safe range, counts (Motor Finder) public static double kP = 0.004, kI = 0, kD = 0.0002, kF = 0.05; public static double down = 0, low = 900, high = 2400; // named heights: any names you like}Why: the lift never goes past min and max, so it cannot break itself; INIT stops if they are missing.
.part(...) is optional on a lift, intake, claw or wrist: it tells the simulator which motor or servo
it is. An arm, a turret and a launcher take the motor’s id in their brackets instead, and need it.
The numbers here are examples: find yours with the Motor Finder.
"lift" must be the motor’s name in your configuration. In .part("gobilda-5203-435"), 5203 is the
goBILDA motor series and 435 its RPM (on the motor’s label); the parts catalog lists them. To send it to a height: from a button.
More: Lesson 4, Lift,
why the bottom switch matters.
My lift goes down when it should go up. How do I fix it?
Section titled “My lift goes down when it should go up. How do I fix it?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// The lift goes DOWN on positive power (the Motor Finder says "add .reversed()"): flip it.add(Lift.slide("lift").reversed());Why: the lift’s limits and heights count up as it goes up, so positive power must move it up. More: Motor Finder, Troubleshooting.
How do I add an arm that moves in degrees?
Section titled “How do I add an arm that moves in degrees?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// An arm in degrees. Its motor's catalog id is in the brackets (not optional, like a turret's// or launcher's): it needs the motor's counts per turn to work in degrees.// gearRatio: motor turns per arm turn. 0 degrees = where it is at INIT.arm = add(Arm.motor("arm", "gobilda-5203-117").gearRatio(2));public static class Arm { public static double min = -10, max = 120; // degrees, 0 = where it is at INIT public static double kP = 0.02, kI = 0, kD = 0.001, kF = 0.1; public static double stowed = 0, pickup = 10, score = 95;}Why: degrees are easier to think about than encoder counts; INIT the program with the arm at its 0. More: Arm.
How do I add a claw?
Section titled “How do I add a claw?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// At the top of the class: public Claw claw; Here in define():claw = add(Claw.servo("claw")); // Tunables.Claw: open, closedpublic static class Claw { // From the Servo Finder's line "Tunables.Claw: P1 = 0.210, P2 = 0.770", P1 and P2 renamed. public static double open = 0.210, closed = 0.770; public static double travelSec = 0.4; // about how long it takes to move}Why: the claw brings its button (operator B opens and closes), and the Servo Finder gives the two numbers. More: Lesson 4, Claw.
How do I add a wrist?
Section titled “How do I add a wrist?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// At the top of the class: public Wrist wrist; Here in define():wrist = add(Wrist.servo("wrist")); // Tunables.Wrist: any names (down, level, up)Why: the wrist brings its buttons (operator D-pad left and right step through the positions). More: Wrist.
How do I add any servo with named positions (a gate, a fold-out)?
Section titled “How do I add any servo with named positions (a gate, a fold-out)?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// Any servo with named positions: a gate, a kicker, a fold-out intake.intakeDeploy = add(Positioner.servo("intake_deploy")); // Tunables.IntakeDeploy: stowed, deployed// Right after START (autonomous and TeleOp): fold the intake out.atStart(() -> Action.named("Deploy").then(() -> intakeDeploy.goTo("deployed")));Why: a Positioner has no standard buttons, so you choose when it moves (here: at START).
More: Positioner, an action at START.
How do I send a part to a position from a button?
Section titled “How do I send a part to a position from a button?”Where: Controls.java, inside buttons(...).
// The names are the ones in Tunables.java (capitals do not matter).g2.y.press(() -> robot.arm.goTo("score")).says("arm to score");g2.a.press(() -> robot.arm.goTo("pickup")).says("arm to pick up");g2.x.press(() -> robot.lift.goTo("high")).says("lift to high");g2.b.press(robot.claw::toggle).says("claw open/close");g2.back.press(() -> robot.intakeDeploy.goTo("stowed")).says("fold the intake in");Why: goTo("name") uses the position by its name, so the number lives only in Tunables.java.
These lines take Y, A, X and B from the lift and the claw (their standard buttons), on purpose; see
pick a free button.
More: Lift, Claw.
How do I add a launcher (flywheel and feeder)?
Section titled “How do I add a launcher (flywheel and feeder)?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// A flywheel (in RPM) and the indexer that feeds it one piece at a time.launcher = add(Launcher.flywheel("launcher", "gobilda-5203-6000") .feeder(Indexer.servo("indexer")));public static class Launcher { public static double speedRpm = 2600, stepRpm = 100, readyBandRpm = 60, idleRpm = 1500; public static double kP = 40, kI = 0, kD = 0, kF = 12.5; // from the Flywheel Finder // {inches to the goal, RPM}: blended between rows (from the Shot Tuner). public static double[][] speedByDistance = {{24, 2200}, {48, 2550}, {72, 2900}, {96, 3250}};}Why: the launcher only feeds a piece when the wheel is at speed, so every shot flies the same way. More: Lesson 9, Launcher.
How do I add a turret that follows the goal?
Section titled “How do I add a turret that follows the goal?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// The camera knows the goal of each alliance (BLUE first, then RED).camera = add(Camera.limelight("limelight").goal("BLUE SCORING", "RED SCORING"));// 5 motor turns per turret turn. follows(camera): it follows the goal from START,// but never turns past Tunables.Turret min and max.turret = add(Turret.motor("turret", "gobilda-5203-117").gearRatio(5).follows(camera));Why: the turret aims for the driver, and its limits keep the wires from twisting. More: Lesson 9: the turret, Turret, Camera.
How do I fire only when aimed, at the right speed?
Section titled “How do I fire only when aimed, at the right speed?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// The speed comes from the distance to the goal (Tunables.Launcher.speedByDistance),// and a piece is only fed when the turret points at the goal.launcher = add(Launcher.flywheel("launcher", "gobilda-5203-6000") .feeder(Indexer.servo("indexer")) .speedFromDistance(camera) .requireReady("turret aimed", () -> turret.isAimed()));Why: a shot fired before the turret is on target, or at the wrong speed, is a lost piece. More: Lesson 9.
Actions
Section titled “Actions”An action is a list of steps that use several parts, with waits between them. It never makes the robot
wait: it does a little every loop. Actions go in Actions.java, one method each. The same action works
on a button and in an autonomous.
How do I do several things one after the other?
Section titled “How do I do several things one after the other?”Where: Actions.java, inside the class.
/** Lift up, open the claw, lift down: one step after the other. */public static Action scoreHigh(Robot r) { return Action.named("Score high") .then(() -> r.lift.goTo("high")) .waitUntil("lift at the top", r.lift::isAtTarget, 2.0) // at most 2 s .then(r.claw::open) .waitSeconds(0.3) // let the piece drop .then(() -> r.lift.goTo("down"));}Why: each step starts when the one before it is done, and the driver can still drive the whole time. More: Lesson 5: scoreHigh, Action.
How do I repeat steps several times?
Section titled “How do I repeat steps several times?”Where: Actions.java, inside the class.
/** Pulses the intake 3 times: the for loop adds the same steps 3 times to the action. */public static Action pulseThree(Robot r) { Action a = Action.named("Pulse three"); for (int i = 0; i < 3; i++) { // runs once, when the action is made: it never waits a.then(r.intake::in) .waitSeconds(0.4) .then(r.intake::stop) .waitSeconds(0.3); } return a;}Why: the for loop runs once, while the action is made, and writes the steps 3 times; the action then does them one after the other without making the robot wait.
More: Action.
How do I move two parts at the same time?
Section titled “How do I move two parts at the same time?”Where: Actions.java, inside the class.
/** Two parts at once: then(a, b) starts both in the same loop. */public static Action grabAndLift(Robot r) { return Action.named("Grab and lift") .then(r.claw::close, r.intake::stop) // both at once .waitUntil("claw closed", r.claw::isDone, 1.0) // isDone: it should be there .then(() -> r.lift.goTo("low"));}Why: then(a, b) starts both parts in the same loop, so neither waits for the other.
More: Lesson 5: grabAndLift.
How do I wait half a second (like sleep in Blocks)?
Section titled “How do I wait half a second (like sleep in Blocks)?”Search words: sleep, wait, pause, delay. In an action (Actions.java):
/** Opens the claw, waits half a second, then lowers the lift. */public static Action dropAndLower(Robot r) { return Action.named("Drop and lower") .then(r.claw::open) .waitSeconds(0.5) // half a second; the robot keeps running .then(() -> r.lift.goTo("down"));}In an autonomous (inside steps(...)): the step gets a name first, for the report:
s.run("Open the claw", robot.claw::open) .waitSeconds("Let the piece drop", 0.5) // a name for the report, then the seconds .run("Lift down", () -> robot.lift.goTo("down"));Why: sleep() stops the whole robot (no driving, no other part); waitSeconds waits while everything keeps running.
More: Lesson 5: the rules, Non-blocking code.
How do I wait until something is true?
Section titled “How do I wait until something is true?”Where: Actions.java, inside the class.
/** Lowers the lift, and opens the claw only once the lift is down. */public static Action lowerAndRelease(Robot r) { return Action.named("Lower and release") .then(() -> r.lift.goTo("down")) // Waits until the test is true. If 2 s pass first, the action STOPS here and says why. .waitUntil("lift down", () -> r.lift.isAt("down"), 2.0) .then(r.claw::open);}Why: every wait has a time limit, so a stuck part can never freeze the action forever. More: Lesson 5: the rules.
How do I start an action from a button?
Section titled “How do I start an action from a button?”Where: Controls.java, inside buttons(...).
// Press Y: the action starts. Press Y again while it runs: it is cancelled, safely.g2.y.press(Actions.scoreHigh(robot)).says("score high");g2.x.press(Actions.grabAndLift(robot)).says("grab and lift");Why: the same button cancels it, and a stick or button that moves the same part also stops it: the driver always wins. More: Lesson 5: start it from a button.
How do I stop every running action?
Section titled “How do I stop every running action?”Where: Controls.java, inside buttons(...).
// Stop every running action at once (each stops what it started, safely).g2.back.press(robot::cancelAll).says("stop all actions");Why: a stopped action leaves each part safe: a lift holds its height, an intake stops. More: RobotDefinition: for your code.
How do I run an action right after START?
Section titled “How do I run an action right after START?”Where: Robot.java, inside define().
// In Robot.java's define(): this action runs once, right after START (autonomous and TeleOp).atStart(() -> Actions.deployIntake(this));Why: nothing may move in INIT (a rule), so a fold-out waits for START. More: Lesson 5: an action at START.
How do I use the same action in an autonomous?
Section titled “How do I use the same action in an autonomous?”Where: a file in autos/, inside steps(...).
s.start("Against our wall, facing the field", 0) .move("To the goal", 24, 0, 0.5) .action(Actions.scoreHigh(robot), 5) // waits until it is done (at most 5 s) .move("Back up", 12, 180, 0.5);Why: write the move once, use it in TeleOp and autonomous; the report says if it timed out. More: Lesson 5: in an autonomous.
Autonomous
Section titled “Autonomous”An autonomous is one file in autos/: a list of named steps. Distances are in inches (1 foot = 12
inches, so 2 feet = 24), directions in degrees (0 = forward, 90 = right), power from 0 to 1.
The robot only drives after the Drive Tuner. Until then the autonomous shows “AUTONOMOUS IS OFF”.
Run the Drive Tuner, copy its lines, then set
calibrated = true in Tunables.java, section Drive. The simulator runs it anyway, so you can try a
route before tuning.
How do I make my first autonomous (drive 2 feet)?
Section titled “How do I make my first autonomous (drive 2 feet)?”Where: a new file in autos/ (in Android Studio: right-click the folder, New, Java Class). The whole file:
// The name on the Driver Station, and the TeleOp it picks next (use YOUR TeleOp's name).@Autonomous(name = "Cookbook Leave the Start", group = "Cookbook", preselectTeleOp = "Cookbook TeleOp")@Disabled // delete this line in your copy, or the Driver Station will not list itpublic class LeaveTheStart extends Auto<Robot> { @Override protected void steps(Steps s, Robot robot) { // s = the list of steps, robot = your Robot.java. Step lines go here: s.start("Against our wall, facing the field", 0) // the note the drive team reads .move("Leave the start", 24, 0, 0.4); // 24 inches = 2 feet, forward, 0.4 power }}Why: preselectTeleOp makes the Driver Station pick your TeleOp next, so the drivers only press INIT.
More: Lesson 6: the file, line by line.
How do I drive forward, sideways and turn?
Section titled “How do I drive forward, sideways and turn?”Where: a file in autos/, inside steps(...).
s.start("Against our wall, facing the field", 0) .move("Forward", 24, 0, 0.5) // inches, direction (0 = forward), max power .move("Sideways to the right", 12, 90, 0.5) // mecanum only .turn("Turn left 90", 90, 0.4) // turns: counterclockwise (left) is + .turn("Turn right 90", -90, 0.4) // so a right turn is minus .turnTo("Face the start way", 0, 0.4); // to a heading: 0 = the way it faced at the startWhy: each move is measured from where the robot is now, so the steps read like directions to a person. More: Driving: move, turn, turnTo.
Does my autonomous need a RED version?
Section titled “Does my autonomous need a RED version?”No. Write it once, for BLUE. On BIOBUZZ’s field (turned half a turn between the alliances) add this to the autonomous class:
// BIOBUZZ's field is turned half a turn between the alliances: one route works for both.@Overrideprotected boolean mirrorForRed() { return false;}Why: RED then uses the same numbers, seen from the red wall; on a mirrored field (the default), RED is flipped for you. More: The autonomous builder: placing the robot.
How do I let the drivers choose before the match?
Section titled “How do I let the drivers choose before the match?”Where: a file in autos/, inside steps(...).
s.start("Against our wall, facing the field", 0) .menuYesNo("Score", true) // the drivers choose in INIT with the D-pad .menuChoice("Park", "Near", "Far") .onlyIf("Score") // these steps run only if "Score" is yes .move("To the goal", Tunables.Auto.toGoalInches, 0, 0.5) .action(Actions.scoreHigh(robot), 5) .end() // never forget end()Why: one file covers several plans, and the drivers choose in INIT with the D-pad (alliance and start delay come free). More: The INIT menu, Branches: onlyIf.
How do I make sure there is time to park?
Section titled “How do I make sure there is time to park?”Add parkWithSeconds before the park steps (this follows the menu recipe above):
.parkWithSeconds(4) // the steps below always get the last 4 s.onlyIf("Park", "Near") .move("Park near", Tunables.Auto.parkInches, 90, 0.7).end().onlyIf("Park", "Far") .move("Park far", Tunables.Auto.parkInches * 2, 90, 0.7).end();Why: when 4 s are left, the main steps stop, so a slow step can never eat the parking points. More: Keep time to park.
How do I run a mechanism while the robot drives?
Section titled “How do I run a mechanism while the robot drives?”Where: a file in autos/, inside steps(...).
s.start("Against our wall, facing the field", 0) .run("Intake on", robot.intake::in) // starts it and goes on at once .run("Lift up", () -> robot.lift.goTo("high")) .move("To the pieces", 24, 0, 0.4) // the intake and lift work while it drives .waitUntil("Lift at the top", robot.lift::isAtTarget, 2.0) .run("Intake off", robot.intake::stop);Why: run starts a part and goes on at once; waitUntil waits only when the next step needs it.
More: Mechanisms: run and wait. Whole actions while driving: alongside(...) (recipes).
How do I wait for my alliance partner?
Section titled “How do I wait for my alliance partner?”Where: a file in autos/, inside steps(...).
s.menuYesNo("Wait for partner", false) .onlyIf("Wait for partner") .waitSeconds("Let our partner pass", 3) // the robot keeps running while it waits .end();Why: the drivers decide in INIT; for a wait before the first step, the menu already has a start delay. More: The autonomous builder: recipes.
How do I write a step the list cannot say?
Section titled “How do I write a step the list cannot say?”Where: a file in autos/, inside steps(...).
// Your own step, for anything the list cannot say. Wait with ctx, never sleep().s.step("Clear the intake", ctx -> { robot.intake.out(); ctx.waitFor(0.3); // every part keeps working while it waits robot.intake.stop();});Why: ctx.waitFor keeps every part updating and the timer working; sleep() would freeze them.
More: Your own steps.
How do I change a distance without building the code again?
Section titled “How do I change a distance without building the code again?”Where: Tunables.java: your own section (no part reads it). Use it in a step as Tunables.Auto.leaveInches.
/** Your own numbers: a section no part reads. Every number here is in the tuning menu too. */public static class Auto { public static double leaveInches = 24, toGoalInches = 30, parkInches = 20; public static double goalX = -30, goalY = -30, goalHeading = 45; public static double parkX = -56, parkY = -36;}Why: every number in your own section is in the tuning menu: change it in the TeleOp, save (A), and
the autonomous uses it from its next INIT. Then copy the good value into Tunables.java: a value
saved only on the hub is lost with a new Control Hub.
More: Lesson 6: route numbers, Tunables.java.
How do I share the same first steps in several autos?
Section titled “How do I share the same first steps in several autos?”Where: your autonomous class (or a shared file), next to steps(...).
/** The same first steps for several autos: a method that adds them to the list. */static Steps scorePreload(Steps s, Robot robot) { return s.move("To the goal", Tunables.Auto.toGoalInches, 0, 0.5) .action(Actions.scoreHigh(robot), 5);}// in each autonomous: scorePreload(s.start("...", 0), robot).move("Park", 20, 90, 0.7);Why: fix the opening once and every autonomous gets the fix. More: The autonomous builder: recipes.
Odometry and driveTo
Section titled “Odometry and driveTo”Odometry tells the robot where it is on the field. Field numbers: x = inches right of the centre line, y = inches away from your wall (negative = your half), heading 0 = facing away from you, counterclockwise (left) is +. See the field map.
How do I add a goBILDA Pinpoint?
Section titled “How do I add a goBILDA Pinpoint?”Where: Robot.java, inside define(), after the drive line.
// After drive(...). The offsets, mm: first how far LEFT of the centre the forward pod is// (right = minus: here 84 mm right), then how far FORWARD the sideways pod is (behind = minus).// Not sure? Write offsetsMm(0, 0): the Odometry Tuner measures them.odometry(Odometry.pinpoint("pinpoint").offsetsMm(-84, -168));Why: the offsets tell it where the pods are, so a turn on the spot does not look like a move. More: Lesson 7: one line in Robot.java, Pinpoint.
How do I add a SparkFun OTOS?
Section titled “How do I add a SparkFun OTOS?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// Where the OTOS sits: inches forward and left of the robot's centre, and its turn in degrees.odometry(Odometry.otos("otos").mountIn(3.5, 0, 0));Why: the OTOS measures at its own spot, so it must know where it sits to give the robot centre’s place. More: OTOS.
How do I add dead wheels?
Section titled “How do I add dead wheels?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
// The configuration names of the motor ports the pods plug into: left, right, sideways pod.// trackWidthIn: left pod to right pod. perpForwardIn: the sideways pod, 6 in BEHIND the centre.odometry(Odometry.threeWheels("par_left", "par_right", "perp") .trackWidthIn(13.2).perpForwardIn(-6));Why: each pod’s encoder plugs into a motor port, so the names are those ports’ configuration names. More: Three dead wheels.
How do I tune the odometry?
Section titled “How do I tune the odometry?”Where: a new file in TeamCode (one per robot). The whole file:
// The Odometry Tuner: which way each pod counts, the offsets, and a loop check.@TeleOp(name = "Cookbook Odometry Tuner", group = "Finders")@Disabled // delete this line in your copy, or you will not see itpublic class OdometryTuner extends OdometryTunerOpMode { @Override protected RobotDefinition robot() { return new Robot(); // your Robot.java: the tuner uses its parts and numbers }}Why: it checks which way each pod counts and measures the offsets; then set Tunables.Odometry.checked = true, or driveTo does nothing on the robot.
It is its own small file because each program in the Driver Station’s list is one class; robot() makes
it use your Robot.java, so it measures the same odometry line that autonomous uses.
More: Lesson 7: the Odometry Tuner, Finders.
How do I drive to a point on the field?
Section titled “How do I drive to a point on the field?”Where: a file in autos/, inside steps(...). Needs a mecanum drive.
// The start spot (x, y, heading) must be where the robot really stands: the pose starts there.s.start("Against our wall, in the left corner tile, facing the field", -56, -61, 0) .driveTo("To the goal", Tunables.Auto.goalX, Tunables.Auto.goalY, Tunables.Auto.goalHeading, 0.6) .parkWithSeconds(4) // The last number: "close enough", in inches (default 1). .driveTo("Park", Tunables.Auto.parkX, Tunables.Auto.parkY, 0, 0.7, 2.0);It replaces .move(...): you give a place on the field, not a distance.
Why: driveTo keeps checking where the robot is, so a bump or wheel slip does not matter: it ends on the point.
On the robot it only works after the Odometry Tuner and checked = true in Tunables.java, section Odometry.
More: Lesson 7: drive to field points, driveTo.
How do I show where the robot is?
Section titled “How do I show where the robot is?”Where: Controls.java, inside buttons(...).
show("Where", () -> String.format(Locale.US, "x %.0f, y %.0f in, heading %.0f", robot.pose().x, robot.pose().y, robot.pose().headingDeg));Why: you can check the odometry by pushing the robot by hand and watching the numbers. More: robot.pose(). (TeleOp also starts where autonomous ended: lesson 7.)
How do I use the robot’s position in an action?
Section titled “How do I use the robot’s position in an action?”Where: Actions.java, inside the class.
/** Runs the intake while the robot is on our half of the field (y below 0). */public static Action intakeOnOurHalf(Robot r) { return Action.named("Intake on our half") .then(r.intake::in) .waitUntil("past the centre line", () -> r.pose().y > 0, 10.0) .then(r.intake::stop);}Why: robot.pose() is fresh every loop, so a wait can test where the robot is.
More: robot.pose().
Pedro paths
Section titled “Pedro paths”Needs the pedro folder in your project (add Pedro),
a mecanum drive and tuned odometry. Paths use the same field numbers as driveTo.
How do I add Pedro Pathing to Robot.java?
Section titled “How do I add Pedro Pathing to Robot.java?”Where: Robot.java, inside define() (and Tunables.java for the numbers).
odometry(Odometry.pinpoint("pinpoint").offsetsMm(-84, -168)); // Pedro needs odometry// Pedro drives the paths with the drive and odometry lines above. PedroConstants holds the// numbers from Pedro's Foresight tuner (docs/modules/pedro.md#tuning-pedro).follower(Pedro.follower(PedroConstants.foresight));Why: Pedro uses your drive and odometry lines, so there is only one robot description. More: Lesson 8: Robot.java, Tuning Pedro.
How do I make a straight path and a curve?
Section titled “How do I make a straight path and a curve?”Where: your autonomous class, above steps(...).
// Make each path once. A line: from (x1, y1) to (x2, y2). A curve: start, a control point// that pulls the curve toward it (the robot does not drive through it), end.static final PedroPath OUT = PedroPath.line(-56, -61, -56, -36).keepHeading(0);static final PedroPath TO_GOAL = PedroPath.curve(-56, -36, -44, -36, -30, -30).turnTo(0, 45);Why: a path is made once (static final) because it is the same every match.
More: PedroPath.
How do I follow a path in an autonomous?
Section titled “How do I follow a path in an autonomous?”Where: a file in autos/, inside steps(...).
s.start("Against our wall, in the left corner tile, facing the field", -56, -61, 0) .follow("Out", OUT) // brakes to a stop at its end .follow("To the goal", TO_GOAL) .run("Intake on", robot.intake::in) .follow("Back", WATCH_GOAL) .parkWithSeconds(4) .driveTo("Park", -56, -36, 0, 0.7); // driveTo works too (it ends any hold first)Why: each follow brakes to a stop at its end; start the next path where the last one ended.
More: Lesson 8: your first path, follow.
How do I drive two paths with no stop, slow down, or hold at the end?
Section titled “How do I drive two paths with no stop, slow down, or hold at the end?”Where: your autonomous class, above steps(...).
// Two parts with NO stop between them (then), a slow part near the goal (maxSpeed),// and hold the end point while it scores (holdEnd: at most 5 s).static final PedroPath IN_ONE_GO = PedroPath.line(-56, -61, -48, -50).keepHeading(0) .then(PedroPath.curve(-48, -50, -44, -36, -30, -30)).turnTo(0, 45) .maxSpeed(0.6) .holdEnd();Why: then saves the time of a stop, and holdEnd pushes back if another robot bumps yours while it scores.
More: PedroPath, Hold the end point.
How do I keep facing the goal along a path?
Section titled “How do I keep facing the goal along a path?”Where: your autonomous class, above steps(...).
// Keep facing one field point the whole way (here x -60, y 60: put your goal's x and y).static final PedroPath WATCH_GOAL = PedroPath.line(-30, -30, -56, -36).facePoint(-60, 60);Why: the robot can slide along the path while it keeps the camera or launcher on the goal. More: PedroPath: the heading.
Telemetry
Section titled “Telemetry”How do I show a value on the Driver Station screen?
Section titled “How do I show a value on the Driver Station screen?”Where: Controls.java, inside buttons(...).
// A line of its own in buttons(...). The value is read again every loop.show("Lift ticks", robot.lift::position);show("Intake on", robot.intake::isRunning); // shows "Intake on : true"show("Flywheel ready", () -> robot.launcher.isReady());Why: you see what the robot “thinks” while you drive. The value must be quick to read (no waiting). More: The button menu: feedback. Debug lines (loop time and more): press gamepad 1 START + BACK.
How do I round a number on the Driver Station?
Section titled “How do I round a number on the Driver Station?”Where: Controls.java, inside buttons(...).
// Round a number: %.0f = no decimals, %.1f = one decimal. Locale.US keeps the dot (not a comma).show("Target RPM", () -> String.format(Locale.US, "%.0f", robot.launcher.targetRpm()));Why: %.0f means “a number with 0 decimals”; %.2f gives 2.
More: The button menu: feedback.
How do I show lines from my own autonomous step?
Section titled “How do I show lines from my own autonomous step?”Where: a file in autos/, inside steps(...).
// In your own step: lines on the Driver Station while it runs, and a note in the report.s.step("Spin up", ctx -> { robot.launcher.spinUp(); double giveUpAt = ctx.secondsLeft() - 3; // a time limit: at most 3 s while (ctx.isActive() && !robot.launcher.isReady() && ctx.secondsLeft() > giveUpAt) { ctx.telemetry().line("Flywheel", "spinning up to %.0f RPM", robot.launcher.targetRpm()); ctx.loop(); // updates every part once: the robot keeps running } ctx.note(robot.launcher.isReady() ? "at speed" : "not at speed after 3 s");});Why: ctx.loop() keeps the robot running inside your loop, and the time limit makes sure the loop ends.
More: Your own steps, The report.
Tuning and finders
Section titled “Tuning and finders”Where do the numbers go?
Section titled “Where do the numbers go?”In Tunables.java, one section per part (see the lift recipe).
A finder shows lines like Tunables.Claw: P1 = 0.210, P2 = 0.770: copy them into that section (keep the other lines) and give
P1 and P2 real names. A number you leave out uses the library’s default (INIT says which), except a
lift’s or arm’s min and max: those must be written.
More: Tunables.java, Finding your numbers.
How do I find a servo’s open and closed numbers?
Section titled “How do I find a servo’s open and closed numbers?”Where: a new file in TeamCode (also in the starter kit). The whole file:
// The Servo Finder: lists every servo in the configuration. No robot needed.@TeleOp(name = "Cookbook Servo Finder", group = "Finders")@Disabled // delete this line in your copy, or you will not see itpublic class ServoFinder extends ServoFinderOpMode {}Use it: robot on a block. Run “Cookbook Servo Finder” (your name for it). Choose the servo (D-pad, A).
A servo cannot say where it is, so choose a start number close to where it sits now (D-pad, A). Nudge it
with the D-pad. At each end of its safe travel (just before it pushes on the frame) press X, then B. Then
move it to each position you need and press A: for example fully open, A (that is P1); closed, A (P2). Copy the line it shows (Tunables.Claw: P1 = 0.210, P2 = 0.770)
into Tunables.java and rename P1, P2 (open, closed).
Why: a servo pushed past its end hums, gets hot and breaks; the finder finds the safe numbers.
More: Servo Finder, the order.
How do I find a lift’s limits and heights?
Section titled “How do I find a lift’s limits and heights?”Where: a new file in TeamCode (also in the starter kit). The whole file:
// The Motor Finder: lists every motor in the configuration. No robot needed.@TeleOp(name = "Cookbook Motor Finder", group = "Finders")@Disabled // delete this line in your copy, or you will not see itpublic class MotorFinder extends MotorFinderOpMode {}Use it: put the lift all the way down first (the count starts at 0 there). Choose the motor (D-pad,
A). Move it slowly with the right stick. Press X just above the bottom and B just below the top, then
A at each height you need. Copy the line it shows (Tunables.Lift: min = 0, max = 2580, P1 = 1200)
into Tunables.java and rename P1 (low).
Why: a wrong top limit drives the lift into its hard stop; measured limits keep it safe.
More: Motor Finder.
How do I change a number while the robot runs?
Section titled “How do I change a number while the robot runs?”No code needed with the five files. In the TeleOp (PRACTICE profile), after START, hold GUIDE for
1 second: the tuning menu opens. D-pad up/down chooses, right/left changes, A saves it on the
hub, B lists the changed values. Then copy each good value into Tunables.java: a value saved
only on the hub is lost with a new Control Hub, and your teammates cannot see it.
Why: you see the effect at once, with no rebuild. More: Tuning, Lesson 4: tune the lift live.
How do I tune the drive for autonomous?
Section titled “How do I tune the drive for autonomous?”Where: a new file in TeamCode. The whole file:
// The Drive Tuner: wheel size and strafe correction, for the drive in your Robot.java.@TeleOp(name = "Cookbook Drive Tuner", group = "Finders")@Disabled // delete this line in your copy, or you will not see itpublic class DriveTuner extends DriveTunerOpMode { @Override protected RobotDefinition robot() { return new Robot(); // your Robot.java: the tuner uses its parts and numbers }}Why: it measures the real wheel size and strafe correction; copy its lines, then set Tunables.Drive.calibrated = true.
More: Lesson 6: tune the drive first, Drive Tuner.
How do I set the flywheel numbers?
Section titled “How do I set the flywheel numbers?”Run the Flywheel Finder (in the starter kit), then copy its line into
Tunables.Launcher (see the launcher recipe).
Why: the right kP..kF make the wheel get back to speed fast after each shot.
More: Flywheel Finder.
How do I build a shot table (speed for each distance)?
Section titled “How do I build a shot table (speed for each distance)?”Where: a new file in TeamCode. Run the Aim Tuner first. The whole file:
// The Shot Tuner: fills Tunables.Launcher.speedByDistance from shots you mark as good.@TeleOp(name = "Cookbook Shot Tuner", group = "Finders")@Disabled // delete this line in your copy, or you will not see itpublic class ShotTuner extends ShotTunerOpMode { @Override protected RobotDefinition robot() { return new Robot(); // your Robot.java: the tuner uses its parts and numbers }}Why: it fills Tunables.Launcher.speedByDistance from shots you mark as good, so the launcher picks the speed for each distance.
More: Shot Tuner.
Simulator
Section titled “Simulator”The simulator runs your real code on a laptop, with the field in 3D. Type these commands in a terminal:
in Android Studio, the Terminal tab at the bottom (how).
First go into the ftc-helper-lib folder by typing cd ftc-helper-lib. (It is the library’s folder
inside your robot project, next to TeamCode in Android Studio’s Project panel. Not there? Ask your
mentor, or see Add it to a project.) ./gradlew runs Gradle, the tool that builds the code; -P... gives it a setting.
How do I start the simulator?
Section titled “How do I start the simulator?”./gradlew :sim:runThen open http://localhost:8780 in a browser. Choose an OpMode, press INIT, then START.
Why: you can try buttons and autonomous routes before the robot is built. More: Simulator: start it.
How do I see my own robot in the simulator?
Section titled “How do I see my own robot in the simulator?”./gradlew :sim:fromRobot -PteamCode=../TeamCode./gradlew :sim:run -PteamCode=../TeamCodeWhy: the first command makes the simulator’s robot file from your Robot.java; the second runs your TeamCode.
More: A robot file from Robot.java, Your own robot code.
How do I test my code with a script?
Section titled “How do I test my code with a script?”Write a scenario file in your robot project, for example simulator/scenarios/intake.sim (the library
runs this same file as a test: sim/scenarios/cookbook-intake.sim):
init "Quick Start TeleOp" # your TeleOp's namestarthold g2.rt 1 # hold RT (the intake runs while it is held)expect within 1 motor intake power > 0.5release g2.rtexpect within 1 motor intake power = 0 0.01expect error noneRun it:
./gradlew :sim:scenario -Pfile=../simulator/scenarios/intake.sim -PteamCode=../TeamCode-PteamCode=../TeamCode runs your own code (it is also the default when the library sits in your robot project).
Why: a script checks the same things every time, with no clicking, and catches a mistake before a meeting.
Button names in a scenario are the FTC names, not the Java ones: g2.rt, g2.lb, g2.a are the same,
but g2.dpadUp is g2.dpad_up and g2.leftStickY is g2.left_stick_y.
More: Scenario files (every command and button name).
Not here?
Section titled “Not here?”- Every class and method: the reference and the module pages.
- Something goes wrong: Troubleshooting.
- Learn it step by step: the lessons.
- A question this page should answer? Tell us.