In this guide, you’ll learn how to use a TDS meter (Total Dissolved Solids) with an Arduino board. A TDS meter indicates the total dissolved solids like salts, minerals, and metals, in a solution. This parameter can be used to give you an idea of water quality and compare water from different sources. One of the main applications of a TDS meter is aquarium water quality monitoring.
We’ll use the TDS meter from keystudio and show you a simple example to measure TDS in ppm units using Arduino IDE.
Table of Contents
In this tutorial, we’ll cover the following topics
- Introducing the TDS Meter
- Interfacing the TDS Meter with the Arduino
- Reading TDS with the Arduino – Code
Introducing the TDS Meter
A TDS meter measures the number of total dissolved solids like salts, minerals, and metals in the water. As the number of dissolved solids in the water increases, the conductivity of the water increases, and that allow us to calculate the total dissolved solids in ppm (mg/L).
Although this is a good indicator to monitor the quality of the water, note that it does not measure contaminants in the water. Thus, you can’t rely solely on this indicator to determine if the water is good for consumption or not.
A TDS meter can be useful to monitor water quality in many applications like pools, aquariums, fish tanks, hydroponics, water purifiers, etc.
In this tutorial, we’ll use the TDS meter from keystudio that comes with an interface module and an electrode probe (see picture above).
For more information about the TDS meter, we recommend taking a look at the official documentation.
Features and Specifications
This tutorial refers to the TDS Meter V1.0 from keystudio. Here are the sensor parameters:
TDS Meter:
- 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
TDS Probe:
- Number of Needle: 2
- Total Length: 60cm
- Connection Interface: XH2.54-2P
- Color: White
- Waterproof Probe
Where to Buy TDS Sensor?
You can check the TDS sensor on Maker Advisor to find the best price:
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
Interfacing the TDS Meter with the Arduino
The TDS meter outputs an analog signal that can be measured using the Arduino analog pins (A0 to A5).
Wire the sensor as in the following table:
TDS Sensor | Arduino |
GND | GND |
VCC | 3.3V |
Data | A0 (or any other Arduino analog pin) |
Reading TDS (water quality) with the Arduino- Code
As we mentioned previously, the sensor outputs an analog signal that can be converted to TDS in ppm. We’re using the code provided by the sensor documentation with some modifications.
To get more accurate results, you’ll probably need to calibrate your sensor against a solution with a known TDS value. However, it might not be needed if you are not concerned about specific values but about a qualitative value of TDS.
Upload the following code to your Arduino.
// Original source code: https://wiki.keyestudio.com/KS0429_keyestudio_TDS_Meter_V1.0#Test_Code
// Project details: https://RandomNerdTutorials.com/arduino-tds-water-quality-sensor/
#define TdsSensorPin A0
#define VREF 5.0 // 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 = 16; // 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 / 1024.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");
}
}
}
How the Code Works
Let’s take a quick look at the code. You can also skip right away to the Demonstration section.
The TdsSensorPin variable saves the GPIO where you want to get the readings. The ESP8266 only has one analog pin, A0.
#define TdsSensorPin A0
Then, insert the analog voltage reference for the ADC. For an Arduino, it is 5V.
#define VREF 5.0 // 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;
}
}
Then, 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 / 1024.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.
It will show a value of approximately 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 the values increase.
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 around 25ppm (the same value when using an ESP8266).
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.
We have tutorials for other popular sensors with the Arduino board that you may like:
- BME680Â Environmental Sensor with Arduino (Gas, Temperature, Humidity, Pressure)
- BME280Â Sensor with Arduino (Pressure, Temperature, Humidity)
- DS18B20Â Temperature Sensor with Arduino
- DHT11/DHT22Â Humidity and Temperature Sensor With Arduino
- LM35, LM335 and LM34 Temperature Sensors with Arduino
- HC-SR04 Ultrasonic Sensor with Arduino
- BH1750Â Ambient Light Sensor with Arduino
- K-Type Thermocouple and MAX6675 Amplifier with Arduino
Learn more about the Arduino with our resources:
- Arduino Mini-Course (free)
- Arduino Step-by-step Projects course
- More Arduino tutorials and projects…
Thanks for reading.
tdsValue=(133.42*compensationVoltage*compensationVoltage*compensationVoltage- 255.86*compensationVoltage*compensationVoltage + 857.39*compensationVoltage)*0.5;
Can you please share any resource regarding the float values that are used as factors for compensation voltage?
Hi.
The link is provided in the blog post: https://wiki.keyestudio.com/KS0429_keyestudio_TDS_Meter_V1.0#Test_Code
Regards,
Sara
Buenas tardes! Estoy realizando un sistema de hidroponÃa automatizado, en el utilizo un sensor TDS y publico datos en firebase. Mi consulta es ¿Se puede dejar el sensor inmerso en la solución nutritiva y realizar lecturas sin sacarlo de dicha solución nutritiva?
Excelentes tutoriales!
Saludos desde Argentina.
Is it works for ESP32?
Yes.
Take a look at this tutorial: https://randomnerdtutorials.com/esp32-tds-water-quality-sensor/
Regards,
Sara
How many accuracy testing should be done? I just need a statement of an expert or user on how many trials/testing should be done.
what have a library for proteus tds meter
Hi, can i change the TDS value from ppm to EC value like (microS/cm). If yes, what are the equation or constants i need to take note of?
My tds sensor not showing any constant result it start with 0 and increased upto 2106 value even it is not deep it in any solution, please guide me
Is it works for STM32?
Hi.
I’m not sure.
I didn’t teste it.
Regards,
Sara