Beyond the Delay Function: Why Basic Traffic Light Code Fails
If you have ever searched for arduino code for traffic light projects, you have likely encountered tutorials relying heavily on the delay() function. While this approach works for a desktop toy, it is fundamentally flawed for real-world infrastructure. The delay() function is "blocking," meaning the microcontroller halts all other operations—ignoring pedestrian crossing buttons, failing to poll vehicle detection sensors, and missing serial communication interrupts—until the timer expires.
In 2026, smart city infrastructure demands responsive, adaptive systems. To build a robust, scalable traffic controller, we must abandon blocking code and implement a Finite State Machine (FSM) paired with non-blocking timing via millis(). This advanced technique ensures your Arduino can manage LED sequencing, sensor polling, and fail-safe watchdog routines concurrently.
Professional Hardware BOM for a 4-Way Intersection
Before diving into the code, we must address the physical layer. Driving standard 5mm LEDs via GPIO pins is insufficient for outdoor visibility. A professional prototype requires high-power 12V LED modules switched via logic-level MOSFETs.
| Component | Model / Specification | Est. Cost (2026) | Purpose |
|---|---|---|---|
| Microcontroller | Arduino Uno R4 Minima | $20.00 | 32-bit Cortex-M4 for complex FSM math |
| LED Modules | 12V 3W/5W Traffic LED Arrays | $18.00 (set of 12) | High-lumen output for daylight visibility |
| Switching | IRLZ44N Logic-Level MOSFETs | $1.20 each | Safe 12V switching via 5V GPIO |
| Vehicle Sensor | RCWL-0516 Microwave Radar | $3.50 | Non-intrusive vehicle detection up to 9m |
| Pedestrian Input | IP65 Piezoelectric Switch | $8.00 | Vandal-resistant, weatherproof button |
Note: Always use a 10kΩ pull-down resistor on the MOSFET gate to prevent floating states during Arduino boot-up, which could cause all lights to turn green simultaneously—a critical safety hazard.
Designing the Finite State Machine (FSM)
A traffic intersection is a classic FSM. The system can only be in one distinct state at a time, and transitions are triggered by time or external inputs. According to the Federal Highway Administration's MUTCD guidelines, yellow clearance intervals must be calculated based on approach speeds (typically 3.0 to 5.0 seconds). Hardcoding these without a state structure leads to spaghetti code.
Defining the States
We define our states using an enum to make the code self-documenting and memory-efficient.
enum TrafficState {
STATE_NS_GREEN,
STATE_NS_YELLOW,
STATE_NS_RED,
STATE_EW_GREEN,
STATE_EW_YELLOW,
STATE_EW_RED,
STATE_PEDESTRIAN_CROSS
};
Non-Blocking Execution with millis()
Instead of delay(4000) for a yellow light, we record the timestamp when the state changes and continuously check if the required interval has passed. As documented in the official Arduino BlinkWithoutDelay guide, this allows the loop() function to run thousands of times per second, checking for pedestrian button presses or radar triggers in the background.
Expert Insight: When calculating time differences withmillis(), always use subtraction (currentMillis - previousMillis >= interval) rather than addition (currentMillis >= previousMillis + interval). This prevents the catastrophic rollover bug that occurs when the 32-bit integer overflows after approximately 49.7 days of continuous uptime.
Adaptive Timing via Microwave Radar
Static timers cause unnecessary congestion. By integrating the RCWL-0516 microwave Doppler radar module, the Arduino can dynamically extend the green light phase. The radar penetrates plastic enclosures and operates reliably in rain, fog, and snow, unlike optical IR sensors.
If the radar detects a continuous queue of vehicles, the FSM overrides the standard 30-second green limit and extends it up to a maximum threshold (e.g., 90 seconds). This aligns with modern FHWA adaptive signal control strategies, which prioritize real-time actuation over fixed-time scheduling.
Sensor Timeout and Edge Case Handling
What happens if a parked car triggers the radar indefinitely? A robust system requires a "max-green" fail-safe. In our FSM logic, we implement a secondary timer:
- Base Green Time: 25 seconds.
- Extension Increment: +5 seconds per radar trigger.
- Hard Maximum: 90 seconds. Once hit, the FSM forces a transition to
STATE_NS_YELLOWregardless of sensor input.
Handling Edge Cases: The Watchdog Timer
In embedded systems, I2C bus lockups or memory leaks can cause the microcontroller to freeze. If an Arduino controlling a traffic light freezes while the North-South axis is green and the East-West axis is red, the result is a catastrophic intersection failure.
To prevent this, we utilize the AVR hardware Watchdog Timer (WDT). The WDT is an independent internal clock that will force a hardware reset if it is not periodically "pet" (reset) by the main code.
Implementing the WDT
#include <avr/wdt.h>
void setup() {
wdt_enable(WDTO_2S); // Reset if loop hangs for > 2 seconds
// Initialize pins and FSM
}
void loop() {
wdt_reset(); // Pet the dog
updateTrafficFSM();
readSensors();
}
If a stray pointer or infinite loop prevents wdt_reset() from executing, the Arduino reboots in under 2 seconds. Upon reboot, the setup() function defaults all pins to a safe "ALL RED" configuration for 5 seconds before resuming normal FSM operations.
Comparison: Blocking vs. FSM vs. RTOS
When architecting your codebase, it is vital to choose the right concurrency model. Below is a comparison of approaches for traffic light control:
| Architecture | Pros | Cons | Best Use Case |
|---|---|---|---|
Blocking (delay) |
Extremely simple to write | Cannot read sensors during delays | Classroom demonstrations |
FSM + millis() |
Deterministic, low memory overhead, handles concurrent I/O | Requires careful state tracking and timestamp math | Standard intersections, pedestrian crossings |
| FreeRTOS | True multitasking, task prioritization | High RAM usage, complex debugging | Networked smart-city nodes with TLS encryption |
Final Calibration and Deployment
Before deploying your advanced Arduino code for traffic light systems to an outdoor enclosure, perform a "burn-in" test. Run the FSM for 72 continuous hours while logging state transitions to an SD card. Verify that the 49-day millis() rollover logic holds up in simulation, and ensure your MOSFET heat sinks remain below 50°C under continuous 12V/2A loads. By combining non-blocking architecture, adaptive radar sensing, and hardware watchdogs, you elevate a simple hobby project into a resilient, industrial-grade control system.






