Now we give the robot a brain. This is the canonical WPILib drivetrain example, the same pattern in the official Zero-to-Robot guide, adapted to PWM SPARK MAX outputs so it works on any KitBot wiring.
Create a new project in WPILib VS Code (Ctrl+Shift+P -> 'WPILib: Create a new project' -> Example -> java -> Getting Started). Replace Robot.java with:
package frc.robot;
import edu.wpi.first.wpilibj.TimedRobot;
import edu.wpi.first.wpilibj.XboxController;
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
public class Robot extends TimedRobot {
private final PWMSparkMax m_leftDrive = new PWMSparkMax(0);
private final PWMSparkMax m_rightDrive = new PWMSparkMax(1);
private final DifferentialDrive m_robotDrive =
new DifferentialDrive(m_leftDrive::set, m_rightDrive::set);
private final XboxController m_controller = new XboxController(0);
public Robot() {
m_rightDrive.setInverted(true); // right side spins opposite
}
@Override
public void teleopPeriodic() {
// left stick Y = forward/back, right stick X = turn
m_robotDrive.arcadeDrive(-m_controller.getLeftY(),
-m_controller.getRightX());
}
}
The modern DifferentialDrive constructor takes two DoubleConsumer outputs, so m_leftDrive::set and m_rightDrive::set (method references) are exactly what it expects.
Why the negatives? On an Xbox controller, pushing the stick forward returns a negative Y value. Negating it makes 'stick forward' equal 'robot forward'. arcadeDrive(xSpeed, zRotation) takes forward speed and rotation, both on [-1, 1].
Why setInverted(true) on the right? The two gearboxes face opposite directions, so a positive command would drive them apart. Inverting the right side makes both sides push forward together. If your robot spins in place instead of driving straight, this flag is almost always the culprit.
Deploy: Connect to the robot radio over Wi-Fi (or USB to the roboRIO), then Ctrl+Shift+P -> 'WPILib: Deploy Robot Code'. Watch the RioLog — you want to see 'Robot program starting'. Open the Driver Station, enable Teleop, and drive. Note that DifferentialDrive has Motor Safety enabled, so you must call arcadeDrive every loop or the motors will time out and stop.
A safety habit: test on blocks first (wheels off the ground). Start at low stick deflection. Map a 'slow mode' later by multiplying the inputs by 0.5 when a bumper is held. This single example — two motor controllers, one DifferentialDrive, one controller — is the foundation every drivetrain builds on.
Key takeaways
- arcadeDrive(xSpeed, zRotation) takes forward speed and rotation, each on [-1, 1]
- Negate Xbox stick Y because forward returns negative; invert one drive side so both push forward
- DifferentialDrive uses Motor Safety — call arcadeDrive every loop, test on blocks, and watch the RioLog for 'Robot program starting'
Keep going
Take the quiz · +10 XP with an accountMore in Worked Examples & Mini-Projects
Sources & corrections
This lesson is AI-assisted: drafted from primary sources, then reviewed and edited by hand. Errors still get through. When one is reported we fix it and write down what changed — publicly, in the corrections log.
Sources and further reading
- Creating your Test Drivetrain Program (WPILib)docs.wpilib.org
- Using the WPILib Classes to Drive your Robotdocs.wpilib.org
- AM14U6 6-Wheel Drop Center Drive Base (AndyMark)andymark.com
Read next
Articles on this, for competition day
The lesson covers how it works. These cover what to do when it breaks.
- 13 min read
WPILib Won't Deploy: Every Cause and How to Fix It
Your WPILib deploy is failing? Work the decision tree: wrong folder in VS Code, team number, roboRIO image, JDK version, macOS network privacy. Fixes for each.
Read - 20 min read
How to Program FRC Swerve Drive with WPILib
A practical guide to programming FRC swerve drive with WPILib: kinematics, SwerveModuleState, field-relative control, odometry, and where PathPlanner fits.
Read - 13 min read
FRC Code Structure Best Practices: Command-Based Project Architecture
How to structure an FRC command-based robot project the right way — subsystems, commands, RobotContainer, and Constants — verified against official WPILib docs.
Read
Lesson quiz
RequiredAll 3 right completes the lesson. Miss one and only that question comes back — anything you already answered correctly stays banked.
0 of 3 answered
01.In WPILib's DifferentialDrive.arcadeDrive(xSpeed, zRotation), what do the two arguments represent?
02.Why is a joystick's Y-axis value typically negated before being passed to arcadeDrive?
03.To avoid a Motor Safety timeout that stops your drivetrain, how must arcadeDrive be called?
Answer every question to submit.
All 28 lessons in Getting Started with FRC
- Not started:Project 1 — Make a NEO Spin with the REV Hardware Client
- Not started:Project 2 — Deploy a Real Arcade-Drive Program
- Not started:Project 3 — Refactor into a Command-Based Drive Subsystem
- Not started:Project 4 — Build a Fuel Launcher for REBUILT
- Not started:Project 5 — A One-Button Autonomous Routine
- Not started:The Connection Chain: When the Driver Station Won't Connect
- Not started:Brownouts: Why the Robot Goes Limp Mid-Match
- Not started:CAN Bus Gremlins: Missing and Conflicting Devices
- Not started:Software Gotchas: Inverted Drives, Scheduler Stalls, and Reading the RioLog
- Not started:Inspection-Day Failures: Bumpers, Size, and Weight
- Not started:Closed-Loop Control: PID + Feedforward for a Consistent Shot
- Not started:Swerve Drive: Omnidirectional Movement with YAGSL
- Not started:AprilTag Vision: Knowing Where You Are with PhotonVision
- Not started:Data-Driven Strategy: Scouting, EPA/OPR, and Alliance Selection
- Not started:Choosing Your Hardware Ecosystem: REV vs CTRE