Heads up: WPILib 2027 is still in alpha. These pages are changing quickly and aren't stable or finished yet — expect edits as the APIs settle.
Gray Matter LogoGray Matter Workshop

Command-Based Framework

KEY CONCEPT

The Command-Based Framework

Command-based programming organizes robot code into three pieces: Triggers (when), Mechanisms (what hardware), and Commands (the actions to run on that hardware). The scheduler is the loop that ties them together: it watches Triggers, schedules Commands, and tracks which command owns which Mechanism so two commands never fight for the same motor.

Commands V3 gives each piece a concrete type: Mechanism for hardware, Command for the actions, and Scheduler for the loop. On this team, most commands are holds (they keep re-sending a setpoint so the motor stays actively commanded) and routines are built by chaining commands together.

The top-level wiring: a Robot class owns the mechanisms, and each mode (driver teleop, an autonomous routine, a calibration task) is its own OpMode class. You'll see that on the Triggers and Running the Program pages.

↳ TAKEAWAY

Triggers schedule Commands. Commands operate on Mechanisms. The Scheduler enforces who-owns-what so nothing collides.

WHEN

Triggers

BooleanSuppliers wired to commands

Buttons, sensor predicates, custom expressions: anything that evaluates to a boolean. Bindings are scoped (global / opmode / command), so they clean themselves up when the scope exits.
WHAT

Mechanisms

One physical thing each

An arm, a flywheel, the drivetrain. The type is Mechanism, a class you extend. Hardware lives in private fields, configuration in the constructor.
HOW

Commands

Named actions, mostly holds

Factory methods on a mechanism, each returning a named Command. Most of ours are holds: they keep the motor at a setpoint until something else takes over.

The big picture

How Command-Based Programming Works

Triggers

Controller buttons, sensors, or custom conditions

"While button A is held..."

Commands

Actions the robot performs — most are holds

"...run the 'scoring (hold)' command"

Mechanisms

One class per physical thing (arm, flywheel, etc.)

"...which controls the Arm mechanism's motor"

Motors & Sensors

Physical robot hardware

"...to physically move the arm up"

Sensors provide feedback: Position, velocity, and status information flows back up to help Commands make decisions

Real Example: Raising an Arm

1. Trigger: Driver holds button A (whileTrue)

2. Command:the arm's "scoring (hold)" command starts running

3. Mechanism: the Arm re-sends the scoring target every tick — the arm reaches it and stays there

4. Hardware: Motor holds the angle, encoder measures position

5. Release A:the hold is cancelled and the arm's default command takes back over

Holds: what our commands actually are

Our mechanism commands (arm.scoring(), flywheel.spinUp(), robot.stow()) are holds: they keep re-sending their setpoint forever, so the motor stays actively commanded. The plain-words version: hold the A button, the arm goes to the scoring angle and stays there, fighting gravity; let go, and the arm's default command takes back over.

THE ONE RULE

A hold never finishes, so nothing may ever wait on a hold

Put a hold inside Command.sequence(...) and the sequence sticks on it forever: the hold has no finish line, so the next step never starts. Every hold is named with (hold) so you can catch this: if a stuck routine is sitting on a (hold)command on the dashboard or in the log, that's the bug. The fix is to give the hold a finish line at the call site: arm.scoring().until(arm::isAtTarget).

Chaining: how routines get built

Routines, especially autos, are built by chaining. Chaining is as far as most routines ever need to go, and it takes just three tools, learned in this order:

  1. Command.sequence(a, b, c): steps that finish on their own (a DriveToPose leg, for example) can sit in a sequence as-is.
  2. .until(mech::isAtTarget): gives a hold a finish line, right where you need one. This is always applied at the call site; mechanisms never bake waiting into their command factories.
  3. Command.race(step, hold): "do this step WHILE holding that pose." A race ends when its first member finishes and cancels the rest. And since a hold never finishes, the step is always what decides.

Plus one seatbelt: .withTimeout(Seconds.of(...))on any step that waits on a sensor condition, so an auto never burns the whole period stuck at a setpoint it can't quite reach.

(robot.stow() is a robot-level preset the template defines: it drives the arm to its stowed angle, which is why the finish line checks robot.arm::isAtTarget. Your own holds will usually live on a mechanism, like arm.stowed().)

DriveStowDriveChainedOpMode.java — the full working example

Decorators

A decorator is a method on the Command interface that returns a wrapped command with some behavior added. The ones below cover the cases that come up daily.

NOTE · DECORATOR RULES

A few rules worth knowing

.named(...) is required: the WPILib compiler plugin makes an unnamed command a build error, so every builder chain ends in .named(...). The interrupt-only cleanup hook is .whenCanceled(...). Time-based options take a Time (e.g. Seconds.of(...)), not a raw double, in keeping with v3's units-everywhere policy. And note what is not here: there are no ...AndWait variants on mechanisms; waiting is always spelled .until(...) at the call site.

The advanced dialects (you can skip these)

Commands V3 has two more ways to write routines. You don't need either one for this workshop (chaining covers everything we build), but you should know they exist so the template code doesn't surprise you.

OPTIONAL · COROUTINES

fork / await / waitUntil

A command body that pauses itself from the inside. Reach for it when a hold must span many steps or the logic needs loops and branches: it keeps every mechanism actively commanded between steps, where a chained sequence can leave one owned by the routine but coasting on its last setpoint. See DriveStowDriveOpMode.java (the same routine as above, written in this dialect).

coroutine.await(command);
OPTIONAL · STATE MACHINE

StateMachine

The robot is always in exactly one named state, and buttons/sensors move it between states; illegal jumps simply don't exist because no transition was declared for them. See StateMachineTeleop.java and the State Machines lesson in Advanced Topics.

machine.when(...)

Implementation sequence

The workshop builds up a command-based project in this order. Each step assumes the previous one is in place.

  1. Mechanisms: hardware fields, configuration, setDefaultCommand.
  2. Commands: hold factories on each mechanism, named with a (hold) suffix.
  3. Triggers: controller bindings + the scoping rules (global / opmode / command).
  4. PID control: closed-loop requests for the holds to re-send.
  5. Motion Magic: profiled-position requests with acceleration and cruise-velocity bounds.
  6. Auto routines: chained sequences of drive legs and holds, exactly the pattern above.

Ground truth for this lesson

Our team's reference implementation, with the hold and chaining rules written up in its onboarding guide:

NOTE · API STATUS

This is the WPILib 2027 alpha

Commands V3 (the staged builder, the compile-time naming enforcement, and the StateMachine class) runs on Java 25 and deploys to SystemCore. The stack is the WPILib 2027 alpha (GradleRIO 2027.0.0-alpha-6, Phoenix 6 26.50.0-alpha-1), so the exact APIs are still moving between alpha builds. This page was last verified against alpha-6 in July 2026.
CHECKPOINT · 5 ITEMS