ESP32 with TDS Sensor (Water Quality Sensor).

Table of Contents

ESP32 with TDS Sensor Water Quality Sensor Arduino

In this tutorial, we’ll cover the adhering to subjects

A TDS meter gauges the number of overall liquified solids like salts, minerals, and metals in the water.

In the loophole(), get new TDS readings every 40 nanoseconds as well as save them in the barrie

Introducing the TDS Meter

Every 800 milliseconds, it gets the newest readings and obtains the ordinary voltage by utilizing the filtering system algorithm developed prior to:

Total dissolved solids TDS meter

Then, it determines a temperature level compensation coefficient as well as computes the TDS worth taking that worth into account:

Finally, it prints the TDS worth in ppm:

After duplicating the code to the Arduino IDE, post the code to your board. We have tutorials for various other prominent sensors that you may such as:

Learn even more about the ESP32 with our sources:

Features and Specifications

Thanks for reading.

Table of Contents

  • Input Voltage: DC 3.3 ~ 5.5V
  • Output Voltage: 0 ~ 2.3V
  • Working Current: 3 ~ 6mA
  • TDS Measurement Range: 0 ~ 1000ppm
  • TDS Measurement Accuracy: ± 10% F.S. (25 ℃)
  • Module Interface: XH2.54-3P
  • Electrode Interface: XH2.54-2P

In this tutorial, we’ll cover the adhering to subjects

  • Number of Needle: 2
  • Total Length: 60cm
  • Connection Interface: XH2.54-2P
  • Color: White
  • Waterproof Probe

Where to Buy TDS Sensor?

A TDS meter measures the number of total dissolved solids like salts, minerals, as well as metals in the water.

Wire the sensor as in the complying with table:

As we stated formerly, the sensing unit outputs an analog signal that can be transformed to TDS in ppm.

Interfacing the TDS Meter with the ESP32

Let’s take a quick appearance at the code.

TDS Sensor ESP32
GND GND
VCC 3.3V
Data GPIO 27 (or any other )

Reading TDS (water quality) with the ESP32 – Code

In the loop(), get new TDS analyses every 40 nanoseconds as well as conserve them in the barrier:

ESP32 with TDS sensor water quality

Every 800 nanoseconds, it gets the newest readings and gets the typical voltage by making use of the filtering algorithm developed prior to:

Then, it calculates a temperature compensation coefficient and computes the TDS value taking that value into account:

Finally, it prints the TDS worth in ppm:

// Original source code: https://wiki.keyestudio.com/KS0429_keyestudio_TDS_Meter_V1.0#Test_Code
// Project details: https://RandomNerdTutorials.com/esp32-tds-water-quality-sensor/

#define TdsSensorPin 27
#define VREF 3.3              // analog reference voltage(Volt) of the ADC
#define SCOUNT  30            // sum of sample point

int analogBuffer[SCOUNT];     // store the analog value in the array, read from ADC
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0;
int copyIndex = 0;

float averageVoltage = 0;
float tdsValue = 0;
float temperature = 25;       // current temperature for compensation

// median filtering algorithm
int getMedianNum(int bArray[], int iFilterLen){
  int bTab[iFilterLen];
  for (byte i = 0; i<iFilterLen; i++)
  bTab[i] = bArray[i];
  int i, j, bTemp;
  for (j = 0; j < iFilterLen - 1; j++) {
    for (i = 0; i < iFilterLen - j - 1; i++) {
      if (bTab[i] > bTab[i + 1]) {
        bTemp = bTab[i];
        bTab[i] = bTab[i + 1];
        bTab[i + 1] = bTemp;
      }
    }
  }
  if ((iFilterLen & 1) > 0){
    bTemp = bTab[(iFilterLen - 1) / 2];
  }
  else {
    bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
  }
  return bTemp;
}

void setup(){
  Serial.begin(115200);
  pinMode(TdsSensorPin,INPUT);
}

void loop(){
  static unsigned long analogSampleTimepoint = millis();
  if(millis()-analogSampleTimepoint > 40U){     //every 40 milliseconds,read the analog value from the ADC
    analogSampleTimepoint = millis();
    analogBuffer[analogBufferIndex] = analogRead(TdsSensorPin);    //read the analog value and store into the buffer
    analogBufferIndex++;
    if(analogBufferIndex == SCOUNT){ 
      analogBufferIndex = 0;
    }
  }   
  
  static unsigned long printTimepoint = millis();
  if(millis()-printTimepoint > 800U){
    printTimepoint = millis();
    for(copyIndex=0; copyIndex<SCOUNT; copyIndex++){
      analogBufferTemp[copyIndex] = analogBuffer[copyIndex];
      
      // read the analog value more stable by the median filtering algorithm, and convert to voltage value
      averageVoltage = getMedianNum(analogBufferTemp,SCOUNT) * (float)VREF / 4096.0;
      
      //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0)); 
      float compensationCoefficient = 1.0+0.02*(temperature-25.0);
      //temperature compensation
      float compensationVoltage=averageVoltage/compensationCoefficient;
      
      //convert voltage value to tds value
      tdsValue=(133.42*compensationVoltage*compensationVoltage*compensationVoltage - 255.86*compensationVoltage*compensationVoltage + 857.39*compensationVoltage)*0.5;
      
      //Serial.print("voltage:");
      //Serial.print(averageVoltage,2);
      //Serial.print("V   ");
      Serial.print("TDS Value:");
      Serial.print(tdsValue,0);
      Serial.println("ppm");
    }
  }
}

After copying the code to the Arduino IDE, submit the code to your board. We have tutorials for other prominent sensing units that you may such as:

