Learning Goals 5 min
By the end of this lesson you will be able to:
- Use
if/elseto branch a sketch — running one block of code when a condition is true and a different block when it's false. - Chain
else ifclauses to pick exactly one of many options, without nestedifblocks. - Use the comparison operators
<,>,<=,>=(alongside L01-19's==and!=) to compare numeric values.
Warm-Up 10 min
L01-19 gave you the smallest possible if — just enough to detect a state change. Today the if gets its full set of partners: else, else if, and the comparison operators that let it test any number, not just compare states.
Quick-fire puzzle
Lakshmi walks to school in Petaling Jaya. She reaches a traffic light at the corner. Translate her decision into plain English:
if (light == GREEN) {
walk();
}
else if (light == YELLOW) {
slowDown();
}
else {
stop();
}- What does Lakshmi do if the light is green?
- What does she do if it's yellow?
- What does she do if it's red — even though "red" never appears in the code? Why does the code still cover that case?
- If the light is somehow purple (a malfunction!), what does Lakshmi do?
Reveal the answer
- Walk. The first condition matched, so the first block ran.
- Slow down. The first condition was false, the second was true, so the second block ran.
- Stop. The
elsebranch is the catch-all: it runs when every previous condition turned out to be false. It doesn't need to name the colour "red" — anything that isn't green or yellow falls intoelse. - Also stop — the
elsecatches that too. Including malfunctions. That's whyelseat the end of a chain makes a sketch defensive: if a value you didn't predict shows up, you have a default plan.
That four-part structure — if for the primary case, else if for alternatives, else for the catch-all — is what we build today.
New Concept 20 min
The big idea — give your sketch a choice
Until now, every line in loop() has run every pass, in order. Adding if let the sketch skip a block when a condition is false. Today's job is to extend that to choosing between two or more blocks: "do this, OR do that, OR if neither matched, do something default."
The three forms of branching
Form 1: if alone (L01-19). Run a block if the condition is true; skip it otherwise.
if (light == GREEN) {
walk();
}
// If light isn't GREEN, nothing happens — the sketch carries on past the block.Form 2: if / else. Two paths. Exactly one runs.
if (light == GREEN) {
walk();
}
else {
stop();
}
// Either walk OR stop will run — never both, never neither.Form 3: if / else if / else. Any number of paths. Exactly one runs — the first that matches.
if (mode == 0) {
showOff();
}
else if (mode == 1) {
showRed();
}
else if (mode == 2) {
showYellow();
}
else {
showAll();
}
// The chain stops at the first match. Order matters!The mental model — "first match wins"
The Arduino reads an if / else if chain from top to bottom. At each line, it asks: "is this condition true?" The moment it gets a YES, it runs that block and skips every other branch. If it reaches the bottom without a single YES, it runs the else block (if there is one). Never zero, never two.
Why this is different from "many separate ifs"
Look at the homework you wrote for L01-19. Five separate if blocks, one per mode. They all ran every loop pass. The Arduino checked all five conditions, every time — even though only one could possibly be true. Wasted effort.
With else if, the moment a condition matches, the rest of the chain is skipped. For five modes, the chip checks at most three conditions before finding the match (on average). And there's a deeper benefit: it's impossible for two branches to run at the same time, because the chain forces "exactly one". Bugs get harder to make.
The comparison operators
L01-19 gave you == and !=. Today you get four more, which lets your conditions compare magnitudes, not just equality.
| Operator | Reads as | True when | Example |
|---|---|---|---|
== | equals | both sides are the same | mode == 3 |
!= | not equals | the sides differ | currentButton != lastButton |
< | less than | left is smaller than right | count < 10 |
> | greater than | left is bigger than right | count > 100 |
<= | less than or equal | left is smaller OR equal | level <= 5 |
>= | greater than or equal | left is bigger OR equal | age >= 13 |
One subtle but important rule: <= and >= are two characters, no space in between. < = with a space is a compile error.
Comparing things means asking which "interval" a number lives in
With < and >, you can build else if chains that ask "which range is this number in?" — a very common pattern in sensors and games.
if (presses < 3) {
// 0, 1 or 2 — too few
}
else if (presses < 6) {
// 3, 4 or 5 — just right
}
else if (presses < 10) {
// 6, 7, 8 or 9 — getting many
}
else {
// 10 or more — too many
}Notice we don't have to write presses >= 3 && presses < 6 for the middle branch. Because of the "first match wins" rule, by the time we reach else if (presses < 6) we already know presses wasn't less than 3. Each else if implicitly inherits "and not anything above me".
Reuse the L01-19 homework wiring
Today's hardware is the same as your L01-19 homework: three LEDs (red on D9, yellow on D10, green on D11) with 220 Ω resistors, plus a button on D7 with INPUT_PULLUP. All sharing GND through the breadboard's − rail. No new components today.
Why it matters
Almost every program ever written contains an if. From a phone deciding whether to ring (silent mode? do not disturb? do ring) to a Tesla deciding whether to brake (object close? object moving? lane clear?), every decision in software comes back to the structure you're learning today.
Worked Example 20 min
Goal: take your L01-19 homework's light-pattern selector (5 separate if blocks) and rewrite it as a clean else if chain. Same behaviour, half the code.
Step 1 — recall the L01-19 homework structure
Your L01-19 sketch had something like this in loop():
// L01-19 homework, the "five separate ifs" version.
if (mode == 0) {
digitalWrite(RED_PIN, LOW);
digitalWrite(YELLOW_PIN, LOW);
digitalWrite(GREEN_PIN, LOW);
}
if (mode == 1) {
digitalWrite(RED_PIN, HIGH);
digitalWrite(YELLOW_PIN, LOW);
digitalWrite(GREEN_PIN, LOW);
}
// ... three more if blocks like this ...Twenty lines of repetitive setting. Every loop pass checked all five if conditions, even though only one could match.
Step 2 — write a tiny helper
Three digitalWrite calls for every mode is the bulk of the repetition. Let's hide them inside a helper called setLeds (the L01-12 pattern):
// Turn each LED on or off in one call.
void setLeds(int red, int yellow, int green) {
digitalWrite(RED_PIN, red);
digitalWrite(YELLOW_PIN, yellow);
digitalWrite(GREEN_PIN, green);
}Step 3 — the new loop() with else if
Now five branches, each one line of work because the helper does the digitalWriting. The whole mode-handling block fits in fifteen lines:
if (mode == 0) {
setLeds(LOW, LOW, LOW);
}
else if (mode == 1) {
setLeds(HIGH, LOW, LOW);
}
else if (mode == 2) {
setLeds(LOW, HIGH, LOW);
}
else if (mode == 3) {
setLeds(LOW, LOW, HIGH);
}
else {
setLeds(HIGH, HIGH, HIGH);
}Step 4 — the full sketch
const int BUTTON_PIN = 7;
const int RED_PIN = 9;
const int YELLOW_PIN = 10;
const int GREEN_PIN = 11;
int lastButton = HIGH;
int mode = 0;
void setLeds(int red, int yellow, int green) {
digitalWrite(RED_PIN, red);
digitalWrite(YELLOW_PIN, yellow);
digitalWrite(GREEN_PIN, green);
}
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
}
void loop() {
int current = digitalRead(BUTTON_PIN);
if (current != lastButton) {
if (current == LOW) {
mode = mode + 1;
if (mode > 4) {
mode = 0;
}
}
}
lastButton = current;
if (mode == 0) {
setLeds(LOW, LOW, LOW);
}
else if (mode == 1) {
setLeds(HIGH, LOW, LOW);
}
else if (mode == 2) {
setLeds(LOW, HIGH, LOW);
}
else if (mode == 3) {
setLeds(LOW, LOW, HIGH);
}
else {
setLeds(HIGH, HIGH, HIGH);
}
delay(20);
}setLeds is 5 lines, setup is 6, and loop is twenty. Bigger than our usual cap — that's because this is a real project-grade sketch combining button input, state-change detection, mode-cycling and branched output. Per-function complexity is what matters; each branch's body is one line of work.
Step 5 — upload and test
- Press the button. The mode advances from 0 → 1 → 2 → 3 → 4 → 0 → 1 → …
- Each mode lights a different combination of LEDs.
- The behaviour is identical to your L01-19 homework, but the sketch is shorter and more clearly says "pick one of five things to do".
Step 6 — what changed vs the homework
| Property | L01-19 homework (separate ifs) | L01-20 version (else if chain) |
|---|---|---|
| Lines in the mode block | ~25 | 15 |
| Number of conditions checked per loop pass | 5 (always) | 1 to 5 (stops at the match) |
| Can two modes accidentally fire together? | Yes, if you write a buggy condition | No — only one branch ever runs |
| Adding a 6th mode requires… | One more if block + updating the cycle limit | One more else if + updating the cycle limit |
Try It Yourself 20 min
Goal: Rewrite the L01-17 button-mirror sketch with if/else instead of the one-line !digitalRead() trick. Same behaviour, longer code, but the branching logic is right there for everyone to read.
void loop() {
if (digitalRead(BUTTON_PIN) == LOW) {
digitalWrite(LED_PIN, HIGH); // pressed → LED on
}
else {
digitalWrite(LED_PIN, LOW); // released → LED off
}
}Questions:
- The L01-17 version was one line; this version is seven. When would each be the better choice? ____ (Hint: think about who else will read the code.)
- Could you flip the meaning (LED on when released) by changing a single character? Which character? ____
Goal: Build a "press budget" indicator. Count the number of button presses; light one LED if you've pressed it a little, two LEDs if a medium amount, three if a lot. Use < and else if to choose the band.
// Sketch sketch — pun intended. Fill in the LED-setting calls.
if (presses < 3) {
// few
setLeds(HIGH, LOW, LOW);
}
else if (presses < 6) {
// medium
setLeds(HIGH, HIGH, LOW);
}
else {
// many — also wrap presses back to 0 so the cycle restarts
setLeds(HIGH, HIGH, HIGH);
if (presses > 8) {
presses = 0;
}
}Combine this with the state-change pattern from L01-19 to count presses, and the L01-18 delay(20) to debounce.
Questions:
- The "few" branch runs for presses 0, 1 and 2. Why not 3? ____ (Hint:
<is strict.) - If you changed
presses < 3topresses <= 3, which press counts would now fall into the "few" band? ____
Goal: Two buttons, two modes. Wire a second button on D6 (also INPUT_PULLUP). Pressing button A advances mode upward; pressing button B sends it back down. Wrap top-to-bottom (mode 4 → press B → mode 3).
You'll need state-change detection on both buttons (two sets of mutable globals), and an if/else if chain that decides which button fired.
void loop() {
int aNow = digitalRead(BUTTON_A);
int bNow = digitalRead(BUTTON_B);
if (aNow != lastA && aNow == LOW) {
mode = mode + 1;
if (mode > 4) {
mode = 0;
}
}
else if (bNow != lastB && bNow == LOW) {
mode = mode - 1;
if (mode < 0) {
mode = 4;
}
}
lastA = aNow;
lastB = bNow;
// then the if/else if chain for displaying the mode
delay(20);
}Sneak peek: the && in this sketch is the "AND" operator — it lets you combine two conditions into one. It gets its proper introduction in L01-21 tomorrow. Today's stretch task is a forward taste.
Questions:
- What happens if you press both buttons at the same time? Does
modechange by 0 (cancel out), by +1, or by -1? Read your code carefully. ____ (Hint: first match wins.) - If you removed the
elsefromelse if, so it became justif, what could go wrong? ____
Mini-Challenge 15 min
The temperature warning
You don't have a temperature sensor yet (Level 2 introduces them), so we simulate: every press of the button raises a fake "temperature" by 5 degrees. The LEDs show different warning levels:
- Temperature < 20 °C → all LEDs off (too cold to care).
- 20 °C ≤ temperature < 30 °C → green LED on (comfortable).
- 30 °C ≤ temperature < 35 °C → yellow LED on (warm).
- 35 °C ≤ temperature → red LED on (hot).
- If temperature ≥ 50 °C, wrap back to 0 °C.
Your task:
- Add a mutable global
int temperatureC = 0;. - On each press (state-change pattern + debounce as usual), add 5 to
temperatureC. - If
temperatureCreaches 50, reset it to 0. - Use a four-branch
if/else if/elsechain to set the LEDs based on the temperature band. - Open Serial Monitor and print
temperatureCafter each press, so you can watch the simulated temperature climb.
It works if:
- 0–15 °C: all off.
- 20, 25 °C: green on.
- 30 °C: yellow on.
- 35, 40, 45 °C: red on.
- After 45 °C, the next press resets to 0 and all LEDs go off.
Reveal one valid sketch
const int BUTTON_PIN = 7;
const int RED_PIN = 9;
const int YELLOW_PIN = 10;
const int GREEN_PIN = 11;
int lastButton = HIGH;
int temperatureC = 0;
void setLeds(int red, int yellow, int green) {
digitalWrite(RED_PIN, red);
digitalWrite(YELLOW_PIN, yellow);
digitalWrite(GREEN_PIN, green);
}
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
Serial.begin(9600);
}
void loop() {
int current = digitalRead(BUTTON_PIN);
if (current != lastButton) {
if (current == LOW) {
temperatureC = temperatureC + 5;
if (temperatureC >= 50) {
temperatureC = 0;
}
Serial.println(temperatureC);
}
}
lastButton = current;
if (temperatureC < 20) {
setLeds(LOW, LOW, LOW);
}
else if (temperatureC < 30) {
setLeds(LOW, LOW, HIGH);
}
else if (temperatureC < 35) {
setLeds(LOW, HIGH, LOW);
}
else {
setLeds(HIGH, LOW, LOW);
}
delay(20);
}Look at the four-branch chain. Each else if only has to ask "less than the upper limit of this band?" — the lower limit is implicit, because the chain already ruled out the bands below. That's the elegance of "first match wins".
Recap 5 min
if/else/else if is how a sketch picks one path from several. The Arduino tests conditions from top to bottom; the first true wins; everything else is skipped. With the new comparison operators <, >, <=, >=, your conditions can ask about magnitudes, not just equality — turning else if chains into "which band is this number in?" decisions.
- if / else
- The two-path branch: run one block if the condition is true, the other block if it's false. Exactly one path runs.
- else if
- A chained branch added between
ifand the finalelse. You can have as manyelse ifs as you like. - First match wins
- The rule for
if/else if/else: the chain stops the moment a condition is true. Conditions written later are never checked. - Comparison operators
- The six operators that build conditions:
==,!=,<,>,<=,>=. All return either true or false. - Band check
- Using a chain of
else if (x < N)conditions to decide which interval a number lives in. Each branch's lower bound is implicit — already ruled out by earlier branches. - Defensive default
- An
elseat the end of a chain catches every value you didn't predict. Good habit for handling malfunctions, sensor glitches and "what if" cases.
Homework 5 min
Refactor and extend. Take your L01-19 light-pattern selector and do two things to it:
- Refactor every separate
ifblock into a singleif/else if/elsechain. Use thesetLedshelper from class. Confirm the behaviour is identical. - Extend with a SIXTH mode: when the user has pressed the button at least 6 times total since startup (regardless of current mode), the LEDs do a quick triple flash before returning to the current mode. Add a press counter mutable global and a comparison in your chain.
Also: a design reflection on paper.
- Count the lines of code in your L01-19 version of the sketch vs your L01-20 refactored version. How many lines did you save? ____
- You're about to learn L01-21 "Combining Two Buttons" — boolean AND (
&&) and OR (||) for conditions. Sketch on paper what anifcondition for "the button is pressed AND the temperature is above 30" would look like. ____ - The Mini-Challenge had four bands. What if you wanted fifty bands — say, an LED that brightens gradually with temperature? Could you do it with 50
else ifs? Would that be a good idea? (Hint: it would work, but there are better tools — themap()function from L01-40 and theswitch / casefrom L01-27.)
Bring back next class:
- The saved
.inofile (call itlight-selector-v2). - A short phone video showing the 6 modes cycling, including the triple-flash easter egg.
- Your design-reflection answers on a notebook page.
Heads up for next class: L01-21 "Combining Two Buttons" introduces && (AND) and || (OR) — the operators that let you put two conditions into one if. The 🔴 stretch task gave you a sneak peek; tomorrow they get their full treatment.