Project showcase
Intersection with Traffic Lights

Intersection with Traffic Lights © GPL3+

Teaching kids how traffic lights work.

  • 4,732 views
  • 0 comments
  • 14 respects

Components and supplies

Ard nano
Arduino Nano R3
×1
Terminal Adapter Board For Arduino Nano 3.0
×1
DC-DC adjustable step-down voltage regulator with LM2596
×2
PCF8575 IO Expander Module I2C To 16 IO
×1
TF card U disk MP3 player / decoder module
×1
12V relay (A12W-K)
×1
Transistor NPN (BC547 or similar)
×1
Diode BA159
×1
15p1093 40
Buzzer, Piezo
×1
PCF8574T (SMD)
×4
Red 1206 SMD LED
×24
Yellow 1206 SMD LED
×12
Green 1206 SMD LED
×16
White (cold) 1206 SMD LED
×4
SX1509 IO expander module
×2
4 channel I2C Logic Level Converter Bi-Directional Module 5V to 3.3V
×1
200 ohm 1206 SMD resistor
There are actually 4 different values used as current limiting resistors for LEDs ranging from 200 to 1K ohms.
×56
DC jack female panel mount
×1
Illuminated tactile switch Blue
×1
Illuminated tactile switch Green
×1
Illuminated tactile switch Red
×1
12V/1A power adapter
×1
Plastic plate 2mm thick, 93x73cm
×1
Plastic pipe 10mm diameter
×1
Electrical installation boxes
×24

Necessary tools and machines

4418162
Drill, Screwdriver
09507 01
Soldering iron (generic)

About this project

I always wanted to make a functional model of traffic lights with LEDs, controlled by microcontroller. When my 3-year-old daughter hit the age of constantly asking "WHY?," I decided now is the time to build this project and use it as a demonstration of how traffic lights work.

1. Traffic lights

While making a functional PCB with LEDs and resistors is very easy I wanted my traffic lights to look as realistic as possible. Unfortunately I don't own a 3D printer so I had to improvise. I decided to use SMT components so the traffic light can be small. 1206 is the size which I can still easily solder with regular soldering iron and with my eyes (with help of magnifying glass).

The enclosure of the lights was made from transparent PE film for laser printers. The light from LED is dispersed with white packing EPE foam sheet, which is inserted as a layer between the transparent film and the PCB with LEDs.

There are three types of traffic lights in this project (all patterns are based on real life version of traffic lights used in my country - Slovakia):

1. Main traffic light with pattern for two traffic lanes (left and forward+right) plus one pedestrian crossing orange warning signal for cars turning right. Contains 7 LEDs. There are 4 pieces of this light, one for each intersection direction.

2. Traffic light for pedestrians - only two LEDs inside - red and green. 8 pieces are needed for the whole project.

3. Traffic light on railway crossing - two red signals and one white - 3 LEDs in total. 4 pieces - 2 on each side of the road in both directions to railway crossing.

The enclosure with SMT LEDs and current limiting resistors on PCB is mounted on pole made from plastic pipe with 10mm diameter. The connecting wires are led inside the pole.

The total number of LEDs is 4x7 + 8x2 + 4x3 = 56. Plus 3 more inside of illuminated buttons. To drive this amount of LEDs we'll clearly need some IO expanders. The main traffic light has 7 LEDs. I wanted to have as few wires as possible from the pole to microcontroller so the IO expander is placed in the traffic light itself on the same PCB as LEDs. The expander is PCF8574T with 8 IO pins in SMT package. It's connected to Arduino via I2C bus. This helped to reduce the number of wires from 8 to 4 - VCC, GND, SCL and SDA. The rest of the lights are driven by two SX1509 which are hidden under the "sidewalk".

2. Microcontroller - Arduino Nano

As the "brain" of the whole project I chose an Arduino Nano. It doesn't have to have many IO pins (since the large amount of LEDs is controlled by IO expanders), the clock speed and memory space is pretty sufficient for such a simple task.

The Nano is placed in terminal adapter board for easy way of connecting the wires. Together with logic level converter it's hidden in the installation box (this box is originally used for installing electrical sockets or switches on surface of the wall). The logic level converter is needed for communication with SX1509 IO expanders via I2C bus, they work with 3.3V logic levels.

3. IO expanders with breathe function

Why did I choose SX1509 instead of PCF8574/PCF8575/MCP23017? I could use any of the latter ICs and could keep the connections simple, without the logic level converter and the 3.3V power supply. The answer is the BREATHE function of LED drivers in SX1509. I wanted to have the railway crossing lights have the effect of "analogue" bulb when blinking, instead of sharp digital ON/OFF blinking of LEDs. It's better explained by these two GIFs:

Only half of the pins of SX1509 can be used for LED breathing. But that's fine for me. I used 8 "digital only" pins for pedestrian signals and 6 of the remaining 8 pins with extended functions (such as breathe) are used for railway signals.

SX1509 has another interesting and useful feature I planned to get use of - "Synchronization of LED Drivers across several ICs". All four railway lights should blink in sync, but unfortunately the library from Sparkfun I used doesn't support this. After inspecting the code of the library I think it's hardcoded to use internal oscillator instead of OSCIO pin as a clock source. I tried to make changes to library but unfortunately my skills aren't that good, I wasn't successful.

4. Buttons

