Skip to content

DevLog #2: Configuring EC, TDS, Temperature Sensor

What is EC and TDS Sensor? How does it work?

Electrical Conductivity (EC)

Electrical conductivity is the measure of the concentration of ions (both positively and negatively charged ions) present within a sample. This is calculated by the ability of the substance to transmit an electrical current over a defined area. The measurement unit for electrical conductivity is called Siemens(S) (E.g. Milli Siemens per centimetre mS/cm or Micro Siemens per cm μS/cm).

Total Dissolved Solids (TDS)

Components

For making your own DIY EC & TDS Sensor, basically you will need a probe and a temperature sensor.

DIY EC Meter Youtube Tutorial by Pierre Hertzog

EC Sensor:

  • Stainless steel rod (2 pcs) for the probe –> in my case, for this initial test & exercise I’m just using the tip of jumper wire😆
  • 1k resistor

Temperature:

  • 10k ohm NTC thermistor (2 wires)
  • 10k resistor

Wiring Connection

wiring

Alt text

Making DIY Probe

I made a super frugal ec probe for initial test by using jumper wire.

Version 1

Alt text Alt text Alt text

Version 2

Fixing the probe holder a bit

probe v2

Programming & Tests

Code (with Serial Monitor)

    int R1= 1000; // Value of resistor for EC probe
    int EC_Read = A0;
    int ECPower = A1;
    int Temp_pin = A3;

    float Temp_C; // Do not change
    float Temp_F; // Do not change
    float Temp1_Value = 0;
    float Temp_Coef = 0.019; // You can leave as it is

    //This part needs your attention during calibration only//
    float Calibration_PPM =1080 ; //Change to PPM reading measured with a separate meter
    float K= 24.09; //You must change this constant once for your probe(see video)
    float PPM_Con=0.5; //You must change this only if your meter uses a different factor
    ////

    float CalibrationEC= (Calibration_PPM*2)/1000;
    float Temperature;
    float EC;
    float EC_at_25;
    int ppm;
    float A_to_D= 0;
    float Vin= 5;
    float Vdrop= 0;
    float R_Water;
    float Value=0;

    void setup() {
        Serial.begin(9600);
        pinMode(EC_Read,INPUT);
        pinMode(ECPower,OUTPUT);

    // Calibrate (); // After calibration put two forward slashes before this line of code

    }
    void loop() {
        GetEC(); //Calls GetEC()
        delay(6000); //Do not make this less than 6 sec (6000)
    }

    void GetEC() {
        int val;
        double Temp;

        val = analogRead(Temp_pin);
        Temp = log(((10240000/val) - 10000));
        Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
        Temp_C = Temp - 273.15; // Kelvin to Celsius
        Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
        Temp1_Value = Temp_C;
        Temperature = Temp_C;

        digitalWrite(ECPower,HIGH);
        A_to_D= analogRead(EC_Read);
        A_to_D= analogRead(EC_Read);
        digitalWrite(ECPower,LOW);
        Vdrop= (Vin*A_to_D) / 1024.0;
        R_Water = (Vdrop*R1) / (Vin-Vdrop);
        EC = 1000/ (R_Water*K);
        EC_at_25 = EC / (1+ Temp_Coef*(Temperature-25.0));
        ppm=(EC_at_25)*(PPM_Con*1000);

        Serial.print(" EC: ");
        Serial.print(EC_at_25);
        Serial.print(" milliSiemens(mS/cm) ");
        Serial.print(ppm);
        Serial.print(" ppm ");
        Serial.print(Temperature);
        Serial.println(" *C ");
    }
    ////////////////////////////////////////////////////////////////////////////////////
    void Calibrate () {
        Serial.println("Calibration routine started");
        float Temperature_end=0;
        float Temperature_begin=0;
        int val;
        double Temp;
        val=analogRead(Temp_pin);

        Temp = log(((10240000/val) - 10000));
        Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
        Temp_C = Temp - 273.15; // Kelvin to Celsius
        Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
        Temp1_Value = Temp_C;
        Temperature_begin=Temp_C;
        Value = 0;
        int i=1;
        while(i<=10){
        digitalWrite(ECPower,HIGH);
        A_to_D= analogRead(EC_Read);
        A_to_D= analogRead(EC_Read);
        digitalWrite(ECPower,LOW);
        Value=Value+A_to_D;
        i++;
        delay(6000);
    };
        A_to_D=(Value/10);
        val=analogRead(Temp_pin);
        Temp = log(((10240000/val) - 10000));
        Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
        Temp_C = Temp - 273.15; // Kelvin to Celsius
        Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
        Temp1_Value = Temp_C;
        Temperature_end=Temp_C;
        EC =CalibrationEC*(1+(Temp_Coef*(Temperature_end-25.0)));
        Vdrop= (((Vin)*(A_to_D))/1024.0);
        R_Water=(Vdrop*R1)/(Vin-Vdrop);
        float K_cal= 1000/(R_Water*EC);
        Serial.print("Replace K value with K = ");
        Serial.println(K_cal);
        Serial.print("Temperature difference start to end were = ");
        Temp_C=Temperature_end-Temperature_begin;
        Serial.print(Temp_C);
        Serial.println("*C");
        Serial.println("Temperature difference start to end must be smaller than 0.15*C");
        Serial.println("");
        Calibrate ();
    }
    ////////////////////////////////////////////////////////////////////////////////////

