MarkU provided a pretty thorough answer with all the whys and hows of
state machines. This is definitely the way to go, and I encourage you to
read that answer very carefully.
I wrote a comment to your question where I suggest dropping delay() in
order for your loop to never block and your program to be responsive to
user input. Now I am writing this answer only to show you how to combine
both ideas, i.e. implement a state machine without delays. I do not
think this code needs any explanation besides those in MarkU's
answer and in the blink
without delay
Arduino example.
// Pinout.
const int red = 13, yellow = 12, green = 11, reset = 10;
void setup() {
pinMode(red, OUTPUT);
pinMode(yellow, OUTPUT);
pinMode(green, OUTPUT);
pinMode(reset, INPUT);
}
void loop() {
static enum { INIT, RED, RED_AMBER, GREEN, GREEN_AMBER } state = INIT;
static uint32_t duration;
static uint32_t last_change;
uint32_t now = millis();
// Sense the reset button.
if (digitalRead(reset) == LOW)
state = INIT;
// Wait at this state for the specified duration.
if (state != INIT && now - last_change < duration)
return;
// Switch to next state.
switch (state) {
case INIT:
case GREEN_AMBER:
state = RED;
duration = 3000;
digitalWrite(green, LOW);
digitalWrite(yellow, LOW);
digitalWrite(red, HIGH);
break;
case RED:
state = RED_AMBER;
duration = 3000;
digitalWrite(green, LOW);
digitalWrite(yellow, HIGH);
digitalWrite(red, HIGH);
break;
case RED_AMBER:
state = GREEN;
duration = 3000;
digitalWrite(green, HIGH);
digitalWrite(yellow, LOW);
digitalWrite(red, LOW);
break;
case GREEN:
state = GREEN_AMBER;
duration = 3000;
digitalWrite(green, HIGH);
digitalWrite(yellow, HIGH);
digitalWrite(red, LOW);
break;
}
last_change = now;
}
I would normally have declared duration as a static const. I kept it
non-const only to follow MarkU's steps and make it easy to have
different durations for different states (really, the green light should
last longer...).