Skip to main content
replaced http://arduino.stackexchange.com/ with https://arduino.stackexchange.com/
Source Link

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 answerMarkU's answer and in the Blink without delay Arduino example.

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.

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.

pinMode(reset, INPUT_PULLUP); as per Nick Gammon's comment.
Source Link
Edgar Bonet
  • 45.2k
  • 4
  • 42
  • 81

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 blinkBlink 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,  INPUTINPUT_PULLUP);  // use internal pull-up
}

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 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 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_PULLUP);  // use internal pull-up
}

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;
}
Source Link
Edgar Bonet
  • 45.2k
  • 4
  • 42
  • 81

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...).