How to avoid delay() OUTSIDE the loop

If you look to my program, I use a delay() outside the loop, but how can I avoid this? I don't want to pause the whole code (and I thought that's what a delay does...)

void setup() { 
   pinMode (6, INPUT); //1 button
 
    pinMode (2, OUTPUT); //LED 1
    pinMode (3, OUTPUT); //LED 2
    pinMode (4, OUTPUT); //LED 3
    pinMode (5, OUTPUT); //LED 4
}

unsigned long but1LastPress = 0;  
const int buttonPressDelay = 600; 

void loop() {
  //Read the state of the button
  int but1read = digitalRead(6);

   //Button 1 is pressed
    if ((but1read == HIGH) && ((millis() - but1LastPress) > buttonPressDelay)) {
      but1LastPress = millis();
      doButton1();
    }else if ((but1read == LOW) && ((millis() - but1LastPress) > buttonPressDelay ))
     but1LastPress = 0;  // reset
}
  
void doButton1() { 
  Blink3Times();
}

void Blink3Times(){
 for (int i=0; i < 3; i++){
   for (int j=2; i <= 5; i++){
     digitalWrite(j,LOW); //turn off the LEDS
   }
   delay(1000); //Wait 1 second
   for (int j=2; i <= 5; i++){
     digitalWrite(j,HIGH); //turn on the LEDS
   }
   delay(1000); //Wait 1 second
   }
}

Look at the demo several things at a time. In it there is very little code in loop() - most of it is outside. And delay() is not used anywhere.

...R

Thanks for the respond, it seems to work :slight_smile: but can I add something so you can call the function updateLedState and choose how long they will blink?

const int Led1 = 2;      // the pin numbers for the LEDs
const int Led2 = 3;
const int Led3 = 4;
const int Led4 = 5;

const int Led1Interval = 150; // number of millisecs between blinks
const int Led2Interval= 2500;
const int Led3Interval = 4500;
const int Led4Interval = 4500;


const int blinkDuration = 150; // number of millisecs that Led's are on - all three leds use this

byte Led1State = LOW;             // used to record whether the LEDs are on or off
byte Led2State = LOW;           //   LOW = off
byte Led3State = LOW;
byte Led4State = LOW;

unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousOnBoardLedMillis = 0;   // will store last time the LED was updated
unsigned long previousLed_A_Millis = 0;
unsigned long previousLed_B_Millis = 0;


void setup() {
 Serial.begin(9600);
 Serial.println("Starting blink without delay test");  // so we know what sketch is running
 
 // set the Led pins as output:
 pinMode(Led1, OUTPUT);
 pinMode(Led2, OUTPUT);
 pinMode(Led3, OUTPUT);
 pinMode(Led4, OUTPUT);
}



void loop() {

     // Notice that none of the action happens in loop() apart from reading millis()
     //   it just calls the functions that have the action code

 currentMillis = millis();   // capture the latest value of millis()
                             //   this is equivalent to noting the time from a clock
                             //   use the same time for all LED flashes to keep them synchronized
 blinkLEDs();

}

//========

void updateLedState() {

 if (Led1State == LOW) {
         // if the Led is off, we must wait for the interval to expire before turning it on
   if (currentMillis - previousOnBoardLedMillis >= Led1Interval) {
         // time is up, so change the state to HIGH
      Led1State = HIGH;
         // and save the time when we made the change
      previousOnBoardLedMillis += Led1Interval;
         // NOTE: The previous line could alternatively be
         //              previousOnBoardLedMillis = currentMillis
         //        which is the style used in the BlinkWithoutDelay example sketch
         //        Adding on the interval is a better way to ensure that succesive periods are identical

   }
 }
 else {  // i.e. if onBoardLedState is HIGH
 
         // if the Led is on, we must wait for the duration to expire before turning it off
   if (currentMillis - previousOnBoardLedMillis >= blinkDuration) {
         // time is up, so change the state to LOW
      Led1State = LOW;
         // and save the time when we made the change
      previousOnBoardLedMillis += blinkDuration;
   } 
 }
}

