Arduino Traffic Lights

Hello,

I've written some code for some traffic lights with red, amber and green, I'm in the UK so they're running as the UK traffic lights do.

There are two sets of lights that work oppositly to each other, set A and set B.

Whilst the code I've written probably isn't very efficient (noob) it does work perfectly on the breadboard.

What I'd like to do is add in a button that puts both sets of lights to red and then after a delay starts the loop cycle from the start again.

With much googling I haven't been able to find out how to do it.

I've tried putting in an attachInterupt but this does nothing.

Can anybody help?

This is my original code that runs the lights but what do I need to code in to put all to red when the button is pressed?

BTW the pins aren't logical becasue I did it with just red and green and then altered it to add in the amber lights too.

void setup() {
  // put your setup code here, to run once:
 
}
#define ARED 3
#define AAMBER 4
#define AGREEN 2
#define BRED 6
#define BAMBER 8
#define BGREEN 7

void loop() {
  pinMode(ARED,OUTPUT);
  pinMode(AAMBER,OUTPUT);
  pinMode(AGREEN,OUTPUT);
  pinMode(BRED,OUTPUT);
  pinMode(BAMBER,OUTPUT);
  pinMode(BGREEN,OUTPUT);
  digitalWrite(ARED,HIGH);
  digitalWrite(AAMBER,LOW);
  digitalWrite(AGREEN,LOW);
  digitalWrite(BRED,LOW);
  digitalWrite(BAMBER,LOW);
  digitalWrite(BGREEN,HIGH);
  delay(10000);
  digitalWrite(ARED,HIGH);
  digitalWrite(AAMBER,LOW);
  digitalWrite(AGREEN,LOW);
  digitalWrite(BRED,LOW);
  digitalWrite(BAMBER,HIGH);
  digitalWrite(BGREEN,LOW);
  delay(3000);
  digitalWrite(ARED,HIGH);
  digitalWrite(AAMBER,LOW);
  digitalWrite(AGREEN,LOW);
  digitalWrite(BRED,HIGH);
  digitalWrite(BAMBER,LOW);
  digitalWrite(BGREEN,LOW);
  delay(3000);
  digitalWrite(ARED,HIGH);
  digitalWrite(AAMBER,HIGH);
  digitalWrite(AGREEN,LOW);
  digitalWrite(BRED,HIGH);
  digitalWrite(BAMBER,LOW);
  digitalWrite(BGREEN,LOW);
  delay(3000);
  digitalWrite(ARED,LOW);
  digitalWrite(AAMBER,LOW);
  digitalWrite(AGREEN,HIGH);
  digitalWrite(BRED,HIGH);
  digitalWrite(BAMBER,LOW);
  digitalWrite(BGREEN,LOW);
  delay(10000);
  digitalWrite(ARED,LOW);
  digitalWrite(AAMBER,HIGH);
  digitalWrite(AGREEN,LOW);
  digitalWrite(BRED,HIGH);
  digitalWrite(BAMBER,LOW);
  digitalWrite(BGREEN,LOW);
  delay(3000);
  digitalWrite(ARED,HIGH);
  digitalWrite(AAMBER,LOW);
  digitalWrite(AGREEN,LOW);
  digitalWrite(BRED,HIGH);
  digitalWrite(BAMBER,LOW);
  digitalWrite(BGREEN,LOW);
  delay(3000);
  digitalWrite(ARED,HIGH);
  digitalWrite(AAMBER,LOW);
  digitalWrite(AGREEN,LOW);
  digitalWrite(BRED,HIGH);
  digitalWrite(BAMBER,HIGH);
  digitalWrite(BGREEN,LOW);
  delay(3000);

  // put your main code here, to run repeatedly:

}

Welcome to the forum

If you want the button press to be recognised at any point in the sequence, which I assume you do, then you will need to get rid of the delay()s because during the delay() nothing else will happen

I suggest that you start by searching the forum for posts about traffic lights as what you want to do is very commonly asked

