TDS (Total Dissolved Solids) Meter Sensor SKU: CQRSENTDS01

From CQRobot-Wiki
Revision as of 06:47, 6 March 2026 by Chenqi (talk | contribs) (Connections and Examples)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
100px TDS Meter Sensor

Description

TDS (Total Dissolved Solids), Chinese name Total Dissolved Solids, also known as Total Dissolved Solids, indicates how many milligrams of dissolved solids are dissolved in 1 liter of water. Generally speaking, the higher the TDS value, the more dissolved substances contained in the water and the less clean the water. Therefore, the value of TDS can be used as one of the basis for reflecting the cleanliness of water. The commonly used TDS testing equipment is a TDS pen. Although it is cheap and easy to use, it cannot transmit data to the control system for long-term online monitoring and water quality analysis. Using a special instrument, although it can transmit data and has high accuracy, it is very expensive.

CQRobot has launched an arduino-compatible TDS sensor, which can be used to measure the TDS value of water after connecting to the arduino controller. This product is specially designed for arduino, plug and play, easy to use. 3.3V~5.5V wide voltage power supply, 0 to 2.3V analog signal output, making this product compatible with 5V and 3.3V control systems, it can be easily connected to existing control systems. With CQRobot ADS1115 16-bit AD conversion module (Model: CQRADS1115), it can accurately collect and convert analog signals to meet the compatibility requirements of the Raspberry Pi motherboard.

The excitation source for measurement uses AC signals, which can effectively prevent probe polarization, extend the life of the probe, and increase the stability of the output signal. The TDS probe is a waterproof probe that can be immersed in water for long-term measurement. This product can be applied to water quality testing in areas such as domestic water and hydroponics. With this sensor, you can easily DIY a TDS detector, easily detect the cleanliness of the water, and check your water quality.

TDS Meter Sensor-7B.jpg

Size Display

TDS Meter Sensor-7C1.jpg

Specifications

Signal Transmitter Board Specifications

  • Input Voltage: 3.3V to 5.5V
  • Output Voltage: 0 to 2.3V
  • Working Current: 3mA to 6mA
  • TDS Measurement Range: 0 to 1000ppm
  • TDS Measurement Accuracy: Plus/Minus 10% F.S. (25 Degree Celsius)
  • Module Size: 43mm * 32.2mm
  • Module Interface: JST 2.0mm 3-Pin
  • Electrode Interface: JST 2.54mm 2-Pin

TDS Probe Specifications

  • Number of Needle: 2
  • Total Length: 60cm
  • Connection Interface: JTS 2.54mm 2-Pin
  • Color: Black
  • Other: Waterproof Probe

Ocean Interface Cable Specifications

  • Cable specifications: 22AWG
  • Material: Silicone
  • Withstand Voltage: Less Than 50V
  • Withstand Current: Less Than 1000MA
  • Length: 21cm
  • Line Sequence: Black-Negative Power Supply, Red-Positive Power Supply, Green-Signal Terminal.

Connections and Examples

Attention

  • The probe can not be used in water above 55 degrees centigrade.
  • The probe can not be left too close to the edge of the container, otherwise it will affect the reading.
  • The head and the cable of the probe are waterproof, but the connector and the signal transmitter board are not waterproof. Please be careful.
  • When using the TDS instrument sensor, the sensor probe is immersed in liquid, and the purchaser needs to Separate the sensor Probe Protective Cover to avoid incorrect data reading and malfunction.
TDS Meter Sensor-7D.jpg
  • The TDS probe cannot be used in water above 55 degrees Celsius;
  • The TDS probe should not be placed too close to the edge of the container, otherwise it will affect the sensor reading parameters;
  • The TDS probe head and lead are made of waterproof material and can be immersed in water, but the cable interface and signal transfer board are not waterproof, please use them carefully.

Arduino Connections

  • The DuPont female single-head wiring we distribute cannot be directly connected to the UNO R3 control board. Need to be in Stack the sensor expansion board on the UNO R3 control board, or connect the male-to-male Dupont wire to the Dupont female single-head wiring(Bread Wire).

Raspberry Pi Test Code

  • Compatible Raspberry Pi needs to be used with ADS1115 16-bit AD conversion module. Please refer to the specific test code: http://www.cqrobot.wiki/index.php/4-CH_16-Bit_ADS1115_ADC_Module_SKU:_CQRADC001
  • Connect the module to the Raspberry Pi 4B, and put the test code in the Raspberry Pi system in the form of a folder. Dial the ADD dial switch to the 0X48 end.
  • In this experiment, the Raspberry Pi I2C communication function is used. You need to turn on the I2C function, enter sudo raspi-config in the Raspberry Pi system, and then perform the following operations.
  • This kit does not include 4-CH 16-Bit ADS1115 ADC Module (Model: CQRADC001), buyers need to purchase it separately.