void blinkLEDs() {
     // this is the code that actually switches the LEDs on and off
 updateLedState();

 digitalWrite(Led1, Led1State);
 //digitalWrite(led_A_Pin, led_A_State);
 //digitalWrite(led_B_Pin, led_B_State);
 //digitalWrite(buttonLed_Pin, buttonLed_State);
}

BramWerbrouck:
Thanks for the respond, it seems to work :slight_smile: but can I add something so you can call the function updateLedState and choose how long they will blink?

Yes just change the value of ledInterval. Or pass a value as a parameter - but then you need to change the function so it uses the parameter rather than the global variable ledInterval.

...R

If I change te value of ledInterval, it just keeps blinking... Faster or slower, but it is still the same

I thought I could pass the time to blink as a variabele to the function something like this:

Blink(10000);

But what do I have to do with this in the function so it wpild stop blinking after 10 seconds?

one way could be something like this:

#define FREQUENCY 100

boolean goBlink = true;
boolean blinkState = false;
unsigned long startTime;

void setup()
{
  Serial.begin(115200);
  pinMode(13, OUTPUT);
}

void loop()
{
  if (goBlink)
  {
    startTime = millis();
    blinkState = true;
    goBlink = false;
    Serial.println("Go blink");
  }
  
  if (blinkState)
  {
    blinkExample(10000);
  }
}

void blinkExample(unsigned long duration)
{
  
  static int lastCount;
  if (millis() - startTime <= duration)
  {
    int count = (millis() - startTime) / FREQUENCY;
    if ( count != lastCount)
    {
      digitalWrite(13, !digitalRead(13));
      Serial.println(count);
    }
    lastCount = count;
  }
  else
  {
    digitalWrite(13, LOW);
    blinkState = false;
  }
}

BramWerbrouck:
If I change te value of ledInterval, it just keeps blinking... Faster or slower, but it is still the same

I thought that is what you were asking for.

If you want the blinking to operate for 10 seconds (say) then the simplest way is to create a boolean variable called blinkInProgress and modify the blink function so it will work when that variable is true - but not if it is false.

When you want the blinking to start change the value to true and start another (separate) millis() timer. When that expires it will change the value back to false.

...R

@BulldogLowell:

I tried the code and when I past yours in a new sketch, everything works fine, but when I want to add it to my code, it just do nothing...

boolean blinkState = false;
unsigned long startTime;
void blinkLEDs(unsigned long duration, int FREQUENCY)
{
  
 static int lastCount = ;
  if (millis() - startTime <= duration)
  {
    int count = (millis() - startTime) / FREQUENCY;
    if ( count != lastCount)
    {
      digitalWrite(2, !digitalRead(2));
      Serial.println(count);
    }
    lastCount = count;
  }  else  {
    digitalWrite(2, LOW);
    blinkState = false;
  }
}
void longpress() {
  unsigned long start_hold = millis();   // mark the time
  Serial.println("Please keep on pressing for 5 seconds..");
    while (sw_state == LOW) {
      if (modus == 1) break; //NO long press in mode_Seconds
      sw_state = digitalRead(hold_pin);      // read input value
      if ((millis() - start_hold) >= HOLD_DELAY){      // for longer than HOLD_DELAY
        Serial.println("LONG PRESS ACCEPTED");
          if (modus == 0){
            iLang = iLang +1;
            int iSize = sizeof(lang) / sizeof(lang[0]); //determine the length of the array (buttons)
            if (iLang >= iSize) iLang = 0;
            EEPROM.write(0, iLang); //Store the chosen language onboard
            Serial.print("Language changed to "); 
            Serial.println(lang[iLang]);
           
            startTime = millis();
            blinkState = true;
            if (blinkState){
                blinkLEDs(3000,250);
            }
            ShowLang(iLang); 
            }
                    
          //Give the position of the Hold_pin number in the array
          int len = sizeof(buttons) / sizeof(buttons[0]); //determine the length of the array (buttons)
          int wantedval = hold_pin;
          int wantedpos;
          for (int i=0; i<len; i++) {
            if (wantedval = buttons[i]) {
            wantedpos = i; //wantedpos = the position of the HOLD button in the arrray of the buttons
            break; //break the loop after initialized
            }
         }
        justpressed[wantedpos] = 0; //Reset the SHORT PRESS for the hold_pin
        break; //break the loop after initialized
        
      }
    }

}

