Non-blocking traffic light code for Arduino replaces the standard delay() function with a state machine driven by the millis() timer. This architecture allows the microcontroller to process light sequences while simultaneously reading pedestrian buttons or vehicle sensors without freezing. This guide is engineered for embedded systems students, Arduino hobbyists, and junior firmware developers transitioning from basic scripts to professional event-driven architectures. The core mechanism relies on tracking elapsed time and switching states only when specific thresholds are met, forming the foundation of robust arduino traffic light code.
Key Takeaways
- Blocking delays freeze the 16 MHz processor, preventing concurrent sensor reads.
- State machines manage light transitions using elapsed time tracking.
- The millis() function provides non-blocking timestamps for multitasking.
The Problem with Blocking Arduino Traffic Light Code
Beginners typically write traffic light sequences using the delay() function to pause between color changes. While simple, this approach halts the central processing unit entirely. The 16 MHz clock speed of the Arduino Uno means it processes millions of instructions per second, but a 3000 ms delay halts all instruction fetching, wasting approximately 48 million clock cycles.
During this frozen period, the microcontroller cannot read a pedestrian crosswalk button or monitor an ultrasonic vehicle sensor. For any non-blocking delay traffic light project requiring real-world responsiveness, blocking functions are fundamentally incompatible with concurrent task execution.
Understanding the Traffic Light State Machine
State Machine: A computational model consisting of a finite number of states, transitions between those states, and specific actions. In embedded C++ programming, it dictates how a microcontroller responds to external inputs based strictly on its current operational mode and elapsed time.
A traffic light state machine divides the intersection logic into discrete phases: Green, Yellow, and Red. Instead of pausing, the firmware continuously checks if the required time for the current phase has elapsed. If the threshold is met, the system transitions to the next state and resets the timer.
State Transition Framework
| Current State | Duration Threshold | Next State | Action Trigger |
|---|---|---|---|
| Green | 3000 ms | Yellow | Timer elapsed |
| Yellow | 1000 ms | Red | Timer elapsed |
| Red | 3000 ms | Green | Timer elapsed or Button press |
Non-Blocking Implementation in C++
Non-Blocking Code: Firmware architecture that executes continuous operations without pausing the central processing unit. By utilizing hardware timers and timestamp comparisons, non-blocking code enables microcontrollers to manage multiple concurrent tasks, such as sensor polling and LED output switching, simultaneously without freezing.
The following C++ source code utilizes the Arduino millis() documentation standard to track time without stopping the main loop. It manages the 2 KB SRAM limitation by using efficient byte-sized variables for state tracking.
Complete Source Code
const int RED_PIN = 4;
const int YELLOW_PIN = 3;
const int GREEN_PIN = 2;
const int BUTTON_PIN = 8;
enum LightState { GREEN, YELLOW, RED };
LightState currentState = GREEN;
unsigned long previousMillis = 0;
unsigned long currentInterval = 3000;
void setup() {
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
updateLights();
}
void loop() {
unsigned long currentMillis = millis();
bool buttonPressed = (digitalRead(BUTTON_PIN) == LOW);
if (currentMillis - previousMillis >= currentInterval || (currentState == RED && buttonPressed)) {
previousMillis = currentMillis;
transitionState();
updateLights();
}
}
void transitionState() {
switch (currentState) {
case GREEN:
currentState = YELLOW;
currentInterval = 1000;
break;
case YELLOW:
currentState = RED;
currentInterval = 3000;
break;
case RED:
currentState = GREEN;
currentInterval = 3000;
break;
}
}
void updateLights() {
digitalWrite(RED_PIN, currentState == RED ? HIGH : LOW);
digitalWrite(YELLOW_PIN, currentState == YELLOW ? HIGH : LOW);
digitalWrite(GREEN_PIN, currentState == GREEN ? HIGH : LOW);
}
Blocking vs. Non-Blocking Execution Comparison
Evaluating execution models reveals why professional firmware avoids blocking calls. The BlinkWithoutDelay official tutorial demonstrates how timestamp subtraction prevents integer overflow errors inherent in direct timer comparisons.
| Feature | Blocking (delay) | Non-Blocking (millis) |
|---|---|---|
| CPU Availability | 0% (Frozen) | 100% (Active Loop) |
| Button Polling | Impossible during delay | Continuous 500 ms debounce ready |
| Memory Overhead | Minimal | Requires 4 bytes per timer |
| Scalability | Single task only | Multiple concurrent state machines |
Frequently Asked Questions
How do I add a pedestrian button to my arduino traffic light code?
Configure a digital pin with INPUT_PULLUP and read its state inside the main loop. If the button is pressed during the RED state, force an immediate state transition to GREEN by bypassing the remaining time threshold.
Why does my non-blocking delay traffic light timer overflow after 49 days?
The millis() function returns an unsigned long integer that maxes out at 4,294,967,295 milliseconds. Using subtraction (currentMillis - previousMillis >= interval) naturally handles this rollover without causing logic failures.
Can I use a traffic light state machine for multiple intersections?
Yes. Instantiate separate state variables and timer intervals for each intersection. Object-oriented C++ structures allow you to encapsulate each intersection into its own class, maintaining clean separation of logic.
Conclusion and Next Steps
Implementing a traffic light state machine transforms a rigid script into a responsive embedded system capable of multitasking. By leveraging the millis() function, your Arduino Uno maintains full processor availability for external interrupts and sensor polling. Your next step is to integrate an HC-SR04 ultrasonic sensor to automatically extend the green light duration when a vehicle is detected within 50 centimeters.






