Close

Three Dollar EC - PPM Meter [Arduino]

A project log for Fly Wars : A Hackers Solution To World Hunger

Using Technology And A Hackers Mindset To Increase Food Security For The Coming Billions Last Updated [21/9/2015]

Michael RatcliffeMichael Ratcliffe 09/04/2015 at 13:56108 Comments

This Blog will Cover How to build a cheap EC meter for your aquaponics/Hydroponics or water quality related projects. We are not going to get into what the ideal value of PPM or EC is, Just cover how to measure and quantify a fluid.

We will be using this for the Urine based aquaponics unit, we need to be able to control the strength of the growing fluid in the system but for the person on a budget a EC meter is just to much money. the Solution a $3 EC Meter for any Arduino.

You can use this to measure drinking water quality to with a small change to the code and changing R1 [see below].

Parts:

-MCU of your choice with ADC

-DS18B20 waterproof temperature sensor

-500 ohm [or 1kohm resistor]

-Type A Two Prong american plug to Figure 8

-Female Socket for Figure 8 connector

So why are we using a plug:

-Cheap

-Available worldwide

-Standard size [makes calibration easy]

Use the solid prong one like below and not the one with holes:

images.jpg

Wiring it up:

Note: You want the Solid Prong type plug

Do not Plug the pronged plug into the mains

Pinout.png

Operating Principal

PPM is calculated from the EC of a fluid, EC is the inverse of the electrical resistance of the fluid. We are estimating the EC or PPM of a fluid by measuring the resistance between two probes [The plug pins] when the plug is submerged in the liquid of interest.

Ec measurement needs to be done using AC or the liquid of interest is polarised and will give bad readings. This has got to be a great example of asking why instead of just accepting a statement as fact, it turns out we can take a very fast DC reading without suffering polarisation. meaning we can make a really cheap EC sensor.

Want to use it and dont care how it works? Skip to the main EC code and using the wiring diagram it will work.

Temperature Compensation

Temperature has an effect on the conductivity of fluids so it is essential that we compensate for this.

It is common to use a liner approximation for small temperature changes[1] to convert them to their equivelant EC at 25*C:

EC25 = EC /( 1 + a (T - 25) )

EC25- Equivelant EC at 25'C

EC - Measured EC

T- Temperature [Decgrees C] of Measurment

a = 0.019 °C [Commonly used for nutrient solutions]

Deciding on Value of R1

//##################################################################################

//----------- Do not Replace R1 with a resistor lower than 300 ohms ------------

//##################################################################################

We can change the Value of R1 in the voltage divider to change the range of EC we want to measure. Below is the Equivalent Voltage divider circuit.

Voltage Divider.png

Ra

Ra the resistance of the digital pins is not stated in the data sheet instead we need to pull it out from a graph.

Going off the graph on page [387] of the atmel 2560 Data Sheet “Figure 32-25. I/O Pin Output Voltage vs. Source Current (VCC = 5V)”

V=IR

Ra= V/I [From Figure] V=0.4 I=1.5e-4 R=25 ohms estimated

Rc


Rc will change with EC [PPM] of the measured fluid. we will calculate the maximum and minimum values we expect to see for the range of fluids we wish to measure taking into account temperature changes and the cell constant K. [We will estimate K to be 3 for the plug probe, estimate from previous tests]

EC = EC25*( 1 + a (T - 25))

R=(1000/(EC*K)) +Ra

Min temp=0 [we arnt going to care about EC if the pond is frozen]

Max Temp = 40 *C [I doubt a pond should be above this]

