Writing Commands
The voltage setter is private and nothing can reach it. On branch mech-2-Commands three commands become the way in: one slow, one fast, one that holds the mechanism still.
- Branch
mech-1-Mechanismsbuilding clean, with a privatesetVoltageandstopMotoronArmandFlywheel. - Lambdas, method references, and
privatefrom Java Basics. robot.armandrobot.flywheelfrom Mechanisms. The bindings below reach the mechanisms through that object.
What mechanism are you working on?
The lesson below is written for the one you pick. Switch back any time to read it for the other.
The armflywheel has a working motor and no way yet to ask it for anything. A command is the way in, and it wraps that private setter. The scheduler hands the mechanism to one command at a time, so two of them can never fight over the same motor.
One import
Open src/main/java/first/robot/mechanisms/Arm.javasrc/main/java/first/robot/mechanisms/Flywheel.java. One line goes in at the top, alphabetically, just above Mechanism.
import com.ctre.phoenix6.signals.NeutralModeValue;import org.wpilib.command3.Command;import org.wpilib.command3.Mechanism;That is the only edit to what is already in the file. setVoltage is already private, and the stop helper is already called stopMotor, so the command below can take the name stop without displacing anything.
Three commands
Three methods, and they all look alike.
// Each command uses runRepeatedly, which runs its action every loop while the // command is scheduled. Every one of them is a hold: it never finishes on its own. /** Push the arm at 3 volts and keep pushing. Never finishes. */ public Command runSlow() { return runRepeatedly(() -> setVoltage(3.0)).named("runSlow (hold)"); } /** Push the arm at 6 volts and keep pushing. Never finishes. */ public Command runFast() { return runRepeatedly(() -> setVoltage(6.0)).named("runFast (hold)"); } /** Stop the arm motor and keep it stopped. Never finishes. */ public Command stop() { return runRepeatedly(this::stopMotor).named("stop (hold)"); } // Same setup as Arm. runRepeatedly runs the action every loop while the command // is scheduled. Every one of these is a hold: it never finishes on its own. /** Spin the flywheel at 3 volts and hold it there. Never finishes. */ public Command runSlow() { return runRepeatedly(() -> setVoltage(3.0)).named("runSlow (hold)"); } /** Spin the flywheel at 6 volts and hold it there. Never finishes. */ public Command runFast() { return runRepeatedly(() -> setVoltage(6.0)).named("runFast (hold)"); } /** Stop the flywheel and keep it stopped. Never finishes. */ public Command stop() { return runRepeatedly(this::stopMotor).named("stop (hold)"); }Three things happen on the runSlow line.
() -> setVoltage(3.0)is a lambda. It does not callsetVoltagehere. It hands that call torunRepeatedly, which makes it for you every loop.runRepeatedlycomes fromMechanism, which is what the class extends..named(...)gives the command a name. Names show up in logs and on the dashboard, which is what makes one findable when it misbehaves later.
this::stopMotor is the same as () -> stopMotor(), pointing at the private helper from last lesson. Every name ends in (hold) because runRepeatedly has no exit: these run until something else claims the mechanism. Even stop() holds, sending zero every loop rather than once.
A hold never finishes
So never make anything wait for one. A hold inside Command.sequence sticks there forever and the sequence never reaches its next step. When a step needs an ending, add it where you use the command rather than writing a new method here: armflywheel.runSlow().until(someCondition). The (hold) in every name is there so a stuck sequence names its own bug in the log.
Check your work
Run WPILib: Build Robot Code. You should see BUILD SUCCESSFUL. If it does not, the error is almost always one of these three.
| Error | Cause | Fix |
|---|---|---|
NeedsNameBuilderStage cannot be converted to Command | A .named(...) is missing from that method. | Name the command. javac prints both types package-qualified. |
cannot find symbol: method withPriority(int) | A builder method landed after .named(...). | Move it in front of the name. Same for whenCanceled. |
cannot find symbol: class Command | The import is missing. | Add import org.wpilib.command3.Command;. This stack is never edu.wpi.first. |
Check yourself
setVoltage is private on both mechanisms. What does keeping it that way buy you?
In runRepeatedly(() -> setVoltage(3.0)).named("runSlow (hold)"), what does runRepeatedly(...) hand back before .named(...) runs?
You bind driver.a().whileTrue(robot.flywheel.runFast()) and leave the whileFalse off. You release A. What happens?