Gray Matter
WorkshopOpModes
WPILib 2027 is still in alpha: these pages change as the APIs settle.
LESSON 15

OpModes

Each way the robot can run is its own class, marked with an annotation. Project Setup generated one marked @Teleop. On branch mech-2-Commands you edit it so the controller's buttons run the commands you just wrote.

Branchmech-2-Commands7 minutes
You’ll need
  • The three commands on each mechanism from Writing Commands, building clean.
  • The robot.arm and robot.flywheel fields from Mechanisms.

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 generated MyTeleop

Open src/main/java/first/robot/opmode/MyTeleop.java. The New Project Creator wrote it with @Teleop on the class, and that annotation is the whole registration. The framework finds every annotated class on its own, and the driver station lists each one by name. There is no RobotContainer in this project and nothing else to edit.

The generated file holds five empty methods with comments in them: disabledPeriodic, start, periodic, end and close. A teleop built from commands needs none of them, and you replace the whole class below. Edit this file rather than making a new one. A second @Teleop class puts two teleops on the driver station.

MyAuto.java beside it is marked @Autonomous and stays empty for now. Autonomous routines arrive in Workshop 4, where Coroutines writes the first one.

Bind the buttons

Replace everything below the copyright header with this.

src/main/java/first/robot/opmode/MyTeleop.java
package first.robot.opmode;
 
import first.robot.Robot;
import org.wpilib.command3.button.CommandNiDsXboxController;
import org.wpilib.opmode.PeriodicOpMode;
import org.wpilib.opmode.Teleop;
 
@Teleop(name = "Teleop")
public class MyTeleop extends PeriodicOpMode {
private final CommandNiDsXboxController driver = new CommandNiDsXboxController(0);
 
public MyTeleop(Robot robot) {
// Left trigger: push the arm up while held, stop when released.
driver.leftTrigger().whileTrue(robot.arm.runFast()).whileFalse(robot.arm.stop());
 
// Right trigger: spin fast while held, drop back to the slow voltage when released.
driver.rightTrigger().whileTrue(robot.flywheel.runFast()).whileFalse(robot.flywheel.runSlow());
 
// A: spin fast while held, stop when released.
driver.a().whileTrue(robot.flywheel.runFast()).whileFalse(robot.flywheel.stop());
}
}

name = "Teleop" is the label in the driver station's list. Leave it off and the list shows the class name. The 0 is the controller's port on the driver station. The constructor is handed the one Robot, so every binding reaches a mechanism as robot.arm or robot.flywheel.

whileTrue schedules its command when the button goes down and cancels it when the button comes up. whileFalse schedules its command on the way up. So the left trigger runs runFast while held and stop after, and the right trigger drops the flywheel to runSlow rather than to zero. Every hold here has a whileFalse behind it, and Hardware Simulation shows what a release does without one.

Building only the armflywheel? Delete the robot.flywheelarm bindings. They call commands on a class your project does not have.

Bindings made in the constructor belong to this OpMode. The framework removes them when the mode changes, so there is no cleanup to write. Bind in the constructor, but never drive a motor from it, because the robot can still be disabled when that code runs.

Check your work

Run WPILib: Build Robot Code. Nothing moves until Hardware Simulation, so check the file as well as the build.

Check

You should see

  • BUILD SUCCESSFUL as the last line of the terminal.
  • One hit when you search the opmode folder for @Teleop.
  • A whileFalse on every binding line you kept.

cannot find symbol: variable flywheel, or arm, means a binding names a mechanism your Robot does not build. Delete that line.

Check yourself

You release the right trigger. What is the flywheel doing a moment later?

Instead of editing MyTeleop, a teammate makes a new TeleopOpMode.java with @Teleop on it and the same bindings. What goes wrong?

You built only the arm, and the build stops on cannot find symbol: variable flywheel. What do you change?

The driver switches from Teleop to Autonomous. What happens to the bindings made in MyTeleop's constructor?

Pick an answer for each.