Minimum EC 25=0.3 EC= 0.3*(1+0.019*(0-25) Min EC= 0.16 S/sm

Maximum EC 25= 3 EC= 0.3*(1+0.019*(40-25) Max EC = 3.9 S/cm

Min Resistance = 1000/(MaxEC*K)+25 = 1000(3.9*2.88) =114 ohms

Max Resitance = 1000/(MinEC*K)+25 = 1000/(0.16*2.88) = 2195 ohms

R1

Now we have enough information to calculate a good value for R1 to get the best resolution over our intended measuring range. We could sum it all up mathematically and differentiate to find the peak, but that hurts my head so I just did a quick excel spreadsheet for the Voltage divider for the EC I expect to see:

Exel.png


As we can see we get the largest difference using a value for R1 of 500 ohm, I only had 1Kohm to hand so I will have to live with a little less range.

So we chose a 500 ohm resistor

EC – Range /Voltage Range * (5/ADC steps)

(3.9-0.16)/3.14 * 5/1024 = 5.8e-3 resolution so that is a resolution of 0.0058

To put this is PPM [Tranchen [Australia] PPMconversion: 0.7] this is a resolution of 4ppm.

Much more than we need for aquaponics or hydroponics.

If you want to measure the quality of drinking water you will need to calculate the expected Ec values and increase R1 accordingly.


Calibration Code

If you want the best readings from your system it is advisable to calibrate your sensor with some known fluid. But If you dont need to if you use the plug probe shown above, it will still work well.

>Add your EC in S/cm into the definitions

>Plug your K value from the terminal window into the main EC code

you will need to use the modified one wire and Dallas library [download from www.michaelratcliffe.com] or add a pull up for the temperature probe data line [google it]

/*
  ElCheapo Arduino EC-PPM measurments Calibration
 
  This Script is used for calibration of the sensor and fine tuning of the Cell Constant K
  Submerge the sensor and temperature probe in the calibration solution and leave for a while so the temperature probe can settle
  Change the value of the calibration solution to suit the solutiton strength
  Stir the probe to make sure the solution is well mixed and upload the code to the arduino
  Open the terminal for an update of the estimated Cell Constant K [should be around 3] and use this new value in the main EC code.
 
 
  28/8/2015  Michael Ratcliffe  Mike@MichaelRatcliffe.com
 
 
          This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see .
 
    Parts:
    -Arduino - Uno/Mega
    -Standard American two prong plug
    -1 kohm resistor
    -DS18B20 Waterproof Temperature Sensor
 
 
    See www.MichaelRatcliffe.com/Projects for a Pinout and user guide or consult the Zip you got this code from
 
*/
 
 
//************************** Libraries Needed To Compile The Script [See Read me In Download] ***************//
// Both below Library are custom ones [ SEE READ ME In Downloaded Zip If You Dont Know how To install Use them or add a pull up resistor to the temp probe
 
 
#include <OneWire.h>
#include <DallasTemperature.h>
 
 
 
 
 
 
//************************* User Defined Variables ********************************************************//
 
 
float CalibrationEC=1.38; //EC value of Calibration solution is s/cm
 
 
 
 
//##################################################################################
//-----------  Do not Replace R1 with a resistor lower than 300 ohms    ------------
//##################################################################################
 
 
int R1= 1000;
int Ra=25; //Resistance of powering Pins
int ECPin= A0;
int ECGround=A1;
int ECPower =A4;
 
 
//*************Compensating for temperature ************************************//
//The value below will change depending on what chemical solution we are measuring
//0.019 is generaly considered the standard for plant nutrients [google "Temperature compensation EC" for more info
float TemperatureCoef = 0.019; //this changes depending on what chemical we are measuring
 
 
 
 
//************ Temp Probe Related *********************************************//
#define ONE_WIRE_BUS 10          // Data wire For Temp Probe is plugged into pin 10 on the Arduino
const int TempProbePossitive =8;  //Temp Probe power connected to pin 9
const int TempProbeNegative=9;    //Temp Probe Negative connected to pin 8
 
 
 
 
//***************************** END Of Recomended User Inputs *****************************************************************//
 
 
OneWire oneWire(ONE_WIRE_BUS);// Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire);// Pass our oneWire reference to Dallas Temperature.
 
 
float TemperatureFinish=0;
float TemperatureStart=0;
float EC=0;
int ppm =0;
 
 
float raw= 0;
float Vin= 5;
float Vdrop= 0;
float Rc= 0;
float K=0;
 
 
 
 
int i=0;
float buffer=0;
 
 
//*********************************Setup - runs Once and sets pins etc ******************************************************//
void setup()
{
  Serial.begin(9600);
  pinMode(TempProbeNegative , OUTPUT ); //seting ground pin as output for tmp probe
  digitalWrite(TempProbeNegative , LOW );//Seting it to ground so it can sink current
  pinMode(TempProbePossitive , OUTPUT );//ditto but for positive
  digitalWrite(TempProbePossitive , HIGH );
  pinMode(ECPin,INPUT);
  pinMode(ECPower,OUTPUT);//Setting pin for sourcing current
  pinMode(ECGround,OUTPUT);//setting pin for sinking current
  digitalWrite(ECGround,LOW);//We can leave the ground connected permanantly
 
  delay(100);// gives sensor time to settle
  sensors.begin();
  delay(100);
  //** Adding Digital Pin Resistance to [25 ohm] to the static Resistor *********//
  // Consule Read-Me for Why, or just accept it as true
  R1=(R1+Ra);
 
  Serial.println("ElCheapo Arduino EC-PPM measurments Calibration");
  Serial.println("By: Michael Ratcliffe  Mike@MichaelRatcliffe.com");
  Serial.println("Free software: you can redistribute it and/or modify it under GNU ");
  Serial.println("");
  Serial.println("Make sure Probe and Temp Sensor are in Solution and solution is well mixed");
  Serial.println("");
  Serial.println("Starting Calibration: Estimated Time 60 Seconds:");
 
 
 
};
//******************************************* End of Setup **********************************************************************//
 
 
 
 
//************************************* Main Loop - Runs Forever ***************************************************************//
//Moved Heavy Work To subroutines so you can call them from main loop without cluttering the main loop
void loop()
{
 
 
  i=1;
  buffer=0;
sensors.requestTemperatures();// Send the command to get temperatures
TemperatureStart=sensors.getTempCByIndex(0); //Stores Value in Variable
 
 
//************Estimates Resistance of Liquid ****************//
while(i<=10){
 
 
 
digitalWrite(ECPower,HIGH);
raw= analogRead(ECPin);
raw= analogRead(ECPin);// This is not a mistake, First reading will be low
digitalWrite(ECPower,LOW);
buffer=buffer+raw;
i++;
delay(5000);
};
raw=(buffer/10);
 
 
 
 
sensors.requestTemperatures();// Send the command to get temperatures
TemperatureFinish=sensors.getTempCByIndex(0); //Stores Value in Variable
 
 
//*************Compensating For Temperaure********************//
EC =CalibrationEC*(1+(TemperatureCoef*(TemperatureFinish-25.0))) ;
 
//***************** Calculates R relating to Calibration fluid **************************//
Vdrop= (((Vin)*(raw))/1024.0);
Rc=(Vdrop*R1)/(Vin-Vdrop);
Rc=Rc-Ra;
K= 1000/(Rc*EC);
 
 
 
 
Serial.print("Calibration Fluid EC: ");
Serial.print(CalibrationEC);
Serial.print(" S  ");  //add units here
Serial.print("Cell Constant K");
Serial.print(K);
 
 
if (TemperatureStart==TemperatureFinish){
  Serial.println("  Results are Trustworthy");
  Serial.println("  Safe To Use Above Cell Constant in Main EC code");
 
}
else{
  Serial.println("  Error -Wait For Temperature To settle");
 
}
 
 
}
//************************************** End Of Main Loop **********************************************************************//

EC PPM Measurement Code

>If you are using PPM and not EC make sure you note what conversion factor you are using [it isnt universal]

>Dont call the read function more than once every 5 seconds or you will get bad readings and a damaged probe

I tested this code in a solution for 48 hours reading at 5 second intervals without any polarisation or probe damage, the longer you leave between readings the longer your probe will last. 5 seconds is the minimum wait between readings not the maximum.

you will need to use the modified one wire and Dallas library [download from www.michaelratcliffe.com] or add a pull up for the temperature probe data line [google it]

/*
  ElCheapo Arduino EC-PPM measurments
 
  This scrip uses a common USA two prong plug and a 47Kohm Resistor to measure the EC/PPM of a Aquaponics/Hydroponics Sytem.
  You could modift this code to Measure other liquids if you change the resitor and values at the top of the code.
 
  This Program will give you a temperature based feed controller. See Read me in download file for more info.
 
  28/8/2015  Michael Ratcliffe  Mike@MichaelRatcliffe.com
 
 
          This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see .
 
    Parts:
    -Arduino - Uno/Mega
    -Standard American two prong plug
    -1 kohm resistor
    -DS18B20 Waterproof Temperature Sensor
 
    Limitations:
    -
    -
 
    See www.MichaelRatcliffe.com/Projects for a Pinout and user guide or consult the Zip you got this code from
 
*/
 
 
//************************** Libraries Needed To Compile The Script [See Read me In Download] ***************//
// Both below Library are custom ones [ SEE READ ME In Downloaded Zip If You Dont Know how To install] Use them or add a pull up resistor to the temp probe
 
 
#include <OneWire.h>
#include <DallasTemperature.h>
 
 
 
 
 
 
//************************* User Defined Variables ********************************************************//
 
 
//##################################################################################
//-----------  Do not Replace R1 with a resistor lower than 300 ohms    ------------
//##################################################################################
 
 
int R1= 1000;
int Ra=25; //Resistance of powering Pins
int ECPin= A0;
int ECGround=A1;
int ECPower =A4;
 
 
//*********** Converting to ppm [Learn to use EC it is much better**************//
// Hana      [USA]        PPMconverion:  0.5
// Eutech    [EU]          PPMconversion:  0.64
//Tranchen  [Australia]  PPMconversion:  0.7
// Why didnt anyone standardise this?
 
 
float PPMconversion=0.7;
 
 
//*************Compensating for temperature ************************************//
//The value below will change depending on what chemical solution we are measuring
//0.019 is generaly considered the standard for plant nutrients [google "Temperature compensation EC" for more info
float TemperatureCoef = 0.019; //this changes depending on what chemical we are measuring
 
 
 
 
//********************** Cell Constant For Ec Measurements *********************//
//Mine was around 2.9 with plugs being a standard size they should all be around the same
//But If you get bad readings you can use the calibration script and fluid to get a better estimate for K
float K=2.88;
 
 
 
 
//************ Temp Probe Related *********************************************//
#define ONE_WIRE_BUS 10          // Data wire For Temp Probe is plugged into pin 10 on the Arduino
const int TempProbePossitive =8;  //Temp Probe power connected to pin 9
const int TempProbeNegative=9;    //Temp Probe Negative connected to pin 8
 
 
 
 
//***************************** END Of Recomended User Inputs *****************************************************************//
 
 
OneWire oneWire(ONE_WIRE_BUS);// Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire);// Pass our oneWire reference to Dallas Temperature.
 
 
float Temperature=10;
float EC=0;
float EC25 =0;
int ppm =0;
 
 
float raw= 0;
float Vin= 5;
float Vdrop= 0;
float Rc= 0;
float buffer=0;
 
 
 
 
//*********************************Setup - runs Once and sets pins etc ******************************************************//
void setup()
{
  Serial.begin(9600);
  pinMode(TempProbeNegative , OUTPUT ); //seting ground pin as output for tmp probe
  digitalWrite(TempProbeNegative , LOW );//Seting it to ground so it can sink current
  pinMode(TempProbePossitive , OUTPUT );//ditto but for positive
  digitalWrite(TempProbePossitive , HIGH );
  pinMode(ECPin,INPUT);
  pinMode(ECPower,OUTPUT);//Setting pin for sourcing current
  pinMode(ECGround,OUTPUT);//setting pin for sinking current
  digitalWrite(ECGround,LOW);//We can leave the ground connected permanantly
 
  delay(100);// gives sensor time to settle
  sensors.begin();
  delay(100);
  //** Adding Digital Pin Resistance to [25 ohm] to the static Resistor *********//
  // Consule Read-Me for Why, or just accept it as true
  R1=(R1+Ra);// Taking into acount Powering Pin Resitance
 
  Serial.println("ElCheapo Arduino EC-PPM measurments");
  Serial.println("By: Michael Ratcliffe  Mike@MichaelRatcliffe.com");
  Serial.println("Free software: you can redistribute it and/or modify it under GNU ");
  Serial.println("");
  Serial.println("Make sure Probe and Temp Sensor are in Solution and solution is well mixed");
  Serial.println("");
  Serial.println("Measurments at 5's Second intervals [Dont read Ec morre than once every 5 seconds]:");
 
 
};
//******************************************* End of Setup **********************************************************************//
 
 
 
 
//************************************* Main Loop - Runs Forever ***************************************************************//
//Moved Heavy Work To subroutines so you can call them from main loop without cluttering the main loop
void loop()
{
 
 
 
 
GetEC();          //Calls Code to Go into GetEC() Loop [Below Main Loop] dont call this more that 1/5 hhz [once every five seconds] or you will polarise the water
PrintReadings();  // Cals Print routine [below main loop]
 
 
delay(5000);
 
 
}
//************************************** End Of Main Loop **********************************************************************//
 
 
 
 
//************ This Loop Is called From Main Loop************************//
void GetEC(){
 
 
//*********Reading Temperature Of Solution *******************//
sensors.requestTemperatures();// Send the command to get temperatures
Temperature=sensors.getTempCByIndex(0); //Stores Value in Variable
 
 
 
 
//************Estimates Resistance of Liquid ****************//
digitalWrite(ECPower,HIGH);
raw= analogRead(ECPin);
raw= analogRead(ECPin);// This is not a mistake, First reading will be low beause if charged a capacitor
digitalWrite(ECPower,LOW);
 
 
 
 
//***************** Converts to EC **************************//
Vdrop= (Vin*raw)/1024.0;
Rc=(Vdrop*R1)/(Vin-Vdrop);
Rc=Rc-Ra; //acounting for Digital Pin Resitance
EC = 1000/(Rc*K);
 
 
//*************Compensating For Temperaure********************//
EC25  =  EC/ (1+ TemperatureCoef*(Temperature-25.0));
ppm=(EC25)*(PPMconversion*1000);
 
 
;}
//************************** End OF EC Function ***************************//
 
 
 
 
//***This Loop Is called From Main Loop- Prints to serial usefull info ***//
void PrintReadings(){
Serial.print("Rc: ");
Serial.print(Rc);
Serial.print(" EC: ");
Serial.print(EC25);
Serial.print(" Simens  ");
Serial.print(ppm);
Serial.print(" ppm  ");
Serial.print(Temperature);
Serial.println(" *C ");
 
 
/*
//********** Usued for Debugging ************
Serial.print("Vdrop: ");
Serial.println(Vdrop);
Serial.print("Rc: ");
Serial.println(Rc);
Serial.print(EC);
Serial.println("Siemens");
//********** end of Debugging Prints *********
*/
};

Got any questions let me know.

The next tutorial will be on coding a self learning nutrient doser.

References:

[1]

John J. Barron & Colin Ashton "The Effect of Temperature on Conductivity Measurement" Technical Services Department, Reagecon Diagnostics Ltd

http://www.reagecon.com/pdf/technicalpapers/Effect_of_Temperature_TSP-07_Issue3.pdf

Discussions

vikash kumar wrote 7 days ago point

I'm not able to get the code of the modified onewire dallas library. Kindly help me to get the modified onewire dallas library installed as soon as possible. 

  Are you sure? yes | no

charlie bart wrote 07/20/2018 at 12:54 point

Hi everybody.

I've been successfully reading EC with your code and my arduino UNO & Mega.

But the thing is, when i try with my ESP32 on an ADC port, i get incoherent voltages. Same thing with an arduino + ADS1115.

For example : 

Arduino+EC sensor+ 1.55mS calibration solution : Value read : 403 (so 1.96v)

Arduino+EC sensor+ 2.8mS calibration solution : Value read : 277 (so 1.35v)

Arduino without sensor plugged : Value read : 1023 (so 5v)

Arduino+ADS1115@15bits+EC sensor+ 1.55mS calibration solution : Value read : 24560 (so 4.6v)

Arduino+ADS1115@15bits+EC sensor+ 2.8mS calibration solution : Value read : 25520 (so 4.79v)

Arduino+ADS1115@15bits without sensor plugged : Value read : 27000 (so 5v under 15bits)

I'm always using a 1kohms resistor for those tests (between +5v and analog signal).

I don't have the numbers now for the ESP32+sensor@3.3v but it's the same than with the ADS (voltages read higher than on arduino).

Do someone have an idea about what is happening here?

I'm reaching the limit of my basic knowledge here :-(

  Are you sure? yes | no

466 wrote 04/10/2018 at 16:29 point

Nice project you got here!


Looking forward, creating an EC meter myself :)

Would you mind explaining:

Minimum EC 25=0.3 EC

and

Maximum EC 25= 3 EC 

?

Where do those values come from?

(Ok, 3 EC:  3 could be cell constant? but 0.3? did not find any info about it)

Thanks!

  Are you sure? yes | no

Cedric wrote 03/03/2018 at 12:39 point

Hi Michael, instead of a plug, I'm planning on using pin headers as the probe. However, I'm not sure what should the K constant would be. How did you get the K constant of the plug? 

  Are you sure? yes | no

Zaidan Alif Muttaqin wrote 02/02/2018 at 10:05 point

Hi Mike, can you solve my problem? the temperature is always -127 C.

  Are you sure? yes | no

Cody Lawson wrote 02/28/2018 at 03:36 point

Kind of late reply but -127 reading means bad probe or bad connection. If you have another probe try using that. If only one probe try resoldering the contacts or making a new plug depending on how it connects. That error likes to randomly pop up with these probes.

  Are you sure? yes | no

carlos.bruckner wrote 01/28/2018 at 14:48 point

Hi there. I've been playing with a similar project for a few days and fighting some distortion on my readings as the conductivity goes up. My guess is that it is due to polarization.
I'd like to add a few notes and questions:

a) When you say "You want the Solid Prong type plug" you meant the right end of the cable or to not use plugs with those little holes on the side? I changed one with the little holes to to another one (solid, round, little smaller area) and I think I got better readings.