Test 1: Calibration with placeholder K value & using probe v1

Calibration

Because at the moment, I still dont have the reference EC/TDS meter with me, so for the calibration, I’m just using placeholder PPM value, just to test if the code & physical circuit actually works

Alt text

Measurement

After calibration, I did the test right away by submerging the probe in the water and then pulling it out.

The EC measurement seems about right. As you can see when it’s not in the water the EC value is almost ~0.00 mV. The ppm value also showing corresponding result when submerge and not in the water. However, not sure why the Temperature ended up in minus.

Code (With OLED I2C Display)

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET 2

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int R1= 1000; // Value of resistor for EC probe
int EC_Read = A0;
int ECPower = A1;
int Temp_pin = A3;

float Temp_C; // Do not change
float Temp_F; // Do not change
float Temp1_Value = 0;
float Temp_Coef = 0.019; // You can leave as it is
/////////////////This part needs your attention during calibration only///////////////
float Calibration_PPM =380 ; //Change to PPM reading measured with a separate meter
float K= 10.18; //You must change this constant once for your probe(see video)
float PPM_Con=0.5; //You must change this only if your meter uses a different factor
/////////////////////////////////////////////////////////////////////////////////////
float CalibrationEC= (Calibration_PPM*2)/1000;
float Temperature;
float EC;
float EC_at_25;
int ppm;
float A_to_D= 0;
float Vin= 5;
float Vdrop= 0;
float R_Water;
float Value=0;
//Leave the next 2 lines in if you need help later on///////////////////////////////////
//Ask any questions that you may have in the comment section of this video
//https://youtu.be/-xKIczj9rVA

void setup() {
  Serial.begin(9600);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0,15);
  display.println("Electrolyte Meter");
  display.display();
  delay(3000);
  pinMode(EC_Read,INPUT);
  pinMode(ECPower,OUTPUT);

//////////////////////////////////////////////////////////////////////////////////////////
// Calibrate (); // After calibration put two forward slashes before this line of code
//////////////////////////////////////////////////////////////////////////////////////////
}
void loop() {
  GetEC(); //Calls GetEC()
  delay(6000); //Do not make this less than 6 sec (6000)
}