what about configuring the pins in setup(), turning on the red ones and adding a delay for how long you want them in this state. A reset would restart this again

Copy these lines below to void setup.
You only need to configure the Pins as OUTPUT once.

Look in the Arduino ide Examples > Digital > Button.

But first make a plan with the Delays as mentioned by @UKHeliBob.

here's another approach for you to consider that is easier to read

// UK traffic light

enum {                   ARED, AAMBER, AGREEN,  BRED, BAMBER, BGREEN };
const byte PinLed [] = {   3,      4,      2,     6,      8,      7 };
const int  Nled      = sizeof (PinLed);

// -----------------------------------------------------------------------------
const unsigned long Sec10 =  10000;
const unsigned long Sec3  =   3000;

enum {   _ = LOW, Off = LOW, On = HIGH };

struct Seq {
    const byte     led [Nled];
    unsigned long  msecDelay;
} seq [] = {
//         ARED   AAMBER   AGREEN     BRED   BAMBER   BGREEN
    { {      On,       _,       _,       _,       _,      On, }, Sec10 },
    { {      On,       _,       _,       _,      On,       _, }, Sec3  },
    { {      On,       _,       _,      On,       _,       _, }, Sec3  },
    { {      On,      On,       _,      On,       _,       _, }, Sec3  },

    { {       _,       _,      On,      On,       _,       _, }, Sec10 },
    { {       _,      On,       _,      On,       _,       _, }, Sec3  },
    { {      On,       _,       _,      On,       _,       _, }, Sec3  },
    { {      On,       _,       _,      On,      On,       _, }, Sec3  },
};
const int Nseq = sizeof(seq)/sizeof(Seq);

int idx;

// -------------------------------------
void loop ()
{
    for (int n = 0; n < Nled; n++)
        digitalWrite (PinLed [n], seq [idx].led [n]);

    delay (seq [idx].msecDelay);

    if (Nseq <= ++idx)
        idx = 0;
}

// -----------------------------------------------------------------------------
void setup()
{
    for (int n = 0; n < Nled; n++)  {
        pinMode      (PinLed [n], OUTPUT);
        digitalWrite (PinLed [n], Off);
    }

    digitalWrite (PinLed [ARED], On);
    digitalWrite (PinLed [BRED], On);

    delay (Sec10);
}

Thanks, so with a bit of looking around I've found I need to use unsigned long instead of delay, is that correct?

I'm very lost with how I set that up though, even with reading other people's code on it.

Thanks I'll look into that.

Thank you, I've had a look at button set up in the examples and ran a basic setup of that which worked. I'm a bit lost with how to enter that into my code for the traffic lights.

Thanks, I coppied that into a new sketch and ran it on the board and they work as intended. I can get my head around how that's working by looking at the code but there's no way I'd be able to write that with an end result of it working.

I just need to get my head around how to add in a button to the code, that's the bit I'm struggling with.

The reason that I suggested that you search for other topics on the subject is that it will give you examples of how to use millis() for non blocking timing that would allow the button press to be detected whilst the timing periods are occuring

Whilst it is true that millis() should be used with unsigned long variables there is more to it than that

Maybe start by looking at Using millis() for timing. A beginners guide

Once you have got the hang of using millis() for timing then look into state machines. The sketch will be in one of several states displaying LED combinations and millis() is used for the timing. Button presses are detected and acted upon in the loop() function that is running freely dues to non blocking timing being used

I really do urge you to search the forum for previous topics on traffic lights

just wire a button to the reset pin

the delay in the code i posted could be replaced by a non-blocking timer allowing a check for a button each iteration of loop()

Here is a sketch showing how to use millis() for non blocking timing using millis()

I have deliberately not included all of the states, have only used a single traffic light output and there is no button code. Start by adding the missing state or states to control the LEDs and as many sets of LEDs as you like. Once that works like your current sketch add the button reading and actions where indicated.

