How to calibrate PH sensor and TDS sensor using ESP32

This project is using Blynk application. I can see the readings of the sensor in the app and also on the web dashboard. My only problem in this project is that the PH sensor’s reading is above 14. A normal PH of water ranges from 6-8. And also the reading of TDS sensor is high. I need to calibrate the sensor so that the reading can be accurate but I don’t know how. I know this is not Blynk-related problem but I really need your help. Thank you.

I am using 3 sensors. PH sensor, TDS sensor and FLOAT sensor. But I just need to calibrate the two.
This is the code:

#define BLYNK_TEMPLATE_ID "For privacy purposes"
#define BLYNK_DEVICE_NAME "For privacy purposes"

#define BLYNK_FIRMWARE_VERSION        "0.1.0"

#define BLYNK_PRINT Serial
#include "BlynkEdgent.h"

#define USE_WROVER_BOARD

#define PH_Pin 33          //pH meter Analog output to Arduino Analog Input 
unsigned long int avgValue;  //Store the average value of the sensor feedback
float b;
int buf[10],temp;

int fl_Sensor=32;
int fval=0;
int Redled=5;
//BlynkTimer timer;
int f;  // for float value to string converstion
float val; // also works with double. 
char buff2[10];
String valueString = "";
String PH_Val = ""; 

int DSPIN = 14; // Dallas Temperature Sensor
int TDS_Sensor = 34;
float Aref = 3.3;

float ec_Val = 0;
unsigned int tds_value = 0;
float ecCal = 1;

WidgetLED ledglow(V5); // add virtual LED to V5

void float_Data()
{
 fval = digitalRead(fl_Sensor);  
 if (fval == LOW) 
  { 
    digitalWrite(Redled, HIGH);
    ledglow.on();
    Serial.println( "WATER LEVEL - HIGH"); 
  } 
  else 
  { 
    digitalWrite(Redled, LOW);
    ledglow.off();
    Serial.println( "WATER LEVEL - LOW" ); 
  } 
}
void TDS_Data()
{
//  double wTemp = TempRead()* 0.0625;  // conversion accuracy is 0.0625 / LSB
  double wTemp= 27;
  float V_level= Aref / 1024.0;
  float rawEc = analogRead(TDS_Sensor) * V_level;  // Raw  data of EC
  float T_Cof = 1.0 + 0.02 * (wTemp - 25.0);  // Temperature Coefficient
  
  ec_Val = (rawEc / T_Cof) * ecCal;// temperature and calibration compensation
  tds_value = (133.42 * pow(ec_Val, 3) - 255.86 * ec_Val * ec_Val + 857.39 * ec_Val) * 0.5; 
  double Far= (((wTemp *9)/5) + 32); // Temp in Far*
  
  Serial.print("TDS:"); Serial.println(tds_value);
  Serial.print("EC:"); Serial.println(ec_Val, 2);
  Serial.print("Temperature (oC):"); Serial.println(wTemp,2);   
  Serial.print("Temperature (oF):"); Serial.println(Far,2);
  Serial.print("");
 
  Blynk.virtualWrite(V1, wTemp);
  Blynk.virtualWrite(V2, Far);
  Blynk.virtualWrite(V3, tds_value);
  Blynk.virtualWrite(V4, ec_Val);
}
void PH_Data()
{
  for(int i=0;i<10;i++)       //Get 10 sample value from the sensor for smooth the value
  { 
    buf[i]=analogRead(PH_Pin);
    delay(10);
  }
  for(int i=0;i<9;i++)        //sort the analog from small to large
  {
    for(int j=i+1;j<10;j++)
    {
      if(buf[i]>buf[j])
      {
        temp=buf[i];
        buf[i]=buf[j];
        buf[j]=temp;
      }
    }
  }
  avgValue=0;
  for(int i=2;i<8;i++)                      //take the average value of 6 center sample
  avgValue+=buf[i];
  float phValue=(float)avgValue*3.3/1024/6; //convert the analog into millivolt
  phValue=3.5*phValue;                      //convert the millivolt into pH value
  
  PH_Val =  dtostrf(phValue, 4, 2, buff2);  //4 is mininum width, 6 is precision
  Serial.print(PH_Val);
  valueString = "";
  delay(1000);
  Blynk.virtualWrite(V0, PH_Val);
}
void setup()
{
  Serial.begin(9600);
  delay(100);
  pinMode(fl_Sensor, INPUT_PULLUP); // Float Sensor
  pinMode(Redled, OUTPUT); // Red LED

  BlynkEdgent.begin();
}

