Skip to content

Tutorial 03: More than one subsystem

James Hagborg edited this page Dec 17, 2018 · 1 revision

I don't know what robot you (whoever you are) has access to, but nearly every robot has one of the following:

  • Some sort of lifter/arm. Spinning a motor one way makes it go up, and the other way makes it go down.
  • Some sort of shooter or wheel, for something other than driving.
  • At least one motor for something other than driving, that you may want to spin one way or the other.

I'm going to be calling it a lifter, but you can change the name to whatever makes sense for your bot. We're going to make it so that holding one button makes the motor spin forwards/upwards, and holding the other button makes it spin backwards/downwards. We won't be introducing any new concepts here, so as an exercise, you might want to see if you can guess how this would work and try it, before reading. Really, I just want to set up for the next tutorial, where I'll talk about how commands and subsystems work in more detail, and I need the robot to be able to do two things independent of each other.

As before, add a constant for the motor to RobotMap:

public class RobotMap {
    public static class Drive {
        @Port(type = Type.PWM) public static final int LEFT_MOTOR = 0;
        @Port(type = Type.PWM) public static final int RIGHT_MOTOR = 1;
    }
    
    public static class Lifter {
        @Port(type = Type.PWM) public static final int LIFT_MOTOR = 2;
    }
}

Create a subsystem, with three commands: up, down, and stop. Here, I use constants UP_POWER and DOWN_POWER, to avoid having "magic numbers" floating around in the code. Later, we'll replace these with preferences, so that we can adjust them without having to compile and deploy again.

public class LifterSubsystem extends Subsystem {
    private VictorSP liftMotor = new VictorSP(RobotMap.Lifter.LIFT_MOTOR);
    
    private static final double UP_POWER = 0.5;
    private static final double DOWN_POWER = -0.5;

    @Override
    protected void initDefaultCommand() {
        setDefaultCommand(stopCmd());
    }

    public Command upCmd() {
        return QuickCommand.continuous(this, () -> {
            liftMotor.set(UP_POWER);
        });
    }
    
    public Command downCmd() {
        return QuickCommand.continuous(this, () -> {
            liftMotor.set(DOWN_POWER);
        });
    }
    
    public Command stopCmd() {
        return QuickCommand.continuous(this, () -> {
            liftMotor.set(0.0);
        });
    }
}

Remember to add an instance to the Robot class, and initialize it in initSubsystems. I'll leave this part as an exercise to the reader.

Last, bind the up and down commands to buttons in OIMap. I'll add them to the right driver stick. (I'm assuming that instance you made earlier is called lifter here.)

public static class RightDriver {
    @WhileHeld(1) public final Command lifterUp = Robot.lifter.upCmd();
    @WhileHeld(2) public final Command lifterDown = Robot.lifter.downCmd();
}

And that should be it! While you test this, notice how you can start/stop driving, and move the lifter up/down/stopped, independent of each other. However, upCmd and downCmd should never run at the same time, just how driveCmd and stopCmd didn't in the last tutorial. Next, we'll look at how commands and subsystems work in more detail to see why this is, and how you can use it effectively.

Clone this wiki locally