Traffic lights simulation using Arduino

Learn how to create a traffic light system for vehicles and pedestrians for your Arduino powered city

An entertaining project for those of you who have some spare LEDs is an intersection controlled by two traffic lights: one for the pedestrians and another for vehicles. Let's use Arduino to start this project. The goal is to create a traffic light that automatically toggles at each 10 seconds, or manually by pedestrians.


  • Arduino (Duemillanove, Uno or Leonardo)
  • 5 LEDs (2 red, 2 green e 1 yellow/orange) + 5 resistors 330 Ω
  • 1 seitch button + 1 resistor 1 kΩ
  • 1 Buzzer + 1 resistor 330 Ω (optional)
  • Breadboard and enough enough wiring to connect everything


Esquema de ligação para os semáforos


  1. Put the car traffic light LEDs in breadboard (3 in total), as well as the resistors (all are 330 Ω). Make sure that you connect the LEDs in right position and connect the groud connector to the resistor, which in turn should connect to Arduino ground. Later we will connect the LED positive to the Arduino digital ports.
    1. If you have a buzzer, place it after the the LEDs. Connect the 330 Ω resitor to the negative side of buzzer and connect the resitor to ground, like we did on previous step
    2. You can now place the pedestrian traffice light (red and green) just like we did for the vehicles.
    3. Right now you should have 3 LED (red, yellow, green), a buzzer (optional) and 2 LED (red and green). After the last LEDs, place the pedestrian button and connect it to the 1KΩ resistor, which in turns connects to ground. Connect the oposite wire to the Arduino power source (5V).

Let's connect the wires which control the traffic lights to the Arduino digital ports.

  1. On the vehicle red LED positive, wire it to digital port 10.
  2. On the vehicle yellow LED positive, wire it to digital port 9.
  3. On the vehicle green LED positive, connect it to digital port 8
  4. If you are using a buzzer, connect it to digital port 6.
  5. Wire the button positive to the digital port 5.
  6. On the pedestrian red LED positive, wire it to digital port 3
  7. On the pedestrian green LED positive, wire it to digital port 2

It is everything for connections. We can now connect the breadboard positive to Arduino 5V source, and the ground to Arduino's ground. On the software side, you just need to upload the following code:

 // variables to adjust the traffic light cycle
 long cycle = 10 * 1000l; // 10 segundos
 long last_cycle = 0;

 // variables to countrol button behavior
 int debounce_delay = 800;
 int pedestrian_req = 0;
 int req_state = 0;

 // digital ports on Arduino
 const byte PEDESTRIAN_GREEN = 2;
 const byte PEDESTRIAN_RED = 3;
 const byte BUTTON = 5;
 const byte BELL = 6;
 const byte CAR_GREEN = 8;
 const byte CAR_YELLOW = 9;
 const byte CAR_RED = 10;

 // Bell sound for pedestrians
 const byte NOTE_A6 = 1760;  // try to change for different sounds

  * Startup routine
 void setup(){
   // pedestrians traffic light
   digitalWrite(PEDESTRIAN_GREEN, LOW);
   digitalWrite(PEDESTRIAN_RED, HIGH);

   // vehicles traffic lights
   pinMode(CAR_GREEN, OUTPUT);
   digitalWrite(CAR_GREEN, HIGH);
   digitalWrite(CAR_YELLOW, LOW);
   pinMode(CAR_RED, OUTPUT);
   digitalWrite(CAR_RED, LOW);

   // push button
   pinMode(BUTTON, INPUT);

   // last cycle
   last_cycle = millis();


  * Loop routine to manage traffic lights
 void loop(){
   // button read with debounce protection -
   long ms = millis();
   byte request = digitalRead(BUTTON);

   if( last_cycle + cycle < ms ){
     // automatic cycle - allow pedestrians to pass
     Serial.print("Give way to pedestrians (cycle)\n");
     req_state = 1;
     last_cycle = millis();
   } else if( request == 1 && (ms - debounce_delay) > pedestrian_req ){
     // some pedestrian touched the button
     Serial.print("Give way to pedestrians (manual)\n");
     req_state = 1;
     last_cycle = millis(); // reset the cycle so it doesn't trigger right away
   } else if( request != req_state ){
     // debounce protection
     pedestrian_req = ms;

   req_state = request;

  * Function to start the transiction to vehicles red light 
  * (green -> yellow -> red) with 1 second delay
 void stopVehicles(){
   digitalWrite(CAR_GREEN, LOW);
   digitalWrite(CAR_YELLOW, HIGH);

   digitalWrite(CAR_YELLOW, LOW);
   digitalWrite(CAR_RED, HIGH);


  * Turn vehicle traffic ligth green (no delay)
 void startVehicles(){
   digitalWrite(CAR_RED, LOW);
   digitalWrite(CAR_YELLOW, LOW);
   digitalWrite(CAR_GREEN, HIGH);

  * Activate the green light to pedestrians.
  * Traffic light turns greeen for 5 seconds and then issues 5 beeps telling the 
  * light is about to turn red.
 void pedestriansCycle(){
   digitalWrite(PEDESTRIAN_RED, LOW);
   digitalWrite(PEDESTRIAN_GREEN, HIGH);

   blinkLigths(PEDESTRIAN_GREEN, 5, 400);
   digitalWrite(PEDESTRIAN_GREEN, LOW);
   digitalWrite(PEDESTRIAN_RED, HIGH);

  * Blinks a given light for a determined number of times
 void blinkLigths(byte light, byte times, int duration){
   for( byte i = 0; i < times; i++ ){
     digitalWrite(light, LOW);
     tone(BELL, NOTE_A6, 1000/2);
     digitalWrite(light, HIGH);

After uploading the code, the vehicles traffic light should start green, while the pedestrian traffic light should be red. Try to push the button or wait 10 seconds, and follow the sequence:

  1. Car traffic light turns yellow, then red
  2. After 1.5 seconds, pedestrians traffic light turns green
  3. After 5 seconds, pedestrians traffic light starts blinking while issuing an audible alert (if a buzzer is installed)
  4. The pedestrian traffic light turns red while the car traffic light turns green

This sequence repeats ad eternum.


If you notice, the read of the push button is done in a different way you generally see on other tutorials. Because Arduino's digital ports are very sensible, just by touching the wires you can cause a change in the current amount, causing an incorrect detection of a push on the button by Arduino. In the code we're using the Debounce technique to avoid this problem.

Debounce goal is to check the button is pressed continously during a short period of time (800 ms in the code above). This means the button will not be activated by a current change: we need to press the button for at least 800ms to activate the trafic light sequence. This delay can be adjusted on the debounce_delay variable.

O objetivo do Debounce é verificar se o estado do botão é constante durante um período de duration (neste caso, o código verifica durante 800 ms). Assim, uma alteração de estado de poucos milissegundos não irá ativar por acidente o semáforo. Uma implicação deste sistema é que necessitamos de carregar no botão por 800 ms para ser considerada a mudança de estado no sistema. Este período de duration pode ser ajustado na variável debounce_delay. Read more:

This tutorial was improved resulting of a question I received by e-mail about the traffic light automation. On the previous version, the traffic lights only changed on-demand. In the new version, the traffic ligth changes automatically on inactivity of the button. You can check both old (semaforos-antigo.ino) and newer (semaforos2.ino) versions on the archive in Downloads section. The schematic is the same for both versions.


Code, schematics and some pictures