How to set delta and millis based timer simultaneously?

Hello,

i want to make delayed turn on and off statement with delta condition. If voltage drops below 8V for more than 5 seconds i want to turn pump off and turn it back on if voltage will be higher or equal to 12V for more than 5 second.

The problem is that when voltage varies between 8 and 12 volts, pump on and off timers are not reset to currentTime. Because of that turn on/off delay is almost never implemented.

I understand why this is happening but i don’t know how to solve it.

  if ((voltage < 8.0) && (currentTime - pump_offTime >= 5000))
  {
    turn_on = false;
    pump_onTime = currentTime;
  }
  else if ((voltage >= 12.0) && (currentTime - pump_onTime >= 5000))
  {
    turn_on = true;
    pump_offTime = currentTime;   
  }
  if ((voltage < 8.0) && (currentTime - pump_offTime >= 5000))

How is voltage defined? How is it calculated?

Nested if statements are far easier to follow the logic of, and to debug. I suggest that you try that.

What should happen, regardless of time, if the voltage is between 8 and 12?

Voltage is defined as "float" and it's calculated like this

voltage = ((analogRead(A0) / 1024.0) * 30.0);

PaulS: What should happen, regardless of time, if the voltage is between 8 and 12?

If "turn_on" boolean variable is set to "false" it shouldn't go to "true" unless voltage is higher than 12V (so if voltage is for example 9, 10 or 11 volts, it should stay in "false"). The same is when it reach 12 volts and go to "true" state. Than it shouldn't go to "false" state unless voltage is lower than 8V (now if voltage is 9, 10 or 11 volts, it should stay in "true"). I set "turn_on" variable in setup to "false" in case that the voltage varies between 8 and 12 volts when program starts.

I need this difference between turning pump on and off because when the pump starts, voltage drops for about 2-3 volts. If i use only "if (voltage > 10) true, else false" i get pump flickers when voltage is around 10 volts.

Nested if statements are far easier to follow the logic of, and to debug.

I am not so good at coding. Can you give me an example how should i change my current part of code?

... Ok, i googled "nested statements" and i know what you mean. I still don't know how to change my part of code.

Personally I would have a “state” variable, and a “transition time” variable.

You don’t care about what the current voltage is, only when the voltage transitioned from “good” to “bad” or vice versa.

For instance:

#define STATE_GOOD 1
#define STATE_BAD 0

static byte state = STATE_BAD; // Start in the bad state for safety.
static unsigned long transition = 0;

// If the state was good, but it's now dropped below the low
// threshold, note that it's bad, and save the time.
if ((voltage < 8.0) && (state == STATE_GOOD)) {
  state = STATE_BAD;
  transition = millis();
}

// Likewise, if it's currently bad, but it's risen to above the high
// threshold, then record that it's good, and save the time.
if ((voltage > 12.0) && (state == STATE_BAD)) {
  state = STATE_GOOD;
  transition = millis();
}

// If there has been a transition, and the transition time is greater or
// equal to 5 seconds ago, then turn the pump on/off depending on the state.
// Set the transition to 0 to say there has been no new transition.
if (transition > 0) {
  if (millis() - transition >= 5000)
  {
    transition = 0;
    if (state == STATE_BAD) {
      digitalWrite(PUMP_PIN, LOW);
    } else {
      digitalWrite(PUMP_PIN, HIGH);
    }
  }
}

Of course, that’s untested, but it gives you an idea about how to monitor for changes in state, not the current state.

I still don’t know how to change my part of code.

Nested if statements:

  if (voltage < 8.0)
  {
     if(currentTime - pump_offTime >= 5000))
     {
        turn_on = false;
        pump_onTime = currentTime;
     }
  }

From this, you can see that you can add a Serial.print() statement to confirm that the voltage is low. You can add another one to confirm that the time is right to do something.

You can also see that one might be true while the other is false. While this is true of the code you wrote, it is impossible, in your code, to add Serial.print() statements to determine which part of the if statement is true or false.

PaulS:
From this, you can see that you can add a Serial.print() statement to confirm that the voltage is low. You can add another one to confirm that the time is right to do something.

You can also see that one might be true while the other is false. While this is true of the code you wrote, it is impossible, in your code, to add Serial.print() statements to determine which part of the if statement is true or false.

So, for my case completed code should look like this?

  if (voltage < 8.0)
  {
    if (currentTime - pump_offTime >= 5000)
    {
      turn_on = false;
      pump_onTime = currentTime;
      Serial.println("1-false");
     }
  }
  else if (voltage > 12.0)
  {
    if (currentTime - pump_onTime >= 5000)
    {
      turn_on = true;
      pump_offTime = currentTime;
      Serial.println("2-true");
     }
  }

But that’s still not ok… If voltage varies from 8 to 12 volts, Serial.println stops writting (because no condition is satisfied) but because of that, delay timers (pump_offTime and pump_onTime) are not reset to currentTime. Because of that i don’t get delay while voltage drops below 8 or go higher than 12.

So why not just add an else clause to the voltage if/else if where you set pump_offTime and pump_onTime to the current time?

Also, you’re wasting CPU time on calculating floats, when you can use some simple algebra to determine the value of the sensor which is associated to 8 volts and 12 volts:

