Limiting action time in ' if statement'

Hi guys,

I've been trying to establish if it's possible (and if so, how) to limit the duration of an action that is performed as a result of a true result in an if statement.

I need to read an analogue input from a current sensor, when the threshold is exceeded, write a digital pin to HIGH, but only for 5 seconds. At this point I it would need to either stop altogether or add more functionality to do the next part.

I'm considering adding a voltage sensor, so that after the 5 seconds, a check is made for a positive output. If no output, repeat the 5 second scenario above, but do this entire process a maximum of 3 times.
I'm using it to initiate a generator start sequence, with the second part a retry if there is no output (assume engine failed to start)
.
I'm new to coding and read the post about structuring sketches and am resisting modifying a basic sketch from the tutorials as outlined there. I've found posts about adding timers, but nothing that seems to fit my scenario.

Can anyone guide me please?

Cheers
Chris

What you need is a timer.
For that you use the millis() function.

unsigned long starTime = 0    // global variable
unsigned long interval = 5000     // 5 seconds

void setup () {}

void loop () {
if (condition) {
    digitalWrite(pin,HIGH);
    starTime = millis();    // start timer
  }
 }
// other stuff
if (millis() - starTime >= interval) {   // current time - start time >= 5 seconds 
   digitalWrite (pin,LOW);
 }
}

If the condition only happens once, what will happen after ~49 days?

Larry, I don't intend on leaving the system powered all the time. Would this then reset the arduino so the 49 days isn't a concern?

I was just trying to suggest there can be a potential bug.
If you make the below change you can eliminate the potential problem.
Note, there would be no issue in this example but, you should always think of long term bugs too.

unsigned long starTime = 0    // global variable
unsigned long interval = 5000     // 5 second
bool flag = false;

void setup () {
. . .
}

void loop () {
if (condition) {
    digitalWrite(pin,HIGH);
    starTime = millis();    // start timer
    flag = true;
  }
 }
// other stuff
if (flag && millis() - starTime >= interval) {   // current time - start time >= 5 seconds 
   digitalWrite (pin,LOW);
   flag = false;
 }
}

Many thanks to both of you.
If I want to go the route of repeating the sequence to a max of three times if another condition isn't met (voltage hasn't increased from 0), I need some kind of counter don't I? I've read about incrementing a count total somewhere but cant find it now.

