How to make a variable stay true without delay

Hey everyone,

is there a way to make a variable stay true without delaying the loop?

It should work like this: When pushbutton is pressed initially then motion=true.
Now stay true for three seconds (do not check buttonState for three seconds),
then reset motion to false and start checking for buttonState again.

Thank you for your help :slight_smile:

Yes , instead of delay(); you will use millis();..
Take a look here to see how it works :http://arduino.cc/en/Tutorial/BlinkWithoutDelay

This may prove helpful as well: http://arduino.cc/en/Tutorial/ButtonStateChange

Something like this - code not tested but you should get the idea

bool buttonPressed = false;
uint32_t buttonTime= 0;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if (digitalRead(5) == HIGH) // button pressed connected to pin 5 
  {
    buttonTime = millis();
    buttonPressed = true;
    Serial.println("+");
  }

  if (buttonPressed && (millis() - buttonTime > 3000))
  {
    buttonPressed = false;
    Serial.println("-");
  }
  
  if (buttonPressed)
  {
    digitalWrite(motor, HIGH);
  }
  else
  {
    digitalWrite(motor, LOW);
  }
 }

BlinkWithoutDelay will show you how to do timing without using delay() so that the program does not halt for 3 seconds which allows you to do other things like read inputs but people find it difficult to apply the BWD principle to things other than turning a pin on and off.

Something like this will do what you want

start of loop()
  if the button is pressed and motion is false
    set startTime to millis()
    set motion to true
  end if
  
  if motion is true and millis() - startTime >= period
    set motion to false
  end if
  //any code that does not block loop() from running goes here
end of loop()

Hey everyone,

great - thanks so much for your detailled help!

BWD is exactly the kind of command I was looking for.
BUT: I did not quite manage to solve the problem yet.
@robtillaart and @UKHeliBob - I think what you mean
is that when pressed, set timer. But wouldn't that
mean, that as soon as I release, the whole millis() function
would be stopped?

I have been working with buttonState and lastButtonState
for this sketch, because it is meant to output something,
when the button is pressed initially, not "as long as its pressed".

But how can I combine this with the mills() function?
So that it says: If button initially changes its state from
unpressed to pressed, set motion=true and set the timer.
Now (even though I might have released the button already,)
continue executing the loop but keep the motion=true for
three seconds. Then reset and start checking buttonstates
again.

Does that make sense, or did I misunderstand your answers?
Thanks again, I really appreciate your help.

Did you look at : Blink Without Delay example on http://arduino.cc/en/Tutorial/BlinkWithoutDelay ???
We use millis(); when we want to do other things at the same time , without using delay();, thus stopping our code for a certain time! Just take a look at that link and carefully see how millis(); works !!
In my opinion you just need to make use of that function !
Do not expect someone to post the code you need, you can make a try, then post your code here and check for errors or why it won't work as it should be , i think that's better !

But how can I combine this with the mills() function?
So that it says: If button initially changes its state from
unpressed to pressed, set motion=true and set the timer.

Both my (pseudo)code and Rob's code deal with the timing. The motion variable will remain true for the duration of period in my code (buttonTime in Rob's). If you want an action to take place only when the button becomes pressed, ie the timing starts, then set another boolean variable to true once the action has been taken and don't take the action again until the new variable is false. Reset the variable to false at the end of the timing test

start of loop()
  if the button is pressed and motion is false
    set startTime to millis()
    set motion to true
    set actionTaken to false
  end if
  
  if millis() - startTime >= period
    set motion to false
  end if

  if motion == true and actionTaken == false
    take action
    actionTaken = true
  end of if
end of loop()

Hey and thanks again,

@zaxarias - sure I read through the link you posted, as I said:
That was exactly the kind of command I was looking for. Of course
I tried to implement it into my sketch, but had difficulties to apply it
to that specific problem. Of course I do not expect you to wright it for me,
I just thaught I could spare you the details by asking more generally.
I've posted my code further down.

@Bob - I tried to apply what you suggested, but it appears I didn’t
get it right. I do not get any errors, but I

Here are the main excerpts of my code ( I left out some bits that I
think are irrelevant for now):

Just a brief introduction: There are two buttons. What I am trying to achieve:
When button1 is pressed and button2 is pressed or has been pressed within
the last 3 seconds, play a sound.

int pirPin = 5;
int pirPin2 = 10;

int buttonState = 0;         
int lastButtonState = 0;
int buttonState2 = 0;       
int lastButtonState2 = 0;

boolean bike = false;
boolean pedestrian = false;
boolean pressed = false;
long startTime = 0; 
long interval = 4000;

