Search code examples
c++arduinoserial-portdelayarduino-uno

How to use serial.available() inside a void loop() during delay in an Arduino?


I want to execute a function whenever I send a value through a serial monitor but when a void loop() executing delay function serial. available() function doesn't work, So if I send any values in serial monitor during a periiod of delay function serial.available won't work

#define red 9
#define yellow 8
void setup() {
    // put your setup code here, to run once:
    Serial.begin(9600);
    pinMode(red, OUTPUT);
    pinMode(yellow, OUTPUT);
}
void LightON() {
    digitalWrite(red, HIGH);
    delay(1000);
    digitalWrite(yellow, HIGH);
    delay(1000);
    digitalWrite(red, LOW);
    delay(1000);
}
void LightOff() {
    digitalWrite(red, LOW);
    digitalWrite(yellow, LOW);
}
void loop() {
    // put your main code here, to run repeatedly:
    LightON();
    if (Serial.available())
    {
        LightOff();
    }
}

How can I fix this so when ever I enter a value in serial monitor during a delay period of function Lighton() so Lightoff() is executed?


Solution

  • LightON will always take 3 seconds. To avoid this, things get more complicated. You can use a state machine to keep track of where the blink sequence is at any given time. This also has the benefit of allowing your code to do other things instead of busy-looping or waiting on delay().

    // This class represents the led state machine
    class blinker {
        public:
        // Possible states
        enum {START, RED_ON, YELLOW_ON, RED_OFF, INACTIVE};
    
        // Constructor
        blinker(byte red_pin, byte yellow_pin, long timeout=1000) :
                red(red_pin), yellow(yellow_pin), timeout(timeout), state(INACTIVE) {
            pinMode(red, OUTPUT);
            pinMode(yellow, OUTPUT);
        }
    
        // Start the loop from the beginning
        void start() {
            stop();
            state = START;
        }
        // Stop the loop
        void stop() {
            digitalWrite(red, LOW);
            digitalWrite(yellow, LOW);
            state = INACTIVE;
        }
    
        // Update
        void loop() {
            // Only change if started and time is up
            if (state != INACTIVE && timer_expired()) {
                switch (state) {
                    // Starting over?
                    case START:
                        state = RED_ON;
                        digitalWrite(red,HIGH);
                        break;
                    // Red is on, turn yellow on
                    case RED_ON:
                        state = YELLOW_ON;
                        digitalWrite(yellow,HIGH);
                        break;
                    // Yellow is on, turn red off
                    case YELLOW_ON:
                        state = RED_OFF;
                        digitalWrite(red,LOW);
                        break;
                    // Red is off, start over
                    case RED_OFF:
                        state = START;
                }
            }
        }    
    
        protected:
        byte red, yellow;
        long timeout;
    
        // Returns true when time is up. 
        // Also resets the timer    
        bool timer_expired() {
            if ((millis() - last_time) >= timeout) {
                last_time = millis();
                return true;
            }
            return false;
        }
    };
    
    // Create the state machine
    blinker blinky(9, 8);
    void setup() {
        Serial.begin(9600);
        // Start loop
        blinky.start();
    }
    
    void loop() {
        // Call this everytime
        blinky.loop();
    
        // Stop?
        if (Serial.available()) {  
            blinky.stop();
        }
    
        // Can do other stuff here
    }
    

    I just threw this together. You can improve on it.