Traffic Light with Pedestrian Crossing Controller (using Raspberry Pi Pico 2, ARM Assembly)

A breadboard traffic-light controller with a pedestrian request button, built on a Raspberry Pi Pico 2. Six LEDs simulate car + pedestrian signals, while an ARM Cortex-M0+ assembly state machine handles safe timing, debouncing, and all-red transitions.
Traffic Light with Pedestrian Crossing Controller

Overview

This project is a compact embedded-systems build that models a real intersection controller: car traffic and pedestrians each have a red/yellow/green signal, and a pedestrian can request a crossing using a button. I built the hardware on a breadboard using a Raspberry Pi Pico 2 and wrote the control logic in ARM Cortex-M0+ assembly, using simple GPIO helper routines and millisecond sleep calls.

The goal was to keep the circuit minimal but make the behavior realistic: predictable timing, safe transitions (both red), and responsive buttons through debouncing and frequent input polling. The design comes the following features:

  • Car signal cycle: Green → Yellow → Red
  • Pedestrian signal cycle: Green → Yellow → Red
  • Pedestrian request button: latched request that is serviced safely (no abrupt phase flips)
  • Power/Start button: toggles the entire system between running and all off
  • Safe transition: both directions red before switching right-of-way
  • Button debouncing: simple 20 ms confirm + (for power) release-wait

Hardware

Parts:

  • Raspberry Pi Pico 2
  • 6× LEDs (2× Red, 2× Yellow, 2× Green)
  • 6× current-limiting resistors (typical 220–1kΩ)
  • 2× momentary pushbuttons (Power SW + Ped Request)
  • Breadboard + jumper wires

GPIO map:

FunctionGPIO
Car Green / Yellow / RedGP13 / GP14 / GP15
Ped Green / Yellow / RedGP10 / GP11 / GP12
Pedestrian Request ButtonGP16
Power/Start ButtonGP17

Note: Both buttons are active-low (pressed reads 0), meaning the input is normally pulled up and goes to GND when pressed.

Build photos & diagram:


Figure 1 shows the full pin mapping and how each LED and button ties back to the Pico 2. I kept the layout intentionally simple: six GPIO outputs for the lights and two GPIO inputs for the controls.

Trafic light GPIO Pico diagram

Figure 1 – Wiring diagram (GPIO assignments)

Figure 2 is the working hardware build I used for testing the state machine in assembly. The LEDs are grouped by function (car vs. pedestrian), with the buttons placed low on the board for easy interaction during timing tests.

Trafic light breadboard circuit

Figure 2 – Breadboard prototype (Pico 2 + LEDs + buttons)

Traffic Light Controller Demo:

In Video 1, I walk through the full sequence and show how the pedestrian request interrupts the normal cycle in a controlled (safe) way. You can also see that button input remains responsive because the firmware checks inputs frequently instead of using one long blocking delay.

Video 1. Demonstration of the Raspberry Pi Pico 2 traffic light controller running on a breadboard. The controller cycles the car signal and pedestrian signal through green/yellow/red timing, while continuously monitoring the Power/Start button and the Pedestrian Request button. When a pedestrian request is pressed, the system safely completes the current phase, transitions through yellow, enforces a both-red safety window, and then services the pedestrian crossing before returning to the normal cycle.

Software approach (assembly “state machine”)

Even though this is assembly, the logic is structured like a clean finite-state controller. I use three flags in .data:

  • running_flag — system enabled/disabled (toggled by Power SW)
  • ped_flag — pedestrian request latched (set by Ped button)
  • ped_active — blocks new requests while pedestrian phase is being served

Main loop behavior:

  1. Check the Power SW (toggle run/stop)
  2. If stopped: force everything off (all_off)
  3. If running:
    • Force Ped = Red, run Car cycle
    • Force Car = Red, run Ped cycle
    • Repeat

This keeps the behavior stable and predictable, while still allowing the pedestrian request to interrupt at safe points.

Timing and responsiveness:

Instead of using one long “blocking delay,” the code waits in 10 ms chunks and checks inputs repeatedly. That makes the system feel responsive even during a long green phase.

From the constants in my assembly:

  • Green duration: ~10 s
  • Yellow duration: ~3 s
  • All-red safety transition: ~1 s

Pedestrian request logic:

When the pedestrian button is pressed:

  • the request is latched (ped_flag = 1)
  • the controller finishes the current step safely:
    • transitions through yellow
    • then goes to both red
    • then services the pedestrian sequence
  • the request flag is cleared after it’s handled

This is the same principle used in real traffic controllers: requests are acknowledged, but right-of-way changes only happen at safe transition points.

Code (high-level structure):

I’m including my assembly below as the core deliverable for this project. The important routines are:

  • traffic_light_run — main control loop
  • check_powerSW — toggles running state with debounce + release-wait
  • check_ped_button — latches pedestrian request (debounced)
  • phase_wait — green-phase wait with frequent input polling
  • yellow_full_duration — timed yellow/red windows that still allow power checks
  • both_red and all_off — safety and shutdown states
  • set_cycle_common — reusable cycle logic (green → yellow → red)
  • set1_cycle / set2_cycle — wrappers for car vs pedestrian signals

Results

This build behaves cleanly and reliably:

  • No flickering or ambiguous LED states
  • Pedestrian requests are serviced safely
  • The controller can be stopped at any time (everything turns off)
  • Buttons feel responsive thanks to frequent polling and debouncing

It was also a great exercise in writing structured firmware in assembly—using flags, subroutines, and state transitions instead of a single giant loop.

Future Improvement

If I iterate on this project, I’d like to add:

  • A real WALK interval (ped green held for N seconds)
  • A flashing DON’T WALK phase (ped yellow blink)
  • Timer interrupts instead of sleep_ms for a more “RTOS-like” design
  • UART logging for debugging state transitions in real time

References

Jeremias Vigo
Electrical Engineering Student

Aspiring electrical engineer specializing in hardware design and embedded systems. Previous hands-on experience as an electronics technician, with a solid background in circuit and PCB design. Driven by a passion for new technologies and dedicated to creating efficient engineering solutions.

Share