Robot Lifecycle Methods
Every line you have written in Units 2 through 4 lives inside a method. This lesson is about which methods WPILib calls, when it calls them, and how long you have to finish. The 20ms loop is not a software preference — it is a hard constraint enforced by the Driver Station. Violate it and the robot disables itself.
By the end of this lesson, you will:
- Identify every WPILib lifecycle method, when it fires during a match, and how many times
- Explain the 20ms loop contract and what the watchdog does when it is violated
- Choose the correct lifecycle method for hardware construction, sensor polling, dashboard updates, PID loops, and autonomous sequences
- Predict the crash that results from creating hardware objects inside a periodic method
- Write a structurally correct
Robot.javathat initializes hardware inrobotInit()and drives behavior from periodic methods
From Java Concepts to Physical Robot
Up to this point, your code has only run on your laptop. Unit 5 changes that. When you deploy to a roboRIO, your compiled code runs on a real Linux computer physically connected to motors, sensors, and a battery. The programs you write now have physical consequences.
The first thing to understand is that you do not call your own code. WPILib does. Your Robot.java class extends TimedRobot, and WPILib's main loop calls your lifecycle methods at specific moments during the match. Your job is to understand exactly when each method runs and what to put in it.
The 20ms Loop Contract
WPILib's runtime calls your periodic methods on a 50Hz timer — once every 20 milliseconds. That is the contract. In exchange, your periodic methods must always return within that 20ms window. If a periodic method takes longer, the watchdog fires.
20ms is enough to read a controller, run a PID calculation, and command four motors. It is not enough to wait for a sensor to stabilize, construct a hardware object, or run a polling loop. Those block the thread and delay every subsequent cycle.
The WPILib watchdog prints to the Driver Station console when your loop overruns: "Loop time of 32.4ms overrun". Occasional 1–2ms overruns during JVM warmup at startup are normal. Consistent overruns above ~5ms indicate a problem — usually a blocking call, a slow sensor read, or accidental hardware construction in periodic. Check the Driver Station Messages tab after every test session.
A Match From the Robot's Perspective
Step through the match timeline below. Each step shows which lifecycle method WPILib calls, how many times, and what the robot is expected to do at that moment.
Every Lifecycle Method at a Glance
robotPeriodic() is the one method that runs every single loop cycle regardless of mode — use it for things that should always be checked, like dashboard updates and fault logging.
The Most Important Design Question: Init or Periodic?
Almost every mistake beginners make in TimedRobot code comes from putting the wrong thing in the wrong method. Ask about any line: does this need to run once, or does it need to run every 20ms?
Anything that creates objects, configures hardware, sets initial state, or runs one-time setup belongs in an init() method. These operations are slow relative to the 20ms budget and pointless to repeat.
Anything that needs fresh sensor data, responds to live input, or drives ongoing hardware behavior belongs in periodic. These need to run continuously to be useful.
Some patterns should never appear in any lifecycle method. They block the robot thread, violate the 20ms contract, or corrupt hardware state.
"What Belongs Here?" — Scenario Explorer
Each scenario shows a code snippet and intent. Select it to see which lifecycle method is correct, which is wrong, and why — including the hardware consequence.
intakeMotor = new TalonFX(8);constructing the intake motor objectwhile (!beamBreak.get()) { }waiting for the intake sensor to trigger before stopping motorgyro.reset();resetting the gyro heading to zero for auto startSmartDashboard.putNumber("Amps", motor.getStatorCurrent()...);publishing motor current to the dashboarddriveMotor.setNeutralMode(NeutralModeValue.Brake);switching drivetrain to brake mode at the start of teleopdouble speed = controller.getLeftY();
driveMotor.set(speed);reading the joystick and commanding the drive motorThe most common lifecycle mistake I see in competition robots is hardware construction in a periodic method. It's usually introduced as a quick fix — someone commented out the field declaration and re-added new TalonFX(5) inside teleopPeriodic() to test something, and it got committed. Every 20ms, that code sends a CAN initialization frame to a running motor, clears its configuration, and floods the bus with traffic. The motor stutters. The drive team reports the robot "feels laggy." The programmer spends two hours checking wiring before someone reads the actual code. A single review catch during PR would have prevented it entirely.
robotPeriodic: The Universal Baseline
robotPeriodic() is unique: it runs in every mode — disabled, auto, teleop, test, simulation — every 20ms, always. It runs before the mode-specific periodic. This makes it the right place for anything that should always be happening regardless of match state: dashboard updates, odometry, fault logging.
During robot inspections I always ask to see the drive team's dashboard while the robot is disabled. Teams that put dashboard updates only in teleopPeriodic() show stale values until the match starts — they have no way to verify sensor health, battery voltage, or subsystem state in the queue. Teams that put updates in robotPeriodic() can verify everything is functional before they ever enable. Pre-match verification takes sixty seconds and has saved robots from competing with a broken encoder or a dead battery that nobody noticed. Put your diagnostics in robotPeriodic().
- All hardware objects declared as fields and constructed in
robotInit(). A field declared but never assigned inrobotInit()isnull. The first periodic cycle that calls a method on it throwsNullPointerExceptionand disables the robot. - No
new HardwareClass()calls inside any periodic method. Creating hardware objects in periodic floods the CAN bus, resets motor configuration, and causes stutter. This is a match-ending bug if it reaches competition. - No blocking calls in periodic: no
Thread.sleep(), no pollingwhile()loops. These hold the thread past the 20ms deadline. Use a per-cycle state check instead of a blocking wait. - Dashboard calls in
robotPeriodic(). This ensures the pit crew can see live sensor data and battery state before enabling — not just during a match. - Mode-transition setup in the corresponding
init()method. Gyro reset, odometry reset, state machine priming — once at the start of a mode, not every 20ms. - Check the Driver Station Messages tab after every test session. Watchdog overrun messages tell you exactly which cycle ran long. Address them before competition — a 35ms loop produces input lag the drive team can feel.
Knowledge Check
private TalonFX motor; as a field but constructs it inside teleopPeriodic(). After a full match, approximately how many times has new TalonFX(5) been called?Your First Structured Robot.java
Build a complete, structurally correct Robot.java without any hardware attached. This is a code structure exercise — focus on putting things in the right methods, not on making mechanisms move.
- Declare the fields. Add three private fields: a
TalonFX driveMotor, anXboxController controller, and anAutoState autoStateenum. Do not assign or construct them yet. - Populate
robotInit(). Construct the motor with a CAN ID fromConstants.java. Construct the controller on port 0. Apply a current limit usingTalonFXConfiguration. This is the only place any constructor is called. - Populate
robotPeriodic(). Publish battery voltage, drive motor stator current, and the auto state name to SmartDashboard. Confirm it runs in disabled by testing before enabling. - Populate
autonomousInit(). SetautoState = AutoState.DRIVE. Log the selection to the console withSystem.out.println(). - Populate
autonomousPeriodic(). Implement a two-state machine using yourAutoStateenum:DRIVEsets motor to 0.4; after 2 seconds transitions toSTOP, which sets motor to 0.0. NoThread.sleep()— use timer logic across cycles. - Populate
teleopPeriodic(). Readcontroller.getLeftY(), apply a 0.05 deadband withMathUtil.applyDeadband(), and command the motor. - Populate
disabledInit(). Set the motor to 0.0 and log "Robot disabled." - Bonus: For every line in your completed
Robot.java, write which method it lives in, why it belongs there, and what would go wrong if you moved it to a different method.