Spinner Subsystem
Now that we have the robot driving, it it time to make it shoot as well. The shooting mechanism (shown below) is composed for four wheels which are attached to two motors, with two wheels on the left and two on the right. The wheels are then spun at high speed and once the motors are up to speed, a separate mechanism is used to raise the ball to engage with the spinning wheels, causing the ball to be propelled outward at a high rate of speed.
This new subsystem will be designed to operate the two motors that spin the wheels.
The first step is to create our new SpinnerSubsystem class. Right click on the subsystem folder in the Package Explorer of Eclipse and add a new class named SpinnerSubsystem. Then as before, open the ExampleSubsystem.java file and copy all of the lines and paste them into the newly created SpinnerSubsystem.java file, replacing the existing text. Then in the SpinnerSubsystem.java file, replace all instances of ExampleSubsystem with SpinnerSubsystem. Your SpinnerSubsystem.java file should now look like:
package subsystem; import robotCore.Logger; import wpilib.Subsystem; /** * */ public class SpinnerSubsystem extends Subsystem { public void initDefaultCommand() { Logger.Log("SpinnerSubsystem", 2, "initDefaultCommand()"); } public void Init() { Logger.Log("SpinnerSubsystem", 2, "Init()"); } }
First we need to create two variables that will be used to control the two motors operating the spinners. Add the following two lines at the beginning of your SpinnerSubsystem class, and remember to import the I2CPWMMotor class.
I2CPWMMotor m_rightSpinner = null; I2CPWMMotor m_leftSpinner = null;
Note that, once again, we are not initializing these variables in the constructor. We need to do that by adding the following lines to the Init() function:
m_rightSpinner = new I2CPWMMotor(k_rightSpinnerPin1, k_rightSpinnerPin2); m_leftSpinner = new I2CPWMMotor(k_leftSpinnerPin1, k_leftSpinnerPin2);
Once again, the I2CPWMMotor class takes two integers in it’s constructor which specify the PWM pins to which the motor is attached. For our robot, the right motor is attached to pins 6 and 7, and the left motor is attached to pins 10 and 11. As before, we use constants which are defined as follows:
private static final int k_rightSpinnerPin1 = 6; private static final int k_rightSpinnerPin2 = 7; private static final int k_leftSpinnerPin1 = 10; private static final int k_leftSpinnerPin2 = 11;
For this subsystem, we will do not have a default command to run, so we will leave the initDefaultCommand() function unchanged.
Now we need to add a functions that will turn the spinner motors on when we want to shoot and turn them back off when we are done. Add the following functions to your SpinnerSubsystem class:
public void SpinUp() { m_rightSpinner.set(1.0); m_leftSpinner.set(1.0); } public void Stop() { m_rightSpinner.set(0); m_leftSpinner.set(0); }
The Spinup() function will run the spinner motors at full power, and the Stop() function will turn them back off.
Your SpinnerSubsystem.java file should now look like:
package subsystem; import robotCore.I2CPWMMotor; import robotCore.Logger; import wpilib.Subsystem; /** * */ public class SpinnerSubsystem extends Subsystem { private static final int k_rightSpinnerPin1 = 6; private static final int k_rightSpinnerPin2 = 7; private static final int k_leftSpinnerPin1 = 10; private static final int k_leftSpinnerPin2 = 11; I2CPWMMotor m_rightSpinner = null; I2CPWMMotor m_leftSpinner = null; public void initDefaultCommand() { Logger.Log("SpinnerSubsystem", 2, "initDefaultCommand()"); } public void Init() { m_rightSpinner = new I2CPWMMotor(k_rightSpinnerPin1, k_rightSpinnerPin2); m_leftSpinner = new I2CPWMMotor(k_leftSpinnerPin1, k_leftSpinnerPin2); Logger.Log("SpinnerSubsystem", 2, "Init()"); } public void SpinUp() { m_rightSpinner.set(1.0); m_leftSpinner.set(1.0); } public void Stop() { m_rightSpinner.set(0); m_leftSpinner.set(0); } }
The next step is to add an instance of this class to our main Robot class. Open the Robot.java file and add the following line to the beginning of the Robot class. Remember to import the SpinnerSubsystem class.
public static final SpinnerSubsystem m_spinnerSubsystem = new SpinnerSubsystem();
Next we need to call the Init() function of our new class. Add the following line to the robotInit() function:
m_spinnerSubsystem.Init();
Now your Robot.java file should look like:
package robot; import robotCore.IterativeRobot; import robotCore.Logger; import subsystem.DriveSubsystem; import subsystem.ExampleSubsystem; import subsystem.SpinnerSubsystem; import wpilib.Scheduler; public class Robot extends IterativeRobot { public static final SpinnerSubsystem m_spinnerSubsystem = new SpinnerSubsystem(); public static final DriveSubsystem m_driveSubsystem = new DriveSubsystem(); public static final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); public static OI m_OI = null; Robot() { Logger.Log("Robot", 2, "Robot()"); } /** * Called once to initialize the robot */ @Override public void robotInit() { Logger.Log("Robot", 2, "robotInit()"); m_driveSubsystem.Init(); m_exampleSubsystem.Init(); m_spinnerSubsystem.Init(); m_OI = new OI(); } /* * Called at the start of autonomous mode */ @Override public void autonomousInit() { Logger.ResetElapsedTime(); Logger.Log("Robot", 2, "autonomousInit()"); } /** * Called periodically during autonomous */ @Override public void autonomousPeriodic() { Logger.Log("Robot", -1, "autonomousPeriodic()"); Scheduler.getInstance().run(); Sleep(10); } /** * Called at the start of teleop mode */ @Override public void teleopInit() { Logger.ResetElapsedTime(); Logger.Log("Robot", 2, "teleopInit()"); } /** * Called periodically during operator control */ @Override public void teleopPeriodic() { Logger.Log("Robot", -1, "teleopPeriodic()"); Scheduler.getInstance().run(); Sleep(10); } /** * Called a the start of test mode */ @Override public void testInit() { Logger.ResetElapsedTime(); Logger.Log("Robot", 2, "testInit()"); } /** * Called periodically during test mode */ @Override public void testPeriodic() { Logger.Log("Robot", 0, "testPeriodic()"); Sleep(10); } /** * Main program entry point * */ public static void main(String args[]) { Robot Robot = new Robot(); Robot.Start(args); } } // END SNIPPET: serial-snippet
Now that we have our SpinnerSubsystem in place, we want to create a command which will start the spinner motors spinning to prepare for a shot.
We will call this new command SpinupCommand. As we did with the ArcadeDriveCommand, create a new class called SpinupCommand under the commands folder of the project. Then copy the ExampleCommand.java file into the newly created SpinupCommand.java file. Finally replace all instances of ExampleCommand with SpinupCommand. The resulting SpinupCommand.java file should look like:
package commands; import robotCore.Logger; import wpilib.Command; /** * */ public class SpinupCommand extends Command { public SpinupCommand() { Logger.Log("SpinupCommand", 3, "SpinupCommand()"); // Use requires() here to declare subsystem dependencies // requires(Robot.m_exampleSubsystem); } // Called just before this Command runs the first time protected void initialize() { Logger.Log("SpinupCommand", 2, "initialize()"); } // Called repeatedly when this Command is scheduled to run protected void execute() { Logger.Log("SpinupCommand", -1, "execute()"); } // Make this return true when this Command no longer needs to run execute() protected boolean isFinished() { Logger.Log("SpinupCommand", -1, "isFinished()"); return (false); } // Called once after isFinished returns true protected void end() { Logger.Log("SpinupCommand", 2, "end()"); } // Called when another command which requires one or more of the same // subsystems is scheduled to run protected void interrupted() { Logger.Log("SpinupCommand", 2, "interrupted()"); } }
Now this command will use the new SpinnerSubsystem, so change the requires(…) call to the following. Note that you will need to import your Robot class.
requires(Robot.m_spinnerSubsystem);
Now when this command is started, we want to start the spinner motors spinning, so add the following line to the initialize() function:
Robot.m_spinnerSubsystem.Shoot();
We don’t need to do anything else in the execute() function so we will leave that alone. Also, we want run this command as long a button on the joystick is held, so we always want to return false from the isFinished() function.
However, when this command is terminated by releasing the button on the joystick, the interrupted() function will be called, and we will want to turn the motors back off, so add the following line to that function:
Robot.m_spinnerSubsystem.Stop();
Your SpinupCommand.java file should now look like:
package commands; import robot.Robot; import robotCore.Logger; import wpilib.Command; /** * */ public class SpinupCommand extends Command { public SpinupCommand() { Logger.Log("SpinupCommand", 3, "SpinupCommand()"); // Use requires() here to declare subsystem dependencies requires(Robot.m_spinnerSubsystem); } // Called just before this Command runs the first time protected void initialize() { Logger.Log("SpinupCommand", 2, "initialize()"); Robot.m_spinnerSubsystem.SpinUp(); } // Called repeatedly when this Command is scheduled to run protected void execute() { Logger.Log("SpinupCommand", -1, "execute()"); } // Make this return true when this Command no longer needs to run execute() protected boolean isFinished() { Logger.Log("SpinupCommand", -1, "isFinished()"); return (false); } // Called once after isFinished returns true protected void end() { Logger.Log("SpinupCommand", 2, "end()"); } // Called when another command which requires one or more of the same // subsystems is scheduled to run protected void interrupted() { Logger.Log("SpinupCommand", 2, "interrupted()"); Robot.m_spinnerSubsystem.Stop(); } }
The last thing we need to do is make it so that pressing one of the buttons on the joystick will cause this command to be executed. We do this using the OI.java file. Open this file.
package robot; import robotCore.Joystick; import wpilib.Button; import wpilib.JoystickButton; /** * This class is the glue that binds the controls on the physical operator * interface to the commands and command groups that allow control of the robot. */ public class OI { //// CREATING BUTTONS // One type of button is a joystick button which is any button on a joystick. // You create one by telling it which joystick it's on and which button // number it is. Joystick m_stick = new Joystick(0); Button m_trigger = new JoystickButton(m_stick, 1); // There are a few additional built in buttons you can use. Additionally, // by subclassing Button you can create custom triggers and bind those to // commands the same as any other Button. //// TRIGGERING COMMANDS WITH BUTTONS // Once you have a button, it's trivial to bind it to a button in one of // three ways: // Start the command when the button is pressed and let it run the command // until it is finished as determined by it's isFinished method. public OI() { // m_trigger.whenPressed(new ExampleCommand()); } }
First we define which button we are going to use to control the spinner. Add the following line right after the Button m_trigger … line:
Button m_spinup = new JoystickButton(m_stick, 2);
This allows us to access button 2 of the joystick. Now we need to tell the program what to do when that button is pressed. Add the following line to the OI() constructor:
m_spinup.whileHeld(new SpinupCommand());
This line tells the system to run the SpinupCommand whenever button 2 on the joystick is being held down, and to terminate the command once it is released.
At this point, your OI.java should look like:
package robot; import commands.SpinupCommand; import robotCore.Joystick; import wpilib.Button; import wpilib.JoystickButton; /** * This class is the glue that binds the controls on the physical operator * interface to the commands and command groups that allow control of the robot. */ public class OI { //// CREATING BUTTONS // One type of button is a joystick button which is any button on a joystick. // You create one by telling it which joystick it's on and which button // number it is. Joystick m_stick = new Joystick(0); Button m_trigger = new JoystickButton(m_stick, 1); Button m_spinup = new JoystickButton(m_stick, 2); // There are a few additional built in buttons you can use. Additionally, // by subclassing Button you can create custom triggers and bind those to // commands the same as any other Button. //// TRIGGERING COMMANDS WITH BUTTONS // Once you have a button, it's trivial to bind it to a button in one of // three ways: // Start the command when the button is pressed and let it run the command // until it is finished as determined by it's isFinished method. public OI() { // m_trigger.whenPressed(new ExampleCommand()); m_spinup.whileHeld(new SpinupCommand()); } }
Now run your program, and you should be able to start the spinner motors by pressing button 2 on the joystick, and stop them again by releasing that button.