Automatic pH dosing system using Arduino

Programming, Tutorial, Coding, New Design, and/or any project for your Arduino and Raspberry Pi can post your topic /suggestion here.
User avatar
dondon pramis
Location: Philippines
Posts: 67
Joined: Sun Feb 12, 2017 8:28 am

Automatic pH dosing system using Arduino

Thu Sep 24, 2020 3:55 pm

We will be making a pH dosing system. It has the capability of maintaining the sample within a defined pH range, in this case, 8-8.5. If the readings go out of range, small amounts of either pH UP or pH DOWN solution is automatically added to the sample until the readings return within the limits. The pH level will be monitored by the EZO pH sensor while the UP/DOWN solutions will be dispensed using peristaltic pumps. Operation is via I2C protocol and readings are displayed on the Arduino serial monitor

a) Calibrate pH sensor and pumps. For the calibration process refer to the following: pH Calibration, Pump Calibration.

b) Set the pH sensor and pumps protocol to I2C and each device needs a unique I2C address. In accordance with the sample code for this project, the following addresses are used: pH sensor address is 99, pump for pH UP solution is 103 and pump for pH DOWN solution is 104. For information on how to change between modes and set addresses, refer to this Link.

The calibration and the switch to I2C MUST be done before implementing the sensors into this project.

Circuit Diagram
ph-wiring-diagram.png (51.61 KiB) Viewed 1349 times
Connect the hardware as shown in the schematic.

The peristaltic pump has two power lines. The line that goes to the Arduino's 5V pin is for the circuitry attached to the pump while the external 12V supply is for the pump itself. The five pin headers are used to mount the data cables of the pumps to the breadboard after which jumper wires make the appropriate connections to the Arduino.

The two 4.7kΩ resistors serve as pull up resistors for the SDA (Serial Data) and SCL (Serial Clock) lines.

Load Program onto Arduino
The code for this project makes use of a customized library and header file for the EZO circuits in I2C mode. You will have to add them to your Arduino IDE in order to use the code. The steps below include the process of making this addition to the IDE.

a) Download Ezo_I2c_lib, a zip folder from GitHub onto your computer.

b) On your computer, open the Arduino IDE (You can download the IDE from HERE if you do not have it).

c) In the IDE, go to Sketch -> Include Library -> Add.ZIP Library -> Select the Ezo_I2c_lib folder you just downloaded. The appropriate files are now included.

d) Copy the code from pH_dosing_pump onto your Arduino work panel. You can also access this code from the Ezo_I2c_lib folder downloaded above.

e) Compile and upload the pH_dosing_pump code to your Arduino UNO.

f) Readings are displayed on the serial monitor. To open the serial monitor, go to Tools -> Serial Monitor or press Ctrl+Shift+M on your keyboard. Set the baud rate to 9600 and select "Carriage return". The pH readings should display and the pumps will be triggered accordingly to dispense pH UP and pH DOWN solution. Remember this sample code takes into consideration the pH between 8-8.5, so the pumps will only turn on when out of this range.


Code: Select all

  The goal is to maintain the pH level of the sample between 8 and 8.5
  The sensors must be calibrated and switched to I2C mode before using this code. The ability to send commands to the sensors is not incorporated here.
  After uploading the code to your arduino, open the serial monitor, set the baud rate to 9600 and select "Carriage return". The pH dosing system is now active.*/

#include <Ezo_i2c.h> //include the EZO I2C library from
#include <Wire.h>    //include arduinos i2c library

Ezo_board PH = Ezo_board(99, "PH");                            //create a PH circuit object, whose address is 99 and name is "PH"
Ezo_board PMP_UP = Ezo_board(103, "PMP_UP");                   //create a pump circuit object, whose address is 103 and name is "PMP_UP". This pump dispenses pH up solution.
Ezo_board PMP_DOWN = Ezo_board(104, "PMP_DOWN");               //create a pump circuit object, whose address is 104 and name is "PMP_DOWN". This pump dispenses pH down solution.

bool reading_request_phase = true;                             //selects our phase

uint32_t next_poll_time = 0;                                   //holds the next time we receive a response, in milliseconds
const unsigned int response_delay = 1000;                      //how long we wait to receive a response, in milliseconds

void setup() {
  Wire.begin();                                                //start the I2C
  Serial.begin(9600);                                          //start the serial communication to the computer

void loop() {
  if (reading_request_phase) {                                 //if were in the phase where we ask for a reading

    //send a read command. we use this command instead of PH.send_cmd("R");
    //to let the library know to parse the reading

    next_poll_time = millis() + response_delay;               //set when the response will arrive
    reading_request_phase = false;                            //switch to the receiving phase
  else {                                                      //if were in the receiving phase
    if (millis() >= next_poll_time) {                         //and its time to get the response

      receive_reading(PH);                                    //get the reading from the PH circuit
      Serial.print("    ");

      if (PH.get_last_received_reading() <= 8) {                            //test condition against pH reading
        Serial.println("PH LEVEL LOW,PMP_UP = ON");
        PMP_UP.send_cmd_with_num("d,", 0.5);                  //if condition is true, send command to turn on pump (called PMP_UP) and dispense pH up solution, in amounts of 0.5ml. Pump turns clockwise.
      else {
        PMP_UP.send_cmd("x");                                 //if condition is false, send command to turn off pump (called PMP_UP)

      if (PH.get_last_received_reading() >= 8.5) {                          //test condition against pH reading
        Serial.println("PH LEVEL HIGH,PMP_DOWN = ON");
        PMP_DOWN.send_cmd_with_num("d,", -0.5);               //if condition is true, send command to turn on pump (called PMP_DOWN) and dispense pH down solution, in amounts of 0.5ml. Pump turns counter-clockwise.
      else {
        PMP_DOWN.send_cmd("x");                               //if condition is false, send command to turn off pump (called PMP_DOWN)

      reading_request_phase = true;                           //switch back to asking for readings

void receive_reading(Ezo_board &Sensor) {                      // function to decode the reading after the read command was issued

  Serial.print(Sensor.get_name()); Serial.print(": ");         // print the name of the circuit getting the reading

  Sensor.receive_read_cmd();                                       //get the response data and put it into the [Sensor].reading variable if successful

  switch (Sensor.get_error()) {                                //switch case based on what the response code is.
    case Ezo_board::SUCCESS:
      Serial.print(Sensor.get_last_received_reading());                      //the command was successful, print the reading

    case Ezo_board::FAIL:
      Serial.print("Failed ");                                 //means the command has failed.

    case Ezo_board::NOT_READY:
      Serial.print("Pending ");                                //the command has not yet been finished calculating.

    case Ezo_board::NO_DATA:
      Serial.print("No Data ");                                //the sensor has no data to send.