I have a pH meter (DIY-4502C) hooked up with Raspberry Pi. However, the instructions to calibrate is in Chinese and seems outdated.

Does anybody know how to calibrate it for measuring pH4-7?

3 Answers 3


I recommend this repository for ADS1115 for RPi in Python and for Arduino.

  1. Calibration
  2. Reset
  3. Read the values


Screenshot of output

You can adapt to your project and works properly.

 * @brief Init The Analog pH Sensor
void begin();

 * @brief Convert voltage to PH with temperature compensation
 * @param voltage     : Voltage value
 *        temperature : Ambient temperature
 * @return The PH value
float readPH(float voltage, float temperature);

 * @brief Calibrate the calibration data
 * @param voltage     : Voltage value
 *        temperature : Ambient temperature
 *        cmd         : enterph -> enter the PH calibration mode
 *                      calph   -> calibrate with the standard buffer solution, two buffer solutions(4.0 and 7.0) will be automaticlly recognized
 *                      exitph  -> save the calibrated parameters and exit from PH calibration mode
void calibration(float voltage, float temperature, char* cmd);
  • Your recommendation is very good. Rpi uses python to control ADS1115 to do ADC of PH4502C analog output. Rpi without any analog input pin must use an ADC module to help doing ADC. ADC1115 is a good choice.
    – tlfong01
    Commented Jun 11, 2019 at 5:35
  • How to use it ? just run the py file? or ? Commented Oct 1, 2020 at 20:42

From How to use a pH sensor with Arduino

Calibrate the sensor

As we can see that there are two potentiometers in the circuit. Which it is closer to the BNC connector of the probe is the offset regulation, the other is the pH limit.

  • Offset: The average range of the probe oscillates between negative and positive values. The 0 represents a pH of 7.0. In order to be able to use it with Arduino this circuit adds an offset value to the value measured by the probe, so the ADC will only have to take samples of positive voltage values. Therefore we will force a pH of 7.0 by disconnecting the probe from the circuit and short-circuiting the inside of the BNC connector with the outside. With a multimeter measure the value of Po pin and adjust the potentiometer to be 2.5V.
  • PH Limit: This potentiometer is to set a limit value of the pH sensor circuit that causes the red LED to light up and the Do pin signal to turn ON.

In addition we have to calculate the voltage conversion that will give us the pH sensor so we will need two pH reference value and measure the voltage returned by the sensor on the pin Po. The best thing to do is to use a calibration solution in powders, there are also in liquid but it is easier to preserve the powders. These solutions are sold in different values but the most common are pH 4.01, pH 6.86 and pH 9.18.

Graph of the measured voltage and pH equation

Graph of the measured voltage and pH equation. y= -5.70 * x + 21.34

Using the powders with pH 4.01 and pH 6.86 we obtain the voltages on the pin Po 3.04V and 2.54V respectively. The sensor is linear so by taking two points we can deduce the equation to convert the measured voltage to pH. The general formula would be y = mx + b, so we have to calculate m and b since x would be the voltage and y the pH. The result is y = -5.70x + 21.34.

Connection with Arduino

To connect with Arduino we will need an analog input (A0), power (5V) and two GND that actually in the sensor circuit are separated but we can use the same.


The code consists of taking 10 samples of the analogue input A0, ordering them and discarding the highest and the lowest and calculating the mean with the six remaining samples by converting this value to voltage in the variable pHVol, then using the equation that we have calculated with the pH reference values we convert pHVol to pHValue and send it to the serial port to see it in the serial monitor.

