Trying to implement Pushbutton detect with Millis() function

Hello,

I have been working on this project for a few days and quickly realized that the delay() function has its limits, especially considering the code relies on information being displayed on a 16x2 LCD for long periods of time (10 seconds). This means that the "read" button state function in the code has to wait upwards of 10-15 seconds before it is detected and any action is subsequently executed.

Basically, This code is intended to control a servo motor connected to a grille shutter mechanism in a car. The servo motor opens and closes the grille shutter at different angles to control the airflow and improve the aerodynamics of the vehicle. Therefore, the button state detection has to be snappy.

I researched about millis() function and how it can help the code run parallel processing instead of only sequential events. I have implemented the millis() function into the code but the delay has not changed. Just wondering if I am implementing this the wrong way and if there is a better alternative to tackle the issue.

Overall, the codes is fully functional with my prototype. The only issue is the pushbutton delay.

Code :

#include <Servo.h>
#include <ezButton.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);                          // I2C address 0x27, 16 columns and 2 rows

byte arrow[8] = {                                            //Custom arrow LCD character
  0b00100,
  0b01110,
  0b11111,
  0b00100,
  0b00100,
  0b00100,
  0b00100,
  0b00100
};

// Constants
const int BUTTON_PIN = 7;                                     // Arduino pin connected to button's pin
const int SERVO_PIN = 9;                                      // Arduino pin connected to servo motor's pin
const int LED_PIN = 2;                                        // Green LED connected to digital pin 2
const int LED2_PIN = 3;                                        // Red LED connected to digital pin 3
const int POS_SWITCH = 4;                                     // State of position switch (HIGH = SHUT)
const int POS_ERROR = 8;                                      // State of both position switches (HIGH = error)
const unsigned long buttonPeriod = 100;
Servo servo;                                                  // Create servo object to control a servo

// Variables
int angle = 0;                                                // The current angle of servo motor
int posSwitchState = HIGH;                                    // Variable to store the read value
int posErrorState = LOW;                                      // Variable to store the read value
int buttonState = LOW;
unsigned long currentMillis = 0;
unsigned long buttonPreviousMillis = 0;

  
void Button() {

  if (currentMillis - buttonPreviousMillis >= buttonPeriod) {
    buttonPreviousMillis = currentMillis;

    if (digitalRead(BUTTON_PIN) == LOW) {
      buttonState = HIGH;
    } else {
      buttonState = LOW;
    }

    if (buttonState == HIGH) {
      digitalWrite(LED_PIN, HIGH);                           // Turn the LED on (HIGH is the voltage level)
      delay(500);                                            // Wait for a second
      digitalWrite(LED_PIN, LOW);                            // Turn the LED off (LOW is the voltage level)
      delay(500);                                            // Wait for 0.5 seconds

      // Change the angle of the servo motor
      if (angle == 0)
        angle = 45;
      else if (angle == 45)
        angle = 90;
      else
        angle = 0;

      servo.write(angle);                                     // Control the servo motor according to the angle
    }
  }
}


void setup() {
  
  Serial.begin(9600);                                       // Initialize serial
  pinMode(BUTTON_PIN, INPUT_PULLUP);                        // Set Arduino pin to input pull-up mode
  pinMode(LED_PIN, OUTPUT);
  pinMode(LED2_PIN, OUTPUT);
  pinMode(POS_SWITCH, INPUT);                               // Shutter grille status (open or shut)
  pinMode(POS_ERROR, INPUT);                                // Detects position error
  servo.attach(SERVO_PIN);                                  // Attach the servo on pin 9 to the servo object
  servo.write(angle);
  lcd.init();                                               // Initialize the LCD
  lcd.backlight();                                        
  lcd.createChar(0, arrow);                                 // create a new custom character (index 1)


  lcd.setCursor(0, 0);                
  lcd.print("   HONDA CR-Z   ");      
  lcd.setCursor(1, 1);                
  lcd.print("    WELCOME  ");            
  delay(5000);

}

