Go Down

Topic: Why add a flag to Blink Without Delay (BWD) ? (Read 699 times) previous topic - next topic

larryd

Feb 13, 2019, 12:49 am Last Edit: Feb 15, 2019, 07:35 am by larryd
Newcomers to Arduino, soon discover the usefulness of using the Blink Without Delay (BWD) technique for timing events.
As most old timers realize, adding a Flag to this technique can prevent subtle and often unanticipated problems.

For those newcomers who do not know what and why a Flag should be used, hopefully this discussion will shed some light on the topic.

In the code segment below, we are looking for a time interval to expire.
When we want to start event timing with BWD, we make ledOnMillis = millis().
When our time test is true, we turn off a LED.

//**************************************
//has the timing interval expired?
if (millis() - ledOnMillis >= 5000)
{
//turn the red LED OFF 5 seconds after it went ON
digitalWrite(redLED, OFF) ;
}
//**************************************

The problem with the code is, once the interval is up, we repeatably turn off the LED each time this code segment is run.
i.e.  millis() - ledOnMillis >= 5000 continues to be true.

This might be okay for most applications but, let's say our LED was turned on somewhere in the sketch in a seldom used part of code, the LED will immediately be turned off when this code segment is executed.
Also, since we are constantly turning off the LED once the delay interval is up, this takes up processor time, therefore, we slow down total code execution time.

We can add a Flag variable to our timing test to prevent the above.
Now when we want to do event timing, we make our Flag = true (which enables timing) and as before, make ledOnMillis = millis().
Once the interval has expired, the Flag variable is set to false.
Since Flag is now false, time checking only happens once.

Each time we need to time our event, we make Flag = true and make ledOnMillis = millis().

//**************************************
//when timing is enabled, has the timing interval expired?
if ( Flag == true && millis() - ledOnMillis >= 5000)
{
//turn the red LED OFF 5 seconds after it went ON
digitalWrite(redLED, OFF);

//disable timing
Flag = false;                      // <------<<<<<
}
//**************************************

The following two sketches further develop this concept:

Code: ("Sketch #1") [Select]

#define OFF LOW
#define ON  HIGH

unsigned long ledOnMillis;
const unsigned long onTime = 5000;

boolean ledOnFlag          = false;

const byte redLED          = 13;
const byte onSwitch        = 2;
const byte auxOnSwitch     = 3;
const byte auxOffSwitch    = 4;

byte lastOnSwitchState;


//*********************************************************************************
void setup()
{
  Serial.begin(9600);

  pinMode(redLED, OUTPUT);
  digitalWrite(redLED, OFF);

  //switch wired so a press give a LOW
  pinMode(onSwitch, INPUT_PULLUP);
  //initialize the last State variable
  lastOnSwitchState = digitalRead(onSwitch);

  pinMode(auxOnSwitch, INPUT_PULLUP);
  pinMode(auxOffSwitch, INPUT_PULLUP);

} //END of setup()


//*********************************************************************************
void loop()
{
  //**************************************
  byte currentState = digitalRead(onSwitch);

  //has the ON switch changed state?
  if (lastOnSwitchState != currentState)
  {
    //update to the new state
    lastOnSwitchState = currentState;

    //did the switch just get pressed i.e. LOW?
    if (currentState == LOW)
    {
      //turn the red LED ON
      digitalWrite(redLED, ON);
      Serial.println("Turn LED ON");

      //the time when the red LED went ON
      ledOnMillis = millis();

    }

    //switch must have gone HIGH
    else
    {
      //nothing to do here
    }

  } //END of    if( lastOnSwitchState != currentState);

  //**************************************
  //has the timing interval expired?
  if (millis() - ledOnMillis >= onTime)
  {
    //turn the red LED OFF 5 seconds after it went ON
    digitalWrite(redLED, OFF);
    Serial.println("Turn LED OFF");

    //maybe do some other stuff

  }

  //**************************************
  //when the auxiliary on switch is pressed, turn ON the red LED
  if (digitalRead(auxOnSwitch) == LOW)
  {
    //turn the red LED ON
    digitalWrite(redLED, ON);
    Serial.println("Turn LED ON");
  }

  //**************************************
  //when the auxiliary off switch is pressed, turn OFF the red LED
  if (digitalRead(auxOffSwitch) == LOW)
  {
    //turn the red LED Off
    digitalWrite(redLED, OFF);
    Serial.println("Turn LED OFF");
  }


} //END of loop()