const int analogInPin = A0; 
int sensorValue = 0; 
unsigned long int avgValue; 
float b;
int buf[10],temp;
void setup() {
void loop() {
 for(int i=0;i<10;i++) 
 for(int i=0;i<9;i++)
  for(int j=i+1;j<10;j++)
 for(int i=2;i<8;i++)
 float pHVol=(float)avgValue*5.0/1024/6;
 float phValue = -5.70 * pHVol + 21.34;
 Serial.print("sensor = ");
  • any chance you could elaborate or walk a person through how you derived y = -5.70x + 21.34. from your voltage and pH readings? Commented Oct 15, 2019 at 1:35
  • Check the link, this is a quote from the link Commented Oct 15, 2019 at 9:27

Update 2019oct07hkt1128

Newbie FAQ

Q1. How to adjust offset?

A1. Following Part1 Section 1,

(1) Power supply 5V to module,

(2) Do the 3 offset calibrating steps until your multi-meter read about 2.5V.


(a) For offset calibration, you don't use any liquid sample. You just short the BNC plug input (centre) to ground (outside sleeve), using to clips, so that the input to pH sensor is zero.

Part 1 - Calibration Procedure

1. Analog output offset procedure

Offsetting meter analog output of 0.0V to + 2.5V for compatibility with ADC such MCP3008, MCP3208 etc. Cheap pH meters are almost always designed for Arduino which has an analog pin A0 to convert analog signal to digital. Rpi, sadly does not have any analog pin, so must use an external ADC moduel. More about this later.)

ph meter

offset procedure

2. pH limit potentiometer adjustment procedure

Set 2.46V which corresponds to the pH value of tap water as threshold voltage, by adjusting the limit potentiometer so that the red LED just turns on.

ph limit setup

Self/Auto Calibration Procedure

So the US10 cheap meter gives 2.46V for water. That is amazing, considering that I was too lazy to clean the glass, which for sure has some faint whisky traces left. My offset to the pH meter ADC is +2.5V, implying that water should read +2.50V. Again I am only using home tap water. Perhaps whisky and heavy metal traces in the tap water balances out. :)

Now that I think I should test at the other extremes, very acid and very basic. I have googled the following pH value charat, and see if I can find some more things to test/calibrate.

ph values chart

I just finished lunch. Before eating the desert Chinese mandarin dessert, I toodk a photo, and checked it pH.

chinese madarin

And green tea pH again.

green test 2.58v

Now there is the pH of something that I really want to know, the body wash I am suing every day.

I found the analog voltage. Now I need to calcule the pH value.

jj 2 in 1 pH5.5

ph results table

Now soy sauce and vinegar

soy sauce and vinegar

And finally, very acidic bleach and drain pipe cleaner!

bleach and drian pipe cleaner

The time has come to write the python program

Now I have enough samples, from very acidic to very basic, to get the ph per volt ratio which is used to get from measure analog output in volts to the pH value.

I am making morning coffee with milk, and a selfie!

coffee with milk

I updated the Excel worksheet with coffee and tea. Now the average pH/V is recursively self calibrating, meaning that more measurements added, more "accurate" is the average pH/V.

What I found too good to be true is that the measured pH value of the JJ body wash claiming pH5.5 is 5.51!

ph table

Now I have drawn the graph to get the values for the Rpi python program to calculate the pH values.

ph worksheet

pH meter temperature compensation calibration procedure

I read more references and found a problem. That is Arduino has ADC pins to read the pHmeter's analog signals Po and To, but Rpi has no built in ADC pins, and must use external ADC to do the conversion.

ph meter pinout

Coca Cola is pH 2.2

coca cola ph

PCF8591 ADC for pH meter

Now I am thinking of using PCF8591 to do ADC of pH meter for Rpi to read.

pcf8591 module

Temperature Calibration Notes

The PCF8591 ADC will be used to convert two analog out signals - Ao, voltage proportional to pH, and To, NTS 103 thermistor temperature output.

So I am reading the following notes to learn how to calibrate the temperature reading To.

Designing With pH Electrodes, Application Notes AN1852 - TI 2013April

Recalibration and ADC Calculation Notes

Now I have moved both the ph meter and PCF5891 ADC module to a protoboard. This time I did a recalibration using tap water. I found the reading is off by about 3%. The PCF8591 8bit ADC is 1/256 ~= 4%. So I think it is OK for now as a feasibility study to use a 8-bit ADC. Later I think I should replace the 8-bit to a 12/16 or even 24 bit ADC, if I am measuring the pH values not in 1~14 (0V to 5V), but say for soft drinks, in a narrow voltage range of 1 to 2 volts.

