using millis for controlling relay timing

Hello,

I’m struggling with my project a bit, so hopefully someone with more experience might be able to shed some light.

Here is the code I’m using:

unsigned long CurrentMillis = millis();
 int PMax = 3000;
int PVal = 0; // Variable for current pressure value

  

if ((ValveState == LOW) && (CurrentMillis - PreviousMillis >= Interval1)) {
    //CycleStage = 1;
    PreviousMillis = CurrentMillis;  // Remember the time
    ValveState = HIGH;
    digitalWrite(ValveRly, ValveState);
    int PVal = 0; // Variable for current pressure value
    //PSensorVal = analogRead(AnalogInPin);    // read the analog in value:
    //PVal = map(PSensorVal, 0, 1023, 0, 400);  // map PVal to the range of the sensor (psi)
     //while (PVal < PMax){
      //PVal++;
      //Serial.println(PVal);
      //if (CurrentMillis - PreviousMillis >= Interval3)
      //Serial.println("Millis reached");
      //}
    
}else if ((ValveState == HIGH) && (CurrentMillis - PreviousMillis >= Interval2)) {
    //CycleStage = 0;
    PreviousMillis = CurrentMillis;   // Remember the time
    ValveState = LOW;
    digitalWrite(ValveRly, ValveState); 
    //LastPVal = PVal;
    Start_counts ++;

}}

So the gist of the program is to switch a relay controlling a valve, the on / off states are controlled with Interval1 and Interval2 using comparison for millis. I have this part working correctly. However, when the relay state is high, I’d like to wait until a pressure (Pval) is achieved (PMax) before it goes low again, during this period if a third interval has passed Interval3, then stop the cycle. In other words, if the pressure is achieved move on, if not allow a further interval then stop if it still isn’t reached.

I have had part of the latter portion of my code working, but I can’t seem to get bot cases to complete:

PVal >= PMax;

//or

(CurrentMillis - PreviousMillis) >= Interval3;

I can’t seem to get my head around the while loop and using if within it. I have include the while loop in question, but // it out.

Many thanks

So the gist of the program is to switch a relay controlling a valve, the on / off states are controlled with Interval1 and Interval2 using comparison for millis.

I would separate the what-to-do from the when-to-do-it part.

     //while (PVal < PMax){
      //PVal++;
      //Serial.println(PVal);
      //if (CurrentMillis - PreviousMillis >= Interval3)
      //Serial.println("Millis reached");
      //}

Do you want to wait for the pressure to increase OR for some interval to pass? If so,

   while(PVal < PMax || CurrentMillis - PreviousMillis >= Interval3)
   {
   }

Thanks Paul

I have tried this line

 while(PVal < PMax || CurrentMillis - PreviousMillis >= Interval3)
   {
   }

But it only counts up to PMax, the timer part is ineffective.

I’d like to check the pressure has been reached after the first interval, if so carry on, otherwise allow another interval to pass. If the pressure is reached during the second interval carry on to next part of the code otherwise go to stop procedure.

Try putting some ( ) to force the logic you want to occur:

 while((PVal < PMax) || ((CurrentMillis - PreviousMillis) >= Interval3))
   {
   }

Ok, I had tried this line before:

while((PVal < PMax) || ((CurrentMillis - PreviousMillis) >= Interval3))
   {
   }

But I couldn’t get the condition to work correctly while under the while loop. Due to my lack of experience, I’ve opted to find another way of getting the same result:

int PVal = 0; // Variable for current pressure value
 PSensorVal = analogRead(AnalogInPin);    // read the analog in value:
  PVal = map(PSensorVal, 0, 1023, 0, 400);  // map PVal to the range of the sensor (psi)

 unsigned long CurrentMillis = millis();
 int PMax = 400;
 int i = 0;