Ok, I found something:

I changed

void longpress() {
  unsigned long start_hold = millis();   // mark the time
  Serial.println("Please keep on pressing for 5 seconds..");
    while (sw_state == LOW) {
      if (modus == 1) break; //NO long press in mode_Seconds
      sw_state = digitalRead(hold_pin);      // read input value
      if ((millis() - start_hold) >= HOLD_DELAY){      // for longer than HOLD_DELAY
        Serial.println("LONG PRESS ACCEPTED");
          if (modus == 0){
            iLang = iLang +1;
            int iSize = sizeof(lang) / sizeof(lang[0]); //determine the length of the array (buttons)
            if (iLang >= iSize) iLang = 0;
            EEPROM.write(0, iLang); //Store the chosen language onboard
            Serial.print("Language changed to "); 
            Serial.println(lang[iLang]);
           
            startTime = millis();
            blinkState = true;
            if (blinkState){
                blinkLEDs(3000);
            }
            ShowLang(iLang); 
            }
                    
          //Give the position of the Hold_pin number in the array
          int len = sizeof(buttons) / sizeof(buttons[0]); //determine the length of the array (buttons)
          int wantedval = hold_pin;
          int wantedpos;
          for (int i=0; i<len; i++) {
            if (wantedval = buttons[i]) {
            wantedpos = i; //wantedpos = the position of the HOLD button in the arrray of the buttons
            break; //break the loop after initialized
            }
         }
        justpressed[wantedpos] = 0; //Reset the SHORT PRESS for the hold_pin
        break; //break the loop after initialized
        
      }
    }
}

into

void longpress() {
  unsigned long start_hold = millis();   // mark the time
  Serial.println("Please keep on pressing for 5 seconds..");
    while (sw_state == LOW) {
      if (modus == 1) break; //NO long press in mode_Seconds
      sw_state = digitalRead(hold_pin);      // read input value
      if ((millis() - start_hold) >= HOLD_DELAY){      // for longer than HOLD_DELAY
        Serial.println("LONG PRESS ACCEPTED");
            startTime = millis();
            blinkState = true;
            while(blinkState == true) blinkLEDs(3000);


          if (modus == 0){
            iLang = iLang +1;
            int iSize = sizeof(lang) / sizeof(lang[0]); //determine the length of the array (buttons)
            if (iLang >= iSize) iLang = 0;
            EEPROM.write(0, iLang); //Store the chosen language onboard
            Serial.print("Language changed to "); 
            Serial.println(lang[iLang]);
           
            
            ShowLang(iLang); 
            }
                    
          //Give the position of the Hold_pin number in the array
          int len = sizeof(buttons) / sizeof(buttons[0]); //determine the length of the array (buttons)
          int wantedval = hold_pin;
          int wantedpos;
          for (int i=0; i<len; i++) {
            if (wantedval = buttons[i]) {
            wantedpos = i; //wantedpos = the position of the HOLD button in the arrray of the buttons
            break; //break the loop after initialized
            }
         }
        justpressed[wantedpos] = 0; //Reset the SHORT PRESS for the hold_pin
        break; //break the loop after initialized   
      }
    }
}

So I changed the IF into a WHILE statement
now it works, but I have another issue:
It is not 1 LED that has to blink but 4! so I added in the blinkState()-function this:

void blinkLEDs(unsigned long duration){
 Clear_CornerLEDS();
 static int lastCount;
  if (millis() - startTime <= duration){
    int count = (millis() - startTime) / FREQUENCY;
  
    if (count != lastCount){
      digitalWrite(2, !digitalRead(2));
      digitalWrite(3, !digitalRead(3));
      digitalWrite(4, !digitalRead(4));
      digitalWrite(5, !digitalRead(5));
      Serial.println(count);
    }
    lastCount = count;
   } else {
    digitalWrite(2, LOW);
    blinkState = false;
  }
}

What I see is this: they blink very "weak", not on "full power" if you understand what I mean, is there any solution?

BramWerbrouck:
What I see is this: they blink very "weak", not on "full power" if you understand what I mean, is there any solution?

Is pinMode set correctly?

Are the ON periods too short?

...R

Robin2:
Are the ON periods too short?

It think this is the problem, when I add a delay(10), it works, but then again I have the delay function which I want to avoid...

BramWerbrouck:
It think this is the problem, when I add a delay(10), it works, but then again I have the delay function which I want to avoid...

You really need to post your entire sketch, even if that entire sketch is chopped down to do just this part.

This is what I have so far...

//blink LEDs
boolean blinkState = false;
unsigned long startTime;

//Corner LEDs (minutes)
const byte LEDs[] = {2, 3, 4, 5};
const int NUMLEDS = sizeof(LEDs);

// Here is where we define the buttons that we'll use. button "6" is the first, button "9" is the 4th, etc
const byte buttons[] = {6, 7, 8, 9};
#define DEBOUNCE 10  // button debouncer, how many ms to debounce, 5+ ms is usually plenty
// This handy macro lets us determine how big the array up above is, by checking the size
const int NUMBUTTONS = sizeof(buttons);
//track if a button is just pressed, just released, or 'currently pressed' 
byte pressed[NUMBUTTONS], justpressed[NUMBUTTONS], justreleased[NUMBUTTONS];
byte previous_keystate[NUMBUTTONS], current_keystate[NUMBUTTONS];

const int HOLD_DELAY = 5000;  // Sets the hold delay for the LONG PRESS 
int sw_state = HIGH;
const int hold_pin  = 6;  //pin number for the LONG PRESS


void setup() { 

  //SETUP PINS
  for (byte i=0; i< NUMBUTTONS; i++) {
    pinMode (buttons[i], INPUT_PULLUP);
  }
  
  for (byte i=0; i< NUMLEDS; i++) {
    pinMode (LEDs[i], OUTPUT); 
  }

}
void loop() {

 byte thisSwitch=thisSwitch_justPressed();
  
  switch(thisSwitch){  
  case 0:
    Serial.println("switch 1 just pressed");
    doButton1();
    break;
  case 1:    
    Serial.println("switch 2 just pressed");
    doButton2();
    break;
  case 2: 
    Serial.println("switch 3 just pressed");
    doButton3();
    break;
  case 3: 
    Serial.println("switch 4 just pressed");
    doButton4();
    break; 
  }  
}


void Clear_CornerLEDS(){
  for (int i=0;i<=NUMLEDS;i++)
  digitalWrite(LEDs[i],LOW);
}

void Light_CornerLEDS(){
  for (int i=0;i<=NUMLEDS;i++)
  digitalWrite(LEDs[i],HIGH);
}



void doButton1() { 
  //do something
}

void doButton2() {
  //do something
}

void doButton3() { 
  //do something
}

void doButton4() {
  //do something
}

void check_switches()
{
  static byte previousstate[NUMBUTTONS];
  static byte currentstate[NUMBUTTONS];
    static long lasttime;

  byte index;
  if (millis() < lasttime) {
    // we wrapped around, lets just try again
    lasttime = millis();
  }
  if ((lasttime + DEBOUNCE) > millis()) {
    // not enough time has passed to debounce
    return; 
  }
  // ok we have waited DEBOUNCE milliseconds, lets reset the timer
  lasttime = millis();
  for (index = 0; index < NUMBUTTONS; index++) {
    justpressed[index] = 0;       //when we start, we clear out the "just" indicators
    justreleased[index] = 0;
    
    currentstate[index] = digitalRead(buttons[index]);   //read the button
    if (currentstate[index] == previousstate[index]) {
      if ((pressed[index] == LOW) && (currentstate[index] == LOW)) {
        // just pressed
        justpressed[index] = 1;
        sw_state = digitalRead(hold_pin);
        //Long press??
        if (sw_state==LOW) longpress();
      }
      else if ((pressed[index] == HIGH) && (currentstate[index] == HIGH)) {
        justreleased[index] = 1; // just released

      }
      pressed[index] = !currentstate[index];  //remember, digital HIGH means NOT pressed
    }
    previousstate[index] = currentstate[index]; //keep a running tally of the buttons
  }
}
 
