pH electrode for Arduino

Hey guys, im a chemistry tutor and also very interested in arduino. Right now my students and i are discussing the pH, how pH electrodes work und how we can use micro controllers to obtain the value and based on that control different pumps. Unfortunately, i'm Not really sure, which model (of electrode and booster circuit) is worth buying. Do you guys have any experiences? Thanks in advance!!!

No personal experience with those sensors, but DFRobot has PH sensors that can interface with an Arduino.
Both the hobby version SEN0161 and industrial SEN0169 can be ordered from the main electronics sites (Digikey, RS, Amazon, etc.).

If using the Arduino Uno and an analog sensor, the device will read the pH, and the Arduino will interpret that as a value from 0 to 1023. To calibrate the system, control measurements of known pH (low, middle, high) will be taken (or one sample of 7.0pH). The test sample will take running average, and require interpolation from the control sample(s).

"Worth buying" - I see nothing less than $50 per kit, so I would hope that includes (1) authentic Uno, (2) quality sensor with replacement consumable parts, and (3) known pH control samples.

DFRobot has several kits.

I have used Atlas Scientific pH sensors mainly with ESP32 microcontrollers

Thank you for all these very helpful answers! Unfortunately, i cant find the DFRobot Kits on amazon. Either there sold out here in germany or the supplier has a B2B policy.
And from the official site, it'll take 6 weeks and Costa 80 bucks. Maybe someone has an idea/alternative? :D

found results of some experiments using Atlas pH and DO sensors

// Altos Scientific pH_EZO
// from https://github.com/Atlas-Scientific/Ezo_I2c_lib

#include <stdlib.h>
#include <esp_task_wdt.h>

#include <Ezo_i2c.h>  //include the EZO I2C library from https://github.com/Atlas-Scientific/Ezo_I2c_lib
#include <Wire.h>     //include arduinos i2c library


// enum errors {SUCCESS, FAIL, NOT_READY, NO_DATA, NOT_READ_CMD};

Ezo_board EZO_device = Ezo_board(20, "found device");  //object which holds the device we'll find

bool EZO_pH_OK = false, EZO_DO_OK = false;

// device I2C addresses
#define DOaddress 0x61  //C2
#define pHaddress 0x63  //C6
  float result, resultPH, resultDO;
// test EZO pH sensor - return 1 if OK
int EZO_pH_test(void) {
  char data[32] = { 0 };
  EZO_device.set_address(pHaddress);  // pH_EZO board
  Serial.printf("Altos pH_EZO device 0x%x ", EZO_device.get_address());
  EZO_device.send_cmd("i");  //, data, 24, 0));
  delay(10);
  EZO_device.receive_cmd(data, 24);
  Serial.printf("I command data '%s' ", data);

  int error = 0;
  while ((error = EZO_pH_read(&result, 1)) == 0) delay(100);
  if (error == 1) {
    Serial.printf("EZO pH = %f  OK\n", result);
    EZO_pH_OK = true;
    return 1;
  }
  Serial.printf("EZO pH FAIL! %d\n", error);
  return -1;
}
// test EZO DO sensor - return 1 if OK
int EZO_DO_test(void) {
  char data[32] = { 0 };
  EZO_device.set_address(DOaddress);  // pH_EZO board
  Serial.printf("Altos DO_EZO device 0x%x ", EZO_device.get_address());
  EZO_device.send_cmd("i");  //, data, 24, 0));
  delay(10);
  EZO_device.receive_cmd(data, 24);
  Serial.printf("I command data '%s' ", data);
  float result;
  int error = 0;
  while ((error = EZO_DO_read(&result, 1)) == 0) delay(100);
  if (error == 1) {
    Serial.printf("EZO DO = %f  OK\n", result);
    EZO_DO_OK = true;
    return 1;
  }
  Serial.printf("EZO DO FAIL! %d\n", error);
  return -1;
}

// power up test to see if pH and DO sensors are present
void Ezo_board_check(void) {
  EZO_pH_test();
  EZO_DO_test();
}

// read EZO disolved oxygen DO into fresult - print if true
// none blocking 
// returns 0 wait for data 1 if OK -1 error
//   EZO enum errors {SUCCESS, FAIL, NOT_READY, NO_DATA, NOT_READ_CMD};
//        SUCESS - result in fresult return 1
//        NOT_READY  - device is still reading data is not ready return 0
//        anything else is error return -1
int EZO_DO_read(float *fresult, int print) {
  static bool doread = true;
  if (doread || !EZO_device.is_read_poll()) {
    doread = false;
    EZO_device.set_address(DOaddress);  // pH_EZO board
    if (print) Serial.printf("\nDO_EZO device 0x%x   ", EZO_device.get_address());
    EZO_device.send_read_cmd();
    delay(10);
    return 0;
  }
  int error;
  if ((error = EZO_device.receive_read_cmd()) == Ezo_board::NOT_READY) return 0;
  doread = true;
  if (error != Ezo_board::SUCCESS) {
    if (print) Serial.printf("EZO_DO_read FAIL! %d\n", error);
    EZO_DO_OK = false;
    return -1;
  }
  *fresult = EZO_device.get_last_received_reading();
  if (print) Serial.printf("EZO_DO_read DO = %f\n", *fresult);
  EZO_DO_OK = true;
  return 1;
}

