'while' function question

I finally got this to comply and wondered if anybody might be kind enough to tell me if the ‘while’ function will likely do what I expect, which is run the entire sketch when pH is less than 8.3 and only run the part of the sketch not bracketed by the ‘while’ function when pH is greater than 8.3.

I don’t have a pH probe yet to run a bench test. Also, pointing out problems/deficiencies would be appreciated.

Thanks
Joe

int pH = A7;

unsigned long hoursInMs(int h){ 
  return h*3600000UL;         //values will be expressed in hours 
 
}

  long minutesInMs(int m){ 
  return m*60000L;           //values will be expressed in minutes 
 
} 

  long secondsInMs(int s){ 
  return s*1000L;            //values will be expressed in seconds
} 

 
 void setup() {
  pinMode(A7, INPUT);       // pin A7 connected to pH probe
  pinMode(7, OUTPUT);       // pin 7 controls mixing pump
  pinMode(8, OUTPUT);       // pin 8 controls kalk slurry
  pinMode(9, OUTPUT);       // pin 9 controls washing soda
  pinMode(10, OUTPUT);      // pin 10 controls calcium chloride
}


  void loop() {
 



while( pH < 8.3 )             // if pH is higher than 8.3, kalk slurry will not be added
{

 
  digitalWrite(8, LOW);     // turn kalk slurry dosing pump off
  digitalWrite(9, LOW);     // turn washing soda dosing pump off
  digitalWrite(10, LOW);    // turn calcium chloride dosing pump off
  digitalWrite(7, HIGH);    // turn mixing pump on using a NO relay
  delay(secondsInMs(20));   // let mixing pump run for 20 seconds, or long enough to create kalk slurry    
  digitalWrite(7, LOW);     // turn mixing pump off
  delay(secondsInMs(30));   // leave mixing pump off for 30 seconds to allow any solid chunks to fall to the bottom of container
  
  digitalWrite(8, HIGH);    // turn kalk slurry dosing pump on using a second NO relay      
  delay(secondsInMs(3));    // let kalk slurry dosing pump run only briefly to prevent a precipitation event
  digitalWrite(8, LOW);     // turn kalk slurry dosing pump off 
  delay(secondsInMs(30));   // leave kalk slurry dosing pump off briefly to allow slurry to dissipate
  
  digitalWrite(8, HIGH);    // same as above, this just spreads out the kalk slurry.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);   
  delay(secondsInMs(30));
 
  digitalWrite(8, HIGH);    // same as above.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);
  delay(secondsInMs(30));

  digitalWrite(8, HIGH);    // almost same as above.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);      
  delay(minutesInMs(5));    // in 5 minutes, the dosing series continues
}
 






  digitalWrite(7, LOW);        // turn mixing pump off
  digitalWrite(8, LOW);        // turn kalk slurry dosing pump off   
  digitalWrite(10, LOW);      // turn calcium chloride dosing pump off
  digitalWrite(9, HIGH);       // turn washing soda dosing pump on using a third NO relay
  delay(secondsInMs(5));    // dose washing soda for 5 seconds   
  digitalWrite(9, LOW);        // turn washing soda dosing pump off 
  delay(minutesInMs(5));    // wait 5 minutes before next dose
 

  digitalWrite(7, LOW);       // turn mixing pump off
  digitalWrite(8, LOW);       // turn kalk slurry dosing pump off
  digitalWrite(9, LOW);       // turn washing soda dosing pump off
  digitalWrite(10, HIGH);    // turn calcium chloride dosing pump on using a fourth NO relay
  delay(secondsInMs(5));   // dose calcium chloride for 5 seconds
  digitalWrite(10, LOW);     // turn calcium chloride dosing pump off 
  delay(hoursInMs(6));       // wait 6 hours before starting at the top with a new dosing series. 

  
  }

Your functions aren't done right. They should be like this.

int longhoursInMs(int h){

int result;
result = h * 3600000;
return result; //values will be expressed in hours
}

