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

Mechanisms

KEY CONCEPT

Mechanisms with WPILib Commands V3

A mechanism models one physical part of the robot: an arm, a flywheel, the drivetrain. In Commands V3, Mechanism is a base class you extend. It gives you factory methods (run(), runRepeatedly(), idle()) that hand you a Command builder you can name, schedule, or compose.

One class per physical thing, hardware as private fields, configuration in the constructor. Default behavior comes from an automatic idle() default (override it with setDefaultCommand) rather than a periodic() override, and the WPILib compiler plugin enforces .named(...) on every command at build time; forget it and the project won't compile.

The commands a mechanism exposes are holds: runRepeatedly(...) re-sending the closed-loop setpoint forever, named with a "(hold)" suffix. The setters stay private: anything that wants to move the arm does it through a command, which is how the scheduler prevents two things fighting over the motor.

↳ TAKEAWAY

One mechanism per physical thing. The base class gives you Command factories; you give it your hardware and hold commands for each preset.

Anatomy of a Mechanism

A v3 mechanism is a regular Java class that extends Mechanism. Hardware lives in private fields, configuration happens in the constructor, and every public method that callers will schedule returns a Commandbuilt from one of the base class's factory methods.

THE ONE RULE

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

The holds above keep re-sending their setpoint forever. Put one bare in a Command.sequenceand the routine sticks there, which is why every hold's name ends in (hold): a stuck routine sitting on a (hold) command on the dashboard is the bug. Give a hold a finish line at the call site (arm.scoring().until(arm::isAtTarget)); never bake an ...AndWait variant into the mechanism. This class follows Arm.java in the 2027-Template line for line.

Wiring it into Robot

A mechanism class does nothing until the robot owns one. Every mechanism lives on the Robot class as a public finalfield, built once when the program starts, and alive for the whole match. That's the entire wiring step: one line per mechanism.

Everything else (button bindings, autos) receives this Robot and reaches the mechanisms through it. When you see robot.arm.scoring() on the Triggers page, robot.arm is this field.

The factory methods you'll actually use

The Mechanismbase class provides three built-in factories that cover most of the commands you'll write. Each returns a builder; chain .named("...") to finish it.

THE HOLD FACTORY

mech.runRepeatedly(runnable)

Calls the runnable every scheduler tick (20 ms) for as long as the command is scheduled, which re-sends a closed-loop request forever. This is how holds are written, and it's the factory behind nearly every command in the workshop. (It also covers every-tick work like telemetry: the v3 stand-in for periodic(), scoped to a command.)

runRepeatedly(() -> setPosition(X)).named("x (hold)")
RUN ONCE

mech.run(coroutine -> { ... })

Runs the lambda once; the command finishes when the body returns. The coroutineparameter is the advanced dialect (covered on the Commands page); for workshop code you'll rarely need run at all.

run(coroutine -> { ... }).named("...")
DO NOTHING

mech.idle()

Returns a ready-made command that yields forever at LOWEST_PRIORITY. Every mechanism already has this wired as its default; set your own to override.

setDefaultCommand(idle())
NOTE · NAMING IS ENFORCED

.named(...) is a compile-time requirement

Both run(...) and runRepeatedly(...) hand back a staged builder, not a finished Command. A WPILib compiler plugin watches for that builder escaping a method without a matching .named(...) call and turns it into a build error, so every command shows up in telemetry under a name you actually chose.

Default commands and idle behavior

Every mechanism has a default command. The scheduler runs it whenever no higher-priority command requires that mechanism, and pre-empts it the moment one does. Out of the box, the default is idle(), a no-op park at LOWEST_PRIORITY, so a fresh mechanism just sits there safely. Call setDefaultCommand(...)to override it. A default that needs no controller (like hold-in-place) can be set right in the mechanism's constructor; a default that depends on a joystick is set from the Teleop OpModeinstead, because the mechanism's constructor has no controller (see the Triggers page).

A second example: leader/follower flywheel

Multi-motor mechanisms follow the same shape. The Phoenix 6 follower control still lives in the constructor, the hold commands still come from runRepeatedly(...), and reads stay as plain getters.

Physical hardware vs. code example

The workshop's flywheel mechanism only has one physical motor. The follower wiring above is included to show the multi-motor pattern. If you're running this on the workshop hardware, either drop the follower lines or add a second physical motor.
NOTE · API STATUS

This is the WPILib 2027 alpha

Commands V3 (including the Mechanism base class, the builder-chain factories, and the compile-time .named(...) enforcement) 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. This page was last verified against alpha-6 in July 2026.
CHECKPOINT · 5 ITEMS