How the Code Works

Learn more about the ESP32 with our resources:

Thanks for analysis.

#define TdsSensorPin 27

Then, insert the analog voltage reference for the ADC. For the ESP32 is 3.3V, for an Arduino, for example, it is 5V.

#define VREF 3.3    // analog reference voltage(Volt) of the ADC

Before getting a measurement value, we’ll apply a median filtering algorithm to get a more stable value. The SCOUNT variable refers to the number of samples we’ll filter before getting an actual value.

#define SCOUNT  30  // sum of sample point

Then, we need some arrays to store the readings as well as some index variables that will allow us to go through the arrays.

int analogBuffer[SCOUNT];     // store the analog value in the array, read from ADC
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0;
int copyIndex = 0;

Initialize the averageVoltage variable and tsdValue as float variables.

float averageVoltage = 0;
float tdsValue = 0;

The temperature variable saves the current temperature value. The temperature influences the readings, so there is an algorithm that compensates for fluctuations in temperature. In this example, the reference temperature is 25ºC, but you can change it depending on your environment. For more accurate results, you can add a temperature sensor and get the actual temperature at the time of reading the sensor.

float temperature = 25;       // current temperature for compensation

The following function will be used to get a stable TDS value from an array of readings.

// median filtering algorithm
int getMedianNum(int bArray[], int iFilterLen){
  int bTab[iFilterLen];
  for (byte i = 0; i<iFilterLen; i++)
  bTab[i] = bArray[i];
  int i, j, bTemp;
  for (j = 0; j < iFilterLen - 1; j++) {
    for (i = 0; i < iFilterLen - j - 1; i++) {
      if (bTab[i] > bTab[i + 1]) {
        bTemp = bTab[i];
        bTab[i] = bTab[i + 1];
        bTab[i + 1] = bTemp;
      }
    }
  }
  if ((iFilterLen & 1) > 0){
    bTemp = bTab[(iFilterLen - 1) / 2];
  }
  else {
    bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
  }
  return bTemp;
}

In the setup(), initialize the Serial Monitor at a baud rate of 115200.

Serial.begin(115200);

Set the TDS sensor pin as an input.

pinMode(TdsSensorPin,INPUT);

In the loop(), get new TDS readings every 40 milliseconds and save them in the buffer:

static unsigned long analogSampleTimepoint = millis();
if(millis()-analogSampleTimepoint > 40U){     //every 40 milliseconds,read the analog value from the ADC
  analogSampleTimepoint = millis();
  analogBuffer[analogBufferIndex] = analogRead(TdsSensorPin);    //read the analog value and store into the buffer
  analogBufferIndex++;
  if(analogBufferIndex == SCOUNT){ 
    analogBufferIndex = 0;
  }
}   

Every 800 milliseconds, it gets the latest readings and gets the average voltage by using the filtering algorithm created before:

static unsigned long printTimepoint = millis();
if(millis()-printTimepoint > 800U){
  printTimepoint = millis();
  for(copyIndex=0; copyIndex<SCOUNT; copyIndex++){
    analogBufferTemp[copyIndex] = analogBuffer[copyIndex];
      
    // read the analog value more stable by the median filtering algorithm, and convert to voltage value
    averageVoltage = getMedianNum(analogBufferTemp,SCOUNT) * (float)VREF / 4096.0;

Then, it calculates a temperature compensation coefficient and calculates the TDS value taking that value into account:

//temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0)); 
float compensationCoefficient = 1.0+0.02*(temperature-25.0);
//temperature compensation
float compensationVoltage=averageVoltage/compensationCoefficient;
      
//convert voltage value to tds value
tdsValue=(133.42*compensationVoltage*compensationVoltage*compensationVoltage - 255.86*compensationVoltage*compensationVoltage + 857.39*compensationVoltage)*0.5;

Finally, it prints the TDS value in ppm:

Serial.print("TDS Value:");
Serial.print(tdsValue,0);
Serial.println("ppm");

Demonstration

After copying the code to the Arduino IDE, upload the code to your board. Don’t forget to select the right board in Tools > Board and the right COM port in Tools > Port.

After uploading, open the Serial Monitor at a baud rate of 115200 and press the ESP32 RST button so that the code starts working.

TDS meter testing ESP32 ESP8266 Arduino

It will show a value of 0 if the probe is not submerged. Put the probe on a solution to check its TDS. You can try with tap water and add some salt to see if the values increase.

TDS Sensor Arduino IDE Serial Monitor

I measured the TDS value for tap water in my house, and I got a value of around 100ppm, which is a good value for drinking water.

I also tested tea, and the TDS value increased to about 230ppm, which seems a reasonable value.

Finally, I also measured the TDS value of bottled water and I got a value of 0ppm. I’m not sure if this value is correct because the water is advertised as mineral water, so the minerals dissolved in the water should account for a TDS value. I think this value can be explained due to the non-linearity of the ESP32 ADC pins for small voltage values. Do you have one of these sensors? What values did you get for bottled water?

Wrapping Up

A TDS meter can measure the total dissolved solids in a solution. It can be used as an indicator of water quality and allows you to characterize the water. The meter returns the TDS value in ppm (parts per million—mg/L). The TDS value has many applications but it cannot be used by itself to determine if the water is drinkable or not.

A great application of this type of sensor is an aquarium water quality monitor. You can use this sensor alongside a waterproof DS18B20 temperature sensor to monitor your fish tank, for example.

Are you interested in an Aquarium Water Quality Monitor? I was thinking about creating a web app to monitor and control your aquarium temperature and water quality and additionally, also be able to control a pump via an output pin of the ESP32. What do you think?