more info here.

https://www.arduino.cc/en/Reference/FunctionDeclaration

Your functions aren’t done right

Disagree. The functions should work. The only thing I would change is to make the constants unsigned long to make sure the integer calcs are done right (eg, 3600000ul).

To answer you question, I think that you probably want the expression to be an ‘if’ and ‘else’ like in your comment. The looping is done by the loop() function call from the main program (which you don’t see).

I am assuming that you are controlling dosages to control pH. This will work as long as you have the delays in the software and each branch of the if statement are only executed once through the loop(). loop() is executed thousands of times a second if you do not slow it down, so remember this if you change the way you do things.

The other way to handle something like this is using a Finite State Machine that executes in a state based manner. If you are familiar with Sequential Function Charts in PLC programming, the concept is similar. FSM is something you can look up via Google.

 if ( pH < 8.3 )             // if pH is higher than 8.3, kalk slurry will not be added
{
  digitalWrite(8, LOW);     // turn kalk slurry dosing pump off
  digitalWrite(9, LOW);     // turn washing soda dosing pump off
  digitalWrite(10, LOW);    // turn calcium chloride dosing pump off
  digitalWrite(7, HIGH);    // turn mixing pump on using a NO relay
  delay(secondsInMs(20));   // let mixing pump run for 20 seconds, or long enough to create kalk slurry    
  digitalWrite(7, LOW);     // turn mixing pump off
  delay(secondsInMs(30));   // leave mixing pump off for 30 seconds to allow any solid chunks to fall to the bottom of container
  
  digitalWrite(8, HIGH);    // turn kalk slurry dosing pump on using a second NO relay      
  delay(secondsInMs(3));    // let kalk slurry dosing pump run only briefly to prevent a precipitation event
  digitalWrite(8, LOW);     // turn kalk slurry dosing pump off 
  delay(secondsInMs(30));   // leave kalk slurry dosing pump off briefly to allow slurry to dissipate
  
  digitalWrite(8, HIGH);    // same as above, this just spreads out the kalk slurry.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);   
  delay(secondsInMs(30));
 
  digitalWrite(8, HIGH);    // same as above.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);
  delay(secondsInMs(30));

  digitalWrite(8, HIGH);    // almost same as above.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);      
  delay(minutesInMs(5));    // in 5 minutes, the dosing series continues
}
else
{
  digitalWrite(7, LOW); 
  digitalWrite(8, LOW);      
  digitalWrite(10, LOW);
  digitalWrite(9, HIGH);   // turn washing soda dosing pump on using a third NO relay
  delay(secondsInMs(5));   // dose washing soda for 5 seconds   
  digitalWrite(9, LOW);    // turn washing soda dosing pump off 
  delay(minutesInMs(5));   // wait 5 minutes before next dose
 

  digitalWrite(7, LOW); 
  digitalWrite(8, LOW);       
  digitalWrite(9, LOW);
  digitalWrite(10, HIGH);  // turn calcium chloride dosing pump on using a fourth NO relay
  delay(secondsInMs(5));   // dose calcium chloride for 5 seconds
  digitalWrite(10, LOW);   // turn calcium chloride dosing pump off 
  delay(hoursInMs(6));     // wait 6 hours before starting at the top with a new dosing series. 
}

backyardfarmer:
Your functions aren't done right. They should be like this.

int longhoursInMs(int h){
int result;
result = h * 3600000;
return result; //values will be expressed in hours
}

No. There is no need to have a variable, and furthermore this code will fail because result is an int, which is not big enough to deal with these kinds of numbers.

it might be best to explicitly use long constants:

unsigned long minutesInMs(int m){
  return m*60000L;           //values will be expressed in minutes
}

As it stands, m*60k will be calculated as an int and so will get truncated. Using a long constant (L on the end), the whole calculation is done as a long.

  delay(hoursInMs(6));     // wait 6 hours before starting at the top with a new dosing series.

