Tutorials
Zero to Hero
Episode 2: The Command-Based Framework
๐ŸŽฉ

The Conductor Analogy: Imagine an orchestra.

  • The Robot is the Concert Hall.
  • The Subsystems (Episode 3) are the musicians (Violin, Drums).
  • The Commands (This Episode) are the sheet music telling them what to play.
  • The RobotContainer is the Conductor, deciding when the music starts.

๐ŸŽฏ Learning Objectives




๐Ÿ—๏ธ Chapter 1: The Big Picture

Why "Command-Based"?

In the "Olden Days" (like... 2015), we wrote code in big loops. It was messy. If you wanted the arm to move while the robot drove, you had to write complex if statements.

Command-Based solves this by letting us write small, isolated pieces of code ("Commands") and the WPILib Scheduler handles running them all at once. It's multi-tasking for free!

The Three Pillars

  1. Subsystems: The physical parts (Drivebase, Arm, Intake). They represent hardware.
  2. Commands: The actions (DriveForward, RaiseArm, EatPizza). They represent verbs.
  3. RobotContainer: The glue. It connects the hardware to the actions using controllers.

๐Ÿ“ฆ Chapter 2: The RobotContainer

RobotContainer.java is where 90% of your configuration happens. It's not a Subsystem. It's a setup class.

public class RobotContainer {
    // 1. Define Subsystems (The Hardware)
    private final DriveSubsystem m_drive = new DriveSubsystem();
    private final ArmSubsystem m_arm = new ArmSubsystem();
 
    // 2. Define Controllers (The Input)
    private final CommandXboxController m_driver = new CommandXboxController(0);
 
    public RobotContainer() {
        // 3. Configure bindings
        configureBindings();
 
        // 4. Set Default Commands (What to do when no buttons are pressed)
        m_drive.setDefaultCommand(
            // Run command that constantly reads joystick
            new RunCommand(
                () -> m_drive.arcadeDrive(-m_driver.getLeftY(), -m_driver.getRightX()),
                m_drive
            )
        );
    }
 
    private void configureBindings() {
        // "When A is pressed, print Hello"
        m_driver.a().onTrue(new InstantCommand(() -> System.out.println("Hello!")));
    }
}
โš ๏ธ

Common Mistake: Do NOT put game logic in Robot.java. Robot.java should mostly be empty in Command-Based programming. Treat RobotContainer as your main entry point!


๐Ÿ“œ Chapter 3: Anatomy of a Command

A Command is a class that implements the Command interface. It has four main methods that run in a specific order.

Code Example: DriveDistanceCommand

public class DriveDistanceCommand extends Command {
    private final DriveSubsystem m_drive;
    private final double m_distance;
 
    // Constructor: Require the subsystem!
    public DriveDistanceCommand(DriveSubsystem drive, double distance) {
        m_drive = drive;
        m_distance = distance;
        addRequirements(m_drive); // Crucial! Tells scheduler "I need the drivebase"
    }
 
    @Override
    public void initialize() {
        m_drive.resetEncoders();
    }
 
    @Override
    public void execute() {
        m_drive.tankDrive(0.5, 0.5); // Drive forward at 50%
    }
 
    @Override
    public boolean isFinished() {
        // Stop if we passed the distance
        return m_drive.getAverageDistance() >= m_distance;
    }
 
    @Override
    public void end(boolean interrupted) {
        m_drive.stop(); // Safety first!
    }
}

๐ŸŽฎ Chapter 4: Triggers & Bindings

This is how we make the robot interactive. In modern WPILib (2024+), we use Trigger objects.

Types of Bindings

  • onTrue(): Runs the command once when the button is pressed. (Good for: "Shoot", "Toggle").
  • whileTrue(): Runs the command repeatedly while holding the button. Ends when released. (Good for: "Spin Intake").
  • onFalse(): Runs when the button is released.
  • toggleOnTrue(): Press to start, press again to stop.
// In RobotContainer.configureBindings()
 
// While holding 'X', spin intake. When released, it stops (because command ends).
m_driver.x().whileTrue(new StartIntakeCommand(m_intake));
 
// When 'A' is pressed, schedule the "Score" command. It runs until it finishes itself.
m_driver.a().onTrue(new ScoreAmpCommand(m_arm, m_shooter));
 
// Triggers can be anything! Not just buttons!
// "If battery voltage drops below 10V, rumble controller"
new Trigger(() -> RobotController.getBatteryVoltage() < 10)
    .onTrue(new InstantCommand(() -> m_driver.getHID().setRumble(RumbleType.kBothRumble, 1)));

๐Ÿง  Checkpoint

You now understand the skeleton of a robot program!

  1. RobotContainer holds the pieces.
  2. Commands are the instructions.
  3. Triggers start the instructions.

But... what are they controlling? In Episode 3, we will build the Muscles (Subsystems) to make this skeleton move!


๐Ÿ“ž Need Help? We've Got Your Back!

Keep coding, keep learning, and remember: every expert was once a beginner who refused to give up! ๐Ÿš€

P.S. - If your robot starts talking back, that's either very advanced AI or you need more sleep. Probably more sleep. ๐Ÿค–๐Ÿ’ค


This documentation is part of the Zero to Hero programming series. For the complete learning experience, watch the accompanying video and practice with real robot code. Remember: the only way to get good at programming is to write lots of bad code first! ๐Ÿ˜„

๐Ÿ’

Special Thanks: To all the students who asked "Why doesn't this work?" and inspired us to create better documentation. Your questions make us better teachers, and your curiosity drives innovation. You're the real MVPs! ๐Ÿ†

Remember: Every expert was once a beginner who refused to give up. Keep coding, keep learning, and most importantly - keep having fun! ๐Ÿš€