To switch operating modes I placed 3 push buttons to the project. One of them (red) is used to switch between the red and white railway signal. The green button switches the main traffic lights operating mode. I planned to have more programs available with different patterns of traffic flow, but for now there is only one usable (mode 1). The next two modes are "night mode" - only orange lights are blinking - the traffic in this mode is controlled by signs or by the "right hand rule" (the driver approaching from the right has the right of way), and demo mode - all LEDs are turned on in sequence with 1 second intervals.

The buttons are illuminated. The LEDs in buttons and the button contacts are wired to another IO expander - PCF8575 (this has 16 IO pins, I could use PCF8574 instead, but I had PCF8575 on hand as breakout module, so I used this one).

Originally I planned to use panel mounted buttons and connect them directly to expander with wires, but I couldn't get any illuminated buttons for panel in small size. So I had to use the PCB mount buttons but I didn't have time to wait for a professional PCB manufacturing service so I made an ugly PCB by cutting the copper by hand. Ew! Nasty, but it does the job.

5. MP3 player module

The purpose of the MP3 player is to play the bell sound of the railway crossing during the red "STOP" signal. I used the module with SD card slot and USB connector. The recording of the sound is saved on SD card. The module is powered by 5V through relay controlled by Arduino (via transistor). I used a 12V relay, again, I could use a 5V relay or some power transistor to make the circuit simpler). The module is connected to a small piezo speaker, the sound volume is pretty sufficient (actually it was too annoying, I had to use a limiting resistor in series with the buzzer).

6. Power supply

The whole circuit is powered by 12V wall socket adapter. The circuit uses 3 voltages:

12V: relay

5V: Arduino, PCF8574 and PCF8575 expanders, MP3 player

3.3V: SX1509 (optional ESP8266)

I used two DC/DC step-down converter modules with LM2596 for 3.3V and 5V.

7. Base board with "sidewalks"

The base board is a 2mm thick plastic plate. I got it many years ago as 1m x 2m plate for some other DIY project and the leftover piece was exactly suitable for my needs. It's dimensions are 93x73cm. The sidewalks are built from electrical installation boxes such as these:

The boxes were sprayed to dark grey color. The road area of plastic board was sprayed to black and the rest of the are to green to imitate the lawn.

Crosswalks and traffic lanes with corresponding arrows were sprayed with white color through stencil.

Traffic lights in action
Railway crossing in action


Code

Main codeArduino
#include <Wire.h>
#include <SparkFunSX1509.h> // Include SX1509 library

const int SX_RESET = 16; // reset pins of SX1509s
const int RAILWAY_SOUND = 6; // control pin of relay for mp3 power
const int PCF_INT = 2; // interrupt from PCF8575

// D4 & D5 = software serial for level shifter / ESP8266; not implemented yet

// SX1509 I2C address (set by ADDR1 and ADDR0 (00 by default):
const byte SX1509_0 = 0x3E;  // SX1509 I2C address
const byte SX1509_1 = 0x70;  // SX1509 I2C address
SX1509 io0; // Create an SX1509 object to be used throughout
SX1509 io1; // Create an SX1509 object to be used throughout

// SX1509_0 Pin definition:
const byte SX1509_0_RWL0 = 6; // rail red left
const byte SX1509_0_RWR0 = 5; // rail red right
const byte SX1509_0_RWW0 = 7; // rail white
const byte SX1509_0_RWL1 = 14; // rail red left
const byte SX1509_0_RWR1 = 15; // rail red right
const byte SX1509_0_RWW1 = 13; // rail white

const byte SX1509_0_PEDR0 = 10; //pedestrian red #3a
const byte SX1509_0_PEDG0 = 11; //pedestrian green #3a
const byte SX1509_0_PEDR1 = 9; //pedestrian red #3b
const byte SX1509_0_PEDG1 = 8; //pedestrian green #3b
const byte SX1509_0_PEDR2 = 1; //pedestrian red #4a
const byte SX1509_0_PEDG2 = 0; //pedestrian green #4a
const byte SX1509_0_PEDR3 = 2; //pedestrian red #4b
const byte SX1509_0_PEDG3 = 3; //pedestrian green #4b

// SX1509_1 Pin definition:
const byte SX1509_1_RWL0 = 6; // rail red left
const byte SX1509_1_RWR0 = 5; // rail red right
const byte SX1509_1_RWW0 = 7; // rail white
const byte SX1509_1_RWL1 = 14; // rail red left
const byte SX1509_1_RWR1 = 13; // rail red right
const byte SX1509_1_RWW1 = 15; // rail white

const byte SX1509_1_PEDR0 = 8; //pedestrian red #1a
const byte SX1509_1_PEDG0 = 9; //pedestrian green #1a
const byte SX1509_1_PEDR1 = 10; //pedestrian red #1b
const byte SX1509_1_PEDG1 = 11; //pedestrian green #1b
const byte SX1509_1_PEDR2 = 0; //pedestrian red #2a
const byte SX1509_1_PEDG2 = 1; //pedestrian green #2a
const byte SX1509_1_PEDR3 = 3; //pedestrian red #2b
const byte SX1509_1_PEDG3 = 2; //pedestrian green #2b



// addresses of PCF8574 IC
const byte PCF8574[] = {0x39, 0x3b, 0x3d, 0x3f};

#define PCF8575 (0x20) //address of PCF8575 (button input, illuminated buttons LED output)


const byte RED = 10;
const byte YLW = 20;
const byte GRN = 30;

unsigned long previousMillis = 0;
const long interval = 20;
byte blink_int = 0;
byte sec20 = 0;
boolean blink_state = false;