b) The first reading gets discarded. Can I do that before setting the power pin, to high?, ie:

analogRead(ECPin);// first reading discarded, outside "HIGH zone"
digitalWrite(ECPower,HIGH); // or PORTA = B00000010; (for digital pin 23)
sensorValue = analogRead(ECPin);
digitalWrite(ECPower,LOW);

That way we spend less time with ECPower "on", so less polarization.

d) By my calculations, the resolution at a given water resisitance Rw is:

Res = R1 / (R1+Rw)^2 - which means that you should use R1 as close to Rw as possible.

However, while having the water at around 974uS (and my system reading it as 530~550ohm), to improve resolution, I used a 520ohm resistor, but the readings were more accurate when I used 1500ohm. My guess is that bigger resistance R1 means less voltage on the water, and thus, less polarization. If that makes sense, I think it is worth noticing.

Oh, and my K is currently 1.965.

Anyways, great project and great description, it was exactly what I was looking for.

  Are you sure? yes | no

Cody Lawson wrote 01/23/2018 at 22:36 point

Have you experimented with the ESP32 at all? My RO system already runs on one and it actually has multiple analog inputs compared to the ESP8266. I started to dive into it but the coding looked beyond my level. It appears a lot of the ADC stuff is still in development.

Some more helpful info. I called HM Digital and found out their inline probe K value is 0.67. Not sure if HM Digital is very big in the UK but in the US they seem to be the standard with RO units. Unfortunately their monitors do not have a data out but doesn't mean you can't recycle the probe! Also no data on the internal thermister (said they would have to call Korea) so an external temp probe is still needed.