HINT : Adding an ALL_RED state might help

enum states
{
    RED,
    RED_AMBER,
    GREEN,
    AMBER
};

states currentState = RED;

byte redLedPin = 3;
byte amberLedPin = 5;
byte greenLedPin = 6;

unsigned long currentTime = millis();
unsigned long stateStart = currentTime;

unsigned long redPeriod = 5000;
unsigned long redAmberPeriod = 1000;
unsigned long greenPeriod = 5000;
unsigned long amberPeriod = 1000;

void setup()
{
    Serial.begin(115200);
    pinMode(redLedPin, OUTPUT);
    pinMode(amberLedPin, OUTPUT);
    pinMode(greenLedPin, OUTPUT);
}

void loop()
{
    currentTime = millis();
    switch (currentState)
    {
        case RED:
            digitalWrite(redLedPin, HIGH);
            if (currentTime - stateStart >= redPeriod)
            {
                allOff();
                currentState = RED_AMBER;
                stateStart = currentTime;
            }
            break;
        case RED_AMBER:
            digitalWrite(redLedPin, HIGH);
            digitalWrite(amberLedPin, HIGH);
            if (currentTime - stateStart >= redAmberPeriod)
            {
                allOff();
                currentState = GREEN;
                stateStart = currentTime;
            }
            break;
        case GREEN:
            digitalWrite(greenLedPin, HIGH);
            if (currentTime - stateStart >= greenPeriod)
            {
                allOff();
                currentState = AMBER;
                stateStart = currentTime;
            }
            break;
    }
		//button reading and action code goes here
}

void allOff()
{
    digitalWrite(redLedPin, LOW);
    digitalWrite(amberLedPin, LOW);
    digitalWrite(greenLedPin, LOW);
}

here's a non-blocking version of loop() for code in post #5

unsigned long msecDelay;
unsigned long msec0;
unsigned long msec;

void trafficLight ()
{
    if (msec - msec0 < msecDelay)
        return;
    msec0 = msec;

    for (int n = 0; n < Nled; n++)
        digitalWrite (PinLed [n], seq [idx].led [n]);

    msecDelay = seq [idx].msecDelay;

    if (Nseq <= ++idx)
        idx = 0;
}

// -----------------------------------------------------------------------------
void loop ()
{
    msec = millis ();

    trafficLight ();
}

you have study other programs to learn how to write efficient code.

To read a button, you will want to know how to wire a button: https://arduinogetstarted.com/tutorials/arduino-button

Next, you will want to debounce the button to get a true reading: https://arduinogetstarted.com/tutorials/arduino-button-debounce

Then, you will want to ensure a held button is not counted as many presses: : https://arduinogetstarted.com/tutorials/arduino-button-long-press-short-press

The State Machine (mentioned above) will allow you to change light patterns using your new button knowledge.

Why the need to debounce in this application ?

@jimbo84 has not indicated what should happen if the button is held down but certainly did not mention that it should only be a short press

I would favour making the sketch as simple as possible in the first instance

Debounce helps reduce button ringing, so one press does not look like many. A bad situation in traffic lights.

Ensure any button press is "one press" and not many presses (or none), just like the walk buttons in crosswalks.

The button could bounce as many times as it likes in the requirements described by @jimbo84 and it would not matter a jot as a button press starts a wait period. If the wait period starts several times within a few milliseconds due to contact bounce then does it matter ?

I agree with you in principle, but as we don't know what is required we cannot suggest a solution

Implementing debouncing and single press recognition are very worthy ambitions but if they are not necessary then they are best not implmented

Yes, i agree with not over-do something, following your Post #12... if the sequence operates on first-syllable detection and has one state direction. My posts are responding to the sketch in Post #1 and the concern in Post #6, I imagine a follow up sketch of "if button is pressed/not-pressed"... and offering how-to links to understand/avoid what might happen. Through other descriptions and methods (for example, what is "ringing" in a button press), the sketches dumped here will be better understood.