if ((ValveState == LOW) && (CurrentMillis - PreviousMillis >= Interval1)) {
    //CycleStage = 1;
    PreviousMillis = CurrentMillis;  // Remember the time
    ValveState = HIGH;
    digitalWrite(ValveRly, ValveState);
    Serial.println("interval1");
    while (PVal < PMax){
      for (i=0; i < 300; i++){
        Serial.println(i);
        Serial.println(PVal);
        delay(10);
      }if (i == 300){
        i = 0;
        break;
      }}

}else if ((ValveState == HIGH) && (CurrentMillis - PreviousMillis >= Interval2)) {
    //CycleStage = 0;
    PreviousMillis = CurrentMillis;   // Remember the time
    ValveState = LOW;
    digitalWrite(ValveRly, ValveState);
    Serial.println("interval2");
    //LastPVal = PVal;
    Start_counts ++;

}}

I’m just a bit concerned about using the 10ms delay, if the estop function is called (from elsewhere in the code) it needs to happen quickly. Can anyone comment on whether this might be a good or otherwise implementation? Please bare in mind this is my first real program.

Many thanks.

P.s. apologies for the lack of annotations, I’ll go back and do those later.

drumsticksplinter:
Thanks Paul

I have tried this line

 while(PVal < PMax || CurrentMillis - PreviousMillis >= Interval3)

{
  }




But it only counts up to PMax, the timer part is ineffective.

I'd like to check the pressure has been reached after the first interval, if so carry on, otherwise allow another interval to pass. If the pressure is reached during the second interval carry on to next part of the code otherwise go to stop procedure.

oh, right. So you are after

while(CurrentMillis - PreviousMillis < Interval1   
         || (CurrentMillis - PreviousMillis < Interval3 && PVal < PMax))
{
}

Do nothing (empty loop) while we haven’t reached interval 1, or until the pressure is low and we haven’t reached interval 2.

We can express it the other way around with some boolean logic:

loop while A | (B & C)
stop when ~ [A | (B & C)]
stop when ~A & ~(B & C)
stop when ~A & (~B | ~C)

which reads “stop when we have gone past interval 1 and either the pressure has reached max or interval2 has also been passed”.

Alternatively, you could do it like this:

for(;;) {
  if(CurrentMillis - PreviousMillis < Interval1) continue;
  if(PVal >= PMax) break;
  if(CurrentMillis - PreviousMillis >= Interval3) break;
}

or

while(CurrentMillis - PreviousMillis < Interval1){} // first delay
while(PVal < PMax && CurrentMillis - PreviousMillis < Interval3){} // sample pressure until second delay

Whatever floats your boat.

Excellent! :slight_smile:

Thanks so much for the examples, I'll play around with those today. That makes my code look embarrassing, haha!

There's also this elapsedmillis library which might be of interest.

Thanks JimboZA,

That's very interesting, will come in handy for sure!

drumsticksplinter:
That's very interesting, will come in handy for sure!

It's just the millis timing stuff in a box, makes it easier to implement, but essentially the same philosophy

    while (PVal < PMax){
      for (i=0; i < 300; i++){
        Serial.println(i);
        Serial.println(PVal);
        delay(10);
      }if (i == 300){
        i = 0;
        break;
      }}

Thiscodejust
looks like
crap.

EVERY statement goes on a line by itself.
ONE } per line is the limit!
NOTHING follows the } on the same line, except a comment.

Thiscodejust
looks like
crap.

I agree! I've got to get in a habit of writing cleaner tidier code, I'm learning slowly.

I've got to get in a habit of writing cleaner tidier code,

Or of using Tools + Auto Format.

drumsticksplinter:
I can't seem to get my head around the while loop and using if within it.

I definitely know how that feels. It's exactly the kind of thing that gets messy when you use the 'normal' way of Arduinio programming.

Last time this happened to me (I was trying to automatically close a garage door while monitoring serveral sensors and timers) I turned to Finite State Machines for a solution and I ended up writing my own framework for it.

Give it a try, it brings order into your software. You can write your own state machines and/or use one of the bundled ones. The Atm_led state machine is very handy for controlling and timing relays in spite of the name.

Say goodbye to while loops...