If using their probes (or similar inline push fitting style) make sure you calibrate them INSIDE their T-fitting. The prongs are compressed together slightly while in the fitting and it appears the calibration point takes that into account. Results still weren't perfect using the 0.67 and 10,000K resistor but they were significantly better compared to measuring a naked probe. At ~100ppm I read 105, ~13-14 I read 15.46, and at ~3-4 I read around 6 ppm. My other probe from China I calculated K to be around 0.565. I would say a K range of 0.4-0.8 would be good for these smaller probes.

  Are you sure? yes | no

Cody Lawson wrote 01/22/2018 at 03:42 point

Great project! I ended up making a few interactive graphs with your equations for both the Arduino and ESP8266 boards. Both the K value and R1 have sliders so you can see how each will change the curve. Apparently if you double the R1 value but halve the K value you get roughly the same curve. That was one of a few different "OOOOOHHHHHHH, that's how that works!" moments. At least this felt better than plugging random numbers in and hoping for the best haha.

Arduino: https://www.desmos.com/calculator/qzbszykkbj

ESP8266: https://www.desmos.com/calculator/moptex4ufc

Feel free to reuse these if you want. Figured someone out there would appreciate the little extra help :)

Graph Instructions:

The X axis is the "Raw" variable, so 1 - 1024. The Y axis is PPM (Hana 0.5). The adjustable equation is the Red line. Sliders can be found at the bottom of the menu on the left. It starts off with what you chose in the tutorial. The next 4 lines can be used for comparison. They are set near my calibration point with Purple = R 10000, K 0.5; Orange = R 10000, K 1; Blue = 20000, K 0.5; Green = R 20000, K 1. Both Orange and Blue should basically be on top of each other  (what I referenced earlier).