void printOLED () {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
}
////////////////////////////////////////////////////////////////////////////////////
void GetEC() {
  int val;
  double Temp;

  val = analogRead(Temp_pin);
  Temp = log(((10240000/val) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
  Temp_C = Temp - 273.15; // Kelvin to Celsius
  Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
  Temp1_Value = Temp_C;
  Temperature = Temp_C;

  digitalWrite(ECPower,HIGH);
  A_to_D= analogRead(EC_Read);
  A_to_D= analogRead(EC_Read);
  digitalWrite(ECPower,LOW);
  Vdrop= (Vin*A_to_D) / 1024.0;
  R_Water = (Vdrop*R1) / (Vin-Vdrop);
  EC = 1000/ (R_Water*K);
  EC_at_25 = EC / (1+ Temp_Coef*(Temperature-25.0));
  ppm=(EC_at_25)*(PPM_Con*1000);

  printOLED ();
  display.setCursor(0,15);
  display.println("EC: ");
  display.print(EC_at_25);
  display.print("mS/cm");
  display.display();
  delay(3000);

  printOLED ();
  display.setCursor(0,15);
  display.println("TDS: ");
  display.print(ppm);
  display.print(" ppm");
  display.display();
  delay(3000);

  printOLED ();
  display.setCursor(0,15);
  display.println("Temp: ");
  display.print(Temperature);
  display.print("C");
  display.display();
  delay(3000);

  // Serial.print(" EC: ");
  // Serial.print(EC_at_25);
  // Serial.print(" milliSiemens(mS/cm) ");
  // Serial.print(ppm);
  // Serial.print(" ppm ");
  // Serial.print(Temperature);
  // Serial.println(" *C ");
}
////////////////////////////////////////////////////////////////////////////////////
void Calibrate () {
  Serial.println("Calibration routine started");

  float Temperature_end=0;
  float Temperature_begin=0;
  int val;
  double Temp;

  val=analogRead(Temp_pin);
  Temp = log(((10240000/val) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
  Temp_C = Temp - 273.15; // Kelvin to Celsius
  Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
  Temp1_Value = Temp_C;
  Temperature_begin = Temp_C;
  Value = 0;
  int i=1;
  while(i<=10){
    digitalWrite(ECPower,HIGH);
    A_to_D= analogRead(EC_Read);
    A_to_D= analogRead(EC_Read);
    digitalWrite(ECPower,LOW);
    Value=Value+A_to_D;
    i++;
    delay(6000);
  };
  A_to_D = (Value/10);
  val = analogRead(Temp_pin);
  Temp = log(((10240000/val) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
  Temp_C = Temp - 273.15; // Kelvin to Celsius
  Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
  Temp1_Value = Temp_C;
  Temperature_end=Temp_C;
  EC =CalibrationEC*(1+(Temp_Coef*(Temperature_end-25.0)));
  Vdrop= (((Vin)*(A_to_D))/1024.0);
  R_Water=(Vdrop*R1)/(Vin-Vdrop);

  float K_cal= 1000/(R_Water*EC);

  Serial.print("Replace K value with K = ");
  Serial.println(K_cal);
  Serial.print("Temperature difference start to end were = ");
  Temp_C=Temperature_end-Temperature_begin;
  Serial.print(Temp_C);
  Serial.println("*C");
  Serial.println("Temperature difference start to end must be smaller than 0.15*C");
  Serial.println("");
  Calibrate ();
}
////////////////////////////////////////////////////////////////////////////////////

Code with OLED i2c 1.3”

//EC-TDS-Temp Sensor + OLED I2C 1.3" 

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>

// OLED display width and height
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

// Initialize the OLED display
#define OLED_RESET -1 // Not connected
#define i2c_Address 0x3C // Change to 0x3D if necessary
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// EC-TDS-Temp measurement pin definitions
int R1= 1000; // Value of resistor for EC probe
int EC_Read = A0;
int ECPower = A1;
int Temp_pin = A2;

float Temp_C; // Do not change
float Temp_F; // Do not change
float Temp1_Value = 0;
float Temp_Coef = 0.019; // You can leave as it is

/////////////////(DEFINE FOR CALIBRATION)///////////////
float Calibration_PPM = 141 ; //Change to PPM reading measured with a separate meter
float K= 0.58; //You must change this constant once for your probe(see video)
float PPM_Con=0.5; //You must change this only if your meter uses a different factor
/////////////////////////////////////////////////////////////////////////////////////
float CalibrationEC= (Calibration_PPM*2)/1000;
float Temperature;
float EC;
float EC_at_25;
int ppm;
float A_to_D= 0;
float Vin= 5;
float Vdrop= 0;
float R_Water;
float Value=0;
//Leave the next 2 lines in if you need help later on///////////////////////////////////
//Ask any questions that you may have in the comment section of this video
//https://youtu.be/-xKIczj9rVA

void setup() {
  Serial.begin(9600);
  display.begin(i2c_Address, true); // Initialize with the I2C address
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(0, 15);
  display.println("Electrolyte Meter");
  display.display();
  delay(3000);

  pinMode(EC_Read,INPUT);
  pinMode(ECPower,OUTPUT);

//////////////////////////////////////////////////////////////////////////////////////////
// Calibrate (); // After calibration put two forward slashes before this line of code
//////////////////////////////////////////////////////////////////////////////////////////
}
void loop() {
  GetEC(); //Calls GetEC()
  delay(6000); //Do not make this less than 6 sec (6000)
}

void printOLED(const char* label, float value, const char* unit) {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(0, 15);
  display.print(label);
  display.print(": ");
  display.print(value);
  display.print(unit);
  display.display();
}
////////////////////////////////////////////////////////////////////////////////////
void GetEC() {
  int val;
  double Temp;

  val = analogRead(Temp_pin);
  Temp = log(((10240000/val) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
  Temp_C = Temp - 273.15; // Kelvin to Celsius
  Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
  Temp1_Value = Temp_C;
  Temperature = Temp_C;

  digitalWrite(ECPower,HIGH);
  A_to_D= analogRead(EC_Read);
  A_to_D= analogRead(EC_Read);
  digitalWrite(ECPower,LOW);
  Vdrop= (Vin*A_to_D) / 1024.0;
  R_Water = (Vdrop*R1) / (Vin-Vdrop);
  EC = 1000/ (R_Water*K);
  EC_at_25 = EC / (1+ Temp_Coef*(Temperature-25.0));
  ppm=(EC_at_25)*(PPM_Con*1000);

  printOLED("EC", EC_at_25, "mS/cm");
  delay(2000);

  printOLED("TDS", ppm, " ppm");
  delay(2000);

  printOLED("Temp", Temperature, "C");
  delay(2000);
}
////////////////////////////////////////////////////////////////////////////////////
void Calibrate () {
  Serial.println("Calibration routine started");

  float Temperature_end=0;
  float Temperature_begin=0;
  int val;
  double Temp;

  val=analogRead(Temp_pin);
  Temp = log(((10240000/val) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
  Temp_C = Temp - 273.15; // Kelvin to Celsius
  Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
  Temp1_Value = Temp_C;
  Temperature_begin = Temp_C;
  Value = 0;
  int i=1;
  while(i<=10){
    digitalWrite(ECPower,HIGH);
    A_to_D= analogRead(EC_Read);
    A_to_D= analogRead(EC_Read);
    digitalWrite(ECPower,LOW);
    Value=Value+A_to_D;
    i++;
    delay(6000);
  };
  A_to_D = (Value/10);
  val = analogRead(Temp_pin);
  Temp = log(((10240000/val) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp);
  Temp_C = Temp - 273.15; // Kelvin to Celsius
  Temp_F = (Temp_C * 9.0)/ 5.0 + 32.0; // Celsius to Fahrenheit
  Temp1_Value = Temp_C;
  Temperature_end=Temp_C;
  EC =CalibrationEC*(1+(Temp_Coef*(Temperature_end-25.0)));
  Vdrop= (((Vin)*(A_to_D))/1024.0);
  R_Water=(Vdrop*R1)/(Vin-Vdrop);

  float K_cal= 1000/(R_Water*EC);

  Serial.print("Replace K value with K = ");
  Serial.println(K_cal);
  Serial.print("Temperature difference start to end were = ");
  Temp_C=Temperature_end-Temperature_begin;
  Serial.print(Temp_C);
  Serial.println("*C");
  Serial.println("Temperature difference start to end must be smaller than 0.15*C");
  Serial.println("");
  Calibrate ();
}
////////////////////////////////////////////////////////////////////////////////////

Test 2: calibrated with Commercial TDS Meter using probe v2

Calibration

calibration with commercial tds meter

What I’m not sure as well is.. how do I know if this meter is accurate and callibrated? I think might be better to use standardized ec/tds calibration solution.

Measurement

The result is not exactly the same with the commercial TDS meter, but kinda fall within close range. There are many factors to this, which I have to debug. But embedded programming-wise, everything works well!

Testing out Tap Water vs Drinking Water

Tap Water Drinking water

Re-working Wiring / System Diagram

WIP wiring schematic in figma

Next action steps:

  • debugging value accuracy
  • Configuring and sync menu selection with Rotary Encoder
  • Configuring pH meter

To figure out:

  • Does the dimension of the probes and the distance between each probe affect the measurement? If so, what would be the ideal dimension and distance (especially in the context of electrolyte research)? Do the code needs to be adjusted then?