Trying to persist a time setting outside loop()

This seems to be a common question across the boards so I'll just post the basis for it here: Alternating which pump will start first in the loop - #14 by Robin2

I've watched the BWD example, but I think I've bitten off a bit more than I can chew with the analog slider. The problem is obviously it's checking the slider value on each loop so constantly updating the lowPumpStart

What I'm trying to make work is if pump1 has been on 30 seconds, then pump2 comes online. However, if I push the slider below 900 then they both cut off. Obviously this was just going to be an IF Pump1 has been on for 30 seconds, set pin HIGH. Any tips?

int motor1 = 1;
int motor2 = 2;
int waterheight = A2;
int lowPumpStart = 0;
int highPumpStart = 0;
int highPumpWait = 10000; 

void setup()
{
	pinMode(motor1, OUTPUT);
	pinMode(motor2, OUTPUT);

	pinMode(waterheight, INPUT);

	digitalWrite(motor1, LOW);
	digitalWrite(motor2, LOW);

}

void loop()
{
	unsigned long currentTime = millis();
	int pumpTrigger = analogRead(waterheight);

	if (pumpTrigger > 900)
	{
		digitalWrite(motor1, HIGH);
		lowPumpStart = currentTime;
	}
}

Hello cormack12

Make and post a timing diagram showing all dependencies.

p.s. I can´t find a pump naming in the sketch.

Hello
If you need to set pin only after 30 seconds

why do you set it HIGH immediately in the code?

Hey!

Pump1 is meant to come on when the water hits a certain height (set by the analog slider going above 900)
Then if Pump1 has been on 30 seconds, Pump2 is meant to kick in to help it.

So all I was going to do was log the time Pump1 came on, and if the state was still HIGH and 30 seconds had elapsed, turn on Pump2 (NOTE: I'm using 10s in the code currently to avoid waiting the full 30 when testing). But obviously because I'm updating that time in the loop it just spams the currentTime to the global var.

Should have mentioned I'm using UnoArduSim as well.

When pump2 comes online then both pumps stay on until waterheight is below 900? I'm trying to understand how you want this to work.

A couple of general comments:

  1. Any variables storing millis() time should be declared as unsigned long
  2. Analog values tend to bounce a little. I would check the value within a +/- delta so that you don't have the pumps turning on and off when the value is around 900

With respect to comparing the value to 900 you need to detect when the value moves from less than 900 to greater than 900. Otherwise you have the issue you have now where you are constantly resetting the time value. This requires that you save the last value of water height and compare it to the current value every time you go through loop().

1 Like

As far I see - you only declared this interval, but not using it in the code....

Yeah that's right.

So basically measure the water height in realtime (hence using the slider)

If it reaches 900 then start Pump1, if it doesn't fall below 900 within 30 seconds then activate Pump2 (so two pumps are active).
Then when the slider is changed to below 900, turn both pumps off again

Good algorithm. Now you can write code following these steps

Yeah, I removed it as once I have the time in the right place, I can figure the rest out. But basically the line below was going to be

if (currentTime - lowPumpStart > highPumpWait)
{
   digitalWrite(motor2, HIGH)
}

something that might get you going.

int motor1 = 1;
int motor2 = 2;
int waterheight = A2;
int lowPumpStart = 0;
int highPumpStart = 0;
int highPumpWait = 10000; 

void setup()
{
  pinMode(motor1, OUTPUT);
  pinMode(motor2, OUTPUT);

  pinMode(waterheight, INPUT);

  digitalWrite(motor1, LOW);
  digitalWrite(motor2, LOW);

}

bool pumponeon=false;

void loop()
{
  unsigned long currentTime = millis();
  int pumpTrigger = analogRead(waterheight);

  if (pumpTrigger > 900)
  {
    digitalWrite(motor1, HIGH);
    lowPumpStart = millis();
    pumponeon = true;
  }

if( ((millis() - lowPumpStart) >= 1000000000000) && pumponeon )
{
turn on pump 2
}

}
  
}
1 Like

Hello cormack12

Take a view here:

1 Like

This will show you how to use state change detection to detect the water height. I don't have time to test but hopefully you see how it works:

int motor1 = 1;
int motor2 = 2;
int waterheight = A2;
unsigned long lowPumpStart = 0;
unsigned long highPumpStart = 0;
const unsigned long highPumpWait = 10000;
const int whDelta = 5;