void loop() {
  BlynkEdgent.run();
  PH_Data();
  TDS_Data();
  float_Data();
  delay(500);
}

You won’t get sendible and repeatable results until you stop doing this in your void loop. You should start by reading this:

You then need to understand what type of analog voltages your sensors output, and check that this is in the range expected by the ESP32. The ESP32 will give the maximum analog reading with an analog input voltage of 3.3v.
If your sensors output more than this then you’ll need to use a voltage divider circuit to adjust that output to a range acceptable by the ESP32.

If this code was written for an ESP8266 (which I suspect it was) then you’ll need to make some adjustments to the code.
The ESP32 uses 12 bit resolution for its analog pins, compared to 10 bits for the ESP8266.
This means that in default mode an input voltage on the analog pin of an ESP8266 will result in an output of 1024 from the ADC.
The same voltage applied to an analog pin of the ESP32 will result in an ADC output of 4095.

Your code is dividing the resulting TDS reading by 1024, which is hy I suspect it was written for the ESP8266…

Pete.

The two analog sensors uses 3.3v

double wTemp= 27;
  float V_level= Aref / 4096.0;
  float rawEc = analogRead(TDS_Sensor) * V_level;  // Raw  data of EC
  float T_Cof = 1.0 + 0.02 * (wTemp - 25.0);  // Temperature Coefficient

I also change the PH sensor

 float phValue=(float)avgValue*3.3/4096/6; //convert the analog into millivolt

Is this correct sir?

I’ve no idea. You need to start by understanding the voltages that are outputted by your sensors.

Pete.

The output of the sensors is 3.3v sir

If it outputted a constant 3.3v then the sensor would be useless. It will output a range of voltages in relation to a range of PH values, ad it’s that range that you need to understand in order to be able to design your circuit and sketch and to calibrate your sensor.

Pete.

Hi, you may try one sensor first. I have a pH sensor and a DO sensor, and have to use an analogue isolation board to get both of them work at the same time.

Kang

I have a question. I browse the internet and saw that the output voltage of SEN0161-V2 Ph sensor ranges from 0~3.0V. And the SEN0244 tds sensor ranges from 0~2.3V. So if I will edit the sketch posted by JunJun, it will look like this:

#define BLYNK_TEMPLATE_ID "For privacy purposes"
#define BLYNK_DEVICE_NAME "For privacy purposes"

#define BLYNK_FIRMWARE_VERSION        "0.1.0"

#define BLYNK_PRINT Serial
#include "BlynkEdgent.h"

#define USE_WROVER_BOARD

BlynkTimer timer;

#define PH_Pin 33          //pH meter Analog output to Arduino Analog Input 
unsigned long int avgValue;  //Store the average value of the sensor feedback
float b;
int buf[10],temp;

int fl_Sensor=32;
int fval=0;
int Redled=5;

int f;  // for float value to string converstion
float val; // also works with double. 
char buff2[10];
String valueString = "";
String PH_Val = ""; 

int DSPIN = 14; // Dallas Temperature Sensor
int TDS_Sensor = 34;
float Aref = 2.3;

float ec_Val = 0;
unsigned int tds_value = 0;
float ecCal = 1;

WidgetLED ledglow(V5); // add virtual LED to V5

