Time-controlled output with millis

Hey Guys,
for days im struggling with a quity simple if-condition in combination with the millis() function.
I would really appreciate it, if anybody can take a quick view on the code and help me out with solving this issue.

My goal is a code to control a pump, which is switched on by an transistor if an certain pressure is going under the target pressure. If the target pressure is reached, the pump should turn off.
For this part, the code is working without problems.

Now i need to ad an time-control for the situation, that the pump is switched on. If there are some leaks in the pump cycle, the pump probably would run for years - so we need to ad an time-control, which switches off the pump, after it runs more then 5 seconds.

With the current code, the red led ist blinking short and fast. the pump is turning on, if were under target pressure, but it does not turn off the pump after 5 seconds. So the failsafe-function does not work.

Does anybody can help out at this point?
Many thanks!! :slight_smile:

best regards
Gustav

current code:

void setup() {
// put your setup code here, to run once:
Serial.begin(9600); //initializes serial communication at set baud rate bits per second

pinMode(2, OUTPUT); // GREEN LED
pinMode(4, OUTPUT); // BLUE LED
pinMode(7, OUTPUT); // RED LED
pinMode(8,OUTPUT); // PUMP

}

void loop() {
// put your main code here, to run repeatedly:
long pumpenlaufzeit = -1; // -1 als Code für Pumpe ist nicht aktiv
int sensorVal = analogRead(A0); // read the value from sensor

Serial.print("sensor value:");
Serial.println(sensorVal); // print sensor value to screen

float voltage = (sensorVal*5.0)/1024.0;
Serial.print("Volts: ");
Serial.print(voltage);

// converting pressure
float pressure_pascal = (3.0*((float)voltage-0.47))*1000000.0;
float pressure_bar = pressure_pascal/10e5;

Serial.print("Pressure = ");
Serial.print(pressure_bar);
Serial.println("bars");
Serial.print("Pressure = ");

if ((pumpenlaufzeit = -1) && (pressure_bar < 0.03)){ //Pumpe ist im agestellten zustand und solldruck wird unterschritte -> pumpe geht an
pumpenlaufzeit = millis();
digitalWrite(2, HIGH); // turn green led on
digitalWrite(4, HIGH); // turn blue led on
digitalWrite(7, LOW); // turn red led off
digitalWrite(8, HIGH);// turn pump on

// triff einmalig zu, wenn pumpe aus UND der Druck NICHT im soll ist
}

if ((pumpenlaufzeit + 5000) < millis()){ // pumpe läuft länger als 5 sekunden, was auf undichtigkeit hindeutet. -> pumpe abstellen, fehler (rot) zeigen
digitalWrite(2, LOW);
digitalWrite(4, LOW);
digitalWrite(7, HIGH); //rote LED on
digitalWrite(8, LOW);
}

if ((pressure_bar < 0.03) == false) { // Standardfall dass der Druck wieder im Soll ist, pumpe abstellen
pumpenlaufzeit = -1;
digitalWrite (4,LOW); // blue LED off
digitalWrite (8,LOW); // pump off
digitalWrite(7,LOW); //red off
digitalWrite(2,HIGH); // green led on

}}

couple issues

