Project Traffic Light: Master Arduino Variables & Logic Control

Project Traffic Light: Master Arduino Variables & Logic Control


📑Table of Contents
What You'll Need 5 items
Arduino Uno R3
Buy
Breadboard
Buy
5mm LED (Red, Yellow, Green)
Buy
220Ω Resistor
Buy
Jumper Wires
Buy

Affiliate links help support this site at no extra cost to you.

Welcome to Day 12. Yesterday, you wrote your first program. You made an LED blink. You proved that you can control hardware with text.

Today, we solve a real-world problem. We are going to build a Traffic Light Controller.

Visualise an intersection. Cars are rushing from all directions. Pedestrians are waiting. If the lights fail, chaos ensues. People get hurt. The logic must be perfect. It’s not just “Red” and “Green”. It is precise timing. It is sequence. Red -> Green? No. Red -> Green -> Yellow -> Red? Maybe.

In this project, we will move beyond the single LED. We will control three independent outputs. And more importantly, we will learn the single most important concept in programming: The Variable.

Traffic Light Intro Art

The Hardware: Building the Tower

We need three LEDs: Red, Yellow (or Amber), and Green. They need to be wired to the Arduino independently.

The Wiring Strategy

We could run 6 wires (3 signals, 3 grounds). But we are engineers. We can be efficient. We will use a Common Ground.

  1. Connect the Cathodes (Short legs) of ALL three LEDs to the Blue Rail on your breadboard.
  2. Connect the Blue Rail to a single GND pin on the Arduino using one jumper wire.
  3. Connect the Anodes (Long legs) to three different Digital Pins (e.g., 12, 11, and 10) through their own resistors.

Wiring Schematic Common Ground

Why 3 resistors? Why not just one resistor on the Common Ground line? Because if you use one shared resistor, the brightness will fluctuate depending on how many LEDs are on. If Red and Yellow are both on, they will be dimmer than if just Red is on. By giving each LED its own resistor (330Ω), they maintain consistent brightness.

Breadboard Render

History: Who invented the Traffic Light?

Before we wrote code, someone had to invent the logic. The first electric traffic light appeared in Cleveland, Ohio in 1914. But the modern 3-position logic is credited to Garrett Morgan, an African-American inventor. He witnessed a terrible accident between a car and a horse-drawn carriage. He realized that just having “Stop” and “Go” wasn’t enough. Drivers needed a warning. He patented the T-shaped traffic signal with a “Wait” position in 1923. General Electric later bought the patent for $40,000. We are essentially programming Garrett Morgan’s logic into silicon today.

Concept 1: The Magic Number Problem

Yesterday, we wrote: digitalWrite(13, HIGH);

What is “13”? To the computer, it’s an address. To a human reading the code in 6 months, it’s a mystery. “What was connected to Pin 13 again? Was it the motor? The alarm?”

Using raw numbers in code is called “Hardcoding” or using “Magic Numbers”. It is bad practice. Imagine you wire your whole city traffic system to Pin 12, 11, 10. Then, you discover Pin 12 is broken. You move the Red light to Pin 9. Now you have to find-and-replace every “12” in your code with “9”. If you miss one… crash.

Concept 2: The Variable (The Labeled Box)

In C++, a variable is a named container. We can label a box “RedLight” and put the number “12” inside it. Then, in our code, we just say: digitalWrite(RedLight, HIGH);

If we need to change the pin later, we just change the number in the box. The rest of the code updates automatically.

The int Data Type

Variables come in types.

  • int: An Integer (Whole number). -32,768 to 32,767. Perfect for pin numbers.
  • float: Decimal number. 3.14. (We don’t need this yet).
  • String: Text. (We don’t need this yet).

So, at the top of our code (before setup), we declare our variables:

int redPin = 12;
int yellowPin = 11;
int greenPin = 10;

Variable Analogy Box

Theory: Why “Common Ground”?

You might see tutorials where the Resistor is on the Anode (Positive) side. Others put it on the Cathode (Negative) side. Does it matter? Electrically? No. The current flows through the loop regardless of where the resistor sits. It’s like a kink in a hose. It slows the water down whether it’s at the tap or the nozzle. However, we use a Common Ground layout for cleanliness. We have one single wire going to GND. If we wired it efficiently, we reduced the “spaghetti” on our board. Pro Tip: If you ever work with “RGB LEDs”, they come in two flavors: Common Cathode (Ground) and Common Anode (Voltage). Knowing the difference is critical.

Deep Dive: Variable Scope (Where does it live?)

We declared int redPin at the VERY TOP of the file. This is called a Global Variable. It means “setup” can see it. “loop” can see it. Everyone can see it.

