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 coroutine-bodied actions, and Scheduler for the loop. You compose commands either with factories (Command.sequence / Command.parallel) or as a real method body, not just a tree of group objects.

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 extends. Hardware lives in private fields, configuration in the constructor.
HOW

Commands

Coroutine-shaped bodies

A single method body that runs on the scheduler. Calls out to coroutine.wait, waitUntil, await, fork whenever it needs to suspend.

The big picture

How Command-Based Programming Works

Triggers

Controller buttons, sensors, or custom conditions

"When button A is pressed..."

Commands

Actions the robot performs

"...run the 'Raise Arm' command"

Subsystems

Robot mechanisms (arm, shooter, etc.)

"...which controls the Arm subsystem's motors"

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 presses button A

2. Command:"RaiseArm" command starts running

3. Subsystem: Arm subsystem receives target position

4. Hardware: Motor spins, encoder measures position

5. Feedback:Encoder reports "target reached!" → Command ends

Decorators

A decorator is a method on the Command interface (or on the builder) 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(...); it's where interrupt cleanup must live, because a cancelled coroutine is simply dropped. Time-based options take a Time (e.g. Seconds.of(...)), not a raw double, in keeping with v3's units-everywhere policy. Chain and group commands with the Command.sequence(...) / Command.parallel(...) factories.

Composition: two ways to combine commands

You combine commands two ways: in sequence or in parallel. The composition factories (Command.sequence / Command.parallel) combine whole commands; the combined command automatically requires everything its children require. The coroutine helpers (await / awaitAll / awaitAny / fork / wait / waitUntil), called from inside a command body, let one command coordinate steps from the inside, which is handy when the sequence depends on runtime data.

FACTORIES · WHOLE COMMANDS

Command.sequence, Command.parallel

Combine commands into one routine, then hand it to the scheduler or bind it to a trigger. The combined command inherits the union of its children's requirements, so the scheduler knows everything the routine will touch before it starts. This is the everyday way to coordinate several mechanisms.

Command.sequence(a, b, c)
COROUTINE · INSIDE ONE BODY

coroutine.await, awaitAll, awaitAny

From inside a run(coroutine -> { ... }) body you can pause on other work: await one command, awaitAll / awaitAny several, fork a background task, or wait / waitUntil for a duration or a condition. Best when the steps depend on sensor readings at runtime.

coroutine.awaitAll(a, b);

The two compose: a coroutine body can await a command built with Command.sequence, and a sequence can contain a command whose body coordinates further work. Pick the spelling that's clearer for the routine in front of you.

The three parallel shapes, in one table

There are three flavors of parallel. One is a factory (Command.parallel); the other two are coroutine helpers.

NOTE

awaitAny and fork cover the asymmetric cases

There's one parallel factory for the symmetric "wait for all" case (Command.parallel), and the coroutine helpers express the asymmetric cases with normal method calls: awaitAny for "first done wins" and fork + await for a background task guarded by a deadline. A forkthat's still running when the outer body exits is cancelled automatically.

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 — coroutine-bodied factory methods on each mechanism (and cross-mechanism routines).
  3. Triggers — controller bindings + the scoping rules (global / opmode / command).
  4. PID control — closed-loop commands wrapping CTRE position / velocity requests.
  5. Motion Magic — profiled-position commands with acceleration and cruise-velocity bounds.
  6. State machines & auto routines — the V3 StateMachine class for anything with phases that can repeat, skip, or interrupt.

Official WPILib documentation

For the full v3 design doc and the upstream API reference:

WPILib Command-Based Programming Guide
NOTE · API STATUS

This is the WPILib 2027 alpha

Commands V3 — the staged builder, the coroutine helpers, the compile-time naming enforcement, and the StateMachine class — run on Java 25 and deploy 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.
CHECKPOINT · 5 ITEMS