void float_Data()
{
 fval = digitalRead(fl_Sensor);  
 if (fval == LOW) 
  { 
    digitalWrite(Redled, HIGH);
    ledglow.on();
    Serial.println( "WATER LEVEL - HIGH"); 
  } 
  else 
  { 
    digitalWrite(Redled, LOW);
    ledglow.off();
    Serial.println( "WATER LEVEL - LOW" ); 
  } 
}
void TDS_Data()
{
//  double wTemp = TempRead()* 0.0625;  // conversion accuracy is 0.0625 / LSB
  double wTemp= 27;
  float V_level= Aref / 4095.0;
  float rawEc = analogRead(TDS_Sensor) * V_level;  // Raw  data of EC
  float T_Cof = 1.0 + 0.02 * (wTemp - 25.0);  // Temperature Coefficient
  
  ec_Val = (rawEc / T_Cof) * ecCal;// temperature and calibration compensation
  tds_value = (133.42 * pow(ec_Val, 3) - 255.86 * ec_Val * ec_Val + 857.39 * ec_Val) * 0.5; 
  double Far= (((wTemp *9)/5) + 32); // Temp in Far*
  
  Serial.print("TDS:"); Serial.println(tds_value);
  Serial.print("EC:"); Serial.println(ec_Val, 2);
  Serial.print("Temperature (oC):"); Serial.println(wTemp,2);   
  Serial.print("Temperature (oF):"); Serial.println(Far,2);
  Serial.print("");
 
  Blynk.virtualWrite(V1, wTemp);
  Blynk.virtualWrite(V2, Far);
  Blynk.virtualWrite(V3, tds_value);
  Blynk.virtualWrite(V4, ec_Val);
}
void PH_Data()
{
  for(int i=0;i<10;i++)       //Get 10 sample value from the sensor for smooth the value
  { 
    buf[i]=analogRead(PH_Pin);
    delay(10);
  }
  for(int i=0;i<9;i++)        //sort the analog from small to large
  {
    for(int j=i+1;j<10;j++)
    {
      if(buf[i]>buf[j])
      {
        temp=buf[i];
        buf[i]=buf[j];
        buf[j]=temp;
      }
    }
  }
  avgValue=0;
  for(int i=2;i<8;i++)                      //take the average value of 6 center sample
  avgValue+=buf[i];
  float phValue=(float)avgValue*3.0/4095/6; //convert the analog into millivolt
  phValue=3.5*phValue;                      //convert the millivolt into pH value
  
  PH_Val =  dtostrf(phValue, 4, 2, buff2);  //4 is mininum width, 6 is precision
  Serial.print(PH_Val);
  valueString = "";
  delay(1000);
  Blynk.virtualWrite(V0, PH_Val);
}

void setup()
{
  Serial.begin(9600);
  delay(100);
  pinMode(fl_Sensor, INPUT_PULLUP); // Float Sensor
  pinMode(Redled, OUTPUT); // Red LED
  timer.setInterval(1000L, PH_Data); //timer will run every sec 
  timer.setInterval(1000L, TDS_Data); //timer will run every sec 
  timer.setInterval(1000L, float_Data); //timer will run every sec 
  BlynkEdgent.begin();
}

void loop() {
  BlynkEdgent.run();
  timer.run();        // run timer every second
}

Please tell me if this is wrong. I want to learn

I can’t really follow the logic of the maths you’ve used, but the original code was far from ideal anyway.

An analog voltage of 3.3v will produce a result of 4095.
As the maximum voltage the PH sensor can provide is 3.0v the maximum analog reading you’ll see is somewhere around 3723.
The ESP32’s ADC isn’t 100% linear, but for the purpose of this discussion we’ll assume it is, and it won’t make much difference in this situation anyway.

Ultimately you want to end-up with a PH reading that is between 0 and 14, and I assume that PH 14 = 3.3v from the sensor = an analog reading of 3723.

I also assume that a voltage of 0 from the sensor equates to a PH reading of 0.