pumpenlaufzeit is set to -1 at the top of loop, overwriting the millis() value captured below when the pump is turned on

 if ((pumpenlaufzeit = -1) && (pressure_bar < 0.03)){

probably intended

(pumpenlaufzeit == -1)

but the logic prevents testing if the pressure is < 0.03 once pumpenlaufzeit is set to by millis

when the pump times out and is turned off, is there any way the pump is turned of, potentially after an additional time (30 mins)

less importantly, i would suggest performing appropriate tests when the pump is on or off. no need for a flag, simply read the pump pin (digitalRead (PumpPin). when OFF, check if it needs to be turned on. when On check if pressure above threshold as well as the timeout and turn pump off

Hallo Gustav
Well, I think you have to extend your sketch with a time handler. This time handler have the following functions:
startTimer() and stopTimer() like a Eieruhr.
PseudoCode:
If (Pump switched on) startTimer()
If (Pump switched off) stopTimer()
If (Timer fired) switch pump off

Hey guys,
thanks for your answers!

I started to implement the tips, so the red LED isnt blinking any longer!
So the goal is coming closer :slight_smile:

As you can see i solved the problem of the red LED with a second if query for the situation, that the pump is running and solving the problem, that the pumpenlaufzeit is settet to millis() every time, the if query is called up.

The only issue left is, that the red LED isnt working so i think, that there´s a issue with the last "if" query.

I would like to not use the timer, because working under time pressure and didnt do anything with timer till now. :smiley:
Is there any possebility, to bring the program with millis(); working?
current code:

int pumpstatus;
long pumpenlaufzeit;

void setup() {
// put your setup code here, to run once:
Serial.begin(9600); //initializes serial communication at set baud rate bits per second

pinMode(2, OUTPUT); // GREEN LED
pinMode(4, OUTPUT); // BLUE LED
pinMode(7, OUTPUT); // RED LED
pinMode(8,OUTPUT); // PUMP

}

void loop() {

// put your main code here, to run repeatedly:
// -1 als Code für Pumpe ist nicht aktiv
int sensorVal = analogRead(A0); // read the value from sensor

Serial.print("sensor value:");
Serial.println(sensorVal); // print sensor value to screen

float voltage = (sensorVal*5.0)/1024.0;
Serial.print("Volts: ");
Serial.print(voltage);

// converting pressure
float pressure_pascal = (3.0*((float)voltage-0.47))*1000000.0;
float pressure_bar = pressure_pascal/10e5;

Serial.print("Pressure = ");
Serial.print(pressure_bar);
Serial.println("bars");
Serial.print("Pressure = ");

if ((pressure_bar < 0.03) == false) { // Standardfall dass der Druck wieder im Soll ist, pumpe abstellen
pumpenlaufzeit = -1;
digitalWrite (4,LOW); // blue LED off
digitalWrite (8,LOW); // pump off
digitalWrite(7,LOW); //red off
digitalWrite(2,HIGH); // green led on
pumpstatus == LOW;
}

if ((pumpenlaufzeit == -1) && pressure_bar < 0.03 && pumpstatus == LOW){ // Startbedingung, timer beginnt
pumpenlaufzeit = millis();
digitalWrite(2, HIGH); // turn green led on
digitalWrite(4, HIGH); // turn blue led on
digitalWrite(7, LOW); // turn red led off
digitalWrite(8, HIGH);// turn pump on
pumpstatus == HIGH;
}

if ((pressure_bar < 0.03) && pumpstatus == HIGH){ //Pumpe ist im agestellten zustand und solldruck wird unterschritte -> pumpe geht an

digitalWrite(2, HIGH); // turn green led on
digitalWrite(4, HIGH); // turn blue led on
digitalWrite(7, LOW); // turn red led off
digitalWrite(8, HIGH);// turn pump on
pumpstatus == HIGH;
}

if ((pumpenlaufzeit + 1000) <= millis() && pumpstatus == HIGH){ // pumpe läuft länger als 5 sekunden, was auf undichtigkeit hindeutet. -> pumpe abstellen, fehler (rot) zeigen
digitalWrite(2, LOW);
digitalWrite(4, LOW);
digitalWrite(7, HIGH); //rote LED on
digitalWrite(8, LOW);
}

}

Here is some code I use that does something similar to the OP's want.

void fDoMoistureDetector( void * parameter )
{
  //wait for a mqtt connection
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  int      TimeToWait = 5000000;
  uint64_t TimePast = esp_timer_get_time(); // used by the Kalman filter
  float    WetValue = 1.35f; // point to deenergize relay for water pump
  float    DryValue = 2.732f; // 
  float    ADbits = 4095.0f;
  float    uPvolts = 3.3f;
  //  float    adcValue_a = 0.0f;
  float    adcValue_b = 0.0f; //Jeanne's potted plant in yellow pot
  float    Range = DryValue - WetValue;
  float    RemainingMoisture = 0.0f;
  //SimpleKalmanFilter KF_ADC_a( 1.0f, 1.0f, .01f );
  SimpleKalmanFilter KF_ADC_b( 1.0f, 1.0f, .01f );
  for (;;)
  {
    adcValue_b = float( adc1_get_raw(ADC1_CHANNEL_3) ); //take a raw ADC reading
    adcValue_b = ( adcValue_b * uPvolts ) / ADbits; //calculate voltage
    KF_ADC_b.setProcessNoise( (esp_timer_get_time() - TimePast) / 1000000.0f );
    adcValue_b = KF_ADC_b.updateEstimate( adcValue_b ); // apply simple Kalman filter    
    RemainingMoisture = 100.0f * (1-((adcValue_b - WetValue) / (DryValue - WetValue))); //remaining moisture =  1-(xTarget - xMin) / (xMax - xMin)
    //energize or denergize water pump
    if ( RemainingMoisture <= 10.0f )
    {
      gpio_set_level( GPIO_NUM_5, LOW); //energize
    } else {
      //keep pump on till desired moisture level is reached
      if ( RemainingMoisture >= 40.0f )
      {
        gpio_set_level( GPIO_NUM_5, HIGH); // deenergize
      }
    }
    log_i( "adcValue_b = %f remaining moisture %f%", adcValue_b, RemainingMoisture );
    // publish to MQTT every 5000000uS
    if ( (esp_timer_get_time() - TimePast) >= TimeToWait )
    {
      //then publish
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles MQTTlient.loop() is running no other mqtt operations should be in process
      MQTTclient.publish( topicRemainingMoisture_0, String(RemainingMoisture).c_str() );
      xSemaphoreGive( sema_MQTT_KeepAlive );
      xSemaphoreTake( sema_mqttOK, portMAX_DELAY ); // update mqttOK
      mqttOK++;
      xSemaphoreGive( sema_mqttOK );
      TimePast = esp_timer_get_time(); // get next publish time
    }
    vTaskDelay( 100 ); //good refresh rate
    //log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
}// end fDoMoistureDetector()

The code is written for an ESP32 so esp_timer_get_time() is used instead of millis(). Otherwise when the pump is energized, the pump is checked for how long its ran and water level, if either water level or timeout is reached then pump is shut off.

should it be initialized to -1?

doesn't this check if the pressure is low and the pump is on? is there any need to turn the pump on if it's already on?

again, how does the system get reset if the pump is turned off due to a timeout

The system should be resettet manually e.d. by switching power off. So there is no need for a reset in the code, if the timeout occurs.

youre right, the third if-quiry dont make any sense.
Ive deleted third if quiry and initialized the pumpenlaufzeit with: long pumpenlaufzeit = -1;

The case, that the system does not go into the fail safe (pump off, red led on) is still here. And to be honest, I dont have anideas to solve it at the current point of time.
Do you see any illogical code, which prevents the system from working correctly?
I would appreciate it very lot, if you can help out with your knowledge :slight_smile:

best regards
Gustav

You should post code by using code-tags
There is an automatic function for doing this in the Arduino-IDE
just three steps

  1. press Ctrl-T for autoformatting your code
  2. do a rightclick with the mouse and choose "copy for forum"
  3. paste clipboard into write-window of a posting

best regards Stefan

i modified your code a bit. neatened it up (added whitespace) , replaced hardcoded values (e.g. 8, LOW) with symbols and added a 3rd pumpstatus state (FAIL) to prevent the pump going back on

yea, post code using </>

#undef MyHW
#ifdef MyHW
# define Led1    10 
# define Led2    11
# define Led3    12
# define Pump    13
#else
# define Led1    2 
# define Led2    4
# define Led3    7
# define Pump    8
#endif

enum { OFF = LOW, ON = HIGH, FAIL = 2 };

int           pumpstatus  = OFF;
unsigned long pumpenlaufzeit;

void setup () {
    // put your setup code here, to run once:
    Serial.begin (9600); //initializes serial communication at set baud rate bits per second
    pinMode (Led1, OUTPUT); // GREEN LED
    pinMode (Led2, OUTPUT); // BLUE LED
    pinMode (Led3, OUTPUT); // RED LED
    pinMode (Pump, OUTPUT); // PUMP
}

void loop () {
    // put your main code here, to run repeatedly:
    // -1 als Code fr Pumpe ist nicht aktiv
    int sensorVal = analogRead (A0); // read the value from sensor
    Serial.print ("   sensor value: ");
    Serial.print (sensorVal); // print sensor value to screen

    float voltage = (sensorVal*5.0)/1024.0;
    Serial.print (", ");
    Serial.print (voltage);
    Serial.print ("V, ");

    // converting pressure
    float pressure_pascal = (3.0*((float)voltage-0.47))*1000000.0;
    float pressure_bar = pressure_pascal/10e5;
    Serial.print (pressure_bar);
    Serial.println (" Bar");

 // Serial.print ("Pressure = ");

    // Standardfall dass der Druck wieder im Soll ist, pumpe abstellen
    if (pressure_bar > 0.03 && pumpstatus == ON) {
        digitalWrite (Led2, OFF); // blue LED off
        digitalWrite (Pump, OFF); // pump off
        digitalWrite (Led3, OFF); //red off
        digitalWrite (Led1, ON); // green led on
        pumpstatus = OFF;
        Serial.println ("pump off");
    }

    // Startbedingung, timer beginnt
    if (pressure_bar < 0.03 && pumpstatus == OFF) {
        pumpenlaufzeit = millis ();
        digitalWrite (Led1, ON); // turn green led on
        digitalWrite (Led2, ON); // turn blue led on
        digitalWrite (Led3, OFF); // turn red led off
        digitalWrite (Pump, ON);// turn pump on
        pumpstatus = ON;
        Serial.println ("pump on");
    }

#define Timeout 3000

    // pumpe luft lnger als 5 sekunden, was auf undichtigkeit
    // hindeutet. -> pumpe abstellen, fehler (rot) zeigen
 // if ((pumpenlaufzeit + Timeout) <= millis() && pumpstatus == ON) {
    if ((pumpenlaufzeit + Timeout) <= millis() && pumpstatus == ON){
        digitalWrite (Led1, OFF);
        digitalWrite (Led2, OFF);
        digitalWrite (Led3, ON); //rote LED on
        digitalWrite (Pump, OFF);
        pumpstatus = FAIL;
        Serial.println ("pump timeout");
    }

    delay (1000);
}

here's some output

   sensor value: 101, 0.49V, 0.07 Bar
   sensor value: 100, 0.49V, 0.05 Bar
   sensor value: 100, 0.49V, 0.05 Bar
   sensor value: 100, 0.49V, 0.05 Bar
   sensor value: 100, 0.49V, 0.05 Bar
   sensor value: 97, 0.47V, 0.01 Bar
pump on
   sensor value: 96, 0.47V, -0.00 Bar
   sensor value: 96, 0.47V, -0.00 Bar
   sensor value: 100, 0.49V, 0.05 Bar
pump off
   sensor value: 100, 0.49V, 0.05 Bar
   sensor value: 101, 0.49V, 0.07 Bar
   sensor value: 95, 0.46V, -0.02 Bar
pump on
   sensor value: 96, 0.47V, -0.00 Bar
   sensor value: 100, 0.49V, 0.05 Bar
pump off
   sensor value: 100, 0.49V, 0.05 Bar
   sensor value: 90, 0.44V, -0.09 Bar
pump on
   sensor value: 89, 0.43V, -0.11 Bar
   sensor value: 89, 0.43V, -0.11 Bar
   sensor value: 89, 0.43V, -0.11 Bar
pump timeout
   sensor value: 89, 0.43V, -0.11 Bar
   sensor value: 95, 0.46V, -0.02 Bar
   sensor value: 100, 0.49V, 0.05 Bar
   sensor value: 104, 0.51V, 0.11 Bar
   sensor value: 104, 0.51V, 0.11 Bar

Some additional hints:

you should use self-explaining names for everything.

I have replaced your hardcoded numbers with such constants

please describe in normal words each case that can happen.
This description shall avoid any programming-word.

You might have misconceptions about programming
The description in 100% normal words makes sure that you know the meaning of the words right.

If you have given this description it is very clear what your program should do
and can be compared what you have programmed so far.

int pumpstatus;
long pumpenlaufzeit;

const byte blueLED_Pin  = 4;
const byte pump_Pin     = 8;
const byte redLED_Pin   = 7;
const byte greenLED_Pin = 2;

const byte pressureSensor_Pin = A0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); //initializes serial communication at set baud rate bits per second

  pinMode(greenLED_Pin, OUTPUT); // no comments needed the constants explain themselves
  pinMode(blueLED_Pin,  OUTPUT); 
  pinMode(redLED_Pin,   OUTPUT); 
  pinMode(pump_Pin,     OUTPUT); 

/*
  pinMode(greenLED_Pin, OUTPUT); // GREEN LED
  pinMode(blueLED_Pin, OUTPUT); // BLUE LED
  pinMode(redLED_Pin, OUTPUT); // RED LED
  pinMode(pump_Pin, OUTPUT); // PUMP
*/
}

void loop() {

  // put your main code here, to run repeatedly:
  // -1 als Code für Pumpe ist nicht aktiv
  int pressureADC = analogRead(pressureSensor_Pin); // read the value from sensor

  Serial.print("pressure ADC :");
  Serial.println(pressureADC); 

  float voltage = (pressureADC * 5.0) / 1024.0;
  Serial.print("Volts: ");
  Serial.print(voltage);

  // converting pressure
  float pressure_pascal = (3.0 * ((float)voltage - 0.47)) * 1000000.0;
  float pressure_bar = pressure_pascal / 10e5;

  Serial.print("Pressure = ");
  Serial.print(pressure_bar);
  Serial.println("bars");
  Serial.print("Pressure = ");
  
  // Standardfall dass der Druck wieder im Soll ist, pumpe abstellen
  if ((pressure_bar < 0.03) == false) { 
    pumpenlaufzeit = -1;
    digitalWrite (blueLED_Pin, LOW); 
    digitalWrite (pump_Pin, LOW); 
    digitalWrite(redLED_Pin, LOW); 
    digitalWrite(greenLED_Pin, HIGH); 
    pumpstatus == LOW;
  }
  
  // Startbedingung, timer beginnt
  if ((pumpenlaufzeit == -1) && pressure_bar < 0.03 && pumpstatus == LOW) { 
    pumpenlaufzeit = millis();
    digitalWrite(greenLED_Pin, HIGH); 
    digitalWrite(blueLED_Pin, HIGH); 
    digitalWrite(redLED_Pin, LOW); 
    digitalWrite(pump_Pin, HIGH);
    pumpstatus == HIGH;
  }

  // Pumpe ist im agestellten zustand und solldruck wird unterschritte -> pumpe geht an
  if ((pressure_bar < 0.03) && pumpstatus == HIGH) { 

    digitalWrite(greenLED_Pin, HIGH); 
    digitalWrite(blueLED_Pin, HIGH); 
    digitalWrite(redLED_Pin, LOW); 
    digitalWrite(pump_Pin, HIGH);
    pumpstatus == HIGH;
  }
  // pumpe läuft länger als 5 sekunden, was auf undichtigkeit hindeutet. 
  // -> pumpe abstellen, fehler (rot) zeigen
  if ((pumpenlaufzeit + 1000) <= millis() && pumpstatus == HIGH) { 
    digitalWrite(greenLED_Pin, LOW);
    digitalWrite(blueLED_Pin, LOW);
    digitalWrite(redLED_Pin, HIGH); 
    digitalWrite(pump_Pin, LOW);
  }

}

best regards Stefan

Dear gcjr, Dear Stefan,

many many thanks for your help!
The code you posted works like a charm.
You made it possible to finish the project in time.

Also many thanks for your hints - theres a lot stuff to learn for me. :slight_smile:

I really like this forum. Never had this problem-solving oriented communication in any internet forum before. Cheers! :cowboy_hat_face:

best regards
Gustav