Learning Goals 5 min
- Explain in your own words how a digital pin can pretend to be analog — by switching on and off very fast.
- Use
analogWrite(pin, value)with values from0to255to set LED brightness on any PWM-capable pin (~3, 5, 6, 9, 10, 11 on the UNO). - Identify which UNO pins support PWM at a glance — the ones with the squiggly
~symbol next to their number.
Warm-Up 10 min
So far every output pin you've driven has been fully on or fully off. An LED is bright or dark; a motor is spinning or stopped. There's no half-bright, no slow-spin.
But you know they're possible — a TV brightness slider clearly works. So how do we get "in-between" from a chip that can only output 0 V or 5 V?
Predict-the-output puzzle
Leo thinks he can dim an LED by switching it on and off really fast. He writes this:
void loop() {
digitalWrite(9, HIGH);
delay(5);
digitalWrite(9, LOW);
delay(5);
}What does the LED look like?
Reveal
The LED is on 5 ms, off 5 ms — about 100 times per second. Faster than your eye can resolve, so you see half-brightness. Leo has just hand-built a PWM signal at 50% duty cycle. Today we replace those four lines with one Arduino built-in: analogWrite.
New Concept · PWM, the "fake analog" trick 20 min
The big idea — duty cycle
The chip can only output 0 V or 5 V. So it cheats: it switches between them very fast. The fraction of time the pin spends HIGH is the duty cycle.
- 0% duty → always
LOW→ LED off. - 25% duty → on for 1 unit of time, off for 3 → LED at about a quarter brightness.
- 50% duty → on/off equal → LED at about half brightness.
- 100% duty → always
HIGH→ LED full bright.
Your eye averages the on/off flicker into a single perceived brightness. Same trick used in TV displays, dimmer switches, motor controllers, even server fans.
The Arduino built-in: analogWrite
analogWrite(pin, value);
// value is an integer 0..255
// 0 = always LOW (0% duty)
// 64 = ~25% duty
// 128 = ~50% duty
// 192 = ~75% duty
// 255 = always HIGH (100% duty)The number 255 isn't magic — it's 28 − 1, the biggest value that fits in one byte. The Arduino uses an 8-bit register for PWM, so you get 256 brightness steps from 0 to 255.
Only PWM-capable pins
Not every UNO pin can do PWM. Look at your board: pins with a squiggly ~ mark are the PWM pins. On a standard UNO they are ~3, ~5, ~6, ~9, ~10, ~11 — six pins out of fourteen.
If you call analogWrite on a non-PWM pin (say, pin 7), the IDE doesn't crash. The pin just acts like a regular digitalWrite: 0 = LOW, anything else = HIGH. No dimming. The lesson: always wire LEDs you want to fade onto a ~ pin.
The PWM frequency
On most UNO pins, PWM runs at ~490 Hz. On D5 and D6 it's ~980 Hz. Either way, much faster than the human eye can see (we top out at roughly 60 Hz of flicker perception). For LEDs you can ignore the frequency entirely; for motors and audio it matters more — but that's a Level 3 conversation.
No pinMode required
One small surprise — you don't need pinMode(pin, OUTPUT) before analogWrite. The function sets the pin to output for you. Adding it does no harm, and most authors include it for symmetry with their other pins. Up to you.
Worked Example · Three brightness levels 20 min
Step 1 — wire one LED on a PWM pin
Same wiring as the L01-07 single-LED circuit, but use pin ~9 (which supports PWM) instead of pin 13. Anode of the LED → 220 Ω resistor → Arduino pin ~9. Cathode → GND rail.
Step 2 — set three brightnesses
Save as three-brightnesses.ino:
// L02-02: three brightness steps with analogWrite
const int LED = 9;
void setup() {
pinMode(LED, OUTPUT);
}
void loop() {
analogWrite(LED, 64); // ~25% — dim
delay(1000);
analogWrite(LED, 128); // ~50% — medium
delay(1000);
analogWrite(LED, 255); // 100% — full
delay(1000);
}Step 3 — upload and watch
The LED cycles between dim, medium, full, dim, medium, full... at one-second steps. No on/off flicker visible — the PWM is far too fast for your eye.
What you should see
(dim ─ 1s) → (medium ─ 1s) → (full ─ 1s) → (dim ─ 1s) → …
Step 4 — predict before you tweak
Before you change any number, ask yourself: what would analogWrite(LED, 0) look like? What about analogWrite(LED, 1)? Or analogWrite(LED, 254)?
0→ dead off, indistinguishable from a broken wire.1→ just barely glowing — in a dark room you'll see it, in daylight you might not.254→ looks identical to255to your eye. The eye can't tell the difference of one PWM step at the top of the range.
Basic 7 min
Goal: Add a fourth brightness step at analogWrite(LED, 192) so the LED cycles dim → medium → bright → full. Predict roughly how bright 192 is before uploading.
Challenge 1 7 min
Goal: Wire a second LED on pin ~10 (also PWM). Drive them at different brightnesses — say pin 9 at 64 and pin 10 at 192 — and watch how clearly different they look side by side. Each PWM pin runs its own duty cycle independently.
const int LED_A = 9;
const int LED_B = 10;
void setup() {
pinMode(LED_A, OUTPUT);
pinMode(LED_B, OUTPUT);
}
void loop() {
analogWrite(LED_A, 64);
analogWrite(LED_B, 192);
}Challenge 2 6 min
Goal: Cycle a single LED through six brightness levels (0, 51, 102, 153, 204, 255) with 300 ms between each step. Use a for loop with step size 51, not six hand-typed lines.
Challenge 3 · Three-level mood lamp 15 min
Build a small sketch where pressing the button (on pin 2 with INPUT_PULLUP) cycles the LED through three brightness levels: low, medium, high, low, medium, high...
It works if:
- Power-up state is
lowbrightness. - Each press of the button advances to the next level (low → medium → high → low → ...).
- The button works reliably — no skipping levels because of debounce noise (recall L01-18). A 200 ms blocking delay after each press is fine here.
- The LED is on a PWM pin (~9 / ~10 / ~11) so the brightness is visibly different at each level.
Recap 5 min
PWM gets analog-style output from a digital pin by switching very fast. analogWrite(pin, value) takes a value from 0 to 255 and sets the duty cycle accordingly. Only the six ~-marked UNO pins support it; the others fall back to plain on/off. Tomorrow we'll do the maths on duty cycle properly — frequency, period and the difference between "average voltage" and "perceived brightness".
- PWM (Pulse-Width Modulation)
- Switching a digital pin on and off so quickly that the average voltage acts analog. Your eye / a motor / a speaker doesn't see the flicker — only the average.
- Duty cycle
- The fraction of time the pin spends
HIGHwithin one PWM period. 0% = always off, 100% = always on.analogWrite's value maps to duty asvalue / 255 × 100%. analogWrite(pin, value)- Arduino built-in.
valueis 0..255 (8-bit). Only works on PWM-capable pins. NopinModerequired. - PWM pins (UNO)
- The six pins marked with
~on the board silkscreen: D3, D5, D6, D9, D10, D11.
Extra Mission 5 min
Part 1 — Design a light with more than on and off
Pick an everyday object that would be better with a dimmable light — a bedside clock, a fish tank, a bike helmet, a front step. On paper, design how PWM could give it the right brightness for each moment.
Your design must include:
- A name and a one-sentence job for the device.
- At least three brightness levels. Give each one its moment ("night", "reading") and its
analogWritevalue from 0 to 255. - What changes the level — a button press, or a timed change.
- The PWM pin the LED uses (~3, ~5, ~6, ~9, ~10 or ~11) and its resistor.
Part 2 — Make it
Wire an LED to your chosen PWM pin and a button on pin 2 with INPUT_PULLUP. Each press moves to your next level, then wraps back to the first. Print the level name and its value to the Serial Monitor.
Bring back next class: the uploaded sketch, a photo of the LED at each level and a Serial Monitor screenshot.