Skip to content
Work in progress. This library and its docs are new and still changing, and some parts are not yet tested on a real robot. Something wrong or unclear?Tell us (a GitHub issue).

Core module

Package: com.crossbotics.ftchelper.core

Learn it hands-on: 12 Organize a whole robot, 14 Mechanisms, 15 Telemetry, profiles and tuning.

The small pieces every other part of the library is built on. You meet them when you write your own mechanism class (Subsystem), switch between practice and competition settings (Profile), or describe your drivetrain once for every program (RobotConfig). With the robot in five files you rarely use them by name. To see how they fit together, start with tutorial 12.

Class Use it to
Subsystem give any mechanism the same shape: update it every loop, stop it, show it on the screen
Subsystems update, stop and show a group of mechanisms together
Profile behave differently in PRACTICE and COMPETITION
SettingsFile keep small settings on the Control Hub between restarts
RobotConfig describe the drivetrain once, for TeleOp and every autonomous

An interface: a promise that a class has these methods. Every mechanism helper in the library (PositionMotor, SpinMotor, Flywheel, Launcher, Turret, cameras…) is a Subsystem.

Method Required? What it is for
update() yes called once every loop; the one place that talks to the hardware
stop() no (default: nothing) stop all motion; called at the end of every OpMode
addTelemetry(RobotTelemetry t) no add this part’s lines (standard and debug)
name() no (default: class name) shown in telemetry

Why one shape for everything? Because then ONE line updates the whole robot, autonomous can keep every mechanism working during a drive move, and next season’s new mechanism plugs in without changing anything else.

Your own mechanism:

// "implements Subsystem": Bucket promises to have update() (and may add stop(), addTelemetry())
public class Bucket implements Subsystem {
private final PositionServo servo;
private boolean dumping = false; // the state: what the bucket should be doing
// Runs once, when the robot code makes the Bucket (in INIT). "bucket" = configuration name.
public Bucket(HardwareMap hw) {
servo = PositionServo.create(hw, "bucket").position("CARRY", 0.3).position("DUMP", 0.9);
}
public void dump() { dumping = true; } // commands only change state: quick, safe from a button
public void carry() { dumping = false; }
// @Override: fills in a method Subsystem asks for. "a ? b : c" = if a then b, else c.
@Override public void update() { servo.goTo(dumping ? "DUMP" : "CARRY"); servo.update(); }
@Override public void addTelemetry(RobotTelemetry t) { t.line("Bucket", dumping ? "DUMP" : "carry"); }
}

The pattern: commands change a little state and are quick (safe from a button); update() turns that state into hardware actions. (A servo command may move the servo at once; never command anything in INIT unless moving then is safe.)

A group of subsystems that acts like one.

Subsystems robot = new Subsystems().add(lift, claw, intake); // null parts are skipped
robot.update(); // each, in order
robot.addTelemetry(t);
robot.stop(); // stops each, even if one throws an error
Method What it does
add(parts...) adds parts (null is skipped, for optional hardware)
update(), stop(), addTelemetry(t) does it for every part, in order. update() times each part: a part slower than 25 ms (it waits or sleeps) shows a warning
getSlowUpdateWarning() “” or the last slow part (shown for 5 s after the last slow update)
all() the list of parts

Subsystems is itself a Subsystem, so groups can contain groups.

PRACTICE or COMPETITION. Saved on the Control Hub, so it stays until someone changes it. Default: COMPETITION (the safe one).

PRACTICE COMPETITION
RobotTelemetry debug lines start ON start OFF
TuningMenu can open stays closed
EasyAuto INIT screen warns “PRACTICE profile” -
Method What it does
Profile.current() the active profile
Profile.set(p) changes and saves it (or use InitMenu.profile())
Profile.pick(practiceValue, competitionValue) one value or the other; works with numbers, true/false, anything
profile.isPractice() true for PRACTICE
// Profile.pick(value in PRACTICE, value in COMPETITION)
c.powerLimit = Profile.pick(0.6, 1.0); // gentle in the shop
boolean showCamera = Profile.pick(true, false); // live view only in practice

Full guide: Profiles.

A tiny file on the Control Hub that keeps settings as name = value pairs: /sdcard/FIRST/ftc-helper/settings.properties. Profile and Tunable use it; you rarely need it yourself.

Method What it does
SettingsFile.shared() the robot’s settings file
get(key, fallback), getDouble(key, fallback), has(key) read
put(key, value), remove(key) change in memory
save() write the file; returns false if it could not (the robot keeps working). It waits until the file is written: use it in INIT or tests
saveInBackground(), saveInBackground(ok -> ...) write the file without waiting (on a background thread) and return at once. Use this while the robot runs.
SettingsFile.useForTests(file) tests only: use another file

A damaged or missing file simply means “nothing saved”: the values in the code are used.

The drivetrain described ONCE: motor names and directions, autonomous tuning, and how the Control Hub is mounted. EasyTeleOp and EasyAuto both read it from their robotConfig(), so TeleOp and every autonomous always match. Change a motor name here and every program gets it.

public class RobotSettings extends RobotConfig {
@Override public DriveConfig drive() {
DriveConfig c = DriveConfig.mecanum();
c.frontLeft = "front_left"; // ... the other three names
c.powerLimit = Profile.pick(0.6, 1.0);
return c;
}
@Override public AutoDriveConfig auto() { ... } // optional: only autonomous uses it
@Override public IMU imu(HardwareMap hardwareMap) {
return ImuSetup.get(hardwareMap, "imu", LogoFacingDirection.UP, UsbFacingDirection.FORWARD);
}
}
// in every TeleOp and autonomous:
@Override protected RobotConfig robotConfig() { return new RobotSettings(); }
// (the tutorials' RobotSettings uses static methods instead: return RobotSettings.config();)
Method Required? What you return
drive() yes the DriveConfig. May be called more than once: make a fresh one each time.
auto() no the AutoDriveConfig. Default: untuned (autonomous refuses to run until you tune it)
imu(hardwareMap) yes the IMU, usually one ImuSetup.get line. Every Control Hub has one. (Null = no IMU: TeleOp is robot-centric only, and autonomous refuses to start.)

The mechanisms are not in here: they go in your own Robot class, so your programs can reach them by name (robot.lift). See the starter kit.