If you want to convert back to EC just delete the *500 at the very end of the equation. To change to a different PPM just change 500 to 640 or 700. To change the range or step of K or R1 click on either the min or max number found on either side of the slide. Everything else I am going to assume you guys can figure it out.

  Are you sure? yes | no

frozenfritz wrote 12/12/2017 at 17:32 point

Hi Michael, I have a question for you: why do you use A8 as a power source? could I also youse a "normal" digital pin from an arduino? Really nice project btw!

  Are you sure? yes | no

Michael Ratcliffe wrote 12/14/2017 at 19:24 point

Yep, for a driving pin you can use a digital pin. 

  Are you sure? yes | no

cyberdevil001 wrote 12/10/2017 at 00:30 point

Hi Michael,

I'm using your code to measure and log the TDS that comes out of my RO system to replace the existing TDS meter. I only noticed that it seems to have issues with the low value measuring of the output water. This is currently 0 while before the DI resin this is between 1 and 7. Currently using the following probe: https://www.aliexpress.com/item/New-G1-4-0-8MPA-Water-quality-probe-TDS-conductivity-test-water-quality-testing-probe/32630094172.html?spm=2114.01010208.3.226.RCrLgm&ws_ab_test=searchweb0_0  . Calibrated this on 342ppm calibration water and when measuring 0 ppm water (demineralised water), it actually shows a few ppm instead:

Rc: 261350.00 EC: 0.00 Simens  2 ppm  21.69 *C 
Rc: 261350.00 EC: 0.00 Simens  2 ppm  21.75 *C 
Rc: 261350.00 EC: 0.00 Simens  2 ppm  21.75 *C 
Rc: 348816.65 EC: 0.00 Simens  1 ppm  21.75 *C 
Rc: 261350.00 EC: 0.00 Simens  2 ppm  21.75 *C 
Rc: 208870.00 EC: 0.01 Simens  3 ppm  21.75 *C 
Rc: 261350.00 EC: 0.00 Simens  2 ppm  21.75 *C 

