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.
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.
Reveal one valid sketch
// Wave: forward filling all 3, hold, backward emptying all 3.
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);
}
delay(500);
for (int i = LAST_PIN; i >= FIRST_PIN; i--) {
digitalWrite(i, LOW);
delay(200);
}
}Notice how the first loop only contains HIGH and the second only contains LOW. Because LEDs stay in whatever state you last set them to, the forward loop accumulates light (each LED stays on after its pass) and the backward loop accumulates darkness. No hand-coded per-pin lines anywhere.
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 (columns 31–36, anode at
B35, cathode atB34, GND drop on the − rail). - 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.