Anyway, the ADC is ready to start testing. In other words,

Rpi python pH value measurement programming only starts now! :)

ph meter and adc

Update 2019apr29hkt2308

I forgot to do temperature calibration. I used a heat gun to do a rough check. I found that at room temperature the output is a steady 4.2V. The reading goes as low as 2V when I used the heat gun to increase the temperature of the NTC thermistor. The reading goes low very fast, and recovers to normal only a couple of minutes after I removed the heat. So I now know it is around 4.2V at room temperature and goes low to 2.0V when very warm. The digital out is 0.8V and the red LED is on. Anyway, I will not be using the alarm warning red light for now.

So now I really start the ADC python programming.

But I need to make sure if the meter offset has not drifted away. I found ph 7 voltage is 2.51V, not drifted at all, but tap water measured 2.37, offset 5.2%. Perhaps the probe is contaminated, because I have been too lazy to rinse it often, ...

water ph

Update 2019may01hkt1136

I found that the PCF8591 ADC with only 8 bit resolution is not precise enough for me to differentiate between Chinese tea and Ceylon tea, Coke from Pepsi etc.

Chinese Tea = 2.58 vs Ceylon Tea 2.64

Voltage difference = 2.58 - 2.64 = 0.06V

Percentage difference = 0.06V

0.06V / 5V (input range) = 1.2% 1 / 256 (8-bit) = 0.4%

So I am thinking of using another ADC. I am considering MCP3008 and MCP3201. SPI MCP3201 seems appropriate for this pH meter project, which needs only one channel, thus the Rpi python SPI program should be easy to write and debug.

MCP3201 SPI 8-bit ADC to convert the pH meter analog signal

Comparing MCP3201 with PCF8591 Record

MCP3201 12-bit SPI 2.7V - 5.5V 100ksps max

PCF8591 Datasheet - NXP

AliExpress PCF8591 AD/DA Converter Module Analog To Digital Conversion temperature illumination For Arduino - US$1.37

lipton tea

Programming MCP3201 to convert pH meter output

MCP3201 seems even easier than MCP3008, because it has only one differential channel, no need to mess around with which channel single ended, which channel differential, and should idle channel pull up to reduce noise etc.


Replacing MCP3201 by MCP3208

I found a problem. I forgot that I actually need to calibrate or convert two analog signals: (1) pH value Po and (2) NTC 103 temperature sensor analog signals. I thought about using MCP3204 the 4 channel version. But MCP3204 and MCP3208 are almost identical in architecture, the only difference is number of channels. So the python program is more or less the same. So I decided to use MCP3208.

mcp3208 summary

Now it is time to start writing the python program. I am thinking of writing 3 big functions:

  1. Write command to MCP3208 ADC

  2. Read results from MCP3208 ADC

  3. Convert binary data to decimal

  4. Try to guess if it is soft drink, coke or pepsi, or if fruit, possbile orange or mandarin, etc,

Update 2019may03hkt1056 - MCP3208 Programming Notes

Now I have enhanced the basic SPI loop back and repeat send with the following functions to make it easier to interface with MCP3008/MCP3201/MCP3208

  1. Added loopback 1, 2, and 3 bytes. Because MCP3008/MCP3208 works by writing and reading two bytes. Other ADC with 16/24 bits resolution work with three bytes write read. So it is important to make sure write/reading 2/3 bytes are OK.

  2. Added repeat write functions for easier scope display. This is important to make sure that the waveforms are not noise interferenced or distorted at high frequencies. For now only 100kHz is tested. A sample scope screen capture is show below, and looking OK.

mcp3208 repeat send

MCP3208 Program Listing

The program should go many debugging and revisions, therefore not to do too frequent updating is this post, a Penzu programming diary is given here. The link will keep the same until next programming milestone. Otherwise the same link will find new program versions updated int eh PenZu diary. Now is version 09.

