Alternating which pump will start first in the loop

Hi there,

I am doing a project with this scenario:

The water level in the sump is to be controlled as follows:
 If the level reaches high one pump will start to pump out.
 If the level does not reach low within about 30 seconds, the second pump will come into action. If the level does reach low before 30 second
pumping should stop.
 If the level does not reach low within about 30 seconds of the second
pump starting, an alarm is given. The alarm is sounded until the level reaches low. If the level does reach low before 30 seconds the pumping should stop.
 The pumps will stop when low is detected.
 To give even loading of the pumps they are to be used alternately, e.g. if
on one pump cycle P1 starts first and P2 is the ‘back up’ then on the next cycle P2 will start first and P1 is the ‘back up’.

I am using a potentiometer to act as a level sensor and LEDs to represent the pumps. I think i have the code for the operation of the pumps sorted, i just cannot figure out how to alternate which pump is used first. So if pump 1 starts first, then in the next loop pump 2 will start first and vice versa.

/*This code is for the control of water level in a sump. This is done using a potentiometer to measure the water level, two pumps to pump the water out and
  an alarm which warns the operator of the system failing to pump water to the low level after a set amount of time.

  A potentiometer is used to sense the water level, full will be indicated by an output of 1023, empty by an output of 0, LOW level will be indicated by 10%
  of 1023 and HIGH level will be indicated by 90% of 1023.

  FULL - 1023
  HIGH - 921
  LOW - 102
  EMPTY - 0

  An LED is used here to represent each water pump.

  The system will alternate which pump starts first to conserve the life of the pumps.
*/


int Pump1 = 8; //An LED to represent Pump 1 is installed at pin 8.
int Pump2 = 9; //An LED to represent Pump 2 is installed at pin 9.
int Alarm = 7; //The alarm is installed at pin 7.
int potPin = A5; //The water level sensing potentiometer is installed at pin analog pin A5.
int potValue = 0;

void setup() {
  pinMode(Pump1, OUTPUT);    //Designate Pump 1 at pin 8 as an OUTPUT.
  pinMode(Pump2, OUTPUT);    //Designate Pump 2 at pin 9 as an OUTPUT.
  pinMode(Alarm, OUTPUT);    //Designate the alarm at pin 7 as an OUTPUT.
}

void loop() {
  int sensorValue = analogRead(A5);    //Read the input from the analog Water level sensing potentiometer.
  if (analogRead(sensorValue) <= 921); //The system is monitoring the water level. If the level does not reach HIGH, system will not do anything.
  digitalWrite(Pump1 , LOW);

  if (analogRead(sensorValue) >= 921); //If water level reaches the HIGH level, the first pump will begin to work.
  digitalWrite(Pump1, HIGH);
  delay(30000);                        //The system will now wait 30 seconds. If the water level has not fell to the LOW level, pump 2 will start.

  if (analogRead(sensorValue) <= 102); //If the water has now reached LOW, Pump 1 will turn off.
  digitalWrite(Pump1 , LOW);
  if (analogRead(sensorValue) >= 102); //If the water has still not reached LOW, Pump 2 will also turn on.
  digitalWrite(Pump2 , HIGH);
  delay(30000);                        //The system will now wait a further 30 seconds.

  if (analogRead(sensorValue) <= 102); //If the water level has fallen to LOW then both pumps will turn off, if not then both pumps will remain on.
  digitalWrite(Pump1 & Pump2 , LOW);   //If the level has not fallen to LOW then a warning alarm will sound to alert the operator.
  if (analogRead(sensorValue) >= 102);
  digitalWrite(Pump1 & Pump2 , HIGH);
  digitalWrite(Alarm, HIGH);
  delay(30000);                        //The system will wait a further 30 seconds.

  if (analogRead(sensorValue) <= 102); //If the water level has reached LOW, the pumps and the alarm will turn off.
  digitalWrite(Pump1 & Pump2 & Alarm , LOW);
  if (analogRead(sensorValue) >= 102); //If the water level has not reached LOW, the pumps will turn off but the alarm will remain on until LOW is reached.
  digitalWrite(Pump1 & Pump2 , LOW);
  digitalWrite(Alarm, HIGH);

  if (analogRead(sensorValue) <= 102);  //Once the LOW level has been reached, the alarm will silence.
  digitalWrite(Alarm, LOW);  
}

code like this

  digitalWrite(Pump1 & Pump2 , LOW);   //If the level has not fallen to LOW then a warning alarm will sound to alert the operator.

does not do what you think. You need 2 separate digitalWrite() calls

code like this

  delay(30000);                        //The system will wait a further 30 seconds.