The simplest way of translating the range of 0-3723 into a PH result of 0-14 is to use the map command in C++ but the problem with this is that the result will be an integer, so you’ll never get results like 7.35. If that’s not an issue then using the map command is the way to go.

Otherwise, you’d probably want to divide 3723 by 14, which gives 266 then divide your analog reading by this value to give a PH value.

For example half of the maximum reading of 3723 is 1861.
1861/266 = 6.99

The alternative is to use a library like MapFloat which allows you to obtain floating point rather than integer results when you map the analog readings to your PH values.

Pete.

Mapf by Natan Lisowski?

void PH_Data()

{

  for(int i=0;i<10;i++)       //Get 10 sample value from the sensor for smooth the value

  {

    buf[i]=analogRead(PH_Pin);

    delay(10);

  }

  for(int i=0;i<9;i++)        //sort the analog from small to large

  {

    for(int j=i+1;j<10;j++)

    {

      if(buf[i]>buf[j])

      {

        temp=buf[i];

        buf[i]=buf[j];

        buf[j]=temp;

      }

    }

  }

  avgValue=0;

  for(int i=2;i<8;i++)                      //take the average value of 6 center sample

  avgValue+=buf[i];

  float phValue=(float)avgValue*3.0/3723/6; //convert the analog into millivolt

  phValue=3.5*phValue;                      //convert the millivolt into pH value

 

  PH_Val =  dtostrf(phValue, 4, 2, buff2);  //4 is mininum width, 6 is precision

  Serial.print(PH_Val);

  valueString = "";

  delay(1000);

  Blynk.virtualWrite(V0, PH_Val);

}

And?

Pete.

Is this correct or not?

Is the library name Mapf by Natan Lisowski?

I don’t know, it’s not what I suggested in my previous post, but it depends what you’re trying to achieve.

I wasn’t recommending a specific library, simply suggesting that this may be a suitable approach.

You said…

So I’ve given you a few of my thoughts to point you in what I consider to be a better direction with your research. I’m not able to give you a definitive answer as I don’t have one of these sensors myself, and don’t know exactly what it is that you are trying to achieve.

Pete.

Can you edit this sketch and write the right code? I really don’t understand. I’m sorry

void PH_Data()

{

  for(int i=0;i<10;i++)       //Get 10 sample value from the sensor for smooth the value

  {

    buf[i]=analogRead(PH_Pin);

    delay(10);

  }

  for(int i=0;i<9;i++)        //sort the analog from small to large

  {

    for(int j=i+1;j<10;j++)

    {

      if(buf[i]>buf[j])

      {

        temp=buf[i];

        buf[i]=buf[j];

        buf[j]=temp;

      }

    }

  }

  avgValue=0;

  for(int i=2;i<8;i++)                      //take the average value of 6 center sample

  avgValue+=buf[i];

  float phValue=(float)avgValue*3.0/3723/6; //convert the analog into millivolt

  phValue=3.5*phValue;                      //convert the millivolt into pH value

 

  PH_Val =  dtostrf(phValue, 4, 2, buff2);  //4 is mininum width, 6 is precision

  Serial.print(PH_Val);

  valueString = "";

  delay(1000);

  Blynk.virtualWrite(V0, PH_Val);

}

I’m not going to write your code for you, especially when I don’t have the hardware available to test it.

If you want to learn then experimentation is the best way.

Pete.

On your reply here, you said that the code is dividing the resulting TDS reading by 1024, is because 1024 is for ESP8266. Now I will edit the code and change it to around 2600-2700 because the maximum output voltage of TDS sensor is 2.3V.

If you can’t make any sense of the code, I won’t even try! :wink: But even if Ricky1 manage to get the sensors to work, this might be a problem later on:

timer.setInterval(1000L, PH_Data);

void PH_Data()
{
...
delay(1000);
...
}

You missed one Pete :stuck_out_tongue:

1 Like