//*********************************************************************************



Code: ("Sketch #2") [Select]

#define OFF LOW
#define ON  HIGH

unsigned long ledOnMillis;
const unsigned long onTime = 5000;

boolean ledOnFlag          = false;

const byte redLED          = 13;
const byte onSwitch        = 2;
const byte auxOnSwitch     = 3;
const byte auxOffSwitch    = 4;

byte lastOnSwitchState;


//*********************************************************************************
void setup()
{
  Serial.begin(9600);

  pinMode(redLED, OUTPUT);
  digitalWrite(redLED, OFF);

  //switch wired so a press give a LOW
  pinMode(onSwitch, INPUT_PULLUP);
  //initialize the last State variable
  lastOnSwitchState = digitalRead(onSwitch);

  pinMode(auxOnSwitch, INPUT_PULLUP);
  pinMode(auxOffSwitch, INPUT_PULLUP);

} //END of setup()


//*********************************************************************************
void loop()
{
  //**************************************
  byte currentState = digitalRead(onSwitch);

  //when timing is disabled, has the ON switch changed state?
  if (ledOnFlag == false && lastOnSwitchState != currentState)
  {
    //update to the new state
    lastOnSwitchState = currentState;

    //did the switch just get pressed i.e. LOW?
    if (currentState == LOW)
    {
      //turn the red LED ON
      digitalWrite(redLED, ON);
      Serial.println("Turn LED ON");

      //the time when the red LED went ON
      ledOnMillis = millis();

      //enable timing
      ledOnFlag = true;

    }

    //switch must have gone HIGH
    else
    {
      //nothing to do here
    }

  } //END of    if (ledOnFlag == false && lastOnSwitchState != currentState)

  //**************************************
  //if timing is enabled, has the timing interval expired?
  if (ledOnFlag == true && millis() - ledOnMillis >= onTime)
  {
    //turn the red LED OFF 5 seconds after it went ON
    digitalWrite(redLED, OFF);
    Serial.println("Turn LED OFF");

    //disable timing
    ledOnFlag = false;    //  <------<<<<<

    //maybe do some other stuff

  }

  //**************************************
  //when the auxiliary on switch is pressed, turn ON the red LED
  if (digitalRead(auxOnSwitch) == LOW)
  {
    //turn the red LED ON
    digitalWrite(redLED, ON);
    Serial.println("Turn LED ON");
  }

  //**************************************
  //when the auxiliary off switch is pressed, turn OFF the red LED
  if (digitalRead(auxOffSwitch) == LOW)
  {
    //turn the red LED Off
    digitalWrite(redLED, OFF);
    Serial.println("Turn LED OFF");

    //disable timing
    ledOnFlag = false;
  }


} //END of loop()

//*********************************************************************************




Also, we can disable switch testing while timing is enabled by adding Flag to our change in state code.
This prevents our timing from resetting if the switch is closed while timing is enabled.


//when timing is disabled, has the ON switch changed state?
if (ledOnFlag == false && lastOnSwitchState != currentState)
{
. . .
}



No technical PMs.
If you are asked a question, please respond with an answer.
If you are asked for more information, please supply it.
If you need clarification, ask for help.

larryd

#1
Feb 13, 2019, 12:49 am Last Edit: Feb 15, 2019, 07:36 am by larryd
Scenario:
At power up and every time we close a switch, we want to toggle a LED for 5 seconds then go OFF.
When our switch is pushed, we turn on the LED then make ledOnMillis = millis().
Our interval test will be true for 5 seconds (<=) during which the LED is toggled.
After the 5 seconds interval is up our timing test goes false and we then make sure the LED is OFF.
Things look good.  :)

//**************************************
if (millis() - ledOnMillis <= onTime)
{
digitalWrite(redLED, ON);
delay(500);
digitalWrite(redLED, OFF);
delay(500);
}
else
{
digitalWrite(redLED, OFF);
}
//**************************************

However, mills() overflows every ~49 days, the LED again toggles for another 5 seconds etc. . . .
Not Good  :smiley-confuse:.



To deal this phenomenon, we can use our Flag.
When the switch is closed, we make Flag = true (which enables timing) and make ledOnMillis = millis().
After the interval is up, we make Flag = false, this prevents a true test from occurring again.