Is there a way that I can tweak it so it will be able to measure these low values? Currently using the 1kOhm resistor as I'm still in the process to understand the R1 calculation you did in combination with my probe.

Thanks!

  Are you sure? yes | no

cyberdevil001 wrote 12/10/2017 at 02:28 point

I did try replacing the 1kOhm resistor with 2 in parallel (=500Ohm) and this seems to provide a bit more stable value, but still off, even with a new sample of 0ppm. (Higher value was spot on the calibration point) Also increased the resolution on the output of EC:

Rc: 268250.00 EC: 0.0050 Simens  2 ppm  16.56 *C 
Rc: 268250.00 EC: 0.0050 Simens  2 ppm  16.62 *C 
Rc: 178650.00 EC: 0.0075 Simens  3 ppm  16.62 *C 
Rc: 268250.00 EC: 0.0050 Simens  2 ppm  16.62 *C 
Rc: 268250.00 EC: 0.0050 Simens  2 ppm  16.69 *C 

So it is close, but for what I need to use it, the 2ppm off is still to much. Maybe 2 spot calibration could solve it?

  Are you sure? yes | no

cyberdevil001 wrote 12/11/2017 at 12:07 point

Trying to see if a 16-bit ADC will solve my issue. Will update in a few days.

  Are you sure? yes | no

Michael Ratcliffe wrote 12/14/2017 at 19:31 point

Hey, I made this with higher ppm in mind. 

Some advice to make it work better for low EC Start with the ones at the top:

>Increase the Value of R1 [ google voltage dividers and resistance of fluid vs ppm for your probe constant] I would imagine a 5-10K reistor would work better for you but I haven't done the maths.

> Potentially increase the delay after powering the probe up and taking a reading [LOW EC water shouldn't polarise easily]

Also you will need to look for a data sheet on temperature correction for low EC water, the one I use in the sketch is a estimate for hydroponics solutions]. Distilled water will absorb CO2 and this means distilled water does have a very small ppm.

  Are you sure? yes | no

yuyouliang wrote 11/09/2017 at 05:09 point

Dear Michael, thank you for sharing this project. It's wonderful. I have some questions. Could you explain 
 why EC = 1000/(Rc*K)? Why the number is 1000?  And the unit is S/cm? not ms/cm? Thanks.

  Are you sure? yes | no

denysafari wrote 10/29/2017 at 09:16 point

HI michael , i built this one, but i had a problem. the temperature result is negative (-127). could you help me please?

  Are you sure? yes | no

kutlutug wrote 11/13/2017 at 08:43 point

I have the same problem.  My temperature is -127 and my ec numbers dont change in different liquids even wiring the plugs polars with a wire. Is this problem about my hardware?

  Are you sure? yes | no

wootten91 wrote 09/17/2017 at 18:32 point

Hi Michael,  Great project. We are a robotics team of students.  We are trying to make a remote probe for water quality measurements using the Particle Electron device.  This differs from Arduino in that it runs on 3.3 Volts (compared to 5 Volts for Arduino).  How can we adapt the wiring to make this work with the Particle?  Thanks for the help.

  Are you sure? yes | no

raychajahanvi21 wrote 08/26/2017 at 06:55 point

hey , Micheal I was trying to download zip file but it is showing me error !!!

can you please help me with that!!

  Are you sure? yes | no

rleyden310 wrote 08/07/2017 at 19:57 point