const byte numChars = 64;
char recChars[numChars];   // an array to store the received data
boolean newData = false;

byte cur_light = 0;
byte b;
String s;
boolean pcf_int_trigger = false;
byte dbnc = 0;
boolean sound = false;

boolean red_btn = false; // LED of red button
boolean grn_btn = false; // LED of green button
boolean blu_btn = false; // LED of blue button

byte mode = 1; // 0 = orange lights blink, 1 - program, 2 - test
byte rail_mode = 1; // 0 = off, 1 - white, 2 - red
byte prog_step = 0;
byte next_step = 1;



void setup()
{
  pinMode(RAILWAY_SOUND, OUTPUT);
  digitalWrite(RAILWAY_SOUND, LOW);
  pinMode(SX_RESET, OUTPUT);
  digitalWrite(SX_RESET, HIGH);

  pinMode(PCF_INT, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PCF_INT), PCF_INT_ISR, FALLING); //interrupt when any of buttons is pressed
  
  Wire.begin();

  pcf8575_write(word(B11111111,B11111111)); //set all IOs as inputs

  Serial.begin(9600);
  while (!Serial);
  Serial.println("\nI2C Scanner");


  if (!io0.begin(SX1509_0, SX_RESET)) {
    Serial.println("SX1509_0 not found");
  }

  if (!io1.begin(SX1509_1, SX_RESET)) {
    Serial.println("SX1509_1 not found");
  }

  sx_init();

  set_rail();
}




void sx_init() {
  // Use the internal 2MHz oscillator.
  // Set LED clock with divider (2MHz / (2^(divider-1)):
  // divider = 2 means 1MHz LED clock (2MHz / (2^(2-1))
  // divider = 3 means 500kHz LED clock (2MHz / (2^(3-1))
  // divider = 4 means 250kHz LED clock (2MHz / (2^(4-1))
  
  // 0x1-0xE: fOSCout = Fosc / 2 ^ (outputFreq - 1) Hz

  const byte divider = 4;
  
  io0.init();
  io1.init();

  io0.clock(INTERNAL_CLOCK_2MHZ, divider, INPUT, 1); //250kHz clock
  io1.clock(INTERNAL_CLOCK_2MHZ, divider, INPUT, 1); //250kHz clock

  //set IOs for pedestrian lights as digital outputs
  //IOs for railway lights will be set as analogue outputs with breath funcion later
  io0.pinMode(SX1509_0_PEDR0, OUTPUT);
  io0.pinMode(SX1509_0_PEDG0, OUTPUT);
  io0.pinMode(SX1509_0_PEDR1, OUTPUT);
  io0.pinMode(SX1509_0_PEDG1, OUTPUT);
  io0.pinMode(SX1509_0_PEDR2, OUTPUT);
  io0.pinMode(SX1509_0_PEDG2, OUTPUT);
  io0.pinMode(SX1509_0_PEDR3, OUTPUT);
  io0.pinMode(SX1509_0_PEDG3, OUTPUT);
  
  io1.pinMode(SX1509_1_PEDR0, OUTPUT);
  io1.pinMode(SX1509_1_PEDG0, OUTPUT);
  io1.pinMode(SX1509_1_PEDR1, OUTPUT);
  io1.pinMode(SX1509_1_PEDG1, OUTPUT);
  io1.pinMode(SX1509_1_PEDR2, OUTPUT);
  io1.pinMode(SX1509_1_PEDG2, OUTPUT);
  io1.pinMode(SX1509_1_PEDR3, OUTPUT);
  io1.pinMode(SX1509_1_PEDG3, OUTPUT);
}




void loop()
{
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {//every 20ms / 50x sec
    previousMillis = currentMillis;

    sec20++;
    if (dbnc > 0) {
      dbnc --;
    }

    if (sec20 == 50) { // 50 x 20 miliseconds elapsed, i.e. new second
      // ****************************  NEW SECOND  ****************************
      sec20 = 0;

      if (next_step > 0) {next_step--;}   //counter for traffic lights program
      if (next_step == 0 || mode == 0 || mode == 2) { //when counter reaches zero, it's time for next program step. In mode=0 and mode=2 it's every second.
        program_step();
      }
    }

    blink_int++;
    if (blink_int == 34) {
      blink_state = true;
      if (mode == 0) { // mode = 0 means only orange lights are flashing with cca. 700ms ON / 700ms OFF interval
        set_leds(0, 5*32 + 5*4);
        set_leds(1, 5*32 + 5*4);
        set_leds(2, 5*32 + 5*4);
        set_leds(3, 5*32 + 5*4);
      }
    }

    if (blink_int == 69) {
      blink_state = false;
      blink_int = 0;
      if (mode == 0) {
        set_leds(0, 5*32 + 5*4);
        set_leds(1, 5*32 + 5*4);
        set_leds(2, 5*32 + 5*4);
        set_leds(3, 5*32 + 5*4);
      }
    }

    if (blink_int == 1) {
      digitalWrite(LED_BUILTIN, HIGH);
    } else {
      digitalWrite(LED_BUILTIN, LOW);
    }
  }

  //check if any of buttons was pressed
  if (pcf_int_trigger) {
    pcf_int_trigger = false;
    b = pcf8575_read();
    b = b | B11101010; // mask only for the buttons
    if (b != 255 && dbnc == 0) { //some button was pressed
      Serial.println(b, BIN);
      dbnc = 8; //set debounce period
      //blue.0
      //red.4
      //grn.2
      if (bitRead(b, 4) == 0) { //RED button pressed, change railway lights mode
        rail_mode++;
        if (rail_mode > 2) {
          rail_mode = 1;
        }
        set_rail();
      } else if (bitRead(b, 2) == 0) {//GREEN button pressed, change traffic lights mode
        mode++;
        next_step = 1;
        prog_step = 0;
        if (mode > 2) {
          mode = 0;
        }
        Serial.print("Mode changed: ");
        Serial.println(mode);
      } else if (bitRead(b, 0) == 0) {//BLUE button pressed, it does nothing so far
        //nothing yet
      }
    }
  }

  //check serial port input buffer
  CheckSerial();
}



