Difference between revisions of "TDS (Total Dissolved Solids) Meter Sensor SKU: CQRSENTDS01"
(→Raspberry Pi Application) |
(→Connections and Examples) |
||
| (3 intermediate revisions by the same user not shown) | |||
| Line 59: | Line 59: | ||
'''Raspberry Pi Test Code''' | '''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/ | + | *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. | *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. | *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''' | + | *This kit does '''not include''' 4-CH 16-Bit ADS1115 ADC Module (Model: CQRADC001), buyers need to purchase it separately. |
| − | |||
| − | |||
| − | |||
---- | ---- | ||
| Line 170: | Line 167: | ||
|[[File:CQRSENTDS01-X5.jpg |left|300px]] | |[[File:CQRSENTDS01-X5.jpg |left|300px]] | ||
|} | |} | ||
| + | ---- | ||
=='''Raspberry Pi Example and Test Code'''== | =='''Raspberry Pi Example and Test Code'''== | ||
| Line 255: | Line 253: | ||
AIN3: 0xF2 | AIN3: 0xF2 | ||
| − | |||
---- | ---- | ||
| − | ==''' | + | =='''Used in Conjunction with the 4-CH 16-Bit ADS1115 ADC Module (SKU: CQRADC001 / ASIN: B0GF12JQ8R)'''== |
| + | [[image:CQRADC001-7.jpg|thumb|1000px| center]] | ||
| − | + | *'''Sample Program''' | |
| − | + | [[File:CQRADC001-80.jpg|700px]] | |
| − | + | *Create a folder and a “.c” file, then write the code. | |
| − | + | [[Media: ADSTDS.zip]] | |
| − | * | + | *You can directly add to the compressed file. |
| − | + | [[File:CQRADC001-811.jpg|700px]] | |
| − | + | 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''' | |
| − | |||
| − | |||
| − | |||
| − | |||
<pre> | <pre> | ||
| − | + | #include <stdio.h> | |
| − | + | #include <stdlib.h> | |
| − | + | #include <stdint.h> | |
| − | + | #include <unistd.h> | |
| − | + | #include <wiringPi.h> | |
| − | + | #include <wiringPiI2C.h> | |
| − | + | #include <math.h> | |
| − | + | #include <time.h> | |
| − | |||
| − | |||
| − | |||
| − | # | ||
| − | |||
| − | # | ||
| − | |||
| − | VREF | + | #define VREF 5.0f |
| − | + | #define SCOUNT 30 // Sampling points | |
| − | + | #define TEMPERATURE 25.0f // Water temperature (Celsius) | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | temperature | ||
| − | + | 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; | |
| − | + | } | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
</pre> | </pre> | ||
| − | |||
| − | + | *Test Results: | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | [[File:CQRADC001-821.jpg|700px]] | |
| − | + | 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. | ||
---- | ---- | ||
Latest revision as of 06:47, 6 March 2026
Contents
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.
Size Display
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.
- 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.
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.
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.
Raspberry Pi Example and Test Code
Raspberry Pi 4B Pinout Diagram
- Create a folder and a “.c” file, then write the code.
- You can directly add to the compressed file.
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:
- 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)
- Sample Program
- Create a folder and a “.c” file, then write the code.
- You can directly add to the compressed file.
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:
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.