Are you aware that the resonators used to clock arduinos can be off by as much as 10%?

You will either want a real-time clock, or you will want to write a test sketch to find out what the clock is like on your particular board. Once you have done this, you can modify your hoursInMs() function to return the number of millis ticks in a real-time hour that your particular arduino does.

I didn't see where you read the pH value... ?

  digitalWrite(7, LOW);
  digitalWrite(8, LOW);     
  digitalWrite(10, LOW);

I hate magic numbers. Put in some constants that say what 7, 8, and 10 actually mean.

lastchancename:
I didn’t see where you read the pH value… ?

Agreed :wink:

Looking at the code, pH is a pin (and an integer) The while loop checks it against a float. You should read the pin (more than likely using analogRead).

int pHpin = A7;
float pHvalue = 0;

...
...

void loop()
{
  // read pH sensor
  int reading = analogRead(pHpin);

  // convert to pH value
  pHvalue = ...;

  while(pHvalue < 8.3)
  {
    ...
    ...

    // you need to get a new reading here, else you will be stuck in the loop

    // read pH sensor
    reading = analogRead(pHpin);

    // convert to pH value
    pHvalue = ...;
  }

}

You need to consult the datasheet of the pH sensor to see how to convert the reading (0…1023) to a pH value. A reading of 0 might reflect a pH of 0 and a reading of 1023 might reflect pH of 14; but the datasheet will tell.

Thanks guys! I edited the easy stuff first-I used UL or L suffixes to explicitly use unsigned long and long constants for the seconds, minutes and hours. (I thought they already were 'long'.) Also, no more magic numbers.

As far as the Arduino clock being off, that's no big deal here as long as it's fairly consistent.

I never used the 'else if' function but I found the 'while' function last night and thought it was a perfect fit for fine tuning my sketch. The only part of the sketch I want controlled by pH is the mixing pump and kalk slurry dosing pump.

Read the pH value? Thanks for that solid help. I have some studying to do there.

Really appreciate the help!

Here’s the sketch with changes. When both pHvalue at the beginning and end of ‘while’ are 8.3, the kalk slurry/mixing pump part of the sketch is skipped as expected. With both pHvalue at 8.2, the kalk slurry/mixing pump part of the sketch repeats. I thought setting pHvalue at 8.2 was the needed new reading.

// you need to get a new reading here, else you will be stuck in the loop

reading = analogRead(pHpin); // read pH sensor

pHvalue = 8.2; // convert to pH value