void button_lights() {
  //sets illuminated buttons LEDs according to variables red_btn, grn_btn and blu_btn
  byte lights;
  lights = pcf8575_read();
  if (red_btn) {
    bitClear(lights, 3);
  } else {
    bitSet(lights, 3);
  }

  if (grn_btn) {
    bitClear(lights, 5);
  } else {
    bitSet(lights, 5);
  }

  if (blu_btn) {
    bitClear(lights, 1);
  } else {
    bitSet(lights, 1);
  }
  lights = lights | B00010101; //set button input IOs always as inputs
  pcf8575_write(word(0xFF,lights));
}







void set_ped_group(byte group, byte color) {
  //there are 4 groups of LEDs for pedestrian signals
  //in every group the signals change simultaneously
  
  boolean rval;
  boolean gval;
  if (color == RED) {
    rval = 0; // RED lights on
    gval = 1; // GRN lights off
  } else {
    rval = 1;
    gval = 0;
  }

    
  if (group == 0) {
    io1.digitalWrite(SX1509_1_PEDR1, rval);
    io1.digitalWrite(SX1509_1_PEDG1, gval);
    io1.digitalWrite(SX1509_1_PEDR2, rval);
    io1.digitalWrite(SX1509_1_PEDG2, gval);
  } else if (group == 1) {
    io1.digitalWrite(SX1509_1_PEDR3, rval);
    io1.digitalWrite(SX1509_1_PEDG3, gval);
    io0.digitalWrite(SX1509_0_PEDR0, rval);
    io0.digitalWrite(SX1509_0_PEDG0, gval);
  } else if (group == 2) {
    io0.digitalWrite(SX1509_0_PEDR1, rval);
    io0.digitalWrite(SX1509_0_PEDG1, gval);
    io0.digitalWrite(SX1509_0_PEDR2, rval);
    io0.digitalWrite(SX1509_0_PEDG2, gval);
  } else if (group == 3) {
    io0.digitalWrite(SX1509_0_PEDR3, rval);
    io0.digitalWrite(SX1509_0_PEDG3, gval);
    io1.digitalWrite(SX1509_1_PEDR0, rval);
    io1.digitalWrite(SX1509_1_PEDG0, gval);
  }
}

void ped_off() {
  //turn off all pedestrian signals
  io0.digitalWrite(SX1509_0_PEDR0, 1);
  io0.digitalWrite(SX1509_0_PEDG0, 1);
  io0.digitalWrite(SX1509_0_PEDR1, 1);
  io0.digitalWrite(SX1509_0_PEDG1, 1);
  io0.digitalWrite(SX1509_0_PEDR2, 1);
  io0.digitalWrite(SX1509_0_PEDG2, 1);
  io0.digitalWrite(SX1509_0_PEDR3, 1);
  io0.digitalWrite(SX1509_0_PEDG3, 1);
  io1.digitalWrite(SX1509_1_PEDR0, 1);
  io1.digitalWrite(SX1509_1_PEDG0, 1);
  io1.digitalWrite(SX1509_1_PEDR1, 1);
  io1.digitalWrite(SX1509_1_PEDG1, 1);
  io1.digitalWrite(SX1509_1_PEDR2, 1);
  io1.digitalWrite(SX1509_1_PEDG2, 1);
  io1.digitalWrite(SX1509_1_PEDR3, 1);
  io1.digitalWrite(SX1509_1_PEDG3, 1);
}






void CheckSerial() {
  //serial port input routine, used only for debugging
    static byte ndx = 0;
    char endMarker = '\n';
    char endMarker2 = '\r';
    char rc;
    byte pgroup, pcolor;
   
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();
 
        if (rc != endMarker && rc != endMarker2) {
            recChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            recChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }

    if (newData == true) {
      int slen = strlen(recChars);
      if (slen > 1) {
      String usbreply = recChars;

      usbreply.trim();
      slen = usbreply.length();
        
      if (usbreply == "TRED") {
        rail_mode = 2;
        set_rail();
      } else if (usbreply == "TREDB") {
        rail_mode = 3;
        set_rail();
      } else if (usbreply == "WHITE") {
        rail_mode = 1;
        set_rail();
      } else if (usbreply == "RED") {
        set_leds(cur_light, 32 + 4);
      } else if (usbreply == "YLW") {
        set_leds(cur_light, 4*32 + 4*4 + 0);
      } else if (usbreply == "GRN") {
        set_leds(cur_light, 3*32 + 3*4 + 1);
      } else if (usbreply == "REDYLW") {
        set_leds(cur_light, 2*32 + 2*4 + 0);
      } else if (usbreply.startsWith("CL")) {
        usbreply = usbreply.substring(2);
        cur_light = usbreply.toInt();
        Serial.println("CURRENT LIGHT: " + String(cur_light));
      } else if (usbreply == "SXRST") {
        digitalWrite(SX_RESET, LOW);
        delay(2); //2ms RESET
        digitalWrite(SX_RESET, HIGH);
        sx_init();
        Serial.println("SXs were RESET");
      } else if (usbreply == "SX0READ") {
        SX1509_print_regs(SX1509_0);
      } else if (usbreply == "SX1READ") {
        SX1509_print_regs(SX1509_1);
      } else if (usbreply.startsWith("PG")) {
        s = usbreply.substring(2,3);
        pgroup = s.toInt();
        s = usbreply.substring(3,4);
        pcolor = s.toInt();
        pcolor = pcolor * 10;
        Serial.println("PED GROUP: " + String(pgroup) + ", color: " + String(pcolor));
        set_ped_group(pgroup, pcolor);
      }
      
    }
    newData = false;
  }
}




