Command-Based Framework
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.
Triggers schedule Commands. Commands operate on Mechanisms. The Scheduler enforces who-owns-what so nothing collides.
Triggers
BooleanSuppliers wired to commands
Mechanisms
One physical thing each
Mechanism, a class you extend. Hardware lives in private fields, configuration in the constructor.Commands
Named actions, mostly holds
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.
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:
Command.sequence(a, b, c): steps that finish on their own (aDriveToPoseleg, for example) can sit in a sequence as-is..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.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().)
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.
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.
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);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.
- Mechanisms: hardware fields, configuration,
setDefaultCommand. - Commands: hold factories on each mechanism, named with a
(hold)suffix. - Triggers: controller bindings + the scoping rules (global / opmode / command).
- PID control: closed-loop requests for the holds to re-send.
- Motion Magic: profiled-position requests with acceleration and cruise-velocity bounds.
- 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:
This is the WPILib 2027 alpha
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.