Learning Goals 5 min
By the end of this lesson you will be able to:
- Read a
forloop and identify its three parts: the start, the condition and the update. - Use the loop counter
ias a pin number to drive a row of LEDs on contiguous pins, removing repetitivedigitalWritecalls. - Rewrite the 3-LED chase from L01-10 in roughly half the lines of code — and explain why the new version scales to 4, 5 or 10 LEDs without growing.
Warm-Up 10 min
Last lesson the loop body started to feel like typing the same thing over and over: digitalWrite(RED_PIN, HIGH), digitalWrite(YELLOW_PIN, HIGH), digitalWrite(GREEN_PIN, HIGH). Three lines that all say the same thing with one detail changed. Today we replace them with one line that says "do this for each pin from 9 to 11".
Quick-fire puzzle
Wei Jie wants to wire ten LEDs in a single long row — pins 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 — and chase a dot from one end to the other. Without writing any code yet, predict:
- If he uses the hand-coded style from L01-10, roughly how many
digitalWritecalls would the chase need inloop()? - And how many
pinModecalls insetup()? - If he later wants to add an 11th LED on pin 3, how many places in the sketch does he need to edit?
Reveal the answer
- Each chase step has 2
digitalWritecalls (one HIGH, one LOW). Ten steps = 20 calls inloop(). - Ten
pinModecalls. - One more
pinModeinsetup()and two moredigitalWritecalls inloop(). Three more lines, in three different places — easy to forget one.
Thirty-plus lines of repetitive code that all do the same idea: "for each pin in this range, switch it on briefly then off". Today's lesson collapses that idea into one short tool.
New Concept 20 min
The big idea — "do this for each pin in the range"
A for loop is a piece of code that runs its body multiple times, with a counter variable taking a different value each time. You write the loop once; the Arduino does the repeating.
The shape of a for loop
for (int i = 9; i <= 11; i++) {
// body — runs once for each value of i (9, then 10, then 11)
}Three parts live inside the parentheses, separated by semicolons:
| Part | Example | What it does |
|---|---|---|
| Start | int i = 9 | Declare a counter variable (i) and give it a starting value (9). This runs once, before the body's first pass. |
| Condition | i <= 11 | "While this is true, keep going." Checked before each pass. The loop stops the moment the condition becomes false. |
| Update | i++ | "After each pass, change i like this." The shorthand i++ means add one to i. Runs after each pass of the body. |
So for (int i = 9; i <= 11; i++) reads as: "start i at 9; while i is at most 11; after each pass add one to i." That makes the body run three times — once with i = 9, once with i = 10, once with i = 11. Exactly the three pins we have wired.
The new comparison operators
The condition uses comparison symbols you may not have seen yet:
i <= 11— "i is less than or equal to 11". True when i is 9, 10 or 11. False at 12.i < 12— "i is less than 12". Same effect asi <= 11for whole numbers. Style choice.i >= 9— "i is greater than or equal to 9". Useful for counting down.
Using the counter as a pin number
This is the trick that makes a chase one line shorter for every LED:
for (int i = 9; i <= 11; i++) {
digitalWrite(i, HIGH);
}On pass 1: i = 9, so the body becomes digitalWrite(9, HIGH) — red on. On pass 2: i = 10, yellow on. On pass 3: i = 11, green on. The loop walked through pins 9, 10, 11 — one line of code did the job of three.
Step through it yourself
Press Step and watch which of the three parts lights up each time. Notice the order: the condition is checked before every pass, including the one that fails — that is the pass where the loop stops.
for (int i = 9; i <= 11; i++) {
digitalWrite(i, HIGH);
delay(200);
digitalWrite(i, LOW);
}startStart: i is created and set to 9. This happens once, before the first pass.
Set LAST_PIN to 11 and switch the comparison from <= to <. The green LED on pin 11 stops coming on, and the tally drops from three to two — one character, one fewer LED. That is the off-by-one bug, and now you can see it happen.
Now do the experiment the callout below is about: leave LAST_PIN at 11 and switch the comparison to i < LAST_PIN. Count the LEDs before you press Run.
Two new naming constants
From today we name the range of pins, not each individual one:
const int FIRST_PIN = 9;
const int LAST_PIN = 11;Combined with the loop:
for (int i = FIRST_PIN; i <= LAST_PIN; i++) {
digitalWrite(i, HIGH);
}Now to add a 4th LED on pin 12, you change one line: LAST_PIN = 12. The loop body never moves.
One important rule — contiguous pins
This trick only works because our LEDs are on contiguous pin numbers: 9, 10, 11. If they were scattered — say, red on pin 2, yellow on pin 5, green on pin 8 — the counter i would not give us the right pins as it counts up. For scattered pins you need an array of pin numbers, which we'll meet in L01-32. Today, keep your three LEDs on cosy neighbouring pins.
Reuse the L01-10 wiring
Today's circuit is the same three-LED breadboard from L01-10: red on D9, yellow on D10, green on D11, each with its 220 Ω resistor, all three sharing GND through the breadboard's − rail. Keep it wired up exactly as you built it last lesson.
Why it matters
Every meaningful Arduino project uses for loops — to scan sensors, drive arrays of LEDs, build menus, send characters over Serial, read keypresses, talk to displays. Today is the smallest possible introduction to the most-used control structure in C++.
Worked Example 20 min
Goal: rewrite the L01-10 chase using a for loop, then count how many lines we saved.
Step 1 — the L01-10 chase, for comparison
This is the chase you wrote last lesson, with every digitalWrite hand-coded:
const int RED_PIN = 9;
const int YELLOW_PIN = 10;
const int GREEN_PIN = 11;
void setup() {
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
}
void loop() {
digitalWrite(RED_PIN, HIGH);
delay(200);
digitalWrite(RED_PIN, LOW);
digitalWrite(YELLOW_PIN, HIGH);
delay(200);
digitalWrite(YELLOW_PIN, LOW);
digitalWrite(GREEN_PIN, HIGH);
delay(200);
digitalWrite(GREEN_PIN, LOW);
}Notice every three lines of loop() are the same shape — only the pin constant changes. That's the pattern a for loop will eat.
Step 2 — type the for-loop version
In the IDE, go to File → New and save the sketch as chase-for-loop. Type this from scratch:
const int FIRST_PIN = 9;
const int LAST_PIN = 11;
void setup() {
for (int i = FIRST_PIN; i <= LAST_PIN; i++) {
pinMode(i, OUTPUT);
}
}
void loop() {
for (int i = FIRST_PIN; i <= LAST_PIN; i++) {
digitalWrite(i, HIGH);
delay(200);
digitalWrite(i, LOW);
}
}Step 3 — upload and watch
The LEDs chase across the breadboard — red, then yellow, then green — exactly the same behaviour as last lesson's hand-coded chase. The Arduino can't tell the difference. The for-loop version is just easier on your fingers.
Step 4 — count the savings
| Version | Lines in setup() | Lines in loop() | Total lines of logic |
|---|---|---|---|
| L01-10 hand-coded chase | 3 | 9 | 12 |
| L01-11 for-loop chase | 3 | 5 | 8 |
| Imagined 10-LED hand-coded | 10 | 30 | 40 |
| 10-LED for-loop (same code!) | 3 | 5 | 8 |
That last row is the real point. The for-loop version doesn't grow when you add LEDs — only the LAST_PIN constant changes. The hand-coded version triples in size.
Step 5 — trace the loop variable on paper
Before moving on, draw a small table in your notebook to confirm you can read the loop:
| Pass | Value of i | Body runs |
|---|---|---|
| 1 | ____ | digitalWrite(____, HIGH); delay(200); digitalWrite(____, LOW); |
| 2 | ____ | digitalWrite(____, HIGH); delay(200); digitalWrite(____, LOW); |
| 3 | ____ | digitalWrite(____, HIGH); delay(200); digitalWrite(____, LOW); |
| 4 | — | Condition i <= 11 is now false; loop ends. |
Try It Yourself 20 min
Three small mutations of the for-loop chase. Predict before you upload.
Goal: Chase only the first two LEDs — red and yellow. Skip green.
Change one number at the top of the sketch and re-upload:
const int FIRST_PIN = 9;
const int LAST_PIN = 10; // was 11Questions:
- Without rewriting anything in
loop(), does the green LED come on at all? Why or why not? ____ - Set
LAST_PIN = 9. What does the chase look like now? ____
Goal: Run the chase backwards — green first, then yellow, then red. The hardware doesn't change; only the loop does. Use a counting-down for loop.
const int FIRST_PIN = 9;
const int LAST_PIN = 11;
void setup() {
for (int i = FIRST_PIN; i <= LAST_PIN; i++) {
pinMode(i, OUTPUT);
}
}
void loop() {
for (int i = LAST_PIN; i >= FIRST_PIN; i--) {
digitalWrite(i, HIGH);
delay(200);
digitalWrite(i, LOW);
}
}Questions:
- What does
i--mean? ____ (Hint: the opposite ofi++.) - The condition is now
i >= FIRST_PIN, noti <= LAST_PIN. Why? ____ - Trace the value of
ion paper for each pass: pass 1 → ____, pass 2 → ____, pass 3 → ____.
Goal: A bouncing chase — forward (red → yellow → green) then backward (yellow → red), repeating. The classic Knight Rider effect from L01-10's Mini-Challenge, but now you'll fit it in two short for loops instead of a long string of hand-coded steps.
// Forward then backward, repeating. Endpoints visited once per cycle,
// middle LED visited twice — exactly the Knight Rider rhythm.
const int FIRST_PIN = 9;
const int LAST_PIN = 11;
void setup() {
for (int i = FIRST_PIN; i <= LAST_PIN; i++) {
pinMode(i, OUTPUT);
}
}
void loop() {
for (int i = FIRST_PIN; i <= LAST_PIN; i++) {
digitalWrite(i, HIGH);
delay(150);
digitalWrite(i, LOW);
}
for (int i = LAST_PIN - 1; i > FIRST_PIN; i--) {
digitalWrite(i, HIGH);
delay(150);
digitalWrite(i, LOW);
}
}Questions:
- Why does the second loop start at
LAST_PIN - 1and not atLAST_PIN? ____ (Hint: which LED was the last one lit by the first loop?) - Why is the second condition
i > FIRST_PINand noti >= FIRST_PIN? ____ - Compare your line count to the hand-coded bounce from L01-10. How many lines did you save? ____
Mini-Challenge 15 min
The wave
Different from a chase: in a wave, the LEDs come on one by one and stay on, building up until all three are lit. Then they go off one by one in the opposite order. Like a wave rolling in and ebbing out. Two for loops, one going up, one going down.
Your task:
- Use the same
FIRST_PIN/LAST_PINconstants and the same wiring as L01-10. - In
loop(), write a forward for loop that turns each LEDHIGHwith a 200 ms delay between each. After this loop, all three LEDs should be on. - Write a backward for loop (using
i--andi >= FIRST_PIN) that turns each LEDLOWwith a 200 ms delay between each. After this loop, all three are off. - Add a 500 ms pause between the two loops so the "all on" moment is visible.
It works if:
- Red comes on, then yellow, then green — and all three are lit together for half a second.
- Then green goes off first, then yellow, then red — back to darkness.
- The whole effect feels like a wave passing left to right.
- Your
loop()body has at most onedigitalWrite(_, HIGH)and onedigitalWrite(_, LOW)— no per-pin lines.
Recap 5 min
A for loop says "do this body of code multiple times, with a counter going through a range of values." Inside setup() it spares you repeated pinMode calls; inside loop() it spares you repeated digitalWrite calls. Using the counter as a pin number works only when your pins are on contiguous numbers — but that single trick is what makes chases, waves and bouncing patterns easy to write.
- for loop
- A C++ control structure that runs a block of code repeatedly, with a counter variable taking a sequence of values. Three parts: start, condition, update.
- Counter variable
- The variable that changes on each pass of the loop. Conventionally called
i(short for "index" or "iterator"). Only visible inside the loop body. - i++ / i--
- Shorthand for "add 1 to i" and "subtract 1 from i". The same as writing
i = i + 1andi = i - 1, but tidier. - Contiguous pins
- Pin numbers that are consecutive — 9, 10, 11. Required for using the loop counter directly as a pin number. Non-contiguous pins (2, 5, 8) need an array (L01-32).
- Off-by-one
- The classic loop bug:
i < 11stops at 10,i <= 11stops at 11. One step's difference, one wrong-coloured LED. Always count the passes carefully.
Homework 5 min
Make it scale. Pretend you've added a 4th LED on pin 12 (you don't need to actually wire one — just write the code). Update only the top of your chase sketch and confirm the loop body never needs to change.
- Open your
chase-for-loopsketch from class. - Change one line:
const int LAST_PIN = 12;. - Verify in the IDE that the sketch still compiles. (It will — the loop logic is untouched.)
- If you have a 4th LED and a 4th resistor in your kit, wire it up on pin 12 using the same module pattern as L01-10: jumper into
E25, resistor along row C fromC25toC30, LED anode atA30and cathode atA29, and the return jumper out ofE29, stepping sideways into column 24 before it climbs to the − rail. Column 30 is the end of the board, so this is the last module that fits. - Upload and watch the 4-LED chase — the body of
loop()didn't change at all.
Also: a small tracing exercise on paper. Given this loop:
for (int i = 4; i <= 8; i++) {
// body runs
}- How many times does the body run? ____
- What is the final value of
iduring the last pass? ____ - What is
i's value after the loop ends? (Trick question — re-read the "iis local" callout.) ____
Bring back next class:
- The updated
.inofile showingLAST_PIN = 12. - A short phone video of the chase running with however many LEDs you wired (3 or 4).
- Your tracing exercise answers on a notebook page.