//**************************************
//is timing enabled and has the timing interval expired?
if (ledOnFlag == true && millis() - ledOnMillis <= onTime )
{
digitalWrite(redLED, ON);
delay(500);
digitalWrite(redLED, OFF);
delay(500);
}
else
{
digitalWrite(redLED, OFF);
//disable timing checking
ledOnFlag = false;        //   <------<<<<<
}
//**************************************


The following two sketches support this discussion:

Code: (Sketch#3) [Select]

#define OFF LOW
#define ON  HIGH

unsigned long ledOnMillis;
const unsigned long onTime = 5000;

boolean ledOnFlag          = false;

const byte redLED          = 13;
const byte onSwitch        = 2;

byte lastOnSwitchState;


//*********************************************************************************
void setup()
{
  Serial.begin(9600);

  pinMode(redLED, OUTPUT);
  digitalWrite(redLED, ON);

  //switch wired so a press give a LOW
  pinMode(onSwitch, INPUT_PULLUP);
  //initialize the last State variable
  lastOnSwitchState = digitalRead(onSwitch);

} //END of setup()


//*********************************************************************************
void loop()
{
  //**************************************
  byte currentState = digitalRead(onSwitch);

  //has the ON switch changed state?
  if (lastOnSwitchState != currentState)
  {
    //update to the new state
    lastOnSwitchState = currentState;

    //did the switch just get pressed i.e. LOW?
    if (currentState == LOW)
    {
      //turn the red LED ON
      digitalWrite(redLED, ON);

      //the time when the red LED went ON
      ledOnMillis = millis();

    }

    //switch must have gone HIGH
    else
    {
      //nothing to do here
    }

  } //END of    if( lastOnSwitchState != currentState);

  //**************************************
  //has the timing interval expired?
  if (millis() - ledOnMillis <= onTime)
  {
    digitalWrite(redLED, ON);
    delay(500);
    digitalWrite(redLED, OFF);
    delay(500);

    //maybe do some other stuff
  }
  else
  {
    //make sure the LED is OFF
    digitalWrite(redLED, OFF);
  }


} //END of loop()

//*********************************************************************************



Code: (Sketch#4) [Select]

#define OFF LOW
#define ON  HIGH

unsigned long ledOnMillis;
const unsigned long onTime = 5000;

boolean ledOnFlag          = true;

const byte redLED          = 13;
const byte onSwitch        = 2;

byte lastOnSwitchState;


//*********************************************************************************
void setup()
{
  Serial.begin(9600);

  pinMode(redLED, OUTPUT);
  digitalWrite(redLED, ON);

  //switch wired so a press give a LOW
  pinMode(onSwitch, INPUT_PULLUP);
  //initialize the last State variable
  lastOnSwitchState = digitalRead(onSwitch);

} //END of setup()


//*********************************************************************************
void loop()
{
  //**************************************
  byte currentState = digitalRead(onSwitch);

  //when timing is disabled, has the ON switch changed state?
  if (ledOnFlag == false && lastOnSwitchState != currentState)
  {
    //update to the new state
    lastOnSwitchState = currentState;

    //did the switch just get pressed i.e. LOW?
    if (currentState == LOW)
    {
      //turn the red LED ON
      digitalWrite(redLED, ON);

      //the time when the red LED went ON
      ledOnMillis = millis();

      //enable timing
      ledOnFlag = true;
    }

    //switch must have gone HIGH
    else
    {
      //nothing to do here
    }

  } //END of    if( lastOnSwitchState != currentState);

  //**************************************
  //is timing enabled and has the timing interval expired?
  if (ledOnFlag == true && millis() - ledOnMillis <= onTime )
  {
    digitalWrite(redLED, ON);
    delay(500);
    digitalWrite(redLED, OFF);
    delay(500);

    //maybe do some other stuff
  }
  
  else
  {
    //make sure the LED is OFF
    digitalWrite(redLED, OFF);
    
    //disable timing
    ledOnFlag = false;   //  <------<<<<<
  }


} //END of loop()

//*********************************************************************************




Also, we can disable switch testing while timing is enabled by adding Flag to our change in state code.
This prevents our timing from resetting if the switch is closed while timing is enabled.


//when timing is disabled, has the ON switch changed state?
if (ledOnFlag == false && lastOnSwitchState != currentState)
{
 . . .
}

No technical PMs.
If you are asked a question, please respond with an answer.
If you are asked for more information, please supply it.
If you need clarification, ask for help.

larryd

No technical PMs.
If you are asked a question, please respond with an answer.
If you are asked for more information, please supply it.
If you need clarification, ask for help.

Go Up