void setup()
{
  pinMode(pirPin, INPUT);
  pinMode(pirPin, HIGH);
  pinMode(pirPin2, INPUT);
  pinMode(pirPin2, HIGH);

void loop()
{
  
    lastButtonState=buttonState;
    buttonState=digitalRead(pirPin);
    lastButtonState2=buttonState2;
    buttonState2=digitalRead(pirPin2);


    if (pressed == false){
    if (lastButtonState == HIGH){
    if (buttonState == LOW) {     
    pedestrian=true; 
    unsigned long startTime = millis();
    pressed = true;
    }}}
    if (pressed == false){
    if (buttonState == HIGH) {     
    pedestrian=false;  
    }}
    if (lastButtonState2 == HIGH){
    if (buttonState2 == LOW) {     
    bike=true;
    }}
    if (buttonState2 == HIGH) {     
    bike=false;  
    }

if (pedestrian==true) {
    if (bike==true) {
    uint8_t result = MP3player.playTrack(random(4));
    delay(3000);

if(millis() - startTime >= interval) {  
    pressed = false;
    }}

best,
lulubel

I have not had the time to examine your code closely but not far into it I found these

  pinMode(pirPin, HIGH);
  pinMode(pirPin2, HIGH);

What are you trying to do ?

Judging by the names the inputs are coming from PIRs. Do these provide a digital or analogue output ? How are the Arduino inputs wired ?

Why have you got a delay() in your program ? It will cause all input to be ignored for 3 seconds.

If the inputs are from PIRs why do their input state names names sound like button inputs ? The program won't care as long as it is correct but it confuses me when reading the code.

Try this , i made some changes , there were some errors . Also i fixed the millis(); function, it should play your sound every 3 sec...
I just didn't check your "if" statements to see if they re right, so if you don't hear a sound i guess there is a problem there !!

int pirPin = 5;
int pirPin2 = 10;

int buttonState = 0;         
int lastButtonState = 0;
int buttonState2 = 0;       
int lastButtonState2 = 0;

boolean bike = false;
boolean pedestrian = false;
boolean pressed = false;
long startTime = 0; 

long previousMillis = 0;
long interval = 3000;

void setup()
{
  pinMode(pirPin, INPUT);
  pinMode(pirPin, HIGH);
  pinMode(pirPin2, INPUT);
  pinMode(pirPin2, HIGH);
}
void loop()
{
  
    lastButtonState=buttonState;
    buttonState=digitalRead(pirPin);
    lastButtonState2=buttonState2;
    buttonState2=digitalRead(pirPin2);


    if (pressed == false){
    if (lastButtonState == HIGH){
    if (buttonState == LOW) {     
    pedestrian=true; 

    pressed = true;
    }}}
    if (pressed == false){
    if (buttonState == HIGH) {     
    pedestrian=false;  
    }}
    if (lastButtonState2 == HIGH){
    if (buttonState2 == LOW) {     
    bike=true;
    }}
    if (buttonState2 == HIGH) {     
    bike=false;  
    }
   unsigned long currentMillis = millis();
if (pedestrian==true && bike==true && (currentMillis - previousMillis > interval)) {
    previousMillis = currentMillis;   
    uint8_t result = MP3player.playTrack(random(4));
    }

}

lulubel:

    if (pressed == false){

if (lastButtonState == HIGH){
   if (buttonState == LOW) {    
   pedestrian=true;
   unsigned long startTime = millis();
   pressed = true;
   }}}

Contracts to:

  if (pressed == false && lastButtonState == HIGH && buttonState == LOW) {     
    pedestrian=true; 
    unsigned long startTime = millis();
    pressed = true;
  }

&& = logical AND.

Set it true, and then don't set it untrue.

Hi,

@UKHeliBob - the two inputs are buttons. They are currently just taking the place
of PIR motion sensors to make testing easier and more reliable. But The motion sensor
are going to run on digital pins as well and should work the same.
The delay() has a different reason, but at that point that is the desired effect.

If you want to sea my project and my set up you can check these two videos:

@Henry_Best - oh, really? what is the difference, I thaught that were just 2 ways of
writing the same thing...

@zaxarias - wow, thanks for the effort. I tried your code and it doesnt work yet. I am
going to recheck the if-statements now as you said and try exchanging the "if() { if() {if() { }}}"
for some "==", as Henry_Best suggested.

What I do not understand about the code you posted: Doesn't

   unsigned long currentMillis = millis();

tell the program to NOW start the counting? Because I want it to start counting, when
button1 is pressed (=pedestrian is being detected) - earlier on in the first if statement
.. or am I wrong?

Cheers,
L

lulubel:
What I do not understand about the code you posted: Doesn't

   unsigned long currentMillis = millis();

tell the program to NOW start the counting? Because I want it to start counting, when
button1 is pressed (=pedestrian is being detected) - earlier on in the first if statement
.. or am I wrong?

Not really.

   unsigned long currentMillis = millis();

says assign the 'timestamp' of right now or millis(), to the variable currentMillis.

you then compare the timestamp of your startTime to the current time (currentMillis):

if ( currentMillis - startTime >= someInterval)

says if the now time minus the start time is greater than or equal to someInterval or rather "since the last time I reset the counter, has someInterval elapsed?"

startTime = millis()

resets the timer to now. and you will subsequently look to compare against that exact moment in time.

I hope that clarifies it for you

Hey BulldogLowell,

very clear decription, thanks. Yes, that actually clarifies things.
But still - that should mean, that startTime = millis()
should be in the position in the code, at which the interval is meant
to begin, right?

@zaxarias - so that is in the first if-statement in my case..

best,
L

lulubel:
Hey BulldogLowell,

very clear decription, thanks. Yes, that actually clarifies things.
But still - that should mean, that startTime = millis()
should be in the position in the code, at which the interval is meant
to begin, right?

to begin the timer OR to reset the timer, yes.

usually it is coded something like this:

unsigned long startTime;  // since millis() is an Unsigned Long, you should use UL for all of the timer variables...
unsigned long currentTime;
unsigned long someInterval = 1000UL;

void setup()
{
  startTime=millis();
}

void loop()
{
  currentMillis = millis();
  if (currentMillis - startMillis >= someInterval)  // Did the timer hit the desired interval?
  {
    //Do the timed function here!!
    startMillis = millis();  // Reset the Timer Here!!
  }
}

lulubel:
Hey everyone,

is there a way to make a variable stay true without delaying the loop?

It should work like this: When pushbutton is pressed initially then motion=true.
Now stay true for three seconds (do not check buttonState for three seconds),
then reset motion to false and start checking for buttonState again.

Thank you for your help :slight_smile:

I think this is sort of what you are looking for... no?

int pirPin = 5;
int pirPin2 = 10;
//
int buttonState1;         
int buttonState2;       
//
boolean lockOut;
//
unsigned long startTime;
unsigned long currentMillis;
unsigned long lockoutInterval = 3000UL;
//
void setup()
{
  pinMode(pirPin, INPUT);
  pinMode(pirPin2, INPUT);
  startTime = millis();
}
void loop()
{
  currentMillis = millis();
  if (currentMillis - startTime >= lockoutInterval) lockOut = false;
  buttonState1=digitalRead(pirPin);
  buttonState2=digitalRead(pirPin2);
  if ((buttonState1 = HIGH || buttonState2 == HIGH) && lockOut == false)
  {
    lockOut = true;
    startTime = millis();
  }
  if (lockOut)
  {
    //Do what you want during the three second lockout
    //lite a LED?
    //Energize a relay?
    //Something else
  }
  else
  {
    //here put what you want to do after the 3second lockout expires
    //turn off LED?
    //de-energize the relay?
    //etc
  }
}

lulubel:
Hey BulldogLowell,

very clear decription, thanks. Yes, that actually clarifies things.
But still - that should mean, that startTime = millis()
should be in the position in the code, at which the interval is meant
to begin, right?

@zaxarias - so that is in the first if-statement in my case..

best,
L

Test the code bellow, it should play your track/sound every three seconds, with no button pressed... If it works then look to fix your If statements !!! You didn't learn the Blink without delay example well !!!
This code will normally run if button 1 is pressed or button 2, you can figure that out here :

pedestrian==true || bike==true
int pirPin = 5;
int pirPin2 = 10;

int buttonState = 0;         
int lastButtonState = 0;
int buttonState2 = 0;       
int lastButtonState2 = 0;

boolean bike = false;
boolean pedestrian = false;
boolean pressed = false;
long startTime = 0; 

long previousMillis = 0; // Last time the track played !!!
long interval = 3000;  // 3 seconds Interval !!!

void setup()
{
  pinMode(pirPin, INPUT);
  pinMode(pirPin, HIGH);
  pinMode(pirPin2, INPUT);
  pinMode(pirPin2, HIGH);
}
void loop()
{
  
    lastButtonState=buttonState;
    buttonState=digitalRead(pirPin);
    lastButtonState2=buttonState2;
    buttonState2=digitalRead(pirPin2);


//    if (pressed == false){
//    if (lastButtonState == HIGH){
//    if (buttonState == LOW) {     
//    pedestrian=true; 
//
//    pressed = true;
//    }}}
//    if (pressed == false){
//    if (buttonState == HIGH) {     
//    pedestrian=false;  
//    }}
//    if (lastButtonState2 == HIGH){
//    if (buttonState2 == LOW) {     
//    bike=true;
//    }}
//    if (buttonState2 == HIGH) {     
//    bike=false;  
//    }

   pedestrian=true; 
   unsigned long currentMillis = millis(); // currentMillis == number of milliseconds since the Arduino board started running its current programm !!!
if ((pedestrian==true || bike==true) && (currentMillis - previousMillis > interval)) { //The code running after 3seconds !!! =>
    previousMillis = currentMillis;  // Save the last time you played the track !!!
    uint8_t result = MP3player.playTrack(random(4));
    }
//Code you want to run anyway, all the time !!!
}

zaxarias:
Test the code bellow, it should play your track/sound every three seconds, with no button pressed... If it works then .....

I'm guessing that the code referenced above won't even compile for the OP, much less play a track...

That is likely due to this:

uint8_t result = MP3player.playTrack(random(4));

probably requires definition from a library.