mcp3208 SPI ADT Test Program Version 09 - tlfong01 2019may03hkt1108

Update 2019may03hkt1531

I checked my urine and found its pH about 8. I googled and concluded that it is alkaline. I also read the following article to know more.

What is the normal pH range for urine? - Rachel Nall 2018dec11, Reviewed by Shilpa Amin MD, CAQ, FAAFP

According to the American Association for Clinical Chemistry, the average value for urine pH is 6.0, but it can range from 4.5 to 8.0. Urine under 5.0 is acidic, and urine higher than 8.0 is alkaline, or basic. Different laboratories may have different ranges for "normal" pH levels.

Anyway, I will move on to python programming.

Update 2019may04hkt2108

But one more sample for self calibrating worksheet.

deer wine pH

Now Chinese deer herb wine voltage output = 2.95V

So pH value = 7 + (2.95 - 2.5) * -5.5 = 4.5

And urine pH measurements

urine pH value

Using DS18B20 to measure the temperature of liquid to refine calibration

When translating the OP's pH meters manuals, I found the DS18B20 is used to measure the liquid temperature, so to adjust the calibration values. So I am now checking out how DS18B20 can be used.


/ to continue, ...

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Part 2 - Chinese to English Manual Translation and Suggestion for Newbies

Well, give me the link of the Chinese manual, I can translate it into broken English. :)


Do you mean the AT instruction set? Or the hardware setup? I guess they are common to all the real and fake boards. I can do the translation if that is what you need.

The basic Hello test is universal. You say "AT\r\n", and the Ph Board will return "OK". The general commands are also universal:

Reset, Current Ph value, Temperature, Threshold, Factory default setting, Current operation mode, ...

ph at code

If you wish to translate the names of the pins. Here is one for the J3, J4 pinouts.


You may have already found the location of DS18B20 temperature sensor pin. This is a standard device, so you don't need to read their instruction.

One other thing is that the vendor recommends to use their CH340 cable. This is important, because other cables using PL23dxx might not work for their board.

You may also find other teething problems. I have a brief suggestion list for the newbies.

  1. Test Rpi 3V3 signals is OK
  2. Test Rpi 3V3 signals converted to 5V logcie is OK.
  3. Test Rpi 3V3/5V0 loopback hardware is OK.
  4. Test Rpi sending string such as "AT/r/n" is OK
  5. Test is CH340 cable is OK

One final reminder to USB CH340 newbies: Remember to disable Serial Console at the Rpi Config Menu:

serial cable disable

And the plug and play, no library required, minimal, complete, verifiable, UART self test/loopabck, repeat send AT/r/n python program.

UART Self Test Program

# uart_test06 tlfong01 2019apr08hkt1603 ***

# Computer = Rpi3B+
# Linux    = $ hostnamectl = raspberrypi Raspbian GNU/Linux 9 (stretch) Linux 4.14.34-v7+ arm 
# Python   = >>> sys.version = 3.5.3 Jan 19 2017

# Test 1   - repeatWriteBytes() - UART port repeatedly send out bytes.  
# Function - Repeat many times sending bytes, pause after each bytes.

# Test 2   - loopBackTest() - UART port send and receive bytes.
# Function - Send one bytes to TX, wait some time (Note 1), then read bytes back from RX. 
# Setup    - Connet Tx pin to Rx pin to form a loop.

# Note 1
# Bolutek BlueTooth BC04 needs at least 10mS to respond

from   time import sleep
import serial

serialPort0 = serial.Serial(port = '/dev/serial0',
        baudrate = 9600,
        parity = serial.PARITY_NONE,
        stopbits = serial.STOPBITS_ONE,
        bytesize = serial.EIGHTBITS,
        timeout= 1)

def setSerialPortBaudRate(serialPort, baudrate):
    serialPort.baudrate = baudrate

def serialPortWriteBytes(serialPort, writeBytes):