void setup()
{
	pinMode(motor1, OUTPUT);
	pinMode(motor2, OUTPUT);

	pinMode(waterheight, INPUT);

	digitalWrite(motor1, LOW);
	digitalWrite(motor2, LOW);

}

void loop()
{
  static int lastPumpTrigger = 0;	
	int pumpTrigger = analogRead(waterheight);
	unsigned long currentTime = millis();

  if (abs(lastPumpTrigger - pumpTrigger) > whDelta)
  {
    // Use state change detection to turn motors on and off
    if (lastPumpTrigger < 900 && pumpTrigger > 900)
    {
      // Water height has exceeded 900 so turn pump 1 on
  		digitalWrite(motor1, HIGH);
	  	lowPumpStart = currentTime;
    }
    else if (lastPumpTrigger > 900 && pumpTrigger < 900)
    {
      // Water height is less than 900 so turn both pumps off
    	digitalWrite(motor1, LOW);
    	digitalWrite(motor2, LOW);
    }
  }
  lastPumpTrigger = pumpTrigger;

  // TBD: test to see if pump 1 has been on more than 30 seconds and if so turn pump 2 on.
  
}
1 Like

Build-in a hysteresis. Not if > 900 or if < 900, rather, if > 915 and if < 885 or your pumps will on/off to death.

Here's a demo with hyteresis and random simulation:

// https://wokwi.com/projects/354300148632449025
const byte P1=13;  // normal pump
const byte P2=12;  // extra pump after 30 sec

int simulateLevel (bool p1, bool p2) {
  // p1 reduces by 4/sec 
  // p1 & p2 reduces by 12/sec 
  // both false increases by 15/sec 
  // random by (-10...10)/sec
  int x = -10 + random(21);
  if (!p1 && !p2) x +=15;
  if ( p1 ) x -=4;
  if ( p2 ) x -=8;
  return x;
}

void setup() {
  pinMode(P1, OUTPUT);
  pinMode(P2, OUTPUT);
  Serial.begin(115200);
}

unsigned int level=800;
bool p1, p2;
unsigned long runTime;


const unsigned int HI = 900;  // switch on
const unsigned int LO = 800;  // switch off
const unsigned long BOOST = 30000; // switch extra pump on after BOOST msec

void loop() {
  Serial.print("800 900 ");Serial.println(level); // for SerialPlotter
  if (level > HI) { 
    if (p1==false) runTime= millis();
    p1 = true; 
  }
  if (p1 && millis() - runTime > BOOST) {
    p2 = true;
  }
  if (level < LO) {
    p1 = false;
    p2 = false;
  }
  digitalWrite(P1, p1);
  digitalWrite(P2, p2);
  level += simulateLevel(p1, p2);
  delay(1000);
}

Have fun.

You should really use HIGH and LOW for p1 and p2.

a link to the demo.

Which is fun, but turn the delay down to like 200 ms. Life to short!

And this modifcation

graphical versionof @michael_x 's simulation

uses a few plotter tricks and shows the hysteresis in action.

a7

p1 and p2 are bool variables.

Do you really suggest
digitalWrite(P1,p1?HIGH:LOW);

Feel free to do so yourself.

I hope the compiler smiles and ignores it. :face_with_peeking_eye:

You can make them byte or int.

No. See above.

What you did will work but only because HIGH and LOW are defined as follows:

#define HIGH 0x1
#define LOW  0x0

However, it is customary to use the defined values should they be changed in the future, otherwise your code will break. It is also more readable, in my opinion, and consistent.

... I won't use Arduino any more. :wink:

Using digitalWrite or digitalRead with bool or boolean variables has advantages.
If you do some research, you'll notice that digitalRead takes a byte data type as value parameter and applies the byte to bool c++ internal rules. (Anything except 0 is HIGH / true)

An int variable is error prone. Give digitalWrite(13, -256); a try and see that int is the wrong type: warning: large integer implicitly truncated to unsigned type [-Woverflow]
BTW: this warning disappears if you use an int variable int i = 256 or = -256

Whilst the chances of the definitions of true, false, HIGH, LOW, etc being changed are small I prefer to use the appropriate words as it tends to make the code easier to read and understand

Code like

if (digitalRead(pin0 == 1)

or

if (started == 1)

always makes me squirm even though I know what it means. To me the state of a digital pin is either HIGH or LOW, not 1 or zero and a bool is either true or false

The defined values exist for a reason so why not use them ?