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 fromfalsetotrue; it won't run again until the trigger goes false and true again. Good for discrete actions ("shoot," "go to a position").onFalseis the same on release.whileTrue(command)— schedules the command when the trigger becomestrueand cancels it when the trigger becomesfalse. Good for "run intake while I hold this" or "hold to aim."whileFalseis 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
RequiredAnswer 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.