byte thisSwitch_justPressed() {
  byte thisSwitch = 255;
  check_switches();  //check the switches & get the current state
  for (byte i = 0; i < NUMBUTTONS; i++) {
    current_keystate[i]=justpressed[i];
    if (current_keystate[i] != previous_keystate[i]) {
      if (current_keystate[i]) thisSwitch=i;
    }
    previous_keystate[i]=current_keystate[i];
  } 
    return thisSwitch; //return the index number (of the array) of which button is pressed
}

void longpress() {
  unsigned long start_hold = millis();   // mark the time
  Serial.println("Please keep on pressing for 5 seconds..");
  //if (modus == 0){
    while (sw_state == LOW) {
      sw_state = digitalRead(hold_pin);      // read input value
      if ((millis() - start_hold) >= HOLD_DELAY){      // for longer than HOLD_DELAY
        //initialize_is_running = true;  // keep loop running even though reset_settings_pin  is low
        Serial.println("LONG PRESS ACCEPTED");
        
        startTime = millis();
        blinkState = true;
        while(blinkState == true) blinkLEDs(1500,100);
           
        justpressed[6] = 0; //Reset the SHORT PRESS for the hold_pin
        break; //break the loop after initialized
      }
    }  
}

void blinkLEDs(unsigned long duration, int FREQUENCY){
 Clear_CornerLEDS();
 static int lastCount;
  if (millis() - startTime <= duration){
    int count = (millis() - startTime) / FREQUENCY;
  
    if (count != lastCount){


        Light_CornerLEDS();
        delay(10); 
    }
    lastCount = count;
   } else {
    blinkState = false;
  }
}

But still there is a DELAY in it...

try to simplify your base code with a Bounce2.h library:

#include <Bounce2.h>

#define NUMBER_OF_SWITCHES 4

Bounce debouncer[NUMBER_OF_SWITCHES];

int oldValue[NUMBER_OF_SWITCHES];
byte switchPin[NUMBER_OF_SWITCHES] = {2,3,4,5}; //<<<<<<<<<<< set your switch pins here


void setup()  
{  
  for (int i = 0; i < NUMBER_OF_SWITCHES; i++)
  {
    pinMode(switchPin[i],INPUT_PULLUP);
    debouncer[i] = Bounce();
    debouncer[i].attach(switchPin[i]);
    debouncer[i].interval(5);
  }
}
//
void loop() 
{
  for (int i = 0; i < NUMBER_OF_SWITCHES; i++)
  {
    debouncer[i].update();
    int value = debouncer[i].read();
    if (value != oldValue[i]) 
    {
      doSomethingWith(i);
    }
    oldValue[i] = value;
  }
} 

void doSomethingWith(int pinNumber) // your pin was triggered, so do something with it here...
{
  //
}

then blend in what we did earlier... (compiles but not tested)

#include <Bounce2.h>

#define NUMBER_OF_SWITCHES 4
#define BLINK_DURATION 20000
#define BLINK_FREQUENCY 100

Bounce debouncer[NUMBER_OF_SWITCHES];

int oldValue[NUMBER_OF_SWITCHES];
byte switchPin[NUMBER_OF_SWITCHES] = {2,3,4,5}; //<<<<<<<<<<< set your switch pins here
byte ledPin[NUMBER_OF_SWITCHES] = {6,7,8,9};
unsigned long startTime[NUMBER_OF_SWITCHES];
boolean blinkState[NUMBER_OF_SWITCHES];