if (voltage < 8.0)
if ((analogRead(A0) / 1024.0) * 30.0) < 8.0)
if (analogRead(A0) < 8.0 * 1024.0 / 30.0)
if (analogRead(A0) < 273) // 8 volts

Arrch:
So why not just add an else clause to the voltage if/else if where you set pump_offTime and pump_onTime to the current time?

Please, give me an example… I am trying but i can’t fix it (or it doesn’t work).


Finally, it works! I never thought this “if-else if-else” statements could be so tricky…

  if (voltage < 8.0)
  {
    if (currentTime - pump_offTime >= 5000)
    {
      turn_on = false;
      pump_onTime = currentTime;
      Serial.print("1-false  ");
      Serial.println(turn_on);
    }
  }
  else if (voltage > 12.0)
  {
    if (currentTime - pump_onTime >= 5000)
    {
      turn_on = true;
      pump_offTime = currentTime;
      Serial.print("2-true  ");
      Serial.println(turn_on);
     }
  }
  else
  {
    pump_onTime = currentTime;
    pump_offTime = currentTime;
    Serial.print("3-reset  ");
    Serial.println(turn_on);
  }

luxy: I am trying but i can't fix it (or it doesn't work).

If you've made your best attempt, and it's still not working, then post the updated code.

 if (voltage < 8.0)
  {
    if (currentTime - pump_offTime >= 5000)
    {
      turn_on = false;
      pump_onTime = currentTime;
      Serial.print("1-false  ");
      Serial.println(turn_on);
    }
  }
  else if (voltage > 12.0)
  {
    if (currentTime - pump_onTime >= 5000)
    {
      turn_on = true;
      pump_offTime = currentTime;
      Serial.print("2-true  ");
      Serial.println(turn_on);
     }
  }
  else
  {
    pump_onTime = currentTime;
    pump_offTime = currentTime;
    Serial.print("3-reset  ");
    Serial.println(turn_on);
  }

Now it’s ok, i did it. The problem was that i tried the same thing before without nested statements. Because of that (and timers) code was always in this else statement. It looks funny simple now :blush:

Arrch:
Also, you’re wasting CPU time on calculating floats, when you can use some simple algebra to determine the value of the sensor which is associated to 8 volts and 12 volts:

if (voltage < 8.0)

if ((analogRead(A0) / 1024.0) * 30.0) < 8.0)
if (analogRead(A0) < 8.0 * 1024.0 / 30.0)
if (analogRead(A0) < 273) // 8 volts

I need whole range of voltage information all the time (in other parts of code), but it’s good to know. I never thought about the problem of wasting cpu time up till now.

luxy: I need whole range of voltage information all the time (in other parts of code), but it's good to know.

You can always read the sensor value into a variable. Unless the determined voltage varies based on other inputs, then there is no need to convert it for just comparisons. If you need it for other purposes, such as displaying on an LCD, then only convert it when you need to use it.

Arrch: If you need it for other purposes, such as displaying on an LCD, then only convert it when you need to use it.

Ok, now i understand what you mean. If i am comparing fixed values i can use only pure adc data, without converting it (to voltage in my case).

Since i am writing voltage to lcd every second.. In that case i can't avoid calculating it like that, right?

luxy:
Since i am writing voltage to lcd every second… In that case i can’t avoid calculating it like that, right?

No, however, assuming your setup is non-blocking and your loop function is running more often than once per second, you can avoid calculating every time, and only calculate it once per second, before you display it.

The problem was that i tried the same thing before without nested statements.

If you think about it, logical AND (&&) would achieve the same thing, without the nesting.

Arrch: No, however, assuming your setup is non-blocking and your loop function is running more often than once per second, you can avoid calculating every time, and only calculate it once per second, before you display it.

I am using delay(1000) in main loop.. I read a lot about how to avoid delay and why it isn't ok but in my case i think it shouldn't be a problem. I don't need fast responses (rather the opposite).

AWOL:
If you think about it, logical AND (&&) would achieve the same thing, without the nesting.

Do you mean like this?

  if ((voltage < 10.0) && (currentTime - pump_offTime >= 5000))
  {
      turn_on = false;
      pump_onTime = currentTime;
      Serial.println("1");
  }
  else if ((voltage > 16.0) && (currentTime - pump_onTime >= 5000))
  {   
      turn_on = true;
      pump_offTime = currentTime;  
      Serial.println("2");
  }
  else
  {
    pump_onTime = currentTime;
    pump_offTime = currentTime;
    Serial.println("3");
  }

That’s not working… Program is always in third, “else” state.

Not sure what AWOL was suggesting, but that wouldn't be the way to do it, because in that example, the else clause will get fired even if the voltage is above or below the associated thresholds, due to the check for timing. You only want to set the pump times to the current time when voltage is between the thresholds, which the most recent code DOESN'T do.

It looks funny simple now

Yeah, it's funny how organizing code properly suddenly makes it clear what is going on.

I prefer the nested if statements, just because I can add or comment out stuff to see what part of the compound if statement is triggered.

But, you should be able to make the compound if statements work. Judicious use of Serial.print(), in each if block and the else block will tell you why it is getting into the else block. As a hint, the last else needs to care about time, which it currently doesn't.

In the nested case, time and voltage are separated. In the compound case, they are not, and the else clause is executed, regardless of time. Clearly, it should not be.

I am glad that i solve it.. I learned something new again. Thank you for all your help!