Documentation for programming with the Romis can be found at https://docs.wpilib.org/en/stable/docs/romi-robot/programming-romi.html
In order to program an FRC robot, you first need to know how to program! On our team, we do this in Java. To learn the core skills of Java, we will be using a tutorial from Codecademy and then having you apply what you learned to FRC programming. This first lesson will deal with creating statements, lines of code that tell the computer to do something.
Here's the link to the tutorial: https://www.codecademy.com/courses/learn-java/lessons/hello-world-java/exercises/introduction-to-java
In the core Java lesson, you learned how to use statements to make a computer do something. In this case, print output to the screen. It turns out, driving a robot can be as simple as printing to the screen!
In this first exercise, we're going to start with some existing code to control a mini-robot called a Romi. Our goal will be to have this robot drive in a rectangle measuring 36 by 24 inches. While this robot might be small, all of the programming you're doing now is exactly the same as what you'll be doing on the big robots!
Download the starter code that you will be using here: https://drive.google.com/file/d/1ZN6T4PtKhtkPzq9P8lF3OZPmPZ7jlK-E/view?usp=share_link
Unzip the folder you just downloaded.
From the start menu, open "2022 WPILib VS Code" (make sure to use the 2022 version and not 2021).
In VS Code, go to File -> Open Folder and choose the folder you just extracted. This will open the project we will be working with.
Inside of this VS Code, navigate to src\main\java\frc\robot\Robot.java this is the file we will be working in for this lesson.
In this file, use "CTRL+F" to find the text "FINDME" in the code. This text marks where we will be putting our code for this lesson.
Notice that instead of this comment being inside of public static void main(String[] args), the code is inside of public void autonomousInit(). In FRC, we are able to use templates to run certain code when we want to. In this case, the autonomousInit() function will run once in the beginning of autonomous mode.
Notice that there are two statements inside of autonomousInit(), m_drivetrain.driveDistance(24), which tells the robot to drive forward by 24 inches and m_drivetrain.turnAngle(90), which tells the robot to turn 90 degrees to the left.
These methods are very similar to System.out.println(). The System.out component is an object, in that case, a reference to the system console. This corresponds to m_drivetrain, a reference to the robot's drivetrain. The println() component is the name of the method we'd like to use. In other words, the thing we'd like our object to do. In the case of println(), this is to print something to the console. This corresponds to driveDistance() and turnAngle(), which tell the robot to drive a certain distance or turn a certain angle respectively.
Notice what we put inside of the parenthesis for these methods. For System.out.println("Hello World"), we print whatever is between the parenthesis, in this case, "Hello World". The quotes are because we're using a String, which we'll cover in the next lesson. Whatever we put into these parenthesis is what we call an argument. We use this to tell a method how we want it performed. This is similar to specifying 24 in m_drivetrain.driveDistance(24), which says that we want to drive 24 inches forward, and 90 in m_drivetrain.turnAngle(90), which specifies that the robot should turn 90 degrees to the left (in this case, positive angles are to the left, negative angles are to the right).
Now, we will run this code. To do this, start by turning your robot on.
After a minute or two, you will see a network with the same name as your Romi. Connect to this network.
In VS Code, press F5 to start the robot code running.
This will start a window to run your robot from. In the upper left, click the "Autonomous" button to run the existing autonomous code.
Observe what the robot does when you run this code.
Find the stop button in VS Code (an orange square at the top of the text editor) and stop the code from running.
Next, modify the code in autonomousInit(), so that your robot will drive in a rectangle measuring 36 inches by 24 inches.
So far, we have created code that does exactly the same thing every time. While this works well for simple programs, for anything more complicated, we will need to let our code respond to inputs from the driver or sensor data.
The first course introduces the types of variables you'll use in Java: https://www.codecademy.com/courses/learn-java/lessons/learn-java-variables/exercises/introduction
The next course introduces how to manipulate these variables in Java: https://www.codecademy.com/courses/learn-java/lessons/learn-java-manipulating-variables
Now that we have learned how to use variables, we will apply this to driving your robot with a joystick!
Start with the same code you used for your autonomous program.
The first thing we need to do is create a variable that lets us read values from a joystick. Under line 10, which reads:
private final RomiDrivetrain m_drivetrain = new RomiDrivetrain();, press enter to create a new line of code.
On this newly created line of code, write the following:
private final XboxController m_driveController = new XboxController(0);
What this newly created line of code does is create a variable. Let's break it down. For now, you can ignore the words private and final. We will discuss these in a future lesson. The next line of code is XboxController. This is the variable's type, similar to creating an int or a String variable. As you're probably guessing, the creators of Java didn't decide to create a variable type to represent an Xbox controller. Instead, this variable type was created by a group of Java programmers working on a project called the WPI Library, which provides useful functions for FRC teams. When a programmer creates a new variable type, we call this a class. This is an application of Object Oriented Programming, a concept we'll learn more about in our next core Java lesson.
Similar to declaring an int variable as int myInt = 2;, when we say
private final XboxController m_driveController = new XboxController(0);, we're giving the variable a name and an initial value. Don't worry about how we assign this initial value right now, that will make sense after the next core Java lesson.
Now, we're going to start creating our teleoperated (driver controller) program. To do this, scroll down to find teleopPeriodic(). This function runs many times per second when we are in teleoperated mode.
Next to this function name, you will see two curly braces { }. Click between those curly braces and press the "Enter" key.
The rest of the code we'll be writing must be between the two curly braces associated with teleopPeriodic().
In order to drive, we're concerned with two speeds, a forward/backwards speed, and a left/right speed.
First, we're going to figure out our forward/backward speed. To do this, create a new line of code saying the following:
double speedForward = m_driveController.getRightTriggerAxis() - m_driveController.getLeftTriggerAxis();
What this will do is create a new double (decimal number) variable called speedForward. The value we're setting this variable to is the right trigger axis of the joystick minus the left trigger axis. This makes it so the right trigger axis will make the robot drive forward and the left trigger axis will drive the robot backwards.
Next, we need to figure out what speed to make the robot turn at. For this, create a new line of code and enter:
double turningSpeed = m_driveController.getLeftX();
This creates a new double variable called turningSpeed and sets it to the X axis (left-right) of the left joystick on the Xbox controller.
Now that we know the speeds we want the robot to move at, we need to actually tell the robot to move. To do this, create a new line of code and enter the following:
m_drivetrain.arcadeDrive(speedForward, turningSpeed);
This is similar to what we did in our autonomous program with commands such as m_drivetrain.driveDistance(24);. In both cases, m_drivetrain is a reference to the robot's drivetrain. Next, we have a dot, and then the name of the method that tells the drivetrain to do something. In the case of our autonomous program, what we wanted to do was have our robot drive a certain distance forward or backward. In the case of our teleoperated program, we want to have our robot drive at a specified speed forward and turn at a specified speed. Similar to how we put 24 between the parenthesis for driveDistance, we can put arguments into arcadeDrive. In this case, the first argument tells the robot how fast forward or backwards it should be moving and the second argument tells the robot how fast it should be turning. When we have multiple arguments, we separate these arguments with a comma.
Now that your code is complete, you can run it the same way you did in the previous lesson. Instead of going to autonomous mode, you will go into teleoperated mode. With this code, you should now be able to drive the robot using your joysticks!
Object oriented programming essentially means creating custom variable types that contain useful information and are able to do useful things. In FRC programming, this concept is critical to the work that we do. We use these custom variable types, or, classes, all the time, to represent things like our drivetrain, shooter, intake and many other things on the robot. This module is broken into three parts.
First, there will be a video explaining what classes and objects are: https://www.codecademy.com/courses/learn-java/videos/classes-and-objects-video
Next, there will be a lesson explaining how we create and use classes in Java: https://www.codecademy.com/courses/learn-java/lessons/java-introduction-to-classes/exercises/introduction-to-classes
Finally, there will be a lesson explaining how to use methods to make your classes and objects do useful things: https://www.codecademy.com/courses/learn-java/lessons/learn-java-methods/exercises/introduction
On our robots, we use a design pattern called the Command Framework. This is simply a way of organizing our code that allows us to easily add new functionality without breaking existing code. Before we get into the robot, please go through this presentation that explains how this framework works: https://docs.google.com/presentation/d/e/2PACX-1vR-73OU1Q2f0C9cJMM9ZtX12QNSbEe2apapZbLYKaGS0XfJnP8J3QAU7D-1nZo5ITMpi3DTCwoR9Yh5/pub?start=false&loop=false&delayms=3000&slide=id.ga185f7c88c_0_0
Now, let's get into the code! The code we'll be starting with can be found here: https://drive.google.com/file/d/1aU992aT5Ev5FcSWFibNt8-hV3oyiLD7u/view?usp=share_link.
We're going to start by adding a simple command. Our goal is to make it so whenever we press the Start button on our joystick, the yellow LED on the Romi turns on for a specified number of seconds.
Right click on the commands folder and click on "Create a new class/command". In the dialogue that comes up, select "Command (New)". It will then let you name this command, we will name it "TurnOnLightForTime". Press Enter and you will see that a new file gets created called TurnOnLightForTime.java.
Open TurnOnLightForTime.java.
First, we need to give this command some things to work with. To start, we will give it a reference to the Romi's OnBoardIO subsystem. Click at the very end of line 9, after the curly brace, and press the Enter key to create a new line.
On the new line that was created, type OnBoardIO m_IO;. This gives our command a reference to the Romi's onboard lights and buttons.
Create a new line under this and type private final Timer m_Timer = new Timer(); this creates a new timer that we will use to know how long to keep our light on.
Create another new line under this and type private double m_Seconds; this creates a new variable that we will use to track how long we want to keep our LED on.
Now, we need to configure the constructor. This constructor is called when we start the robot's code and configures variables that we will use late. In this case, we'll be configuring the reference to the Romi's onboard IO and the number of seconds to keep the light on. First, we need to configure the inputs that we'll be putting into the constructor.
Find the line that says public TurnOnLightForTime() { and change it to
public TurnOnLightForTime(OnBoardIO io, double seconds) {
Inside of the constructor, we'll need to specify that the command is using the onboard IO subsystem. To do this, create a new line after the comment stating // Use addRequirements() here to declare subsystem dependencies.
On this new line, type addRequirements(io);
Enter a new line under this and type m_IO = io; then, on a new line under that, type m_Seconds = seconds; this sets the fields that we will use to the values we inputted into the constructor
Now, we need to do some stuff when the command actually starts running. Find the line that says
public void initialize() {}
Click between the curly braces and press Enter
Now, we will reset and start the timer. To do this, type m_Timer.reset(); then create a new line and type m_timer.start();
Next, we need to specify what the command does when it's running. In this case, we will turn on the yellow LED. To do this, find the line saying public void execute() {} click between the two curly braces, then press Enter.
On the new line, type m_IO.setYellowLed(true);
After this, we'll tell the command what to do when it ends. In this case, we'll turn the yellow LED off. To do this, find the line that says public void end(boolean interrupted) {} click between the curly braces and press Enter.
On the new line that was created, type m_IO.setYellowLed(false);
Finally, you will find a method called public boolean isFinished() this method tells the framework when the command should end. In this case, the command will end when the timer has run for more than the specified amount of time.
Replace the text that says return false; with return m_Timer.get() > m_Seconds;
Now, we just need to map our new command to a joystick button. To do this, go back to RobotContainer.java.
Find the method called public void configureButtonBindings()
Add a new line at the end of this method and type
m_controller.withAButton().whenPressed(new TurnLightOn(m_onboardIO, 2));
Now, let's test our code! Start the simulator, go into Teleoperated and press the A button. The yellow LED will turn on for 2 seconds and then turn off.
Now that we know how to use the command framework in teleoperated, let's explore how to use it in autonomous.
Go to AutonomousDistance.java
Notice lines 18 and 19. These lines configure a set of commands for the autonomous to follow.
Edit these two lines and add a third line to make the robot drive 36 inches forward, turn 180 degrees, then drive 36 inches to return where it started.
Note: If you have previous experience in another programming language, talk to a mentor. You might have enough knowledge to skip over this lesson.
This module will teach you how to use flow control structures, which allows your code to make decisions based on certain conditions.
Complete the first course, dealing with flow control statements: https://www.codecademy.com/courses/learn-java/lessons/java-conditionals-and-control-flow/exercises/introduction-to-control-flow
Complete the second course, dealing with conditional operators: https://www.codecademy.com/courses/learn-java/lessons/java-boolean-operators/exercises/introduction-to-conditional-operators
This lesson will cover some of the most important concepts in object oriented design, encapsulation. This means creating self contained units of code that can safely be used in other areas of your project, or in entirely unrelated project.
This first course deals with access, encapsulation and scope: https://www.codecademy.com/courses/learn-java/lessons/access-encapsulation-and-scope-lesson/exercises/what-are-access-and-scope
Next, there is a quick page that discusses some useful math methods you can use: https://www.codecademy.com/courses/learn-java/articles/static-methods-of-the-math-class
Finally, there is a lesson on static variables and methods: https://www.codecademy.com/courses/learn-java/lessons/static-variables-and-methods-lesson/exercises/static-methods-refresher
Our final core Java lesson will deal with an important concept in object oriented design called inheritance. This allows us to create flexible, easy to extend designs.
Here's the course: https://www.codecademy.com/courses/learn-java/lessons/java-inheritance-and-polymorphism/exercises/introducing-inheritance