Arduino Application

Note: The DuPont female single-ended wiring we distribute cannot be directly connected to the UNO R3 control board. When wiring, you need to stack the sensor expansion board on the UNO R3 control board, or connect the male-to-male Dupont wire (bread wire) on the Dupont wire female single-ended wire.

CQRSENTDS01-107.jpg

Download and Run the Test Examples

Media: TDS Meter Sensor-Arduino.rar

#define TdsSensorPin A1
#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, copyIndex = 0;
float averageVoltage = 0, tdsValue = 0, temperature = 25;

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];
    averageVoltage = getMedianNum(analogBufferTemp, SCOUNT) * (float)VREF / 1024.0; // read the analog value more stable by the median filtering algorithm, and convert to voltage value
    float compensationCoefficient = 1.0 + 0.02 * (temperature - 25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
    float compensationVolatge = averageVoltage / compensationCoefficient; //temperature compensation
    tdsValue = (133.42 * compensationVolatge * compensationVolatge * compensationVolatge - 255.86 * compensationVolatge * compensationVolatge + 857.39 * compensationVolatge) * 0.5; //convert voltage value to tds value
    //Serial.print("voltage:");
    //Serial.print(averageVoltage,2);
    //Serial.print("V   ");
    Serial.print("TDS----Value:");
    Serial.print(tdsValue, 0);
    Serial.println("ppm");
  }
}
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;
}

Test Results

Connect according to the wiring diagram, upload the code on the arduino board, after power-on, open the serial monitor, set the baud rate to 115200, insert the TDS probe into the water to be measured, stir a few times, and observe the value printed on the serial monitor, This value is the TDS value of water. As shown below.

CQRSENTDS01-101.jpg


TDS Meter Sensor vs. Xiaomi Water Quality Test Pen

This is a sample of tap water from the urban pipe network.

TDS value of Xiaomi's test pen: 84 ppm

TDS value of our sensor: 92 ppm


The test result is only responsible for the sample, but you can serve as a reference.

CQRSENTDS01-X22.jpg
CQRSENTDS01-X3.jpg
CQRSENTDS01-X4.jpg
CQRSENTDS01-X5.jpg

Raspberry Pi Example and Test Code

CQRADC001-76.jpg

CQRADC001-73.jpg

Raspberry Pi 4B Pinout Diagram

CQRADC001-75.jpg

CQRADC001-77.jpg

  • Create a folder and a “.c” file, then write the code.

Media: ADS1115.zip

  • You can directly add to the compressed file.

CQRADC001-781.jpg


gcc -Wall -g -o ADS1115 ADS1115.c -lm

./ADS1115

Go to the folder created in the terminal, use GCC cross-compilation and run the file.

  • Sample Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <math.h>

#define ADS1115_ADDR 0x48

int main() {
    int fd = open("/dev/i2c-1", O_RDWR);
    if (fd < 0) return 1;
    if (ioctl(fd, I2C_SLAVE, ADS1115_ADDR) < 0) return 1;
    
    while(1) {
        unsigned char config[3] = {0x01, 0xC2, 0x83};
        unsigned char result[2];
        
        write(fd, config, 3);
        usleep(8000);
        
        unsigned char reg[1] = {0x00};
        write(fd, reg, 1);
        read(fd, result, 2);
        
        int16_t adc = (result[0] << 8) | result[1];
        float voltage = adc * 0.125 / 1000.0;
        float ntc_r = (3.3 * 10000.0 / voltage) - 20000.0;
        float temp = 1.0 / (log(ntc_r / 10000.0) / 3950.0 + 1.0 / 298.15) - 273.15;
        
        printf("Temp: %.1fC\n", temp);
        sleep(1);
    }
    
    return 0;
}
  • Test Results:

CQRADC001-791.jpg

  • Temporarily set the I2C address to 0x48. You can modify this line of code as needed: #define ADS1115_ADDR 0x48.
  • Temporarily set the input channel to AIN0. You can replace it with the following code based on actual needs:

AIN0: 0xC2

AIN1: 0xD2

AIN2: 0xE2

AIN3: 0xF2


Used in Conjunction with the 4-CH 16-Bit ADS1115 ADC Module (SKU: CQRADC001 / ASIN: B0GF12JQ8R)

CQRADC001-7.jpg
  • Sample Program

CQRADC001-80.jpg

  • Create a folder and a “.c” file, then write the code.

