Digital Output: LEDs & Traffic Lights
By the end of this lesson you will be able to wire LEDs to any digital pin, build a traffic light that runs through its colours with correct timing, and control a row of LEDs with an array and a loop. You will also learn the difference between a pin that gives current and a pin that takes it.
Digital output means a pin can be in only two states: HIGH (5 V on an UNO) or LOW (0 V). It is like a light switch on your wall: on or off, nothing in between.
Step 1: One external LED
First, move from the built-in L LED to an LED on the breadboard. Unplug the USB cable before wiring.
| Part | Pin or leg | Connects to |
|---|---|---|
| Arduino | D12 | 470 Ω resistor, leg 1 |
| 470 Ω resistor | Leg 2 | Red LED anode (long leg) |
| Red LED | Cathode (short leg) | Arduino GND (through the - rail) |
The resistor limits the current to about 6 mA, safely below the UNO R4's 8 mA pin limit, as you worked out in Ohm's Law & Power.
const int RED_PIN = 12;
void setup() {
pinMode(RED_PIN, OUTPUT);
Serial.begin(9600);
while (!Serial && millis() < 3000) { }
}
void loop() {
digitalWrite(RED_PIN, HIGH);
Serial.println("red ON");
delay(500);
digitalWrite(RED_PIN, LOW);
Serial.println("red OFF");
delay(500);
}red ON red OFF red ON red OFF
After uploading, the red LED should flash twice per second, and the monitor shows a matching line for each change. If the LED stays dark, check that the long leg faces the resistor.
Step 2: Wire the traffic light
Add two more LEDs. Each LED gets its own resistor and its own pin.
| Part | Pin or leg | Connects to |
|---|---|---|
| Red LED + 470 Ω | Resistor to D12, LED cathode to GND | Stop light |
| Yellow LED + 470 Ω | Resistor to D11, LED cathode to GND | Wait light |
| Green LED + 470 Ω | Resistor to D10, LED cathode to GND | Go light |
| Arduino | GND | Breadboard - rail, shared by all three cathodes |
All three cathodes can share the same GND rail. Do not try to share one resistor between three LEDs: each LED needs its own.
Step 3: Program the sequence
A simple traffic signal, like the one at a crossing in Lucknow, goes red, then green, then yellow, then back to red. A helper function sets all three lights at once, which keeps the main loop short and readable:
const int RED_PIN = 12;
const int YELLOW_PIN = 11;
const int GREEN_PIN = 10;
void setLights(bool red, bool yellow, bool green) {
digitalWrite(RED_PIN, red ? HIGH : LOW);
digitalWrite(YELLOW_PIN, yellow ? HIGH : LOW);
digitalWrite(GREEN_PIN, green ? HIGH : LOW);
}
void setup() {
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
Serial.begin(9600);
while (!Serial && millis() < 3000) { }
}
void loop() {
setLights(true, false, false);
Serial.println("RED: stop (5 s)");
delay(5000);
setLights(false, false, true);
Serial.println("GREEN: go (4 s)");
delay(4000);
setLights(false, true, false);
Serial.println("YELLOW: get ready to stop (1 s)");
delay(1000);
}RED: stop (5 s) GREEN: go (4 s) YELLOW: get ready to stop (1 s) RED: stop (5 s) GREEN: go (4 s) YELLOW: get ready to stop (1 s)
One full cycle takes 5 + 4 + 1 = 10 seconds. bool is a type that holds true or false. The expression red ? HIGH : LOW means "if red is true, use HIGH, otherwise LOW".
Step 4: Many LEDs with an array
Writing pinMode ten times is boring and error-prone. An array stores many values under one name, like the numbered seats in a railway coach. You reach each seat with an index that starts at 0.
This "running light" lights the three LEDs one after another, then runs back, like the lights on a festive decoration:
const int pins[] = {10, 11, 12};
const int COUNT = 3;
void setup() {
for (int i = 0; i < COUNT; i++) {
pinMode(pins[i], OUTPUT);
}
Serial.begin(9600);
while (!Serial && millis() < 3000) { }
}
void showOnly(int index) {
for (int i = 0; i < COUNT; i++) {
digitalWrite(pins[i], i == index ? HIGH : LOW);
}
Serial.print("LED on pin ");
Serial.println(pins[index]);
delay(300);
}
void loop() {
for (int i = 0; i < COUNT; i++) { // forwards: 0, 1, 2
showOnly(i);
}
for (int i = COUNT - 2; i > 0; i--) { // backwards: 1 (skip the ends)
showOnly(i);
}
}LED on pin 10 LED on pin 11 LED on pin 12 LED on pin 11 LED on pin 10 LED on pin 11 LED on pin 12 LED on pin 11
The backward loop skips both end LEDs, so the ends do not light twice in a row. With three LEDs it only visits index 1. Add more pins to the array and change COUNT, and the same code runs a longer chain.
Sourcing and sinking
So far each pin sources current: it pushes current out through the LED to GND, so HIGH means on. A pin can also sink current: connect the LED from 5V, through the resistor, into the pin. Then LOW means on, because current flows into the pin when it is at 0 V.
| Wiring | LED on when | Often used for |
|---|---|---|
| Pin → resistor → LED → GND (sourcing) | Pin is HIGH | Most beginner circuits |
| 5V → resistor → LED → pin (sinking) | Pin is LOW | Some modules, relay boards and LED displays |
This is why some modules are called active LOW. Neither way is wrong, but your code must match your wiring. The same 8 mA limit applies in both directions.
Common mistakes
- Forgetting
pinMode(pin, OUTPUT). The pin stays an input, and the LED glows very dimly or not at all. - One resistor shared by several LEDs in parallel. The brightness becomes uneven and one LED can take most of the current. Use one resistor per LED.
- Array index out of range.
pins[3]in a three-element array reads random memory. Valid indexes are 0, 1 and 2. - Mixing up active HIGH and active LOW wiring and code.
Key points
- Digital outputs are HIGH or LOW;
digitalWriteswitches them afterpinMode(pin, OUTPUT). - Every LED needs its own series resistor; 470 Ω keeps an UNO R4 pin within its 8 mA limit.
- Helper functions such as
setLights()keep sequences readable. - Arrays and
forloops control many pins with little code; indexes start at 0. - A pin can source (HIGH = on) or sink (LOW = on) current; match code to wiring.
Exercise
Add a pedestrian signal to the traffic light: a second green LED on pin 9 that is ON only while the main signal is RED. Print a line such as RED: stop (5 s), walk ON for each step. What is the full cycle time?
Show solution
const int RED_PIN = 12;
const int YELLOW_PIN = 11;
const int GREEN_PIN = 10;
const int WALK_PIN = 9;
void setLights(bool red, bool yellow, bool green) {
digitalWrite(RED_PIN, red ? HIGH : LOW);
digitalWrite(YELLOW_PIN, yellow ? HIGH : LOW);
digitalWrite(GREEN_PIN, green ? HIGH : LOW);
digitalWrite(WALK_PIN, red ? HIGH : LOW); // walk only while cars stop
}
void setup() {
const int all[] = {RED_PIN, YELLOW_PIN, GREEN_PIN, WALK_PIN};
for (int i = 0; i < 4; i++) pinMode(all[i], OUTPUT);
Serial.begin(9600);
while (!Serial && millis() < 3000) { }
}
void loop() {
setLights(true, false, false);
Serial.println("RED: stop (5 s), walk ON");
delay(5000);
setLights(false, false, true);
Serial.println("GREEN: go (4 s), walk OFF");
delay(4000);
setLights(false, true, false);
Serial.println("YELLOW: get ready (1 s), walk OFF");
delay(1000);
}RED: stop (5 s), walk ON GREEN: go (4 s), walk OFF YELLOW: get ready (1 s), walk OFF
The pedestrian light simply follows the red light. The cycle time is still 5 + 4 + 1 = 10 seconds.
Quick Quiz
1. What voltage does a HIGH digital output give on an Arduino UNO?
UNO pins work at 5 V. ESP32 and Raspberry Pi pins work at 3.3 V.
2. In the array {10, 11, 12}, what is pins[0]?
Array indexes start at 0, so index 0 holds the first value, 10.
3. An LED is wired from 5V through a resistor into a pin. When does it light?
The pin sinks current: at LOW (0 V) there is a voltage difference across the LED, so current flows.
4. Why should each LED have its own resistor?
LEDs in parallel do not share current evenly, so each one needs its own current-limiting resistor.
Learn IoT & Arduino with a mentor
Free lessons get you started. Our IoT & Embedded Systems training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