[ int pHpin = A7;
  float pHvalue = 0;

  unsigned long hoursInMs(int h)  
{  
  return h*3600000UL;         //values will be expressed in hours 
 
}

  long minutesInMs(int m){ 
  return m*60000L;           //values will be expressed in minutes 
 
} 

  long secondsInMs(int s){ 
  return s*1000L;            //values will be expressed in seconds
} 

 
 void setup() 
{  
  pinMode(A7, INPUT);       // pin A7 connected to pH probe
  pinMode(7, OUTPUT);       // pin 7 controls mixing pump
  pinMode(8, OUTPUT);       // pin 8 controls kalk slurry
  pinMode(9, OUTPUT);       // pin 9 controls washing soda
  pinMode(10, OUTPUT);      // pin 10 controls calcium chloride
}


  void loop() 
{
 int reading = analogRead(pHpin);  // read pH sensor

 pHvalue = 8.2;                    // convert to pH value

 while( pHvalue < 8.3 )            // if pH is 8.3 or higher, kalk slurry will not be added and mixing pump will not turn on
{

 
  digitalWrite(8, LOW);     // turn kalk slurry dosing pump off
  digitalWrite(9, LOW);     // turn washing soda dosing pump off
  digitalWrite(10, LOW);    // turn calcium chloride dosing pump off
  digitalWrite(7, HIGH);    // turn mixing pump on using a NO relay
  delay(secondsInMs(3));    // let mixing pump run for 20 seconds, or long enough to create kalk slurry    
  digitalWrite(7, LOW);     // turn mixing pump off
  delay(secondsInMs(3));    // leave mixing pump off for 30 seconds to allow any solid chunks to fall to the bottom of container
  
  digitalWrite(8, HIGH);    // turn kalk slurry dosing pump on using a second NO relay      
  delay(secondsInMs(3));    // let kalk slurry dosing pump run only briefly to prevent a precipitation event
  digitalWrite(8, LOW);     // turn kalk slurry dosing pump off 
  delay(secondsInMs(3));    // leave kalk slurry dosing pump off briefly to allow slurry to dissipate
  
  digitalWrite(8, HIGH);    // same as above, this just spreads out the kalk slurry.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);   
  delay(secondsInMs(3));
 
  digitalWrite(8, HIGH);    // same as above.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);
  delay(secondsInMs(3));

  digitalWrite(8, HIGH);    // almost same as above.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);      
  delay(secondsInMs(3));    // in 5 minutes, the dosing series continues

                            // you need to get a new reading here, else you will be stuck in the loop
 
 reading = analogRead(pHpin);  // read pH sensor
 
 pHvalue = 8.2;             // convert to pH value

}
 






  digitalWrite(7, LOW);       // turn mixing pump off
  digitalWrite(8, LOW);       // turn kalk slurry dosing pump off   
  digitalWrite(10, LOW);      // turn calcium chloride dosing pump off
  digitalWrite(9, HIGH);      // turn washing soda dosing pump on using a third NO relay
  delay(secondsInMs(3));      // dose washing soda for 5 seconds   
  digitalWrite(9, LOW);       // turn washing soda dosing pump off 
  delay(secondsInMs(3));      // wait 5 minutes before next dose
 

  digitalWrite(7, LOW);       // turn mixing pump off
  digitalWrite(8, LOW);       // turn kalk slurry dosing pump off
  digitalWrite(9, LOW);       // turn washing soda dosing pump off
  digitalWrite(10, HIGH);     // turn calcium chloride dosing pump on using a fourth NO relay
  delay(secondsInMs(3));      // dose calcium chloride for 5 seconds
  digitalWrite(10, LOW);      // turn calcium chloride dosing pump off 
  delay(hoursInMs(6));        // wait 6 hours before starting at the top with a new dosing series. 

  
  }






/code]

while( pHvalue < 8.3 ) { … }

will continue repeating the code in the {}'s until pHvalue is not less than 8.3.

A while loop checks the test, then if it’s true, runs the block of code in the {}'s. Repeat until the test is false, at which point execution continues after the end of the while statement. Since there’s no way there for pHvalue to be anything other than 8.2, the while loop will run forever.

It sounds like you think it does something else.

pH8.2 is smaller than pH8.3, so you will stay in the while loop. Make the new resding 8.4 and you will get out of the while loop.

I was mistaken about what 'while' does. I replaced 'while' with 'if' and now the sketch works as planned. Amazing stuff. Thanks guys.

Now to get the NANO to make sense of what the pH probe sends. Trouble is, pH probes change over time so the monitor that displays pH needs to be periodically calibrated to compensate. I have an LCD. The only thing I've done with it is get it to say "HELLO WORLD" and I'd love to put it to good use. Would it be very doable to make the LCD display pH as well as having the ability to manually change displayed pH values? I imagine that's a lot of heavy programming-I could always just keep a close eye on things with my Pinpoint pH monitor.

I pulled the circuit from the Pinpoint pH monitor to see if there is a way to send the signal from it to pin A7, but I couldn't see it.

You can store variables in eeprom. So you can store calibration values in there (e.g. one or more offsets or calibrated values for each full pH value).

Small algorithm around it to adjust the reading before displaying the value and you should be good to go.

Thanks sterretje. I'll check out the tutorial on eeprom this weekend. Took a quick peek just now.