Once again, thanks for the help so far. Larry could you explain what it is in the changes you made that prevents a problem?
(I like to understand how it's working)

Cheers
Chris

You can use a "flag" variable to limit or stop an action.
If you have:
if (millis() - starTime >= 5000UL)
{
digitalWrite (pin,LOW);
}
After 5 seconds, the digitalWrite(pin,LOW); is executed every time through loop().
This can be 1000s of times a second there after.

Adding a "flag" can limit digitalWrite(pin,LOW); to execute once.
if (flag == true && millis() - starTime >= 5000UL)
{
digitalWrite (pin,LOW);
flag = false;
}
After 5 seconds, "flag" is made false.
This prevents further "if" evaluations.
Hence, the digitalWrite(pin,LOW); only happens once.

If you use subtraction and unsigtned long variables with millis() -
if (millis() - prevMillis >= interval)

  • then it works perfectly through the rollover thanks the the delights of integer maths

The demo Several Things at a Time illuistrates the use of millis() to manage timing.

...R

If you use subtraction and unsigtned long variables with millis() -
if (millis() - prevMillis >= interval)

  • then it works perfectly through the rollover thanks the the delights of integer maths

The point I was trying to make (probably not very well) was what happens if you had:
if (millis() - startTime >= 5000UL)
{
//some code you only wanted to execute "once" at the timeout period
//Example: send a message
}

You could use:
if (flag && millis() - startTime >= 5000UL)
{
//some code you only wanted to execute "once" at the timeout period
//Example: send a message
flag = false;
}

Thanks for the explanation Larry. I understand what you mean.
With the flag set to false, would this effect the ability to get the whole thing repeated 3 times if the other condition (voltage) wasn't true and more engine start attempts were required? Or is there a way to embed your suggestion so the program can return to it? I used to write training material which included process navigations and we used if/goto statements. I've read that goto isn't popular in arduino....

I guess in my speak it would be (overall)

Check current sensor, if > threshold, set pin to high
Start the timer
If time >5 secs, set pin to low
Check the voltage sensor
If voltage is 0, go back to 'check the current sensor', (but only three times)
Else do nothing
Move onto final task

Does that make sense?
Cheers
Chris

Also, say we wanted to flash a LED at 1 hertz (forever) after a timeout of 5 seconds.
To start things off you: flag1 = true; startTime = millis();
if (flag1 && millis() - startTime >= 5000UL)
{
if(millis() - flashMillis >= 500)
{
digitalWrite(LED, !digitalRead(LED));
flashMillis = flashMillis + 500;
}
}
The above would work fine for 49 days but, at 49 days minus 5 seconds the flashing would stop and then start up again in five seconds. A bit of a glitch here.

To fix this glitch:
if ((flag1 && millis() - startTime >= 5000UL) || flag2)
{
flag2 = true;
if(millis() - flashMillis >= 500)
{
digitalWrite(LED, !digitalRead(LED));
flashMillis = flashMillis + 500;
}
}
Adding the "flag2" prevents the 49 day glitch since it being true lets flashing run forever.

With the flag set to false, would this effect the ability to get the whole thing repeated 3 times if the other condition (voltage) wasn't true and more engine start attempts were required?

Yes however, your code would be set up so flag is set true again 2 more times then never again.

What you are suggesting makes sense, time to flesh it out :wink:

:sleeping: time.

LarryD:
The point I was trying to make (probably not very well) was what happens if you had:

Maybe you were considering the possibility that after the rollover the time calculation would become true on a second occasion. I confess I had not thought of that and your flag concept is indeed the way to deal with it.

...R

Maybe you were considering the possibility that after the rollover the time calculation would become true on a second occasion. I confess I had not thought of that and your flag concept is indeed the way to deal with it.

Exactly.

Say you were going to Jupiter.
One hour into the flight you momentarily push the fire retro rocket button.
For 60 seconds the rockets fire then turn off.
Now you go to sleep for 49 days.

if(digitalRead(2) == LOW)
{
fireMillis = millis();
}

if(millis() - fireMillis <= 60000UL) // this is evaluated every 49 days! oh my!
{
digitalWrite(retroRocket, HIGH); // turn on retros
}
else
{
digitalWrite(retroRocket, LOW); // turn off retros
}

You wake up 49 days later the retro rockets are still firing, OMG. :o

Adding a flag we get:

if(Retros == false && digitalRead(2) == LOW)
{
fireMillis = millis();
Retros = true;
}

if(Retros == true && millis() - fireMillis <= 60000UL)
{
digitalWrite(retroRocket, HIGH); // turn on the retros
}
else
{
digitalWrite(retroRocket, LOW); //turn off retros
Retros = false;
}

We wake up 49 days later and have breakfast. All is good. :slight_smile:

Larry, below is what I've done so far. Not a lot really as its just based on what we've discussed so far.
Things to ignore are the threshold value as I have to do some testing to see what readings I'll get from the current sensor when its connected to the 32v circuit.
Also the voltage sensor as I'm yet to decide on what I'll use to do this.

From here, how to I get it to repeat the process to the maximum three attempts? Is it by putting in some sort of counter and switching the flag between true and false?

// These constants won't change:
const int currentPin = A0;    // pin that the current sensor is attached to
const int voltagePin = A1;   // pin that the voltage sensor is attached to
const int relayPin = 22;     // pin that the relay is attached to
const int threshold = 400;   // the current draw threshold level that's in the range of the analog input
unsigned long starTime = 0    // global variable
unsigned long interval = 5000     // 5 seconds

void setup() {
  // initialize the RELAY pin as an output:
  pinMode(relayPin, OUTPUT);
  // initialize serial communications:
  Serial.begin(9600);
}

void loop() {
  // read the value of the current sensor:
  int analogValue = analogRead(currentPin);

  // if the analog value is high enough, power the relay:
  if (analogValue > threshold) {
    digitalWrite(relayPin, HIGH);
  starTime = millis();    // start timer
  }
 }
if (millis() - starTime >= 5000UL) {   // current time - start time >= 5 seconds 
   digitalWrite (pin,LOW);
 }

Cheers
Chris

This will turn on the relay a maximum of three times.
You should be able to modify the code as needed from this example:

// These constants won't change:
const int currentPin = A0;     // pin that the current sensor is attached to
const int voltagePin = A1;     // pin that the voltage sensor is attached to
const int relayPin = 22;       // pin that the relay is attached to
//const int relayPin = 2;       // pin that the relay is attached to
const int threshold = 400;     // the current draw threshold level that's in the range of the analog input
unsigned long starTime = 0;    // global variable
unsigned long interval = 5000; // 5 seconds
bool relayFlag = false;
byte attempts = 0;


void setup() {
  // initialize the RELAY pin as an output:
  pinMode(relayPin, OUTPUT);
  // initialize serial communications:
  Serial.begin(9600);
}

void loop() {
  // read the value of the current sensor:
  int analogValue = analogRead(currentPin);
  Serial.println(analogValue);
  
  // if the analog value is high enough, power the relay:
  if (relayFlag == false && attempts < 3 && analogValue > threshold) 
  {
    digitalWrite(relayPin, HIGH);
    relayFlag = true;
    starTime = millis();    // start timer
  }

  if(relayFlag == true && millis() - starTime >= 5000UL) 
  {   
    // current time - start time >= 5 seconds 
    digitalWrite(relayPin,LOW);
    delay(100);  //this could be removed if needed
    relayFlag = false;
    attempts = attempts + 1;
  }

} //END of loop

.