void set_rail() {
  if (rail_mode == 0) {
    //nothing yet
  } else if (rail_mode == 1) {

    //set railway lights as GO - white lights blinking/breathing, red lights off
    
    red_btn = false;
    button_lights();
    digitalWrite(RAILWAY_SOUND, LOW);
    io0.pinMode(SX1509_0_RWL0, OUTPUT);
    io0.pinMode(SX1509_0_RWR0, OUTPUT);
    io0.digitalWrite(SX1509_0_RWL0, HIGH);
    io0.digitalWrite(SX1509_0_RWR0, HIGH);
    io0.pinMode(SX1509_0_RWL1, OUTPUT);
    io0.pinMode(SX1509_0_RWR1, OUTPUT);
    io0.digitalWrite(SX1509_0_RWL1, HIGH);
    io0.digitalWrite(SX1509_0_RWR1, HIGH);
    io0.pinMode(SX1509_0_RWW0, ANALOG_OUTPUT);
    io0.breathe(SX1509_0_RWW0, 600, 700, 5, 5); // Breathe interval: 600ms LOW, 700ms HIGH, 5ms to rise from low to high, 5ms to fall from high to low
    io0.pinMode(SX1509_0_RWW1, ANALOG_OUTPUT);
    io0.breathe(SX1509_0_RWW1, 600, 700, 5, 5);
    io1.pinMode(SX1509_1_RWL0, OUTPUT);
    io1.pinMode(SX1509_1_RWR0, OUTPUT);
    io1.digitalWrite(SX1509_1_RWL0, HIGH);
    io1.digitalWrite(SX1509_1_RWR0, HIGH);
    io1.pinMode(SX1509_1_RWL1, OUTPUT);
    io1.pinMode(SX1509_1_RWR1, OUTPUT);
    io1.digitalWrite(SX1509_1_RWL1, HIGH);
    io1.digitalWrite(SX1509_1_RWR1, HIGH);
    io1.pinMode(SX1509_1_RWW0, ANALOG_OUTPUT);
    io1.breathe(SX1509_1_RWW0, 600, 700, 5, 5);
    io1.pinMode(SX1509_1_RWW1, ANALOG_OUTPUT);
    io1.breathe(SX1509_1_RWW1, 600, 700, 5, 5);
  } else if (rail_mode == 2) {

    //set railway lights as STOP - white lights off, red lights blinking/breathing
    
    red_btn = true;
    button_lights();
    
    io0.sync();
    io1.sync();
    digitalWrite(RAILWAY_SOUND, HIGH);
    io0.pinMode(SX1509_0_RWW0, OUTPUT);
    io0.digitalWrite(SX1509_0_RWW0, HIGH);
    io0.pinMode(SX1509_0_RWW1, OUTPUT);
    io0.digitalWrite(SX1509_0_RWW1, HIGH);
    io0.pinMode(SX1509_0_RWL0, ANALOG_OUTPUT);
    io0.breathe(SX1509_0_RWL0, 500, 500, 40, 40); // Breathe interval: 500ms LOW, 500ms HIGH, 40ms to rise from low to high, 40ms to fall from high to low
    io0.pinMode(SX1509_0_RWL1, ANALOG_OUTPUT);
    io0.breathe(SX1509_0_RWL1, 500, 500, 40, 40);
    io1.pinMode(SX1509_1_RWW0, OUTPUT);
    io1.digitalWrite(SX1509_1_RWW0, HIGH);
    io1.pinMode(SX1509_1_RWW1, OUTPUT);
    io1.digitalWrite(SX1509_1_RWW1, HIGH);
    io1.pinMode(SX1509_1_RWL0, ANALOG_OUTPUT);
    io1.breathe(SX1509_1_RWL0, 500, 500, 40, 40);
    io1.pinMode(SX1509_1_RWL1, ANALOG_OUTPUT);
    io1.breathe(SX1509_1_RWL1, 500, 500, 40, 40);
    delay(650);
    io0.pinMode(SX1509_0_RWR0, ANALOG_OUTPUT);
    io0.pinMode(SX1509_0_RWR1, ANALOG_OUTPUT);
    io1.pinMode(SX1509_1_RWR0, ANALOG_OUTPUT);
    io1.pinMode(SX1509_1_RWR1, ANALOG_OUTPUT);
    io0.breathe(SX1509_0_RWR0, 500, 500, 40, 40);
    io0.breathe(SX1509_0_RWR1, 500, 500, 40, 40);
    io1.breathe(SX1509_1_RWR0, 500, 500, 40, 40);
    io1.breathe(SX1509_1_RWR1, 500, 500, 40, 40);
  } else if (rail_mode == 3) {

    //test mode, just to show how lights work without breathe function
    red_btn = true;
    button_lights();
    io0.sync();
    io1.sync();

    io0.pinMode(SX1509_0_RWW0, OUTPUT);
    io0.digitalWrite(SX1509_0_RWW0, HIGH);
    io0.pinMode(SX1509_0_RWW1, OUTPUT);
    io0.digitalWrite(SX1509_0_RWW1, HIGH);
    io1.pinMode(SX1509_1_RWW0, OUTPUT);
    io1.digitalWrite(SX1509_1_RWW0, HIGH);
    io1.pinMode(SX1509_1_RWW1, OUTPUT);
    io1.digitalWrite(SX1509_1_RWW1, HIGH);

    io0.pinMode(SX1509_0_RWL0, OUTPUT);
    io0.pinMode(SX1509_0_RWL1, OUTPUT);
    io1.pinMode(SX1509_1_RWL0, OUTPUT);
    io1.pinMode(SX1509_1_RWL1, OUTPUT);
    io0.pinMode(SX1509_0_RWR0, OUTPUT);
    io0.pinMode(SX1509_0_RWR1, OUTPUT);
    io1.pinMode(SX1509_1_RWR0, OUTPUT);
    io1.pinMode(SX1509_1_RWR1, OUTPUT);
    io0.blink(SX1509_0_RWL0, 600, 600);
    io0.blink(SX1509_0_RWL1, 600, 600);
    io1.blink(SX1509_1_RWL0, 600, 600);
    io1.blink(SX1509_1_RWL1, 600, 600);
    delay(600);
    io0.blink(SX1509_0_RWR0, 600, 600);
    io0.blink(SX1509_0_RWR1, 600, 600);
    io1.blink(SX1509_1_RWR0, 600, 600);
    io1.blink(SX1509_1_RWR1, 600, 600);
  }
}