If I understand this, I'd only have to store 7.00 and 10.00 in eeprom. I'd put the pH probe in a 7.00 pH solution and it might read 6.87 or 7.19 or whatever. Then I'd plug in 7.00. Then do the same for pH 10.00 solution. Is that about right?

Thanks all.

It's the idea; I don't know how your probe needs to be 'calibrated'.

I went to EEPROM on this site and inserted read(), write() and get() into the sketch as well as adding the EEPROM library. I could not get anything up on the serial monitor-I honestly don’t understand how to use EEPROM. I did remove the second command to read pH after replacing ‘while’ with ‘if’. Ha, at least that made sense to me…

IDK if this project is interesting to you guys or maybe useful to others who might be reading along. In any event, I certainly appreciate the help getting NANO to control pumps. It’s mind boggling that the pump sketch uses such a small fraction of the NANO’s capability.

[ #include <EEPROM.h>
   struct MyObject{
   float field1;
   byte field2;
   char name[10];
};


   int a = 0;
   int value;
   int pHpin = A7;
   float pHvalue = 0;

   unsigned long hoursInMs(int h)  
{  
   return h*3600000UL;         //values will be expressed in hours 
}
  
   long minutesInMs(int m)
{   
   return m*60000L;           //values will be expressed in minutes 
} 
  
   long secondsInMs(int s)
{   
   return s*1000L;            //values will be expressed in seconds
} 

 
    void setup() 
{  
   
  float f = 0.00f;   //Variable to store data read from EEPROM.
   int eeAddress = 0; //EEPROM address to start reading from

   Serial.begin( 9600 );
   while (!Serial) {
     ; // wait for serial port to connect. Needed for Leonardo only
   }
   Serial.print( "Read float from EEPROM: " );

   //Get the float data from the EEPROM at position 'eeAddress'
   EEPROM.get( eeAddress, f );
   Serial.println( f, 3 );  //This may print 'ovf, nan' if the data inside the EEPROM is not a valid float.

   // get() can be used with custom structures too. 
   eeAddress = sizeof(float); //Move address to the next byte after float 'f'.
   MyObject customVar; //Variable to store custom object read from EEPROM.
   EEPROM.get( eeAddress, customVar );

   Serial.println( "Read custom object from EEPROM: " );
   Serial.println( customVar.field1 );
   Serial.println( customVar.field2 );
   Serial.println( customVar.name ); 
   
   
   for (int i = 0; i < 255; i++)
     EEPROM.write(i, 7.00);
   
   Serial.begin(9600);
   
   pinMode(A7, INPUT);       // pin A7 connected to pH probe
   pinMode(7, OUTPUT);       // pin 7 controls mixing pump
   pinMode(8, OUTPUT);       // pin 8 controls kalk slurry
   pinMode(9, OUTPUT);       // pin 9 controls washing soda
   pinMode(10, OUTPUT);      // pin 10 controls calcium chloride
}


   void loop() 
{
   value = EEPROM.read(a);

   Serial.print(a);
   Serial.print("\t");
   Serial.print(value);
   Serial.println(7.00);

   a = a + 1;

   if (a == 512)
   a = 0;

   delay(500);

   int reading = analogRead(pHpin);  // read pH sensor
   pHvalue = 8.3;                    // convert to pH value

   if( pHvalue < 8.3 )            // if pH is 8.3 or higher, kalk slurry will not be added and mixing pump will not turn on
{

 
  digitalWrite(8, LOW);     // turn kalk slurry dosing pump off
  digitalWrite(9, LOW);     // turn washing soda dosing pump off
  digitalWrite(10, LOW);    // turn calcium chloride dosing pump off
  digitalWrite(7, HIGH);    // turn mixing pump on using a NO relay
  delay(secondsInMs(3));    // let mixing pump run for 20 seconds, or long enough to create kalk slurry    
  digitalWrite(7, LOW);     // turn mixing pump off
  delay(secondsInMs(3));    // leave mixing pump off for 30 seconds to allow any solid chunks to fall to the bottom of container
  
  digitalWrite(8, HIGH);    // turn kalk slurry dosing pump on using a second NO relay      
  delay(secondsInMs(3));    // let kalk slurry dosing pump run only briefly to prevent a precipitation event
  digitalWrite(8, LOW);     // turn kalk slurry dosing pump off 
  delay(secondsInMs(3));    // leave kalk slurry dosing pump off briefly to allow slurry to dissipate
  
  digitalWrite(8, HIGH);    // same as above, this just spreads out the kalk slurry.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);   
  delay(secondsInMs(3));
 
  digitalWrite(8, HIGH);    // same as above.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);
  delay(secondsInMs(3));

  digitalWrite(8, HIGH);    // almost same as above.      
  delay(secondsInMs(3));
  digitalWrite(8, LOW);      
  delay(secondsInMs(3));    // in 5 minutes, the dosing series continues

}
 
  digitalWrite(7, LOW);       // turn mixing pump off
  digitalWrite(8, LOW);       // turn kalk slurry dosing pump off   
  digitalWrite(10, LOW);      // turn calcium chloride dosing pump off
  digitalWrite(9, HIGH);      // turn washing soda dosing pump on using a third NO relay
  delay(secondsInMs(3));      // dose washing soda for 5 seconds   
  digitalWrite(9, LOW);       // turn washing soda dosing pump off 
  delay(secondsInMs(3));      // wait 5 minutes before next dose
 

  digitalWrite(7, LOW);       // turn mixing pump off
  digitalWrite(8, LOW);       // turn kalk slurry dosing pump off
  digitalWrite(9, LOW);       // turn washing soda dosing pump off
  digitalWrite(10, HIGH);     // turn calcium chloride dosing pump on using a fourth NO relay
  delay(secondsInMs(3));      // dose calcium chloride for 5 seconds
  digitalWrite(10, LOW);      // turn calcium chloride dosing pump off 
  delay(hoursInMs(6));        // wait 6 hours before starting at the top with a new dosing series. 

  
}


/code]

