Loops
Your robot already runs inside a loop — WPILib calls periodic methods fifty times per second, forever. Understanding Java loops means understanding how to do repetitive work correctly inside that constraint, and how to recognize when a loop will break the constraint entirely.
By the end of this lesson, you will:
- Write correct
while,for,do-while, and enhancedfor-eachloops with appropriate use cases for each - Explain why a blocking
whileloop inside a periodic method locks up the robot and what WPILib does in response - Identify where loops do belong in FRC code — initialization, data processing, iterating over hardware collections
- Use
breakandcontinueto control loop execution for early-exit and filter patterns - Trace through a
forloop step by step — initialization, condition, body, update — and predict the output at any point
The Loop You Didn't Write
Before covering Java's loop keywords, here's the most important loop in your robot program — the one WPILib writes for you:
Every periodic method you write is a loop body that executes on a 20ms timer. WPILib's watchdog monitors each iteration: if your code takes longer than 20ms to complete, the Driver Station logs a loop overrun warning. If the overrun is severe enough, the robot's communication with the field management system can drop, and the robot disables itself.
This is the context for every loop you write in robot code. A loop that runs for half a second in a desktop Java program is annoying. The same loop inside teleopPeriodic() disables your robot mid-match.
The Four Loop Patterns
Java has three loop keywords — while, for, and do-while — plus an enhanced for-each syntax for collections. Click each one to see its structure, when to reach for it, and where it appears in real FRC code.
Tracing a for Loop Step by Step
A for loop has four distinct phases that execute in a specific order. Understanding that order is what lets you predict exactly what any loop does without running it — which matters when you're reading team code or debugging a loop that's off by one. Step through the tracer below one phase at a time.
The Most Dangerous Loop Pattern in FRC
Now for the thing that causes more match losses from software than almost any other mistake: a while loop placed inside a periodic method that waits for a hardware condition.
A blocking while loop inside teleopPeriodic() or autonomousPeriodic() prevents the periodic method from returning. WPILib cannot call the next iteration. The robot stops responding to the Driver Station. After a timeout, it disables. On a real competition field, the robot goes limp mid-match.
The watchdog can't save you from this — the loop isn't just slow, it's infinite from the watchdog's perspective until the hardware condition is met. By then, the match may be over.
The state-based version does the same thing. The intake runs until the beam break triggers. But instead of blocking and waiting, it checks the sensor on each periodic call and returns. The WPILib loop keeps running, the Driver Station keeps getting heartbeats, and the robot stays enabled.
In my second season as an LRI, I watched a team's robot go limp at the start of an autonomous routine. The robot had been working perfectly in the pit. On the field, a particular autonomous path started with a while (!encoderAtTarget()) loop waiting for the drive to complete a turn. In the pit, the turn took about 0.4 seconds — fast enough that the watchdog just barely tolerated it. On the field, something in the drivetrain was slightly off; the turn took 1.1 seconds. The watchdog fired, comms dropped, and the robot sat still for the entire autonomous period. The team lost the match by four points. The fix — converting the blocking loop into a state check — took eleven lines of code and about twenty minutes. They didn't get those three auto points back.
Where Loops Do Belong in Robot Code
The no-blocking-loops rule applies specifically to periodic methods. Loops are entirely appropriate in other parts of your robot code.
robotInit() — one-time setup
Configuring all four swerve modules at startup, applying current limits to every motor in an array, validating that all hardware objects are non-null — this is initialization work that runs once when the program starts. A for loop here is fine and common.
Utility and helper methods
Searching an array for a fault, averaging a set of sensor readings, finding the maximum current among all drive motors — these are data-processing tasks that a method can perform and return a result from. The method is called from periodic code, but the loop completes in microseconds.
Autonomous command sequences
In Unit 6, Command-Based programming gives you tools for sequencing actions without blocking loops. But in a simple TimedRobot autonomous, a for loop iterating over a list of waypoints is acceptable if each waypoint executes synchronously and the whole sequence is fast enough. This pattern doesn't scale to complex autos — it's the reason elite teams move to Command-Based.
Loop Control: break and continue
Two keywords let you alter the normal flow of a loop from inside its body.
break — exit immediately
break terminates the loop entirely, jumping to the first statement after the closing brace. Use it to stop searching once you've found what you need — iterating past the answer wastes time and can cause unintended side effects.
continue — skip to next iteration
continue skips the rest of the current iteration's body and jumps to the loop's update step (for a for loop) or back to the condition check (for while). Use it to filter out elements that don't need processing.
break and continue apply to the innermost loop they appear in — not to the periodic method itself. You can't use break to exit teleopPeriodic(). To exit a periodic method early, use return (same guard clause pattern from Lesson 4). Inside a loop within periodic code, both keywords are safe as long as the loop itself completes in well under 20ms.
The Three Loop Bugs That Appear Every Season
Bug 1: Off-by-one error
The most common loop mistake. Arrays in Java are zero-indexed — a four-element array has valid indices 0, 1, 2, 3. An index of 4 throws ArrayIndexOutOfBoundsException. The condition i <= array.length instead of i < array.length is the standard mistake.
Bug 2: Infinite loop from a condition that never changes
A while loop whose condition depends on a variable that never gets updated inside the loop will run forever. In robot code, this freezes the periodic method.
Bug 3: Loop variable shadowing an outer variable
Declaring a loop variable with the same name as a class field silently hides the field inside the loop. The loop operates on its local copy; the field is unchanged. This produces behavior that looks correct in isolation but fails when you check the field's value later.
🔌 System Check
- Zero blocking loops in periodic methods. Any loop that waits for a hardware event — a sensor reading, a motor reaching a setpoint, a timer — belongs outside periodic code. Convert it to a state check: test the condition each periodic call and return. This is non-negotiable.
- Loops in periodic code must complete in well under 1ms. Iterating over four swerve modules, six motors, or eight CAN devices takes microseconds. Iterating over a large dataset, a long string, or anything dynamically sized requires profiling before it goes into periodic code.
- Always use for-each when you don't need the index.
for (Motor m : motors)is immune to off-by-one errors and bounds violations. Prefer it over indexedforloops for collection iteration whenever the index is not needed. - robotInit() loops are time-budgeted differently. A loop in
robotInit()that takes 200ms is fine — it runs once at startup. The same loop inteleopPeriodic()is a match-ending bug. Know which method you're in. - burnFlash() inside init loops, never inside periodic. Writing configuration to motor controller flash memory (REV) or persistent storage (CTRE) is a slow operation — up to 200ms per motor. It belongs in
robotInit(), inside a loop that touches all motors exactly once.
Knowledge Check
Click an answer to check your understanding.
teleopPeriodic(): while (!beamBreak.get()) { intakeMotor.set(0.6); }. The beam break is slow to trigger. What happens to the robot?for (int i = 0; i <= modules.length; i++) { System.out.println(i); }Loop Audit and Redesign
The following Robot.java skeleton contains three loop-related problems of increasing severity. Identify each one, explain the physical consequence on the robot, and write the corrected version.
- For each of the three problems: name it, explain in one sentence what it does to the robot at runtime, and write the corrected code.
- Problem 2 requires a redesign, not just a syntax fix. Implement the state-based alternative for the intake sequence. Your solution must: keep
teleopPeriodic()returning within 20ms every cycle, correctly setgamePieceHeldtotruewhen the beam break triggers, and stop the intake motor immediately at that point. - Extract the max-current logic from Problem 3 into its own
private double getMaxDriveCurrent()helper method. Show how you call it fromteleopPeriodic(). - Bonus: Add a
for-eachloop inrobotInit()that prints the CAN ID of every drive motor to the console usingSystem.out.println(). Then add a secondfor-eachthat usescontinueto skip any motor whose CAN ID is greater than 3, logging a warning for skipped motors.