Media: ADSTDS.zip

  • You can directly add to the compressed file.

CQRADC001-811.jpg

gcc -o ADSTDS ADSTDS.c -lwiringPi

./ADSTDS

Go to the folder created in the terminal, use GCC cross-compilation and run the file.

  • Example Code
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <math.h>
#include <time.h>

#define VREF 5.0f           
#define SCOUNT 30           // Sampling points
#define TEMPERATURE 25.0f   // Water temperature (Celsius)

int analogBuffer[SCOUNT];        
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0;
float averageVoltage = 0, tdsValue = 0;

int ads1115_fd;
// Function declarations
int getMedianNum(int bArray[], int iFilterLen);
int readADS1115();
unsigned long my_millis();  

unsigned long my_millis() {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}

int getMedianNum(int bArray[], int iFilterLen) {
    int bTab[iFilterLen];
    for (int 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;
}

int readADS1115() {
    // Configure ADS1115: Channel 0
    uint16_t config = 0xC283;  
    wiringPiI2CWriteReg16(ads1115_fd, 0x01, (config >> 8) | (config << 8));
    usleep(8000);  
    
    uint16_t result = wiringPiI2CReadReg16(ads1115_fd, 0x00);
    int16_t adc = ((result >> 8) & 0xFF) | ((result & 0xFF) << 8);
    
 
    static int debug_count = 0;
    if (debug_count++ % 20 == 0) {
        printf("[Raw ADC: %d] ", adc);
    }
    
    int adc_abs = abs(adc);
    // Convert to analog value (0-1023)
    int arduino_value = (adc_abs * 1024) / 30000;
    // Limit to 0-1023 range
    if (arduino_value > 1023) arduino_value = 1023;
    if (arduino_value < 0) arduino_value = 0;
    return arduino_value;
}
int main() {
    printf("=== Raspberry Pi TDS Measurement ===\n");
    printf("TDS = 133.42*V^3 - 255.86*V^2 + 857.39*V * 0.5\n\n");
    
    // Initialize wiringPi
    if (wiringPiSetup() < 0) {
        printf("wiringPi initialization failed\n");
        return 1;
    }
    
    // Initialize ADS1115
    ads1115_fd = wiringPiI2CSetup(0x48);
    if (ads1115_fd < 0) {
        printf("ADS1115 initialization failed (address 0x48)\n");
        printf("Please check: sudo i2cdetect -y 1\n");
        return 1;
    }
    printf("✓ Hardware initialization completed\n");
    printf("Starting measurement...\n");
    printf("----------------------------------------\n");
    
    // Timepoint variables (using custom my_millis)
    unsigned long analogSampleTimepoint = my_millis();
    unsigned long printTimepoint = my_millis();
    
    while (1) {
        unsigned long currentMillis = my_millis();     
        if (currentMillis - analogSampleTimepoint > 40U) {
            analogSampleTimepoint = currentMillis;
            
            int arduinoValue = readADS1115();
            
            analogBuffer[analogBufferIndex] = arduinoValue;
            analogBufferIndex++;
            if (analogBufferIndex == SCOUNT) {
                analogBufferIndex = 0;
            }
        }
         
        if (currentMillis - printTimepoint > 800U) {
            printTimepoint = currentMillis;
           
            for (int copyIndex = 0; copyIndex < SCOUNT; copyIndex++) {
                analogBufferTemp[copyIndex] = analogBuffer[copyIndex];
            }
   
            int medianValue = getMedianNum(analogBufferTemp, SCOUNT);
            averageVoltage = medianValue * VREF / 1024.0;
            float compensationCoefficient = 1.0 + 0.02 * (TEMPERATURE - 25.0);
            float compensationVoltage = averageVoltage / compensationCoefficient;
            tdsValue = (133.42 * compensationVoltage * compensationVoltage * compensationVoltage 
                       - 255.86 * compensationVoltage * compensationVoltage 
                       + 857.39 * compensationVoltage) * 0.5;
            printf("TDS Value: %.0fppm", tdsValue);
           
            printf(" (ADC:%d, Volt:%.3fV)\n", medianValue, averageVoltage);
            
            fflush(stdout);
        }
        
        usleep(1000);  
    }
    
    return 0;
}
  • Test Results:

CQRADC001-821.jpg

The original ADC value represents the raw 16-bit analog-to-digital conversion data from the ADS1115. The ADC value is the stabilized output after median filtering. The voltage value is the calculated corresponding voltage. The TDS value indicates the dissolved solids content in the water.

In this configuration, the I2C address is 0x48, and the analog port is AIN0.