void loop() {
  
  currentMillis = millis();                                       // get the current time

  Button();                                                 // call the Button function

  posSwitchState = digitalRead(POS_SWITCH);                 // Read the input pin

  if (posSwitchState == HIGH) {
    lcd.clear();                        
    lcd.setCursor(0, 0);                
    lcd.print(" GRILLE SHUTTER");      
    lcd.setCursor(2, 1);                
    lcd.print("  [ SHUT ]");            
    delay(5000);
    lcd.clear();                        
    lcd.setCursor(0, 0);                
    lcd.print("AERODYNAMIC MODE");      
    lcd.setCursor(2, 1);                
    lcd.print("[ LOW DRAG ]");          
    delay(5000);

  } else {
  }

  if (angle == 45) {
    lcd.clear();                        
    lcd.setCursor(0, 0);                
    lcd.print(" GRILLE SHUTTER");       
    lcd.setCursor(0, 1);                
    lcd.print("  [ 50 % OPEN ] ");      
    delay(5000);
    lcd.clear();                        
    lcd.setCursor(0, 0);                
    lcd.print(" COOLING MODE 1");       
    lcd.setCursor(2, 1);                
    lcd.print("[     DRAG ] ");          
    lcd.setCursor(4, 1);                
    lcd.write(arrow);             
    lcd.setCursor(5, 1);                
    lcd.write(arrow);
    lcd.setCursor(6, 1);                
    lcd.write(arrow);
    delay(5000);
  }

  else if (angle == 90) {
    lcd.clear();                        
    lcd.setCursor(0, 0);                
    lcd.print(" GRILLE SHUTTER");       
    lcd.setCursor(0, 1);                
    lcd.print(" [ 100 % OPEN ] ");      
    delay(5000);
    lcd.clear();                        
    lcd.setCursor(0, 0);                
    lcd.print(" COOLING MODE 2");      
    lcd.setCursor(0, 1);                
    lcd.print(" [       DRAG ] ");  
    lcd.setCursor(3, 1);                
    lcd.write(arrow);             
    lcd.setCursor(4, 1);                
    lcd.write(arrow);
    lcd.setCursor(5, 1);                
    lcd.write(arrow);
    lcd.setCursor(6, 1);                
    lcd.write(arrow);
    lcd.setCursor(7, 1);                
    lcd.write(arrow);
    delay(5000);
  } else {
  }

  posErrorState = digitalRead(POS_ERROR);                         // Read the input pin

  if (posErrorState == HIGH && ((angle > 5 && angle < 45) || (angle > 45 && angle < 75))) {
    lcd.clear();                        
    lcd.setCursor(0, 0);                
    lcd.print(" GRILLE SHUTTER");       
    lcd.setCursor(1, 1);                
    lcd.print("[ POS. ERROR ]");        
    delay(5000);
    lcd.clear();                        
    lcd.setCursor(0, 0);                
    lcd.print(" CHECK SYSTEM !");       
    lcd.setCursor(0, 1);                 
    lcd.print("[ POSSIBLE JAM ]");                         
    delay(5000);
    digitalWrite(LED2_PIN, HIGH);
    digitalWrite(SERVO_PIN, LOW);
  } else {
  }
}

Thank you !

Look at the topics "Do several things at the same time", and "blink without delay".

@cigar007 - I can't test it just now, but your button handling looks plausible, even with the short-ish delays, both of which would serve as well if they were reduced to, say, 100 milliseconds.

As @Delta_G points out, you have to lose the large delays elsewhere.

To make something happen every seven seconds in a non-blocking manner, the same pattern you are using in your Button() function can be exploited, right in the loop, like

// a global variable to remember the last time we LCDed
unsigned long lastLCDTime;



// then later inside the loop

  if (currentMillis - lastLCDTime > 7000) {

    Serial.println("this gets printed every 7000 milliseconds");

// and anything you put in this block will also run only every 7000 milliseconds

    lastLCDTime =currentMillis;
  }

I usually put the update to the timing variable at the end of the block, but anywhere in there is OK like you have it right away in Button().

When I am in the lab I can try the rest of your code.

Another way to handle it is, still losing the looooong delays, is to use the same logic as in Button() to only print to the LCD when the angle is different, not alla time. If you worked out the Button logic, this should be something you could take a stab at.

HTH

a7

That makes sense. I will give it a try. Still learning the millis() concept and its implementation.

Much appreciated !

OK, your Button() function only looks plausible. I tested this briefly - look at the difference to your version.

Gotta jet, sry - the food bell has just rung.

void Button() {
  static byte oldButtonState;

  if (currentMillis - buttonPreviousMillis >= buttonPeriod) {
    buttonPreviousMillis = currentMillis;

    byte buttonNow = digitalRead(BUTTON_PIN);

    if (buttonNow != oldButtonState) {

      oldButtonState = buttonNow;

      if (buttonNow == LOW) {
        digitalWrite(LED_PIN, HIGH);                           // Turn the LED on (HIGH is the voltage level)
        delay(100);                                            // Wait for a second
        digitalWrite(LED_PIN, LOW);                            // Turn the LED off (LOW is the voltage level)
        delay(100);                                            // Wait for 0.5 seconds

        // Change the angle of the servo motor
        if (angle == 0)
          angle = 45;
        else if (angle == 45)
          angle = 90;
        else
          angle = 0;

        servo.write(angle);                                     // Control the servo motor according to the angle
      }
    }
  }
}

HTH

a7