void program_step() {
  if (rail_mode == 2) {   //whaen railway lights are red, all car traffic in every direction is set to RED signals, all pedestrian signals are GREEN
    set_leds(0, 1*32 + 1*4);
    set_leds(1, 1*32 + 1*4);
    set_leds(2, 1*32 + 1*4);
    set_leds(3, 1*32 + 1*4);
    set_ped_group(0, GRN);
    set_ped_group(1, GRN);
    set_ped_group(2, GRN);
    set_ped_group(3, GRN);
    return;
  }
  
  if (mode == 1) {
    if (prog_step == 0) {
      set_leds(0, 4*32 + 4*4);
      set_leds(1, 4*32 + 4*4);
      set_leds(2, 4*32 + 4*4);
      set_leds(3, 4*32 + 4*4);
      set_ped_group(0, RED);
      set_ped_group(1, RED);
      set_ped_group(2, RED);
      set_ped_group(3, RED);
      next_step = 5;
    } else if (prog_step == 1) {
      set_leds(0, 1*32 + 2*4);
      set_leds(1, 1*32 + 1*4);
      set_leds(2, 1*32 + 2*4);
      set_leds(3, 1*32 + 1*4);
      next_step = 3;
    } else if (prog_step == 2) {
      set_leds(0, 1*32 + 3*4 + 1);
      set_leds(1, 1*32 + 1*4);
      set_leds(2, 1*32 + 3*4 + 1);
      set_leds(3, 1*32 + 1*4);
      set_ped_group(0, GRN);
      set_ped_group(1, RED);
      set_ped_group(2, GRN);
      set_ped_group(3, RED);
      next_step = 12;
    } else if (prog_step == 3) {
      set_ped_group(0, RED);
      set_ped_group(1, RED);
      set_ped_group(2, RED);
      set_ped_group(3, RED);
      next_step = 8;
    } else if (prog_step == 4) {
      set_leds(0, 1*32 + 4*4);
      set_leds(1, 1*32 + 1*4);
      set_leds(2, 1*32 + 4*4);
      set_leds(3, 1*32 + 1*4);
      next_step = 3;
    } else if (prog_step == 5) {
      set_leds(0, 1*32 + 1*4);
      set_leds(1, 2*32 + 1*4);
      set_leds(2, 1*32 + 1*4);
      set_leds(3, 2*32 + 1*4);
      next_step = 3;
    } else if (prog_step == 6) {
      set_leds(0, 1*32 + 1*4);
      set_leds(1, 3*32 + 1*4);
      set_leds(2, 1*32 + 1*4);
      set_leds(3, 3*32 + 1*4);
      next_step = 15;
    } else if (prog_step == 7) {
      set_leds(0, 1*32 + 1*4);
      set_leds(1, 4*32 + 2*4);
      set_leds(2, 1*32 + 1*4);
      set_leds(3, 4*32 + 2*4);
      next_step = 3;
    } else if (prog_step == 8) {
      set_leds(0, 1*32 + 1*4);
      set_leds(1, 1*32 + 3*4 + 1);
      set_leds(2, 1*32 + 1*4);
      set_leds(3, 1*32 + 3*4 + 1);
      set_ped_group(0, RED);
      set_ped_group(1, GRN);
      set_ped_group(2, RED);
      set_ped_group(3, GRN);
      next_step = 12;
    } else if (prog_step == 9) {
      set_ped_group(0, RED);
      set_ped_group(1, RED);
      set_ped_group(2, RED);
      set_ped_group(3, RED);
      next_step = 8;
    } else if (prog_step == 10) {
      set_leds(0, 1*32 + 1*4);
      set_leds(1, 1*32 + 4*4);
      set_leds(2, 1*32 + 1*4);
      set_leds(3, 1*32 + 4*4);
      set_ped_group(0, RED);
      set_ped_group(1, RED);
      set_ped_group(2, RED);
      set_ped_group(3, RED);
      next_step = 3;
    } else if (prog_step == 11) {
      set_leds(0, 1*32 + 1*4);
      set_leds(1, 1*32 + 1*4);
      set_leds(2, 1*32 + 1*4);
      set_leds(3, 1*32 + 1*4);
      next_step = 3;
    } else if (prog_step == 12) {
      set_leds(0, 2*32 + 1*4);
      set_leds(1, 1*32 + 1*4);
      set_leds(2, 2*32 + 1*4);
      set_leds(3, 1*32 + 1*4);
      next_step = 3;
    } else if (prog_step == 13) {
      set_leds(0, 3*32 + 1*4);
      set_leds(1, 1*32 + 1*4);
      set_leds(2, 3*32 + 1*4);
      set_leds(3, 1*32 + 1*4);
      next_step = 15;
    } else if (prog_step == 14) {
      set_leds(0, 4*32 + 1*4);
      set_leds(1, 1*32 + 1*4);
      set_leds(2, 4*32 + 1*4);
      set_leds(3, 1*32 + 1*4);
      next_step = 3;
    }
    
    prog_step++;
    if (prog_step > 14) {
      prog_step = 1;
    }


    
  } else if (mode == 2) {
    if (prog_step == 1) {
      set_leds(0, 0);
      set_leds(1, 0);
      set_leds(2, 0);
      set_leds(3, 0);
      ped_off();
    } else if (prog_step == 2) {
      set_leds(0, 1*32 + 1*4);
      set_leds(1, 1*32 + 1*4);
      set_leds(2, 1*32 + 1*4);
      set_leds(3, 1*32 + 1*4);
      set_ped_group(0, RED);
      set_ped_group(1, RED);
      set_ped_group(2, RED);
      set_ped_group(3, RED);
    } else if (prog_step == 3) {
      set_leds(0, 4*32 + 4*4);
      set_leds(1, 4*32 + 4*4);
      set_leds(2, 4*32 + 4*4);
      set_leds(3, 4*32 + 4*4);
      set_ped_group(0, GRN);
      set_ped_group(1, GRN);
      set_ped_group(2, GRN);
      set_ped_group(3, GRN);
    } else if (prog_step == 4) {
      set_leds(0, 3*32 + 3*4);
      set_leds(1, 3*32 + 3*4);
      set_leds(2, 3*32 + 3*4);
      set_leds(3, 3*32 + 3*4);
      set_ped_group(0, RED);
      set_ped_group(1, RED);
      set_ped_group(2, RED);
      set_ped_group(3, RED);
    } else if (prog_step == 5) {
      set_leds(0, 1);
      set_leds(1, 1);
      set_leds(2, 1);
      set_leds(3, 1);
      set_ped_group(0, GRN);
      set_ped_group(1, GRN);
      set_ped_group(2, GRN);
      set_ped_group(3, GRN);
    }
    prog_step++;
    if (prog_step > 5) {
      prog_step = 1;
    }


    
  } else { // mode = 0, orng blinking
    ped_off();
    set_leds(0, 5*32 + 5*4);
    set_leds(1, 5*32 + 5*4);
    set_leds(2, 5*32 + 5*4);
    set_leds(3, 5*32 + 5*4);
    next_step = 10;
  }
}



