Creating Subsystems Lesson

Creating Subsystems: The Robot's Building Blocks

A Subsystem is a class that represents a single physical mechanism on your robot. It's the digital blueprint for your intake, arm, shooter, or drivetrain, encapsulating all its hardware and providing simple controls.

The Goal of a Subsystem

A well-designed subsystem has two primary goals that are central to the Command-Based model.

1. Encapsulate Hardware

A subsystem "owns" all the code objects for the motors and sensors that make up that mechanism. All the `Spark`, `Encoder`, and `DigitalInput` objects for the arm live inside the `ArmSubsystem` class and should be `private`.

2. Provide a Simple "API"

It offers a set of simple, `public` methods that commands can use to control the mechanism. An `ArmSubsystem` won't have complex logic; it will just have clear, direct actions like `setArmSpeed(double speed)`, `stopArm()`, and `getArmAngle()`.

Anatomy of a Subsystem File

Let's build a subsystem for a simple robot arm that has one motor to control its angle and one encoder to measure its angle. In your IDE, you would create a new class named `ArmSubsystem` in the `subsystems` package.

// Imports for the hardware classes we need
import edu.wpi.first.wpilibj.Encoder;
import edu.wpi.first.wpilibj.motorcontrol.Spark;
import edu.wpi.first.wpilibj2.command.SubsystemBase;

public class ArmSubsystem extends SubsystemBase {
    // 1. Hardware Declaration: Fields are private and final.
    private final Spark m_armMotor;
    private final Encoder m_armEncoder;

    // 2. Constructor: For one-time setup.
    public ArmSubsystem() {
        m_armMotor = new Spark(4); // Arm motor is on PWM Port 4
        m_armEncoder = new Encoder(0, 1); // Arm encoder is on DIO ports 0 and 1

        // Configure the encoder to return degrees
        m_armEncoder.setDistancePerPulse(360.0 / 2048.0); 
    }

    // 3. Public Methods: The "API" for this subsystem.
    // These are the simple actions commands can tell the arm to do.
    public void setArmSpeed(double speed) {
        m_armMotor.set(speed);
    }

    public void stopArm() {
        m_armMotor.set(0.0);
    }

    public double getArmAngle() {
        return m_armEncoder.getDistance();
    }
    
    public void resetEncoder() {
        m_armEncoder.reset();
    }

    // 4. periodic() Method: Runs every 20ms.
    // Perfect for state machine logic or sending data to SmartDashboard.
    @Override
    public void periodic() {
        // This method will be called once per scheduler run
    }
}
    

Test Your Knowledge

Question: A programmer needs to read the state of a joystick to control the arm. Where should the code to read the joystick be placed?