I added at pH meter ( http://www.ebay.com/itm/Analog-PH-Probe-Sensor-Shield-and-PH-Probe-Kit-For-Arduino-R3-Water-Detect/331878771777?ssPageName=STRK%3AMEBIDX%3AIT&_trksid=p2057872.m2749.l2649 ) and get interference on the pH readings.  I even tried using a separate Arduino for the pH reading and the problem persists although as an offset (~ -2 pH) rather than wildly swinging readings.  Any ideas how to fix this?  Correcting the pH offset caused by the EC probe seems an "OK" solution but I worry that it may not be stable.

  Are you sure? yes | no

Michael Ratcliffe wrote 08/07/2017 at 20:25 point

Sure, its a grounding loop problem.  you can solve/fix it mechanically or electrically.

mechanically: make sure the ph meter and Ec probe are as far away from each other as possible

Electronically:  Turn the EC meter pin's into a high impedance state when not in use.  By setting both pins to inputs using pinmode, you will need to reset them as outputs before taking a reading. 

ie  pinmode output --> delay(20) --> Read EC -->> pimode input.

Also this is not the most recent code, just the proof of concept. Check out the main blog for a more recent one with calibration routine etc

  Are you sure? yes | no

rleyden310 wrote 08/07/2017 at 21:13 point

Thanks Michael, 

Setting power and ground pins to input between measurements seems to have corrected the offset.  I figured it was a ground problem but tried to fix it by setting the EC pin to output, low - wrong direction.  My current hydroponic tank is small so I can only get the probes about 10" apart.

So where is your main blog?  "http://www.michaelratcliffe.com/" seems to be mostly coming soon pages.

  Are you sure? yes | no

Michael Ratcliffe wrote 08/07/2017 at 21:36 point

Glad it solved the issue, if your dosing the pH make sure to keep an eye on the system because the probes dont last long. 

Most of the blogs can be found here: 

https://www.element14.com/community/community/design-challenges/vertical-farming/blog/2015/09/13/automated-green-house-blogs-main-page

  Are you sure? yes | no

Nuno Silva wrote 05/09/2017 at 12:58 point

Hi Michael,

I am trying to use your hardware and software to test portable water.

In my case I am calibrating the system for 0,01288 S/cm and I would like to ask you what do you think the R1 should be for this project.

If I follow your methodology Vdrop and Vout Range I should use a 30K ohms resistor.

This is the way?

Thanks

Nuno

  Are you sure? yes | no

Michael Ratcliffe wrote 08/07/2017 at 21:41 point

I havent tested at such a low EC, you might need to use a larger probe surface area to get it to work. 

But hey give it a shot and see if a 30k works?

  Are you sure? yes | no

daniele bordignon wrote 03/17/2017 at 09:12 point

Hi Michael. Great post, i'm going to build one of these. I was guessing.. The sensor can stay into water 24/24 7/7?
Is it durable (after 3 or 6 months, one year..)?

  Are you sure? yes | no

ctsai2 wrote 03/16/2017 at 13:35 point

Hi Michael,

Can I use this code on DUE instead?

  Are you sure? yes | no

Michael Ratcliffe wrote 08/07/2017 at 21:42 point

Sorry for the late reply, 

I havent tested on a DUO, it may have different v drop for digital pins [can work it out from data sheet]. this would be possible, but if it has extra circutry on the analog pins to reduce noise etc it wont work.

  Are you sure? yes | no

ctsai2 wrote 02/07/2017 at 17:35 point

Dear Michael,

Thanks for sharing this thread. 
I need to make a salinity/EC meter for 35ppt/50000EC. Can I use your setup? And what is the reason R1 should be greater than 300ohm?

  Are you sure? yes | no

Michael Ratcliffe wrote 02/08/2017 at 10:09 point

An EC of 50? That is going to be challenging, I cannot confirm 100%. 

But the bet way may be to reduce the probe area [cut them shorter?] to keep the current reqirments down. 

R1: this limits the current drawn by the probe, the arduino digital pin can only supply 20ma. 

  Are you sure? yes | no

ctsai2 wrote 02/15/2017 at 18:57 point

Yes, EC of 50 mS/cm. Right now I have it set up the same as yours. Have not shorten the probes yet. calculated using 1000ohms resistor. Is there anything else I should be concern and change? Because I don't want to burnt any pin. And yes, the conductivity is very high, it's seawater that I am measuring.

Thank you.

  Are you sure? yes | no

ctsai2 wrote 03/15/2017 at 18:41 point

Michael, 

I cut the probe short, change the Calibration value, but my best R1 for resistor is below 300ohms. Since the seawater resistance is so low compare to fresh/drinking water, can I use R1<300 ohm?
Or is this reversed, that I can only use R1 > than like 500 ohms? 

Instead of using analog i/o pins, the ECpower is connect directly to the 5V on the board, since we will have constant flowing water so not worrying about polarized the fluid. Does that also up my current drawn to 500mA?

Trying hard to make this work. Following your direction I already build one to measure 400-1000ppm and it works. Now moving onto measuring 32,000ppm to 40,000ppm

Thanks for any of your help

  Are you sure? yes | no

geert2 wrote 02/05/2017 at 13:20 point

Dear Michael, 

More respect every day. One remark and a cry for help. 

The Electric conductivity is an indicator for ammonia. Meaning that if you have a dead fish in the system, or algae that start dying, , you will notice the EC going thourgh the roof. This means the EC probe is one of the most telling indicators if something is going very wrong. 

I started to work on the system with a NODECMU Kit, and got quite far. 

However the readings are defenitely not right (see below). 

This could be because of the way the analog port reacts in a NODEMCU, or for any other reeason I don't understand, such as using digital pins for delivering the current. 

I am definitely out of my depth. Could you please help me? 

For water: 

Rc: 32808.05 EC: 0.01 Simens  6 ppm  25.50 *C 
raw993.00
Vdrop: 3.20
Rc: 32808.05
0.01Siemens

Salt water: 
Rc: 2752.90 EC: 0.12 Simens  79 ppm  25.50 *C 
raw748.00
Vdrop: 2.41
Rc: 2752.90
0.13Siemens

My code: 

//************************** Libraries Needed To Compile The Script [See Read me In Download] ***************//
// Both below Library are custom ones [ SEE READ ME In Downloaded Zip If You Dont Know how To install] Use them or add a pull up resistor to the temp probe
 

#include <Onewire.h>
#include  <Dallastemperature.h>

 
 
 
 
 
 
//************************* User Defined Variables ********************************************************//
 
 
//##################################################################################
//-----------  Do not Replace R1 with a resistor lower than 300 ohms    ------------
//##################################################################################
 
 
int R1= 1000;
int Ra=25; //Resistance of powering Pins
int ECPin= A0;
int ECGround=D7;
int ECPower =D8; // originally A4 is used by air pressure measurement 
 
 
//*********** Converting to ppm [Learn to use EC it is much better**************//
// Hana      [USA]        PPMconverion:  0.5
// Eutech    [EU]          PPMconversion:  0.64
//Tranchen  [Australia]  PPMconversion:  0.7
// Why didnt anyone standardise this?
 
 
float PPMconversion=0.64;
 
 
//*************Compensating for temperature ************************************//
//The value below will change depending on what chemical solution we are measuring
//0.019 is generaly considered the standard for plant nutrients [google "Temperature compensation EC" for more info
float TemperatureCoef = 0.019; //this changes depending on what chemical we are measuring
 
 
 
 
//********************** Cell Constant For Ec Measurements *********************//
//Mine was around 2.9 with plugs being a standard size they should all be around the same
//But If you get bad readings you can use the calibration script and fluid to get a better estimate for K
float K=2.88;
 
 
 
 
//************ Temp Probe Related *********************************************//
#define ONE_WIRE_BUS D2          // Data wire For Temp Probe is plugged into pin 10 on the Arduino // with me on 2 
//const int TempProbePossitive =8;  //Temp Probe power connected to pin 9  // I don' have that? 
//const int TempProbeNegative=9;    //Temp Probe Negative connected to pin 8  //should I do that? 
// 
 
 
 
//***************************** END Of Recomended User Inputs *****************************************************************//
 
OneWire oneWire(ONE_WIRE_BUS);// Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire);// Pass our oneWire reference to Dallas Temperature.
// 
 
float Temperature=10;
float EC=0;
float EC25 =0;
int ppm =0;
 
 
float raw= 0;
float Vin= 3.3;  // is only 3.3 v with esp was 5
float Vdrop= 0;
float Rc= 0;
float buffer=0;
 
 
 
 
//*********************************Setup - runs Once and sets pins etc ******************************************************//
void setup()
{
  Serial.begin(9600);
//  pinMode(TempProbeNegative , OUTPUT ); //seting ground pin as output for tmp probe
//  digitalWrite(TempProbeNegative , LOW );//Seting it to ground so it can sink current
//  pinMode(TempProbePossitive , OUTPUT );//ditto but for positive
//  digitalWrite(TempProbePossitive , HIGH );
  pinMode(ECPin,INPUT);
  pinMode(ECPower,OUTPUT);//Setting pin for sourcing current
  pinMode(ECGround,OUTPUT);//setting pin for sinking current
  digitalWrite(ECGround,LOW);//We can leave the ground connected permanantly
 
  delay(100);// gives sensor time to settle
//  sensors.begin();
  delay(100);
  //** Adding Digital Pin Resistance to [25 ohm] to the static Resistor *********//
  // Consule Read-Me for Why, or just accept it as true
  R1=(R1+Ra);// Taking into acount Powering Pin Resitance
 
  Serial.println("ElCheapo Arduino EC-PPM measurments");
  Serial.println("By: Michael Ratcliffe  Mike@MichaelRatcliffe.com");
  Serial.println("Free software: you can redistribute it and/or modify it under GNU ");
  Serial.println("");
  Serial.println("Make sure Probe and Temp Sensor are in Solution and solution is well mixed");
  Serial.println("");
  Serial.println("Measurments at 5's Second intervals [Dont read Ec morre than once every 5 seconds]:");
 
 
};
//******************************************* End of Setup **********************************************************************//
 
 
 
 
//************************************* Main Loop - Runs Forever ***************************************************************//
//Moved Heavy Work To subroutines so you can call them from main loop without cluttering the main loop
void loop()
{
 
 
 
 
GetEC();          //Calls Code to Go into GetEC() Loop [Below Main Loop] dont call this more that 1/5 hhz [once every five seconds] or you will polarise the water
PrintReadings();  // Cals Print routine [below main loop]
 
 
delay(10000);// original is 5000 
 
 
}
//************************************** End Of Main Loop **********************************************************************//
 
 
 
 
//************ This Loop Is called From Main Loop************************//
void GetEC(){
 
 
//*********Reading Temperature Of Solution *******************//
sensors.requestTemperatures();// Send the command to get temperatures
Temperature=sensors.getTempCByIndex(0); //Stores Value in Variable
 
 
 
 
//************Estimates Resistance of Liquid ****************//
digitalWrite(ECPower,HIGH);
raw= analogRead(ECPin);
raw= analogRead(ECPin);// This is not a mistake, First reading will be low beause if charged a capacitor
digitalWrite(ECPower,LOW);
 
 
 
 
//***************** Converts to EC **************************//
Vdrop= (Vin*raw)/1024.0;
Rc=(Vdrop*R1)/(Vin-Vdrop);
Rc=Rc-Ra; //acounting for Digital Pin Resitance
EC = 1000/(Rc*K);
 
 
//*************Compensating For Temperaure********************//
EC25  =  EC/ (1+ TemperatureCoef*(Temperature-25.0));
//EC25 = EC;
ppm=(EC25)*(PPMconversion*1000);
 
 
;}
//************************** End OF EC Function ***************************//
 
 
 
 
//***This Loop Is called From Main Loop- Prints to serial usefull info ***//
void PrintReadings(){
Serial.print("Rc: ");
Serial.print(Rc);
Serial.print(" EC: ");
Serial.print(EC25);
Serial.print(" Simens  ");
Serial.print(ppm);
Serial.print(" ppm  ");
Serial.print(Temperature);
Serial.println(" *C ");
 
 




//********** Usued for Debugging ************
Serial.print("raw");
Serial.println(raw);
Serial.print("Vdrop: ");
Serial.println(Vdrop);
Serial.print("Rc: ");
Serial.println(Rc);
Serial.print(EC);
Serial.println("Siemens");
//********** end of Debugging Prints *********




};

  Are you sure? yes | no

Michael Ratcliffe wrote 02/06/2017 at 00:28 point

Replied to the second post :) 

  Are you sure? yes | no