DIY pH Dosing Pump

DIY pH Dosing Pump © CC BY-NC

A system to maintain the pH level of a sample within a defined range.

  • 28 respects

Components and supplies

Apps and online services

About this project

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

Step 1: Pre-assembly Requirements

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.

Step 2: Assemble Hardware

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.

Datasheets: EZO PMP, EZO pH

Step 3: 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.


The goal of the demonstration is to show the maintenance of the pH level of the sample between 8-8.5. In the setup, the sample is in the largest beaker at the center. The pH UP solution is in the left beaker while pH DOWN is in the right. Because of the small sample amount being monitored, the UP and DOWN solutions were diluted so that changes in pH level of the sample are not drastic as they are added. Throughout the process, there are a few back and forths in the addition of the pH UP and DOWN solutions until the readings stabilize at pH = 8.33 which is within the desired range.


pH dosing pump codeC/C++
/*This code was written for the Instructable "DIY PH DOSING PUMP" (Link: 
  and "DIY pH Dosing Pump" (Link:
  It was tested on an Arduino UNO.
  Real time pH monitoring is done using the EZO pH sensor and two EZO PMP. The pumps dispense pH UP and pH DOWN solutions into the sample and they are triggered in accordance with the current pH reading.
  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");                            //creat 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.


pH Dosing Pump Wiring Diagram
Pmp ph wiring diagram mflyjmst2a


Similar projects you might like

DIY Benchtop pH Meter

Project tutorial by Atlas Scientific

  • 25 respects

Arduino pH Sensor Calibration

by Atlas Scientific

  • 10 respects

DIY Aquarium Evaporation Top Off System

by Atlas Scientific

  • 31 respects
Add projectSign up / Login