void SX1509_print_regs(byte devAdd) {
  //only for debug
  b = SX1509_readB(devAdd, 0x1e);
  Serial.print("RegClock (1E): ");
  print_val(b);
  
  b = SX1509_readB(devAdd, 0x1f);
  Serial.print("RegMisc (1F): ");
  print_val(b);

  b = SX1509_readB(devAdd, 0x21);
  Serial.print("RegLEDDriverEnableA (21): ");
  print_val(b);
  
  b = SX1509_readB(devAdd, 0x20);
  Serial.print("RegLEDDriverEnableB (20): ");
  print_val(b);

  b = SX1509_readB(devAdd, 0x44);
  Serial.print("REG_T_ON_7 (44): ");
  print_val(b);

  b = SX1509_readB(devAdd, 0x45);
  Serial.print("REG_I_ON_7 (45): ");
  print_val(b);

  b = SX1509_readB(devAdd, 0x46);
  Serial.print("REG_OFF_7 (46): ");
  print_val(b);

  b = SX1509_readB(devAdd, 0x47);
  Serial.print("REG_T_RISE_7 (47): ");
  print_val(b);

  b = SX1509_readB(devAdd, 0x48);
  Serial.print("REG_T_FALL_7 (48): ");
  print_val(b);
}

void print_val(byte b) {
  Serial.println(b);
  Serial.println(b, HEX);  
  Serial.println(b, BIN);  
}