def serialPortReadBytes(serialPort, maxBytesLength):
    readBytes = serialPort.read(maxBytesLength)
    return readBytes

def serialPortWriteWaitReadBytes(serialPort, writeBytes, maxBytesLength, waitTime):
    readBytes = serialPortReadBytes(serialPort, maxBytesLength)
    print('        bytes written = ', writeBytes) 
    print('        bytes read    = ', readBytes)
    return readBytes

def repeatWriteBytes(serialPort, writeBytes, pauseTimeBetweenBytes, repeatCount):
    print('       Begin repeatWriteOneByte(), ...')   
    for i in range(repeatCount):
        serialPortWriteBytes(serialPort, writeBytes)                
    print('       End   repeatWriteOneByte().')

def serialPortLoopBack(serialPort, writeBytes, maxBytesLength, waitTime): 
    print('        Begin serialPortLoopBack() [Remember to connect Tx to Rx!] , ...')
    serialPortWriteWaitReadBytes(serialPort, writeBytes, maxBytesLength, waitTime)     
    print('        End   serialPortLoopBack(), ...')

setSerialPortBaudRate(serialPort0, 9600)
#repeatWriteBytes(serialPort0, b'AT\r\n', 0.01, 200000000)
serialPortLoopBack(serialPort0, b'AT\r\n', 32, 0.030)

''' Sample output  tlfong01 2019apr0801
=== RESTART: /home/pi/Python_Programs/test1193/uart_test02_2019apr0801.py ===
        Begin serialPortLoopBack() [Remember to connect Tx to Rx!] , ...
        bytes written =  b'AT\r\n'
        bytes read    =  b'AT\r\n'
        End   serialPortLoopBack(), ...

# End 

Good luck to your project! :)


Ph Testing Theory, Ph board pinout translation

Mar 02, 2016, 07:59 pm Last Edit: Mar 02, 2016, 09:26 pm by LeCyb Reason, Arduino Forum

(2) PCF8591 ADC/DAC Program V1.0 - tlfong01 2019no19hkt1718

(3) PCF8591 ADC/DAC Programming V2.0 - tlfong01 2019nov19 penzu

(4) PCF8591 ADC/DAC Programming V2.1 - tlfong01 2019nov19 penzu

(5) Ezo pH sensor Product sheet - Atlas Scientific

(6) Ezo pH sensor User Manual - Atlas Scientific

  • 4
    How is this an answer to the question? Commented Apr 18, 2019 at 5:34
  • 1
    This is an interesting answer, but does not appear to mention calibration. If you update it to include calibration then it would be a good answer. Commented Apr 18, 2019 at 7:31
  • Well, I am just a poor hobbyist. Long time ago, I studied in a trade middle school, focusing on metal work and mechanical drawing, hoping to become a car mechanic one day to earn a living. Actually I have never heard about the pH thing until some year ago when TV ads mention about it. I have never seen any pH equipment in school lab. All I know is from Google. So you see at the end of my answer, I added the pH general knowledge post. I read it a couple of times, memorized the strange names by heart, so I can pretend to know some chemistry, and get more respect than I deserve, ... :)
    – tlfong01
    Commented Apr 18, 2019 at 7:50
  • @Steve Robillard Ah, sorry, I did not understand your comment until I read Greenonline's answer. Originally I thought what the OP wanted is a translation of the manual from Chinese to English, and my job is done. Now after browsing the Arduino forum posts I know even if the OP got the full manual in English, he might not be able to do the calibration which requires subject knowledge in chemistry and pH stuff. I am good in physics but hopeless in chemistry. Anyway, I will try to do some self learning in pH stuff and see if I can improve my answer.
    – tlfong01
    Commented Apr 19, 2019 at 9:10
  • 1
    Big thanks @tlfong01 only your formula works for me - just a bit edited ;) So when I read data via command: gpio -x pcf8591:120:0x48 aread 120 .Then I calculate phValue: 7 + ((parseFloat(data) * 3.3 / 255) - 2.5) * (-5.7) Commented Mar 24, 2020 at 21:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.