Commands
Commands with WPILib Commands V3
A coroutine Command is a single method body that the scheduler runs once. Inside the body you write plain Java; whenever the command needs to wait — for time to pass, for a condition to become true, for a sub-command to finish — you explicitly yield to the scheduler through the Coroutine parameter. There's no separate init / execute / isFinished / end to fill in, just the one body.
Most commands you write will be a single line of motor setup wrapped in mech.run(...). The coroutine parameter is there when you need it (.wait, .waitUntil, .await, .park) and ignored when you don't. Roughly 80% of teaching examples never touch it.
A command is a body. You yield through the coroutine wherever you need to wait. Most bodies don't need to.
The default command shape
Most commands live on a mechanism as a thin factory: take some arguments, configure the hardware, hand the resulting Commandback. The body runs once. If it doesn't yield, the command finishes immediately. If it does, it stays scheduled until the yield resolves and the body falls off the end.
Two ways to write a command
The inline run(coroutine -> …) style above is one way to write a command. The other is the explicit initialize / execute / isFinished / end lifecycle: the workshop template ships utils/ClassicCommand, a small base class that gives you those four methods. Extend it, override only the methods you need, and the instance is a Command you can schedule or bind to a trigger. Reach for ClassicCommand when a command has explicit, stateful steps; reach for the inline run(coroutine -> …) body when a single body reads more clearly.
How the lifecycle runs on the scheduler
ClassicCommand runs initialize() once, then loops execute() + isFinished() with a yield each tick. end(false) runs on a natural finish; end(true)runs on cancellation. The base class wires the cancel hook for you, because (as with any v3 command) a cancelled coroutine is dropped and wouldn't otherwise reach your cleanup.Four shapes cover almost everything
When you're writing a new command, the question to answer first is what does it have to wait for? That answer picks the shape.
Body sets a value and falls off
No yield, no wait. The command finishes the same tick it's scheduled. Good for fire-and-forget actions where the next command in a chain is responsible for whatever comes next.
run(coroutine -> setVoltage(6)).named(...)Body sets a value then parks
Yields forever after the setup. The command stays scheduled until something cancels it (a default-command return, a higher-priority command, a button release). Good for "run while held" bindings.
run(coroutine -> { setVoltage(6); coroutine.park(); })Body sets a value then waits on a condition
Yields until the predicate goes true. Falls off on its own when the condition is met. The bread-and-butter shape for "move there" commands that need to report when they've arrived.
run(coroutine -> { setPosition(t); coroutine.waitUntil(atTarget); })Body awaits child commands in order
Yields for the full duration of each child. The body reads top to bottom like a script. Good when the sequence is fixed and each phase's setup depends only on what came before it.
coroutine.await(a); coroutine.await(b);For shapes 1, 2, and 3 the coroutine parameter is the only thing that lets the scheduler know when to yield. For shape 4 you chain commands together by awaiting them on the same coroutine, inheriting all of their requirements.
Composing across mechanisms
A routine that touches more than one mechanism is hosted on the mechanism it primarily drives. From inside that mechanism's run(coroutine -> { ... }) body it controls its own hardware directly and awaits or forks the other mechanisms' commands. Each awaited command carries its own requirement, so the scheduler still tracks who owns what.
One body coordinates the whole sequence
The body reads top to bottom: setVelocity starts the flywheel and Phoenix holds it while the arm moves, and the await / waitcalls sequence the rest. When you genuinely need a background command that the routine can later cancel, that's what coroutine.fork(...) is for: a forked command is cancelled automatically when the parent body exits.
Beyond static sequences: runtime decisions in command bodies
The four shapes above cover routines that are static: the sequence is known when the command is built. The interesting case is when one phase's outcome decides what the next phase should be. v3 lets the body read a sensor, store the result in a local variable, and branch with plain Java.
The key line is int weight = sensors.readPieceWeight(); followed by a three-way if/else picking between different next phases. Because the whole routine is one method body, a value read in one phase is just a local variable the later phases can branch on.
Write control flow as control flow
if/else, and await the phase you chose. The coroutine parameter suspends and resumes the body at each await / waitUntil, so there's no phase bookkeeping to maintain.Cancellation
A command's normal completion is just the body falling off the end. Cancellation cleanup goes in a .whenCanceled(...) hook on the command builder. This matters because a cancelled coroutine is simply dropped: any code after a park() or an unfinished waitUntilnever runs, so a trailing "cleanup" line at the end of the body won't fire on cancellation.
The whenCanceled callback only fires when the command is interrupted. Code that should run only on normal completion goes at the bottom of the body (it runs when the body returns on its own). Because cancellation drops the coroutine before it reaches that point, interrupt cleanup has to live in whenCanceled. Two clear places, one for each way a command can end.
This is the WPILib 2027 alpha
Coroutine API, the staged builder, and the compile-time enforcement of .named(...) run on Java 25 and deploy to SystemCore. The stack is the WPILib 2027 alpha (GradleRIO 2027.0.0-alpha-6), so the exact APIs are still moving between alpha builds.