What if we declared it inside void loop()?

void loop() {
  int counter = 0;
  // ...
}

That would be a Local Variable. It is created when the loop starts. It is destroyed when the loop ends. If the loop restarts, the variable is forgotten and recreated. Rule: For pin definitions, ALWAYS use Global Variables. You want them to exist forever.

The Algorithm: Defining Reality

Before we type code, we must define the pattern. How does a traffic light work? It’s a “State Machine”.

Standard US Sequence:

  1. State 1: GO. Green is ON. (Others OFF). Wait 5 seconds.
  2. State 2: CAUTION. Yellow is ON. (Others OFF). Wait 2 seconds.
  3. State 3: STOP. Red is ON. (Others OFF). Wait 5 seconds.
  4. Loop.

Flowchart of Logic

Project: The Code

Let’s write it. Open your IDE. Start fresh.

// Project: Traffic Light Controller
// Day 12

// 1. Declare Variables
int redPin = 12;     // Red LED connected to Pin 12
int yellowPin = 11;  // Yellow LED connected to Pin 11
int greenPin = 10;   // Green LED connected to Pin 10

void setup() {
  // 2. Configure Pins
  // We need to tell the Arduino that ALL these pins are outputs
  pinMode(redPin, OUTPUT);
  pinMode(yellowPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
}

void loop() {
  // --- STATE 1: GREEN ---
  // Turn Green ON, others OFF
  digitalWrite(greenPin, HIGH);
  digitalWrite(yellowPin, LOW);
  digitalWrite(redPin, LOW);
  
  delay(5000); // Wait 5 Seconds. Cars are moving.

  // --- STATE 2: YELLOW ---
  // Turn Yellow ON, others OFF
  digitalWrite(greenPin, LOW);
  digitalWrite(yellowPin, HIGH);
  digitalWrite(redPin, LOW);

  delay(2000); // Wait 2 Seconds. Slow down!

  // --- STATE 3: RED ---
  // Turn Red ON, others OFF
  digitalWrite(greenPin, LOW);
  digitalWrite(yellowPin, LOW);
  digitalWrite(redPin, HIGH);

  delay(5000); // Wait 5 Seconds. Stop.
}

Code Logic Comparison

Analyze the cleanliness

Look at digitalWrite(greenPin, HIGH). It reads like English. “Write the Green Pin High.” Even a non-programmer can look at that and understand the safety logic.

Deep Dive: Global Traffic Logic (US vs UK)

Did you know traffic lights work differently around the world? Our code above mimics the USA/Continental Europe standard. Green -> Yellow -> Red -> Green.

But in the UK (and many Commonwealth countries), there is a “Get Ready” phase. Red -> Red + Amber -> Green. This gives drivers a warning that the light is about to turn green, so they can shift into gear.

Challenge: The UK Sequence

Can you modify your code to simulate a London intersection? You need a 4th state.

  1. Green (5s)
  2. Yellow (2s)
  3. Red (5s)
  4. Red (HIGH) AND Yellow (HIGH) (1s)

Try it. The beauty of software is that you don’t need to change a single wire on your breadboard. You just type a new digitalWrite. In the hardware age (using 555 timers from Day 5), changing from US to UK logic would have required tearing the circuit apart and rebuilding it. Here, it takes 30 seconds.

Solution: The UK Sequence Code

Here is how you would implement the London-style logic. Notice Step 4. We turn TWO pins HIGH at the same time.

void loop() {
  // 1. GREEN (Go)
  digitalWrite(greenPin, HIGH);
  digitalWrite(yellowPin, LOW);
  digitalWrite(redPin, LOW);
  delay(5000);

  // 2. AMBER (Stopping)
  digitalWrite(greenPin, LOW);
  digitalWrite(yellowPin, HIGH);
  digitalWrite(redPin, LOW);
  delay(2000);

  // 3. RED (Stopped)
  digitalWrite(greenPin, LOW);
  digitalWrite(yellowPin, LOW);
  digitalWrite(redPin, HIGH);
  delay(5000);

  // 4. RED + AMBER (Get Ready)
  digitalWrite(greenPin, LOW);
  digitalWrite(yellowPin, HIGH); // ON
  digitalWrite(redPin, HIGH);    // ON
  delay(1500);
}

This demonstrates the power of the microcontroller. Complex logic (two lights on, then one off) is trivial.

Global Signal Variations Chart

Deep Dive: Fail-Safe Logic

In the real world, “Traffic Controllers” are Life-Safety Systems. If the Microcontroller freezes, people die. Real traffic lights have a hardware monitor called a Conflict Monitor Unit (CMU).

  • The Job: It watches the wires.
  • The Trigger: If it sees GREEN on Northbound AND GREEN on Eastbound at the same time…
  • The Action: It physically cuts power to the Green lights and forces all Red lights to flash. We can’t build a CMU easily, but in code, we write:
// Safety Check
if (digitalRead(greenNorth) == HIGH && digitalRead(greenEast) == HIGH) {
    emergencyShutdown();
}

This is Defensive Programming. You assume your code will fail, and you plan for it.

Day 12 Challenge: The Intersection

Can you simulate a FULL intersection? Imagine North-South Logic vs East-West Logic.

  • When North is Green, East MUST be Red.
  • When North turns Yellow, East is still Red.
  • When North turns Red, East turns Green. Hardware: Add 3 more LEDs (Pins 7, 6, 5). Software: Write the digitalWrite commands to synchronize them. Safety Check: Ensure Green never overlaps! This is your homework for tonight.

The Trap: The delay() Function

I have a confession. The code we wrote today has a fatal flaw. It relies on delay().

delay(5000) tells the processor: “Go to sleep for 5 seconds. Do nothing. Ignore the world.” What if we wanted to add a Pedestrian Button? If a pedestrian presses the button while the light is Green (during the 5000ms delay), the Arduino won’t see it. It is asleep. It will only check inputs after it wakes up.

This is called “Blocking Code”. For a simple traffic light, it’s fine. But for a robot or a game, it is a disaster. In a few days (Day 15), we will learn the professional solution: millis(). millis() allows the Arduino to “multitask” (check a watch) instead of sleeping. But for today, enjoy the simplicity of the delay.

Block Code Abstract

Troubleshooting

  1. Yellow is too dim: Blue/Green/White LEDs need more voltage than Red/Yellow. Or maybe your resistor is too high. Try swapping resistors.
  2. All ON at once: Did you forget to copy the LOW commands? Remember, digitalWrite is “Sticky”. If you turn a pin HIGH, it stays HIGH until you explicitly tell it to go LOW.
  3. Compilation Error ‘redPin was not declared’: Check your spelling. C++ is case sensitive. RedPin is different from redPin.

The Engineer’s Mindset: The Pedestrian Problem

This simple project exposes a massive flaw in linear programming. Imagine we added a button to Pin 2. We want the light to turn Green IMMEDIATELY when the button is pressed. With our current code (delay(5000)), if you press the button 0.1 seconds after the delay starts, the Arduino ignores you for 4.9 seconds. This is unacceptable for safety. In a real traffic controller, there is no “delay”. There is a State Machine. It constantly checks:

  • Is time up?
  • Is button pressed?
  • Is sensor triggered? If ANY of those are true, it changes state. We will build this advanced version in Day 15 (Multitasking). For now, understand that delay() is a training wheel. Eventually, we must take it off.

Advanced Troubleshooting: Why is my circuit weird?

  1. Ghosting: The Green LED glows faintly when Yellow is on.
    • Cause: You inserted the resistor into the wrong row, bridging the connection. Check your breadboard rows (a-e and f-j are separated by the ravine).
  2. Flickering: The LEDs are unstable.
    • Cause: Your USB cable might be loose, or your laptop port is providing low current. Try a different USB port.
  3. The “Upload” Error: “avrdude: stk500_rect()”.
    • Cause: You selected “Arduino Uno” but you have a “Nano” or “Mega” selected in the Tools menu. Double check!

The Engineer’s Glossary (Day 12)

  • Variable: A named storage location in memory.
  • Integers (int): Whole numbers.
  • Hardcoding: Using specific values (like ‘13’) directly in logic code. Bad practice.
  • Sequence: A specific order of operations.
  • State Machine: A system that relies on distinct phases (Green, Yellow, Red).

The Bill of Materials (BOM)

ComponentQuantityValueNotes
Arduino Uno1R3 or R4The Brain
LED1RedTraffic Light
LED1Yellow/AmberTraffic Light
LED1GreenTraffic Light
Resistor3220Ω - 330ΩCurrent Limiting
Jumper Wires4M-MConnectivity
Breadboard1Half+Prototyping

Conclusion

You have modeled reality. You took a physical concept (Safety logic) and translated it into C++. You learned that Variables are not just for math—they are for legibility. They make your code human-readable.

Tomorrow, on Day 13, we are going to revisit an old friend. Remember the Knight Rider Scanner from Day 10? It required two chips and a mess of wires. Tomorrow, we will build it with the Arduino. We will learn about Arrays and For Loops to create complex animations with just a few lines of code.

See you on Day 13.

Comments