void set_leds(byte pole, byte pattern) {
  byte Pleds;
/*
  LEFT:
  0 - off
  1 - RED
  2 - RED+ORNG
  3 - GRN
  4 - ORNG
  5 - ORNG blink
  
  RIGHT:
  0 - off
  1 - RED
  2 - RED+ORNG
  3 - GRN
  4 - ORNG
  5 - ORNG blink
  
  PEDESTRIAN CROSSING WARNING ORANGE LIGHT:
  0 - off
  1 - on
  2 - blink
  
  LLLRRRPP
  LLLx32 + RRRx4 + PP
*/

  //Left
  b = pattern & B11100000;
  b = b >> 5;

  if (b == 0) {
    Pleds = 0;
  } else if (b == 1) {
    Pleds = 16; //red
  } else if (b == 2) {
    Pleds = 16+64; // red+orng
  } else if (b == 3) {
    Pleds = 32; //grn
  } else if (b == 4) {
    Pleds = 64;
  } else if (b == 5) {
    if (blink_state) {
      Pleds = 64;
    } else {
      Pleds = 0;
    }
  }

  //Right
  b = pattern & B00011100;
  b = b >> 2;

  if (b == 0) {
    //Pleds += 0;
  } else if (b == 1) {
    Pleds += 8; //red
  } else if (b == 2) {
    Pleds += 8+1; // red+orng
  } else if (b == 3) {
    Pleds += 2; //grn
  } else if (b == 4) {
    Pleds += 1;
  } else if (b == 5) {
    if (blink_state) {
      Pleds += 1;
    } else {
      //Pleds += 0;
    }
  }

  //Pedestrian warning orng
  b = pattern & B00000011;

  if (b == 0) {
    //Pleds += 0;
  } else if (b == 1) {
    Pleds += 4; //pedestrian
  } else if (b == 2) {// blink
    if (blink_state) {
      Pleds += 4;
    }
  }

  Pleds = ~Pleds;

  pole = constrain(pole, 0, 3);
  Wire.beginTransmission(PCF8574[pole]);
  Wire.write(Pleds);
  Wire.endTransmission();
}


// Function for writing two Bytes to the I2C expander device
void pcf8575_write(uint16_t data)
{
  Wire.beginTransmission(PCF8575);
  Wire.write(lowByte(data));
  Wire.write(highByte(data));
  Wire.endTransmission();
}



//uint16_t pcf8575_read()
byte pcf8575_read()
{
  byte dataReceived[2]; //a two byte array to hold our data
  uint16_t dat_rec;
  Wire.beginTransmission(PCF8575);
  Wire.endTransmission();
  Wire.requestFrom(PCF8575, 2); //request two bytes of data
  if (Wire.available()){
      dataReceived[0] = Wire.read(); //read byte 1
      dataReceived[1] = Wire.read(); //read byte 2
  }

  dat_rec = word(dataReceived[0], dataReceived[1]);
  //return dat_rec;
  return dataReceived[0]; //we use only one half of PCF8575, so only first byte is enough
}


byte SX1509_readB(byte devAdd, byte regAdd)
{
  byte readValue;
  unsigned int timeout = 1000;

  Wire.beginTransmission(devAdd);
  Wire.write(regAdd);
  Wire.endTransmission();
  Wire.requestFrom(devAdd, (byte) 1);

  while ((Wire.available() < 1) && (timeout != 0))
    timeout--;
    
  if (timeout == 0)
    return 0x55;

  readValue = Wire.read();

  return readValue;
}




void SX1509_writeB(byte devAdd, byte regAdd, byte writeVal)
{
  Wire.beginTransmission(devAdd);
  Wire.write(regAdd);
  Wire.write(writeVal);
  Wire.endTransmission();
}


//IO interrupt routine
void PCF_INT_ISR() {
  pcf_int_trigger = true;
}

Schematics

Schematic
Complete schematic
Semafory schematic bkejmahwz0
Schematic of main traffic light with 7 LEDs and PCF8574T
Schematic of main traffic light with 7 LEDs and PCF8574T
Main 1nstwdjvzu
PCB of main traffic light with 7 LEDs and PCF8574T
PCB of main traffic light with 7 LEDs and PCF8574T
Main pcb xbbxjvqnoo
Schematic of pedestrian traffic light with 2 LEDs
Schematic of pedestrian traffic light with 2 LEDs
Pedestrian sch 9zdnldc17f
PCB of pedestrian traffic light with 2 LEDs
PCB of pedestrian traffic light with 2 LEDs
Pedestrian pcb r9s28bo3nj
Schematic of railway traffic light with 3 LEDs
Schematic of railway traffic light with 3 LEDs
Railway sch cmyglpfeqq
PCB of railway traffic light with 3 LEDs
PCB of railway traffic light with 3 LEDs
Railway pcb pgsj5dy6hq

Comments

Similar projects you might like

Arduino Controlled Traffic Lights for Kids Without Delay()

Project showcase by stemmayhem

  • 9,143 views
  • 9 comments
  • 48 respects

Traffic Lights Controlled by Arduino

Project showcase by lmsousa

  • 8,426 views
  • 2 comments
  • 21 respects

Crossroad Traffic Lights (FSM)

Project showcase by Daniel Turner and Carlos Silva

  • 3,433 views
  • 1 comment
  • 6 respects

Traffic Monitor- Monitors traffic on the Go

Project tutorial by Patel Darshil

  • 6,167 views
  • 0 comments
  • 12 respects

Arduino Bike Blink Lights

Project showcase by Sam

  • 6,713 views
  • 3 comments
  • 15 respects

Sensor-Controlled Guard Lights

Project showcase by LIMPINGLIM

  • 2,340 views
  • 1 comment
  • 6 respects
Add projectSign up / Login