void setup()  
{  
  for (int i = 0; i < NUMBER_OF_SWITCHES; i++)
  {
    pinMode(ledPin[i], OUTPUT);
    pinMode(switchPin[i],INPUT_PULLUP);
    debouncer[i] = Bounce();
    debouncer[i].attach(switchPin[i]);
    debouncer[i].interval(5);
  }
}
//
void loop() 
{
  for (int i = 0; i < NUMBER_OF_SWITCHES; i++)
  {
    debouncer[i].update();
    int value = debouncer[i].read();
    if (value != oldValue[i]) 
    {
      activateLed(i);
    }
    oldValue[i] = value;
  }
  //
  for (int i = 0; i < NUMBER_OF_SWITCHES; i++)
  {
    if (blinkState[i])
    {
      blinkForTime(i, BLINK_DURATION, BLINK_FREQUENCY);  //<<<< you could also store Durations and Frequencies in an array e.g.:
      //blinkForTime(i, blinkDuration[i], blinkFrequency[i]);
    }
  }
} 
//
void activateLed(int pin) // your pin was triggered, so do something with it here...
{
  if (!blinkState[pin])
  {
    startTime[pin] = millis();
    blinkState[pin] = true;
  }
}
//
void blinkForTime(int led, unsigned long duration, int frequency)
{ 
  static int lastCount[NUMBER_OF_SWITCHES];
  if (millis() - startTime[led] <= duration)
  {
    int count = (millis() - startTime[led]) / frequency;
    if ( count != lastCount[led])
    {
      digitalWrite(ledPin[led], !digitalRead(ledPin[led]));
      Serial.println(count);
    }
    lastCount[led] = count;
  }
  else
  {
    digitalWrite(ledPin[led], LOW);
    blinkState[led] = false;
  }
}

BulldogLowell:
try to simplify your base code with a Bounce2.h library:

With this Bounce2.h library,can you use a short press and a long press for each button too?

There seems to be an awful lot of code in Reply #12 if all that is wanted is to read 4 switches and flash some LEDs.

Do you really need any debounce stuff?

The switch code in the Thread planning an implementing a program is much simpler and does not need any explicit debouncing. It has two switches and it does not use arrays although arrays would be a good idea with 4 buttons.

...R

BramWerbrouck:
With this Bounce2.h library,can you use a short press and a long press for each button too?

yup, but you have to use digitalRead() or some other way to detect the long presses.

It would be better if you tried to communicate exactly what you are trying to do, rather than just posting your proposed solutions...

It sounds like you are trying to have 4 buttons with different outputs for long and short button presses... no?

BulldogLowell:
It sounds like you are trying to have 4 buttons with different outputs for long and short button presses... no?

If that is what you want just record millis() when the button changes from high to low (assuming active low) and record it again when the button changes from low to high. Have separate values for each button. Use the difference between the press and release times to decide whether you have a long or a short press (or a press that is just a bounce). That way the same code serves all the purposes. Then you can have action code like

if (buttonApressMills < minMillis) {
  // ignore a bounce
}
else if (buttonApressMillis < longMillis) {
  // do the short stuff
}
else {
   // do the long stuff
}

...R

First of all: I wish you all a Happy New Year!! :slight_smile:

BulldogLowell:
It sounds like you are trying to have 4 buttons with different outputs for long and short button presses... no?

This is exactly what I need. I'm building a little game for my children. 4 buttons and 4 LEDs...
Every button should have a short press action and a long press action and I need combinations too.. (but 1 & but 2, but 1 & but 3, but 1 & but 4, but 2 & but 3, but 2 & but 4 and but 3 & but 4)

I saw that the buttons work better with debouncing.. That's the reason I used this code, I found it somewhere on the net.. But perhaps you guys have a nicer and better code for doing that?

BramWerbrouck:
But perhaps you guys have a nicer and better code for doing that?

That's what Reply #17 was about. If there is something in it you don't understand, let me know.

...R