causes your program to hang and do nothing for 30 seconds rather than continue to monitor the water level and react. Look at the BlinkWithoutDelay example sketch (File->Examples->02.Digital->BlinkWithoutDelay) so learn how to get rid of those delay() functions.

You should also take a look at Several Things at the same time to maybe rethink the structure of your code.

This project seems awfully familiar...

Suppose you have two more variables called pumpFirst and pumpSecond. To start with pumpFirst will have the value of Pump1 and pumpSecond will have the value of Pump2.

Then rather than

digitalWrite(Pump1 , LOW);

have

digitalWrite(pumpFirst , LOW);

with similar changes throughout the program.

When the code gets to the bottom just add something like this

if (pumpFirst = Pump1) {
   pumpFirst = Pump2;
   pumpSecond = Pump1;
}
else {
   pumpFirst = Pump1;
   pumpSecond = Pump2;
}

Separately I am not at all convinced that your use of all those long delay()s gives you the logic you need.

I think you need to record the value of millis() when (say) pumpFirst starts and then keep checking to see if it is still going at a later time with code something like this pseudo code

if (water level too high) {
     if (millis() - pumpFirstStartTime > pumpFirstInterval) {
         // start pumpSecond
         pumpSeconStartTime = millis();
     }
}
else {
    // whatever
}

The functions delay() and delayMicroseconds() block the Arduino from checking anything else until they complete.
Have a look at how millis() is used to manage timing without blocking in Several Things at a Time.

And see Using millis() for timing. A beginners guide if you need more explanation.

...R

There's a lot of weird stuff in your code.

What do you consider the water level if it's between your points?

FULL - 1023
HIGH - 921
LOW - 102
EMPTY - 0

So if it's between 102 and 921, then the level is normal and the pumps don't run?

So are you wanting the pump stage 1 to kick on when the water reaches 921, and then pump until it gets to 102, then remain off until it gets to 921 again, but reverse the order of the primary and secondary pumps?

blh64:
code like this

  digitalWrite(Pump1 & Pump2 , LOW);   //If the level has not fallen to LOW then a warning alarm will sound to alert the operator.

does not do what you think. You need 2 separate digitalWrite() calls

code like this

  delay(30000);                        //The system will wait a further 30 seconds.

causes your program to hang and do nothing for 30 seconds rather than continue to monitor the water level and react. Look at the BlinkWithoutDelay example sketch (File->Examples->02.Digital->BlinkWithoutDelay) so learn how to get rid of those delay() functions.

You should also take a look at Several Things at the same time to maybe rethink the structure of your code.

This project seems awfully familiar...

Thank you, I have added the extra digitalWrite calls and removed the &'s.

Regarding the delays, i think you may be correct after re- reading the scenario. It does say if the level is reached before 30seconds, so ill look into how to delay and monitor at the same time!

Yes this is probably a familiar, its part of an assignment I've been given and its been around a while. Just the online learning materials provided are not the best.

Robin2:
Suppose you have two more variables called pumpFirst and pumpSecond. To start with pumpFirst will have the value of Pump1 and pumpSecond will have the value of Pump2.

Then rather than

digitalWrite(Pump1 , LOW);

have

digitalWrite(pumpFirst , LOW);

with similar changes throughout the program.

When the code gets to the bottom just add something like this

if (pumpFirst = Pump1) {

pumpFirst = Pump2;
  pumpSecond = Pump1;
}
else {
  pumpFirst = Pump1;
  pumpSecond = Pump2;
}






Separately I am not at all convinced that your use of all those long delay()s gives you the logic you need.

I think you need to record the value of millis() when (say) pumpFirst starts and then keep checking to see if it is still going at a later time with code something like this pseudo code


if (water level too high) {
    if (millis() - pumpFirstStartTime > pumpFirstInterval) {
        // start pumpSecond
        pumpSeconStartTime = millis();
    }
}
else {
    // whatever
}