You’re using the wrong function(s) of the EEPROM library. EEPROM.write writes a byte, not a float :wink: Try EEPROM.put instead.

#include <EEPROM.h>

void setup() {

  // initialize serial comms
  Serial.begin(9600);
  // tell us how big a float is (number of bytes)
  Serial.print("sizeof(float): "); Serial.println(sizeof(float));


  // first variable
  float pH1 = 7.3456;
  // write it
  EEPROM.put(0, pH1);

  // second variable
  float pH2 = 1.23;
  // write it in the next position (4 bytes further; see sizeof(float) output)
  EEPROM.put(sizeof(float), pH2);

  float f;
  // read first variable from eeprom and print
  EEPROM.get(0, f);
  Serial.println(f, 3); 
  // read second variable from eeprom and print; start address of second variable is 4 bytes after first variable (see sizeof(float) output)
  EEPROM.get(sizeof(float), f);
  Serial.println(f, 3); 

}

void loop()
{

}

I replaced read(), write() and get() with EEPROM.put. Thank you.

Can I assume that float pH1 and float pH2 are the setpoints for probe calibration? I set them at 7.00 and 10.00. (I only need to measure to the hundreds place.)

Can we have a third pH measurement that indicates pH in the tank? I plan to have the probe in the tank 24/7 unless I am calibrating.

I still get nothing on the serial monitor. Not sure what to send.

I tried to keep it a little in the 'tune' of this thread; they were not written with setpoint in mind. In that case I would call them setpoint_pH7 and setpoint_pH10 as they are more sensible names.

Do you want to store the tank pH in the eeprom? If so, be aware that the eeprom has a limited number of write cycles (roughly 100,000). There are tricks to work around it.

Else I do not understand the question.

My code displays in the serial monitor :wink: Please post an updated version of your code and indicate what it does not print.