Activity 2.4: Trace a Button Press
🎯 Goal
By the end of this unit you will have:
- Traced the complete execution path from the X button press to intake motor output
- Identified every file and method involved in the intake sequence
- Documented your trace using the step-by-step format from Lesson 2.3
- Understood how
toggleOnTrue,SequentialCommandGroup, andInstantCommandwork together
This is your first assignment. You’ll follow the code from the driver’s controller all the way to spinning motors — the same skill you’ll use every time you need to understand what a button does.
The Assignment
Trace what happens when the driver presses the X button on the controller.
Your job is to follow the execution path step by step:
- Where does the button press get detected?
- What command does it trigger?
- What subsystem methods get called?
- What motors actually move?
Open these two files in your IDE — you’ll be jumping between them:
Step 1: Find the Button Binding
Open RobotContainer.java and search for controller.x(). You should find it around line 242. This is where the X button is wired to a command:
controller.x() .toggleOnTrue( new SequentialCommandGroup( new InstantCommand(() -> intakeSubsystem.deployOut(), intakeSubsystem), new WaitCommand(0.3), new InstantCommand(() -> intakeSubsystem.stopDeploy()), new RunCommand(() -> intakeSubsystem.runIntake(0.5), intakeSubsystem) ).finallyDo(() -> { intakeSubsystem.stopIntake(); intakeSubsystem.deployIn(); }) .andThen(new WaitCommand(0.3)) .andThen(new InstantCommand(() -> intakeSubsystem.stopDeploy())) );Let’s break this down piece by piece.
What does toggleOnTrue mean?
Remember from Lesson 2.3: toggleOnTrue means the button acts like a light switch. Press X once → the command starts. Press X again → the command is cancelled (and cleanup runs). The driver doesn’t have to hold the button.
What is a SequentialCommandGroup?
A SequentialCommandGroup runs a list of commands one after another, in order. Each command in the list must finish before the next one starts. Think of it like a recipe — you do step 1, then step 2, then step 3.
The exception is the last command in this group: RunCommand. A RunCommand runs forever (every 20ms) until something cancels it. That’s what keeps the rollers spinning until the driver presses X again.
Step 2: Trace Each Command in the Sequence
Now let’s walk through what happens in order when the driver presses X.
Phase 1: Deploy Out
new InstantCommand(() -> intakeSubsystem.deployOut(), intakeSubsystem)An InstantCommand runs once and finishes immediately. This one calls deployOut() on the intake subsystem. Open IntakeSubsystem.java and find the deployOut() method (around line 110):
public void deployOut() { intakeLeftActivator.set(DEPLOY_SPEED); // 0.15 = 15% power intakeRightActivator.set(DEPLOY_SPEED);}Both deploy motors (TalonFXS, CAN IDs 33 and 34) start spinning at 15% power, swinging the intake outward so the rollers can reach the ground.
Phase 2: Wait
new WaitCommand(0.3)The sequence pauses for 0.3 seconds. This gives the deploy mechanism time to swing out before the rollers start. Without this wait, the rollers would spin before the intake is in position.
Phase 3: Stop Deploy Motors
new InstantCommand(() -> intakeSubsystem.stopDeploy())After the 0.3-second wait, the deploy motors stop. Find stopDeploy() in IntakeSubsystem.java (around line 124):
public void stopDeploy() { intakeLeftActivator.stopMotor(); intakeRightActivator.stopMotor();}The intake is now deployed and the deploy motors are off. The mechanism stays in position because the motors are configured in Coast mode and gravity holds it.
Phase 4: Run Rollers (Continuous)
new RunCommand(() -> intakeSubsystem.runIntake(0.5), intakeSubsystem)A RunCommand calls its lambda every 20ms until cancelled. This one calls runIntake(0.5) — find it in IntakeSubsystem.java (around line 99):
public void runIntake(double speed) { intakeLeftMotor.set(speed); // 50% power intakeRightMotor.set(speed);}Both roller motors (TalonFX, CAN IDs 31 and 32) spin at 50% power, pulling game pieces into the robot. This keeps running until the driver presses X again.
Step 3: What Happens When X Is Pressed Again?
When the driver presses X a second time, toggleOnTrue cancels the running command. The finallyDo block runs as cleanup:
.finallyDo(() -> { intakeSubsystem.stopIntake(); intakeSubsystem.deployIn();}).andThen(new WaitCommand(0.3)).andThen(new InstantCommand(() -> intakeSubsystem.stopDeploy()))Here’s the cancel sequence:
stopIntake()— rollers stop spinningdeployIn()— deploy motors reverse, pulling the intake back into the robot frame- Wait 0.3 seconds for the retract to finish
stopDeploy()— deploy motors stop
The intake is now fully retracted and everything is off. Clean and tidy.
Step 4: See the Complete Trace
Here’s the full trace from button press to motor output, all in one view:
Step 5: Verify Your Understanding
Now it’s your turn. Without looking at the trace above, try to answer these questions. Then check your answers.
-
toggleOnTrue— press once to start, press again to stop (like a light switch). -
deployOut()is called first — the deploy motors swing the intake outward at 15% power so the rollers can reach the ground. -
The WaitCommand gives the deploy mechanism 0.3 seconds to finish swinging out before the rollers start. Without it, the rollers would spin before the intake is in position to grab anything.
-
RunCommand(() -> intakeSubsystem.runIntake(0.5))— a RunCommand calls its lambda every 20ms, sorunIntake(0.5)keeps getting called to maintain 50% roller speed. -
The command is cancelled. The
finallyDoblock runs:stopIntake()stops the rollers,deployIn()retracts the intake, then after 0.3 secondsstopDeploy()stops the deploy motors. Everything is off and retracted.
Step 6: Document Your Trace
For your Trace Worksheet reference sheet, write down the trace you just completed. Use this format for each step:
| Step | File | Location | What Happens |
|---|---|---|---|
| 1 | RobotContainer.java | controller.x().toggleOnTrue(...) | X button trigger fires, starts SequentialCommandGroup |
| 2 | RobotContainer.java | InstantCommand → deployOut() | Calls deployOut() on IntakeSubsystem |
| 3 | IntakeSubsystem.java | deployOut() method | Deploy motors set to 15% power, intake swings out |
| 4 | RobotContainer.java | WaitCommand(0.3) | Waits 0.3s for deploy to finish |
| 5 | RobotContainer.java | InstantCommand → stopDeploy() | Stops deploy motors |
| 6 | IntakeSubsystem.java | stopDeploy() method | Deploy motors stopped, intake held by gravity |
| 7 | RobotContainer.java | RunCommand → runIntake(0.5) | Starts rollers at 50% power (runs every 20ms) |
| 8 | IntakeSubsystem.java | runIntake(0.5) method | Roller motors (CAN 31, 32) spin at 50% |
Save this — you’ll submit it as part of the Trace Worksheet reference sheet.
What’s Next?
You’ve completed your first code trace — from the X button on the controller all the way through to spinning intake motors. This is the core skill of reading robot code: following the execution path from input to output.
In Activity 2.5: Trace a Sensor, you’ll trace in the other direction — from a sensor reading back through the code to see how the robot uses that data to make decisions.