NAV Navbar
cpp python shell


Measure Electrical Conductivity in Siemens, Total Dissolved Solids in PPM, Salinity in PSU and PPT.

Monitor hydroponic nutrient levels, salinity levels in aquariums or the ocean, saltwater pools, soil salinity, water quality etc.

It uses the standard Arduino Wire library to interface with the device. It’s operating principle is based on sending a very short-duration DC pulse from one probe pin and measuring the conductivity on the other pin. The conductivity is then converted to a temperature compensated Siemen. Salinity is also derived from the measure and is in PSU, PPT, and PPM.



I2C Connections

The connection to the device is as follows:

EC Salinity Probe Master device
VCC 2.7 - 5V

Temperature Probe Connections

The temperature probe comes with a 3-wire header. The VCC pin is labeled with a triangle:

EC Salinity Probe Temperature Probe
+ (triangle

EC Probe Connections

The EC probe has two pins and can be connected either way.

Probe Selection

Any 2-electrode probe can be attached to the device.

A probe with a K constant of:

Getting Started

To start developing for the device, you need to install the library. Two are provided, one for the Arduino framework, and the other for python3 on the Raspberry Pi.

If you are using the Arduino IDE, you can go to the library manager (Sketch / Include Library / Manage Libraries…) and search for pH_Probe. Examples are included.

If you are using PlatformIO, you can install the library using the library manager (PlatformIO / PlatformIO Home / Libraries) and search for pH Probe. Examples are included.

For Raspberry Pi 3, you can clone the GitHub repo. Before you can run anything, you will need to enable software I2C, as the Pi’s hardware implementation has a clock-stretching bug that will prevent it from working with the pH probe.

  1. sudo nano /boot/config.txt and scroll to the bottom
  2. Add dtoverlay=i2c-gpio,i2c_gpio_sda=<pin>,i2c_gpio_scl=<pin>, replacing <pin> with whatever pin you’d like to use. Refer here for the pin functions, you will need to use the orange GPIO xx labels in the picture to locate the pins. As an example, to use GPIO 17 as SDA and GPIO 27 as SCL, your line will look like this dtoverlay=i2c-gpio,i2c_gpio_sda=17,i2c_gpio_scl=27 Alternatively, you can choose to use the existing I2C pins, but using a software implementation of I2C, by pasting dtoverlay=i2c-gpio. If you go this route, you will need to edit line #4 from ph = phprobe(3), to ph = phprobe(1).
  3. ctrl + x to exit, y to save, and enter to confirm the filename.
  4. Reboot

From inside repo folder, execute sudo python3 and you should be presented with a > prompt as described below.

The shell Example

An interactive shell interface is provided with both frameworks and is a quick and easy way to get started using the device. You will find the equivalent commands in the code area to the right when applicable. Upload it to your master device and start a serial terminal. You will be presented with a > prompt where you can enter commands and receive a response, similar to a shell command line or REPL. It is often quicker to experiment with things this way rather than rewriting, compiling, and uploading new versions every time.

Changing the I2C Address

If needed, the I2C address can be changed in programatically by calling setI2CAddress(). The device will permanently change it’s address to the passed value and continue to use it after a power reset. If you forget the new address, you will need to use an I2C bus scanner to locate it again.


#include <ECSalinity.h>
EC_Salinity ec;
float calibrationSolution_mS = 2.77;

ec.calibrateProbe(calibrationSolution_mS, ec.tempCoefEC);
from ecsalinity import ecsalinity
ec = ecsalinity(3)
calibrationSolution_mS = 2.77
ec.calibrateProbe(calibrationSolution_mS, ec.tempCoefEC)
k 10.0
cal 2.77

When the device is powered for the first time, it will be uncalibrated. There are two calibration options: single and dual or double point. Single point determines a percent difference between actual readings and expected readings, then uses it to make adjustments. Double point uses two points, a high and low, to determine the adjustment to make. The choice of single or double depends on the expected use. A small range might be better measured by single point calibration whereas a large range may be more accurate with double point.

Before any measurements can be made, to include measurements to calibrate the probe, the cell constant (K) must be specified. This number is typically 0.1, 1.0, or 10.0. The exact value may be found on the probe itself (9.988 for example). After K has been set, you can expect to see values being returned. It will not be accurate and must be calibrated.

For best results, the probe should be cleaned with distilled water and then placed in the solution for 5-10 minutes before the probe is used. It shouldn’t be placed on the bottom or side of the solution container. Note that any turbidity, air bubbles, large particles, etc will effect readings. An unstable temperature will decrease accuracy as well.

When calibrating the probe, it is important to consider the expected temperature range. EC measurements are very temperature dependent, effecting the results by approximately 2% per degree C. The probe should be calibrated at the median expected temperature.

Likewise, the probe should be calibrated in the median expected EC range. For example, if you are planning to measure the salinity of an aquarium, you might expect readings ±5 from 35PPT (equivalent to 53mS). So a 53mS solution for calibration would be appropriate, whereas a hydroponic calibration might be 2.77mS or lower.

Another consideration is the placement of the probe. When the probe sends out a pulse of electricity, it leaves the probe in 360 degrees. If it is near a metal surface, you will experience fringing effects. The probe should be calibrated in an environment as similar as possible to the location it will be deployed in.

Single Point

float solutionEC = 53.0;
ec.calibrationProbe(solutionEC, ec.tempCoefSalinity);
solutionEC = 53.0
ec.calibrationProbe(solutionEC, ec.tempCoefSalinity)
cal 53.0

For this method of calibrating, submerge the probe and wait for the readings to stabilize. Then call calibrateProbe(). The calibration information will be stored in EEPROM and is the percent difference from the measured value and the expected value.

Dual Point

Two calibration solutions are required, the low and high values you expect to measure between.

  1. Determine the lowest and highest measurement you expect. For example, the lowest level you might measure would be 50 mS and the highest might be 58 mS. These points will be referred to as referenceLow and referenceHigh
  2. Put the EC probe in a calibration solution at referenceLow and wait for readings to stabilize, call calibrateProbeLow(). Do the same for referenceHigh by calling ‘calibrateProbeHigh’.
  3. By default, the device does not use dual points. A call to useDualPoint() must be made to enable it each time the device is powered.

You can also set all four values directly using setDualPointCalibration().

float solutionECLow = 53.0;
float solutionECHigh = 58.0;
ec.calibrateProbeLow(solutionECLow, ec.tempCoefSalinity);
ec.calibrateProbeHigh(solutionECHigh, ec.tempCoefSalinity);
solutionECLow = 53.0
solutionECHigh = 58.0
ec.calibrateProbeLow(solutionECLow, ec.tempCoefSalinity)
ec.calibrateProbeHigh(solutionECHigh, ec.tempCoefSalinity)
low 53.0
high 58.0
dp 1

getCalibrateHigh and getCalibrateLow can be used to get the currently set reference values.

Each call to measureEC will use the calibration to adjust the reading. It can be disabled by ec.useDualPoint(false);


float tempC = ec.measureTemp();
tempC = ec.measureTemp()

Once the probe has been calibrated, a reading can be taken. The device will use the most recent temperature measurement obtained from measureTemp if temperature compensation is enabled.The probe should be placed in the solution to be measured and measureEC called to start a measurement.

After the measurement is taken, the following class variables are updated:

Temperature Coefficients

float pureWaterCompensation = 0.0455;
pureWaterCompensation = 0.0455

If the solution to be measured is saltwater, use measureSalinity(), if it is freshwater, use measureEC(). A custom temperature coefficient can be used by passing it to measureEC().

Temperature Compensation

byte tempConstant = 25;

tempConstant = 25

tc 1 25
tc 0
tc 1 255

To set the temperature used for compensation, call setTempConstant() and pass the temperature to use. To use the actual temperature, pass 0xFF.


acc 21

The accuracy of the device can be adjusted by modifying setAccuracy. By default, 9 measurements are taken. A running median sort is applied and the middle third of the nine measurements are averaged together to provide the final result. Any number of measurements can be taken as long as it is evenly divisible by 3. The least number of measures possible is 3.

Measurement Time

Each individual EC measurement takes 10ms. If the default accuracy is set to 9, a call to measureEC will return in about 90ms. A temperature measurement takes 750ms.

More Help

If you have any questions, find a bug, or have any suggestions on improvements, go to this project’s GitHub page and submit an Issue or Pull Request.

Class Members

public float S

EC in Siemens

public float mS

EC in milli-Siemens

public float uS

EC in micro-Siemens

public long PPM_500

Parts per million using 500 as a multiplier

public long PPM_640

Parts per million using 640 as a multiplier

public long PPM_700

Parts per million using 700 as a multiplier

public float salinityPSU

Salinity measured practical salinity units

public float salinityPPT

Salinity measured parts per thousand

public float salinityPPM

Salinity measured parts per million

public float tempC

Temperature in C

public float tempF

Temperature in F

Class Functions

public float measureEC(float tempCoefficient)

Starts an EC measurement.

float mS = EC_Salinity::measureEC(ec.tempCoefEC);
    ms = ec.measureEC(ecsalinity.tempCoefEC)

The device starst an EC measurement. The accuracy can be specified in EC_Salinity::setAccuracy.


uS, mS, S, tempC, tempF, PPM_500, PPM_640, PPM_700, salinityPPM, salinityPPT, and salinityPSU are updated



public float measureEC()

Convenience function to measure EC in freshwater.

float mS = EC_Salinity::measureEC();

Calls EC_Salinity::measureEC(EC_Salinity::tempCoefEC)


EC in mS

public float measureSalinity()

Convenience function to measure salinity.

float PSU = EC_Salinity::measureSalinity();

Calls EC_Salinity::measureEC(EC_Salinity::tempCoefSalinity)


salinity in PSU

public float measureTemp()

Starts a temperature measurement.

float tempC = EC_Salinity::measureTemp();
tempC = ec.measureTemp()

tempC and tempF are updated


temperature in C

public void calibrateProbe(float solutionEC,float tempCoef)

Calibrates the connected probe and saves the result in EEPROM.

EC_Salinity::calibrateProbe(2.77, EC_Salinity::tempCoefEC);
ec.calibrateProbe(2.77, ecsalinity.tempCoefEC)
cal 2.77


offset will be saved in the device’s EEPROM and used automatically thereafter

public void calibrateProbeLow(float solutionEC,float tempCoef)

Calibrates the dual-point values for the low reading and saves them in the devices’s EEPROM.

EC_Salinity::calibrateProbeLow(1.0, EC_Salinity::tempCoefEC);
ec.calibrateProbeLow(1.0, ecsalinity.tempCoefEC)
low 1.0


public void calibrateProbeHigh(float solutionEC,float tempCoef)

Calibrates the dual-point values for the high reading and saves them in the devices’s EEPROM.

EC_Salinity::calibrateProbeHigh(3.0, EC_Salinity::tempCoefEC);
ec:calibrateProbeHigh(3.0, ecsalinity.tempCoefEC)
high 3.0


public void calculateK(float solutionEC,float tempCoef)

Calculates the K value of the connected probe and saves it in EEPROM. Also essentially single-point calibrates the device.

EC_Salinity::calculateK(2.77, EC_Salinity::tempCoefEC);
ec.calculateK(2.77, ecsalinity.tempCoefEC)
calk 2.77


public void setDualPointCalibration(float refLow,float refHigh,float readLow,float readHigh)

Sets all the values for dual point calibration and saves them in the devices’s EEPROM.

EC_Salinity::setDualPointCalibration(1.0, 3.0, 0.9, 3.2);
ec.setDualPointCalibration(1.0, 3.0, 0.9, 3.2)


public void setK(float k)

Updates the device with a new cell constant and saves it in EEPROM.

k 1.121


public float getK()

Retrieves the cell constant from the device.

float k = EC_Salinity::getK();
k = ec.getK()


the cell constant

public void setAccuracy(byte b)

Configures the accuracy of the device.

acc 6

The device maintains a running median of values. It throws out the top and bottom third of values, then averages the middle third together to return a single value. The accuracy increases with a high number. It must be evenly divisible by 3.


public byte getAccuracy()


Retrieves the accuracy configuration of the device.



public void reset()


Resets all the stored calibration information.

public void setTempConstant(byte b)

Configures device to use the provided temperature constant.

EC_Salinity::setTempConstant(0xFF);   // use the actual tempeature
tc 1 25
tc 0
tc 1 255

By default, the temperature constant is set to 0xFF which instructs the actual temperature to be used for temperature compensation, however any number can be specified. To use the actual temperature, restore the value to 0xFF.


public byte getTempConstant()


Retrieves the temperature constant.


the temperature to used for compensation

public void useTemperatureCompensation(bool b)

Configures device to use temperature compensation or not.

tc 1


public bool usingTemperatureCompensation()

Determines if temperature compensation is being used.


true if using compensation, false otherwise

public void useDualPoint(bool b)

dp 1

Configures device to use dual-point calibration.


public bool usingDualPoint()


Determines if dual point calibration is being used.


true if using compensation, false otherwise

public float getCalibrateHigh()


Retrieves the dual-point calibration high value.


the dual-point calibration high value

public float getCalibrateLow()


Retrieves the dual-point calibration low value.


the dual-point calibration low value

public float getCalibrateHighReading()

Retrieves the dual-point calibration reading high value.


the dual-point calibration high value

public float getCalibrateLowReading()

Retrieves the dual-point calibration reading low value.


the dual-point calibration low value

public void setCalibrateOffset(float offset)

Sets the single point offset value.


public float getCalibrateOffset()


Retrieves the single point offset value.

float calibrateOffset = EC_Salinity::getCalibrateOffset();


single point offset value

public byte getVersion()

Retrieves the firmware version of the device.


version of firmware

public void setI2CAddress(uint8_t address)

Changes the i2c address of the device. It will use the new address immediately and continue to use it even after a power reset.


Generated by Moxygen