// read EZO pH into fresult - print if true
// none blocking 
// returns 0 wait for data 1 if OK -1 error
//   EZO enum errors {SUCCESS, FAIL, NOT_READY, NO_DATA, NOT_READ_CMD};
//        SUCESS - result in fresult return 1
//        NOT_READY  - device is still reading data is not ready return 0
//        anything else is error return -1
int EZO_pH_read(float *fresult, int print) {
  static bool doread = true;
  if (doread || !EZO_device.is_read_poll()) {
    doread = false;
    EZO_device.set_address(pHaddress);  // pH_EZO board
    if (print) Serial.printf("\npH_EZO device 0x%x   ", EZO_device.get_address());
    EZO_device.send_read_cmd();
    delay(10);
    return 0;
  }
  int error;
  if ((error = EZO_device.receive_read_cmd()) == Ezo_board::NOT_READY) return 0;
  doread = true;
  if (error != Ezo_board::SUCCESS) {
    if (print) Serial.printf("EZO_pH_read FAIL! %d\n", error);
    EZO_pH_OK = false;
    return -1;
  }
  *fresult = EZO_device.get_last_received_reading();
  if (print) Serial.printf("EZO_pH_read ph = %f\n", *fresult);
  EZO_pH_OK = true;
  return 1;
}

// read EZO pH and DO
void Ezo_board_test(int print) {
  Serial.printf("\nEzo_board_test - hit keyboard to exit");
  float result;
  while (!Serial.available()) {
    if (EZO_pH_OK)
     // while (EZO_pH_read(&resultPH, print) != 1) delay(100);
      while (EZO_pH_read(&resultPH, 0) != 1) delay(100);
    if (EZO_DO_OK)
      while (EZO_DO_read(&resultDO, 0) != 1) delay(100);
    delay(1000);
    //esp_task_wdt_reset();
    yield();
    Serial.printf("pH %4.2f  DO %4.2f \n", resultPH, resultDO);
  }
}

void setup() {
  Wire.begin();          //start the I2C
  Serial.begin(115200);  //Set the hardware serial port to 9600
  delay(3000);           //wait for devices to boot
  Ezo_board_check();
}

void loop() {
  Ezo_board_test(1);
  delay(3000);
}

results of tests

tap water
pH 12.96  DO 12.15 
pH 13.06  DO 12.32 
pH 13.05  DO 12.45 
pH 13.05  DO 12.13 
pH 13.03  DO 12.30 
pH 13.01  DO 12.58 
pH 13.00  DO 12.14 
pH 12.98  DO 12.25 
pH 12.96  DO 12.64 
pH 12.95  DO 12.24 
pH 12.93  DO 12.14 
pH 12.93  DO 12.61 


------------------------------------

tap water boiled
 pH 8.04  DO 10.15 
pH 8.04  DO 10.32 
pH 8.03  DO 10.42 
pH 8.02  DO 10.71 
pH 8.02  DO 10.59 
pH 8.02  DO 10.40 
pH 8.01  DO 10.24 
pH 7.99  DO 10.12 


----------------------------------------

de-ioised water 
H 5.92  DO 10.03 
pH 6.01  DO 10.10 
pH 6.00  DO 10.34 
pH 5.94  DO 10.33 
pH 5.76  DO 10.26 
pH 5.75  DO 10.09 
pH 5.84  DO 10.17 
pH 5.80  DO 10.16 
 
------------------------------------------------

river water (been raining all night)
H 8.58  DO 11.00 
pH 8.54  DO 11.15 
pH 8.57  DO 11.07 
pH 8.59  DO 10.73 
pH 8.55  DO 10.67 
pH 8.56  DO 11.03 
pH 8.59  DO 10.78 
pH 8.66  DO 10.67 
pH 8.65  DO 10.84 
pH 8.64  DO 10.92 
pH 8.65  DO 10.66 
pH 8.71  DO 10.69 
pH 8.76  DO 11.08
 


----------------------------------------------------------------


water from under garage 
(area is marsh same level as river which is tidal so refilled every high tide)
pH 13.06  DO 12.04 
pH 13.05  DO 12.06 
pH 13.03  DO 12.01 
pH 13.03  DO 12.00 
pH 13.05  DO 11.90 
pH 13.07  DO 11.87 
pH 13.06  DO 11.71 
pH 13.07  DO 11.56 
pH 13.05  DO 11.52 
pH 13.05  DO 11.54 
pH 13.04  DO 11.59 
pH 13.04  DO 11.80 
pH 13.04  DO 11.93 
pH 13.06  DO 11.66 
 

----------------------------------------------------------

filter coffee
H 6.94  DO 0.03 
pH 6.82  DO 0.02 
pH 6.71  DO 0.02 
pH 6.62  DO 0.02 
pH 6.55  DO 0.02 
pH 6.48  DO 0.02 
pH 6.42  DO 0.02 
pH 6.37  DO 0.02 
pH 6.33  DO 0.02 
pH 6.28  DO 0.02 
pH 6.25  DO 0.03




photo of simple test setup

This link - https://eu.robotshop.com/products/gravity-analog-ph-meter-kit

might also deliver in Germany?

Yeah, they deliver to germany in a reasonable time, thank you!

The key part of a pH electrode circuit is the buffer amplifier. By buffer, I mean reading the high impedance output of the electrode and changing it to a usable output.

In the past I've operational amplifiers like the MAX407. You need gain and offset.

Ideally the amplifier sits as close to the electrode as possible and sometimes it's built into the electrode cap.

Don't forget temperature compensation and at least two point calibration with known pH buffer solutions.

Easy to implement pH control projects or environmental monitoring coupled with an Arduino

You may want to read the topics at the bottom of this page. Seems like some people have had problems with those cheap pH devices.

Search the internet. Read the specifications. If the specs are right for your application, search on Amazon. If not on Amazon GOTO start.

The Atlas Scientific EZO adaptor board in UART mode is what I have used in a pH monitoring project. The Arduino used was a Nano Every with MegaCoreX to handle the multiple Serial connections and other i/o.