PH automation Logic

Hi guys, I'm fairly new to the world of Arduino,
I've used one in the past to control relays on simple timer based logic.
I'm now looking into making a logic loop.

The project is an automated PH up/down doser. That only doses at specified times. (so it doesn't dose during the night)
Ideally it would check if a PH dose is needed, dose if needed, then run a circulation pump in the water reservoir so the next PH check is accurate.

The hardware is an Uno R3
2 12v Peristaltic pumps
A 12v 5A power supply
5v 4 channel relay board
A Ds3231 RTC
An Atlas scientific Gravity PH sensor/board (5V powered)
2 40mm 12v fans for moving air through the project box when pumps are running.
120V Circulation pump

I had originally bought a 12v to 5v Buck converter to power the Arduino
but I think i'm going to use a 1 A amp phone charger to power the Arduino through the usb port instead of using the buck converter.

The questions I have are as follows:

Could I potentially power the relay board, and PH board just from the buck converter,
But still control the relay coils from the Arduino, and still receive output from the PH board safely?
Would I run into any issues with the Phone charger ground that would be in the usb port, not being tied to the ground of the buck converter?(recirculating current is my first thought)
Or should I also tie the ground from the Buck converter to a ground pin on the Arduino?
(powering them from the buck converter would be just to lighten the ma load on the Arduino)
Or should I just power them from the Arduino?

Second, I posted my logic below, I believe what I'm wanting to do would be feasible with an Arduino
but if someone could put eyes on it and check that would great.
I would like to keep the PH between 5.6 and 6.4

Relay 4 Cooling fans
Relay 3 PH up pump
relay 2 PH down pump
Relay 1 reservoir Circulation pump

Check the time
If the time equals one of the specified times 1pm,2pm,3pm etc, Check PH
If time is not equal to one of the specified times go back to checking the time.
If Ph is above 6.4 then perform PH down dosing,then go back to checking time.
If PH is below 6.4 then check if PH is above 5.6
If PH is below 5.6 Perform PH up dosing,then go back to checking time.
If PH is above 5.6 go back to checking time

PH down dosing :
Close relay 2 and relay 4, and run for X amount of time, then
Close relay 1 and relay 4, and run for x amount of time, then go back to checking time

PH up dosing :
Close relay 3 and relay 4, and run for X amount of time, then
Close relay 1 and relay 4, and run for x amount of time, then go back to checking time

I've attached pictures of the project, I don't have the Circulation pump installed yet as I don't want to lug it around with box,
and the phone charger is not installed yet, and alot of the wiring isn't there. The switches above the dosing pumps will be for manual operation/priming.

Let me know what you think, Cheers!



Did you read the Atlas Scientific PDF for the sensor and see the warnings about noise causing erroneous ph readings?

Yea, all ive seen is that the probe wiring is a twisted shield pair to help with emi, and that the probe is prone to noise with pumps running.

Im only planning on running the reservior circulation pump after a dosing has occured.
And the only other thing i have in my pepper reservoirs is air stones powered by an air pump outside of the reservoir.

Ive got a digital readout with a ph probe (non arduino)that works superb currently, im just trying to automate the ph side of things

I have no experience with the Atlas Gravity consumer grade pH probe, but good quality, research grade glass pH probes tend to stop working if left in a contaminated solution for any significant length of time, and in any case, need to be frequently calibrated against standard pH calibration solutions.

The calibration drifts with temperature, time and changes of ionic strength of the test solution, and there can be no other electrical connections to the test solution.

Before you trust that probe, test it carefully under anticipated working conditions.

If the cheap 70$ off the shelf lcd display with a plastic probe can last 6 months so far in the reservoir hopefully the atlas one can too.

pH measurements and automated dosing come up pretty frequently on this forum, but I've not seen anyone come back to report whether they were successful.

I and others would be interested in hearing about your experience, especially how much the calibration drifts, so please keep us posted.

Nice looking build!

Will do! I'm curious myself. At first I wasn't to confident in the PH meter with digital display that I
bought, but it always matched and still does my little hand held dip meter, within about 0.03 over the past 7 months.
Which for staying in a window is fine for my needs, after seeing your reply earlier I did some digging.
I learned something I didn't know, If you have plain RO water, with nothing in it, these meters can have a hard time reading the PH level since there is nothing in the water to conduct.
Therefore it makes sense to me that if you're not keeping your TDS of nutrients in the water at pretty close to a constant level, it could potentially make your PH drift out of scale.
I'll report back once I get it up and going.

Ive got the entire thing wired up and ready to go including code(hopefully). I ended up ditching the rtc as i dont think i can get it to do what i want.
The code just has a 1hr delay for the checking and dosing loop.
I also ditched the buck converter,for whatever reason when powering the relay board with it
The lights on the board would only barely light
And the relays would not close.
So for now the relay board and atlas board are powered by the arduino.
I ordered a 20x4 lcd to give a readout of the pH
Using a 1amp phone charger for power think i will have enough power available to also power the lcd without stressing the arduino?

Once its confirmed working I'll post the code

Measure the current draw of the setup, with the relays activated. For safety and reliable operation, the power supply should be rated for at least twice that current.

Keep in mind that motors briefly draw excessive current on startup.

What tooling do you generally use to measure current that small?,I dont believe my meter can read that low accurately.

The pump motors are on a 12v power supply seperate from the arduino, so those shouldnt be contributing load to the 5v circuit

I use a multimeter. If you are worried about the 1A phone charger, used power bricks are available at very low cost, in a large range of voltages and currents, at thrift stores and computer recycling outfits.

Finished (mostly) the hardware, lcd is press fit for easy removal of front cover. Code seems to work appropriately, but ill test it fully with actual pH up and down in a bucket of water over the weekend.
Id also like to put hinges on the lid at the bottom so it folds open for ease of access without having to hold the lid.

The switches above each dosing pump are to be able to manually turn them off.
Originally I wanted to use them as manual "on" for each pump to prime them.
But I've found no matter what I do the first pH check is always inaccurate, always
around 1.5pH lower than the solution is, then the next reading is correct.
This way I can just have them off for the first reading, and turn them on after.

Only thing I dont like is that it will display the pH run the loop for the dosing if needed, then display the pH again. But then has a delay to wait an hour before checking again, so the displayed pH isnt real time, rather the reading from the last check/dose on the hour. But eh close enough.



Here is the code, the main portion is from Atlas Scientific.
This hasn't been tested in a real world application yet.

Calibration commands:
 low-point: "cal,4"
 mid-point: "cal,7"
 high-point: "cal,10"
 clear calibration: "cal,clear"
*/

#include "ph_grav.h"                                  //header file for Atlas Scientific gravity pH sensor
#include "LiquidCrystal.h"                            //header file for liquid crystal display (lcd)

String inputstring = "";                              //a string to hold incoming data from the PC
boolean input_string_complete = false;                //a flag to indicate have we received all the data from the PC
char inputstring_array[10];                           //a char array needed for string parsing
Gravity_pH pH = A3;                                   //assign analog pin A3 of Arduino to class Gravity_pH. connect output of pH sensor to pin A3
LiquidCrystal pH_lcd(2, 3, 4, 5, 6, 7);               //make a variable pH_lcd and assign arduino digital pins to lcd pins (2 -> RS, 3 -> E, 4 to 7 -> D4 to D7)
int Fans = 8;
int Uppump = 9;
int Downpump = 10;
int Circpump = 11;

void setup() {
  Serial.begin(9600);                                 //enable serial port
  pH_lcd.begin(20, 4);                                //start lcd interface and define lcd size (20 columns and 4 rows)
  pH_lcd.setCursor(0,0);                              //place cursor on screen at column 1, row 1
  pH_lcd.print("--------------------");               //display characters
  pH_lcd.setCursor(0,3);                              //place cursor on screen at column 1, row 4
  pH_lcd.print("--------------------");               //display characters
  pH_lcd.setCursor(5, 1);                             //place cursor on screen at column 6, row 2
  pH_lcd.print("pH Reading");                         //display "pH Reading" 
  if (pH.begin()) { Serial.println("Loaded EEPROM");} 
  Serial.println(F("Use commands \"CAL,4\", \"CAL,7\", and \"CAL,10\" to calibrate the circuit to those respective values"));
  Serial.println(F("Use command \"CAL,CLEAR\" to clear the calibration"));
  pinMode(Fans, OUTPUT);
  pinMode(Uppump, OUTPUT);
  pinMode(Downpump, OUTPUT);
  pinMode(Circpump, OUTPUT);

 }


void serialEvent() {                                  //if the hardware serial port_0 receives a char
  inputstring = Serial.readStringUntil(13);           //read the string until we see a <CR>
  input_string_complete = true;                       //set the flag used to tell if we have received a completed string from the PC
}


void loop() {
    delay(5000);
  if (input_string_complete == true) {                //check if data received
    inputstring.toCharArray(inputstring_array, 30);   //convert the string to a char array
    parse_cmd(inputstring_array);                     //send data to pars_cmd function
    input_string_complete = false;                    //reset the flag used to tell if we have received a completed string from the PC
    inputstring = "";                                 //clear the string
  }
  Serial.println(pH.read_ph());                       //output pH reading to serial monitor
  pH_lcd.setCursor(8, 2);                             //place cursor on screen at column 9, row 3
  pH_lcd.print(pH.read_ph());                         //output pH to lcd
  delay(5000);
  if (pH.read_ph() > 6.40) {                          //Checks if pH is high than 6.4
     digitalWrite(Fans, LOW);                         //starts cooling fans
     digitalWrite(Downpump, LOW);                     //Start pH down pump
     delay(20000);                                    //time to run pH down pump (20 seconds)
     digitalWrite(Fans, HIGH);                        //stops cooling fans
     digitalWrite(Downpump, HIGH);                    //stops pH down pump
     delay(5000);                                     //Delay between Running pH down pump and starting reservoir cirulation pump (5 seconds)
     digitalWrite(Fans, LOW);                         //starts cooling fans
     digitalWrite(Circpump, LOW);                     //starts reservoir circulation pump
     delay(300000);                                   //time to run reservoir circulation pump (5 Minutes)
     digitalWrite(Fans, HIGH);                        //stops cooling fans
     digitalWrite(Circpump, HIGH);                    //stops reservoir circulation pumps
      Serial.println(pH.read_ph());                    //output pH reading to serial monitor
     pH_lcd.setCursor(8, 2);                          //place cursor on screen at column 9, row 3
     pH_lcd.print(pH.read_ph());                      //output pH to lcd
     delay(3600000);                                    //time to wait until next ph check (1 hour)                                  //time to wait until next ph check (1 hour)
  }
  else if (pH.read_ph() < 5.60) {                     //Checks if pH is lower than 5.6 
     digitalWrite(Fans, LOW);                         //starts cooling fans
     digitalWrite(Uppump, LOW);                       //starts ph up pump
     delay(20000);                                    //time to run pH up pump (20 seconds)
     digitalWrite(Fans, HIGH);                        //stops cooling fans
     digitalWrite(Uppump, HIGH);                      //stops pH up pump
     delay(5000);                                     //Delay between Running pH up pump and starting reservoir cirulation pump (5 seconds)
     digitalWrite(Fans, LOW);                         //starts cooling fans
     digitalWrite(Circpump, LOW);                     //starts reservoir circulation pump
     delay(300000);                                    //time to run reservoir circulation pump (5 Minutes)
     digitalWrite(Fans, HIGH);                        //stops cooling fans
     digitalWrite(Circpump, HIGH);                    //stops reservoir circulation pumps
     Serial.println(pH.read_ph());                    //output pH reading to serial monitor
     pH_lcd.setCursor(8, 2);                          //place cursor on screen at column 9, row 3
     pH_lcd.print(pH.read_ph());                      //output pH to lcd
     delay(3600000);                                    //time to wait until next ph check (1 hour)
  }
  else Serial.println(pH.read_ph());                  //output pH reading to serial monitor
  pH_lcd.setCursor(8, 2);                             //place cursor on screen at column 9, row 3
  pH_lcd.print(pH.read_ph());                         //output pH to lcd
  delay(10000);   
     
}


void parse_cmd(char* string) {                      //For calling calibration functions
  strupr(string);                                   //convert input string to uppercase

  if (strcmp(string, "CAL,4") == 0) {               //compare user input string with CAL,4 and if they match, proceed
    pH.cal_low();                                   //call function for low point calibration
    Serial.println("LOW CALIBRATED");
  }
  else if (strcmp(string, "CAL,7") == 0) {          //compare user input string with CAL,7 and if they match, proceed
    pH.cal_mid();                                   //call function for midpoint calibration
    Serial.println("MID CALIBRATED");
  }
  else if (strcmp(string, "CAL,10") == 0) {         //compare user input string with CAL,10 and if they match, proceed
    pH.cal_high();                                  //call function for highpoint calibration
    Serial.println("HIGH CALIBRATED");
  }
  else if (strcmp(string, "CAL,CLEAR") == 0) {      //compare user input string with CAL,CLEAR and if they match, proceed
    pH.cal_clear();                                 //call function for clearing calibration
    Serial.println("CALIBRATION CLEARED");
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.