The functions delay() and delayMicroseconds() block the Arduino from checking anything else until they complete.
Have a look at how millis() is used to manage timing without blocking in [Several Things at a Time](http://forum.arduino.cc/index.php?topic=223286.0).

And see [Using millis() for timing. A beginners guide](http://forum.arduino.cc/index.php?topic=503368.0) if you need more explanation.

...R

So would you recommend changing the Pump1 to pumpfirst the whole way through the code?

Thank you i will look at millis instead of delays.

Stoopalini:
There's a lot of weird stuff in your code.

What do you consider the water level if it's between your points?

FULL - 1023
HIGH - 921
LOW - 102
EMPTY - 0

So if it's between 102 and 921, then the level is normal and the pumps don't run?

So are you wanting the pump stage 1 to kick on when the water reaches 921, and then pump until it gets to 102, then remain off until it gets to 921 again, but reverse the order of the primary and secondary pumps?

Yes its just a theoretical scenario I've been given, it just wants to empty the water in the tank once it reaches full. Then monitor until high again.

rb1994:
So would you recommend changing the Pump1 to pumpfirst the whole way through the code?

Yes, except that you also need the Pump1 and Pump2 variables as they hold the names of the I/O pins that control each pump.

Sometimes you want Pump1 to be pumpFirst and other times you want Pump2 to be pumpFirst

...R

rb1994:
Yes its just a theoretical scenario I've been given, it just wants to empty the water in the tank once it reaches full. Then monitor until high again.

Ahhh, gotcha ... so this is a class assignment?

And to think I was about to post the code for you LOL ...

I would break this up into separate functions, so my main loop would be something simple like this:

void loop() {
  SetPrimaryPump();
  CheckWaterLevel();
  SetPumpStage();
  ControlPumps();  
  HandleAlarm();
}

Then it'll be much easier to work through the logic.

You'll need more variables to make it easy to follow, and control the various state. Basically, you're building a state machine (or really, a couple of different state machines).

// VARIABLE DEFINITIONS -----------------------------------------------
int LevelSensorValue = 0;                         //Variable to store reading of the water level sensor reading
String WaterLevel = "";                           //Variable to store the water level in clear text
int PumpTime = 30;                                //Set to # of seconds between pump stages
int PumpStage = 0;                                //Variable to control stage of pumping (0 = pumps off, 1 = one pump on, 2 = both pumps)
int PrevPumpStage = 0;                            //Variable to store the previous pumping stage
bool PrimaryPump = 0;                             //Variable to control the alternating of which pump becomes pump 1 and which is pump 2
unsigned long int Stage1TimeStamp = millis();     //Variable to store time stamp for determining duration of pump stage 1 runtime
unsigned long int Stage2TimeStamp = millis();     //Variable to store time stamp for determining duration of pump stage 2 runtime
String Alarm = "No";                              //Variable to store if the Alarm should be sounding

Robin2:
Yes, except that you also need the Pump1 and Pump2 variables as they hold the names of the I/O pins that control each pump.

Sometimes you want Pump1 to be pumpFirst and other times you want Pump2 to be pumpFirst

...R

My solution to that was to have a PrimaryPump variable ... and just use this when they need to swap.

PrimaryPump = !PrimaryPump;

and then a function like this:

void SetPrimaryPump() {
  if (PrimaryPump == 0) {
    int Pump1 = 8;                                    //Set pin 8 as pump 1
    int Pump2 = 9;                                    //Set pin 9 as pump 2
  }
  if (PrimaryPump == 1) {
    int Pump1 = 9;                                    //Set pin 9 as pump 1
    int Pump2 = 8;                                    //Set pin 8 as pump 2
  }
}

Just change the pin assignments when they need to swap. Only call the function when the pumping state changes to both being off though; so they don't continually swap back and forth.

Stoopalini:

void SetPrimaryPump() {

if (PrimaryPump == 0) {
    int Pump1 = 8;                                    //Set pin 8 as pump 1
    int Pump2 = 9;                                    //Set pin 9 as pump 2
  }
  if (PrimaryPump == 1) {
    int Pump1 = 9;                                    //Set pin 9 as pump 1
    int Pump2 = 8;                                    //Set pin 8 as pump 2
  }
}

Those variables go out of scope outside the if() statement so they are useless.

blh64:
Those variables go out of scope outside the if() statement so they are useless.

Why would they go out of scope if they're defined as global variables, at the start of the sketch?

I use this approach all the time in my projects, and have never had it cause an issue.

Stoopalini:
Ahhh, gotcha ... so this is a class assignment?

And to think I was about to post the code for you LOL ...

I would break this up into separate functions, so my main loop would be something simple like this:

void loop() {

SetPrimaryPump();
  CheckWaterLevel();
  SetPumpStage();
  ControlPumps(); 
  HandleAlarm();
}




Then it'll be much easier to work through the logic. 

You'll need more variables to make it easy to follow, and control the various state. Basically, you're building a state machine (or really, a couple of different state machines). 



// VARIABLE DEFINITIONS -----------------------------------------------
int LevelSensorValue = 0;                        //Variable to store reading of the water level sensor reading
String WaterLevel = "";                          //Variable to store the water level in clear text
int PumpTime = 30;                                //Set to # of seconds between pump stages
int PumpStage = 0;                                //Variable to control stage of pumping (0 = pumps off, 1 = one pump on, 2 = both pumps)
int PrevPumpStage = 0;                            //Variable to store the previous pumping stage
bool PrimaryPump = 0;                            //Variable to control the alternating of which pump becomes pump 1 and which is pump 2
unsigned long int Stage1TimeStamp = millis();    //Variable to store time stamp for determining duration of pump stage 1 runtime
unsigned long int Stage2TimeStamp = millis();    //Variable to store time stamp for determining duration of pump stage 2 runtime
String Alarm = "No";                              //Variable to store if the Alarm should be sounding

No it's not for a class, I'm studying a programming course in my spare time to further my knowledge in my field of work. I'm an aircraft engineer, specialising in avionics. I'd like to get into the design of the avionics equipment, you've got to start with the basics like this!

Thanks for the help with the separate functions, I'll report back tomorrow after trying all the advice!

Stoopalini:
Why would they go out of scope if they're defined as global variables, at the start of the sketch?

I use this approach all the time in my projects, and have never had it cause an issue.

Because you are declaring a new, local variable inside the if() statement which will mask the global variable of the same name.

if(true) {
  int var1;  // declaring a variable. it is local to this block
  Pump1 = 9;  // assigning a value to a global variable
}

There is a difference between defining a variable and assignment to a variable.

blh64:
Because you are declaring a new, local variable inside the if() statement which will mask the global variable of the same name.

if(true) {

int var1;  // declaring a variable. it is local to this block
  Pump1 = 9;  // assigning a value to a global variable
}



There is a difference between defining a variable and assignment to a variable.

LOL ... I see it now. I did a quick copy/paste and forgot to remove the "int" from the lines.

For the original poster: If you declare a variable (like in my initial post where I had "int pump1 = 8") inside a function, it is only valid inside that same function.

This is what it should have been:

// Function to control which pump is primary and which is secondary.  This just swaps the pin definitions  --------------------------------------------------------
void SetPrimaryPump() {
  if (PrimaryPump == 0) {
    Pump1 = 8;                                    //Set pin 8 as pump 1
    Pump2 = 9;                                    //Set pin 9 as pump 2
  }
  if (PrimaryPump == 1) {
    Pump1 = 9;                                    //Set pin 9 as pump 1
    Pump2 = 8;                                    //Set pin 8 as pump 2
  }
}

Stoopalini:
My solution to that was to have a PrimaryPump variable ... and just use this when they need to swap.

Most programming problems have at least 23 solutions.

I agree completely that using functions as in your Reply #6 is a much better approach than the OP's original code - I posted this Tutorial about Planning and Implementing a Program
some time ago.

However I deliberately wrote my Reply #2 as an easy transition from the OP's code to a solution to the problem about swapping pumps.

...R

Robin2:
Most programming problems have at least 23 solutions

...although empirically the figure has been determined to be closer to 22.675

Robin2:
Most programming problems have at least 23 solutions.

I agree completely that using functions as in your Reply #6 is a much better approach than the OP's original code - I posted this Tutorial about Planning and Implementing a Program
some time ago.

However I deliberately wrote my Reply #2 as an easy transition from the OP's code to a solution to the problem about swapping pumps.

...R

Yes, of course ... I didn't mean to suggest my approach was better, just another way.

When I was initially learning Arduino, and realized I could flip the state of a Boolean variable by using "variable = !variable", it was an eye opener for me. So I wanted to plant that notion in the OP's mind as a way to go about this in an efficient manner.

Everybody writes code a bit differently. Breaking your code into small function calls as noted above is very good practice. Most beginners write spaghetti code and many programs look like someone threw a plate of spaghetti at the wall to see what sticks - in my opinion that should be resisted at all costs.

Make sure you document the code, helps to get it running and helps you and others to understand what was done later, that helps when next year you are ask to modify a program.

If you do the function calls then it should be relatively simple to write stub or fake code for a function to return a value for instance - aids in troubleshooting - then go back as more of the program works and put real code in place of the stub code - I sometimes do that when I don't have a clue how to make something work - then I go back maybe a day or three later when I have had a chance to think about how to make something work, reduces frustration and maybe banging your head against the wall.

Also save your code, sometimes you can reuse part of it. When I programmed for a living I saved everything in the off chance I needed some part of it, I also had some "standard" code for alarms and such that worked that I could use as a template, sometimes there were notes on things to watch out for. Always document something that is a bit non-standard. One program I worked on I put a remark on a rung (line of code) - "don't delete this rung or it won't work" it was a timer with minimum value that looked like it could be deleted, but that crashed the program - so the timer was there to cause the program to execute one more program scan before doing something, never had the time to go back and figure that out - but by documenting something strange probably saved someone grief down the line.

Good luck with your projects