Skip to content
Drive Team·Lesson 11 of 34

Mapping Controllers to Robot Actions

How button presses become robot motion in WPILib command-based code, and how to design a control scheme that's fast and forgiving.

Sign in to track progress, earn XP, and save lessons.

From button to action in command-based WPILib

In modern WPILib, you bind controls to robot behavior declaratively using Trigger objects, typically in RobotContainer. The recommended class is CommandXboxController. You create it on a port (matching the USB order), then bind its buttons to commands:

// Driver controller on port 0
CommandXboxController driver = new CommandXboxController(0);

// Run the intake while the right bumper is held
driver.rightBumper().whileTrue(intake.runIntakeCommand());

// Score once when A is pressed
driver.a().onTrue(scoreCommand());

The library automatically polls these triggers and schedules the right command — you declare the mapping once at startup.

The binding methods

Per the WPILib Binding Commands to Triggers docs, each Trigger offers these methods:

  • onTrue(command) — schedules the command once when the trigger goes from false to true; it won't run again until the trigger goes false and true again. Good for discrete actions ("shoot," "go to a position"). onFalse is the same on release.
  • whileTrue(command) — schedules the command when the trigger becomes true and cancels it when the trigger becomes false. Good for "run intake while I hold this" or "hold to aim." whileFalse is the mirror.
  • toggleOnTrue(command) — toggles the command on with one press and off with the next. The docs explicitly say toggles are not a highly-recommended option for user control because they require the driver to track robot state; the preferred approach is two buttons (one to turn on, one to turn off).

You can also use the POV/d-pad (driver.povUp()), treat an analog trigger or stick axis as a button with a threshold (e.g., driver.rightTrigger(0.5)), and compose triggers with .and() / .or() and .debounce() to filter noise.

Designing the control scheme

Good mappings are designed for humans under stress, not for code convenience:

  • Split by role: drivetrain on the driver's thumbsticks; mechanisms on the operator's controller or button board.
  • Group related actions physically (all scoring buttons together) and keep dangerous actions (like a climber release) guarded — require a second button, or place it where it can't be hit accidentally.
  • Prefer hold-to-run for transient actions (whileTrue) so releasing instantly stops the mechanism — a built-in safety.
  • Keep it consistent across the season; rebinding the week before champs forces the driver to relearn muscle memory.

Find your button numbers fast

When a control isn't doing what you expect, the USB Devices tab shows live button and axis values so you can press a physical button and see exactly which index lights up. Some teams write a tiny "joystick investigator" that prints button/axis values to the dashboard for the same purpose. Always verify the physical control matches the code mapping before you trust it in a match.

Key takeaways

  • Bind controls declaratively with CommandXboxController and Trigger objects in RobotContainer.
  • Use onTrue/onFalse for one-shot actions, whileTrue/whileFalse for hold-to-run, and prefer two buttons over toggleOnTrue for critical actions.
  • Design mappings around the human: split driver/operator, group actions, guard dangerous ones.
  • Use the USB Devices tab's live feedback to confirm physical controls match code indices.

Lesson quiz

Required

Answer all 3 questions correctly to complete this lesson.

1.What value range does getRawAxis() return for a controller axis, and what does it return at center?

2.In WPILib, what is the difference between the Joystick class and the XboxController class?

3.Why do many teams apply a deadband to a joystick axis before mapping it to a robot action?

Answer every question to submit.