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

An electrically isolated I2C sensor device, a waterproof temperature sensor, and an electrical conductivity probe. It measures the conductivity of a solution and converts it into Siemens (S) total dissolved solids and salinity. The firmware allows for single or dual-point calibration with temperature compensation.


  • EC range: 0.5 - 20 mS/cm
  • Salinity range: 2 - 12
  • Accuracy: ± 5%
  • Temperature range: -55 - 125 C
  • Interface: I2C
  • Current use: ~10mA peak, low idle current ~1mA
  • Voltage range: 3.3 - 5.0V


I2C Connections

If you use the Qwiic connector, it is keyed and can only be inserted one way.

A 2.54mm through-hole header is also available. The pins are as follows:

EC Probe Interface Master device
GND Ground connection
3.3/5v 3.3 - 5 V power source

EC Probe Connections

A BNC connector is used to connect the conductivity probe to the EC Probe Interface. Ensure the connector is fully inserted and the twist-lock is fully locked.

Probe Selection

Any 2-electrode probe with a BNC connector can be used.

Temperature Probe Connections

The temperature probe comes with a 3-wire connector that can only be connected one way.

I2C Bus Pull-ups

There only needs to be one device with active pull-up resistors. Each board comes with the resistors active. Several boards can typically be connected with active resistors, however if connection problems are present, choose one board to keep the resistors active and disable to rest.

Each device comes with 4.7k resistor pullups on the I2C bus. They pass through a 3-pad solder paste jumper. The outer pads are connected to the middle pad by thin traces. To disable the pullups, use a utility knife to cut both traces. To re-enable the pullups, connect all three pads together with solder.

solder paste jumper

Getting Started

To start using the device, you need to install a library for your board/platform.

  • Arduino IDE: go to the library manager (Sketch / Include Library / Manage Libraries...) and search for Isolated EC Probe Interface.

  • PlatformIO: install the library using the library manager (PlatformIO / PlatformIO Home / Libraries) and search for Isolated EC Probe Interface.

  • search for Isolated EC Probe Interface in the Libraries section of the IDE.

  • MicroPython: clone the GitHub repo. The library is located in the python/MicroPython directory. Have a look at the README for instructions.

  • Raspberry Pi: clone the GitHub repo. The library is located in the python/RaspberryPi directory. Have a look at the README for instructions. Be sure to read the section below for instructions.

  • Rust: Download/install/documentation for the crate

Raspberry Pi

Before you can begin, you will need to enable software I2C. The Pi's hardware I2C implementation has a clock-stretching bug that will prevent it from working reliably. From a terminal, type:

  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.

  3. ctrl + x to exit, y to save, and enter to confirm the filename.

  4. reboot

The shell Example

An interactive shell interface is provided and is a quick and easy way to get started using the device. You will find the equivalent commands in the code area below when applicable. Upload it to your device, start a serial terminal and you will get a > prompt where you can enter commands and receive a response like a terminal 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 programatically by calling setI2CAddress. The device will permanently change the address 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 recover it.

If two devices are used, one will require the address to be changed.

Class Initialization

  • C++: There are several class constructors available.
    • Default: uFire_EC ec;
    • Custom I2C Address: uFire_EC ec(0x4C);
    • ESP32 I2C Pins (SDA, SCL): uFire_EC ec(19, 23);
    • ESP32 I2C Pins and I2C Address: uFire_EC ec(19, 23, 0x4C);
  • Raspberry Pi Python: You can optionally pass the I2C address and/or bus.
    • Default: ec = uFire_EC()
    • Custom I2C Address: ec = uFire_EC(address=0x4C)
    • I2C System Bus: ec = uFire_EC(i2c_bus=3)
    • I2C System Bus Address: ec = uFire_EC(i2c_bus=3, address=0x4C)
  • MicroPython: Always pass the I2C pins, optionally pass a custom I2C address.
    • Default: ec = uFire_EC(sda=19, scl=23)
    • Custom I2C Address: ec = uFire_EC(sda=19, scl=23, address=0x4C)
  • Rust: Always pass the I2C system bus, and I2C address.
    • Default: let mut ec = EcProbe::new("/dev/i2c-3", 0x3c);


There are two options to calibrate the device; single or dual point.

Single Point

  • only requires one point to calibrate
  • less accurate
  • accuracy decreases as the difference between the measured solution and calibration point increase

Steps to calibrate

  1. Select a calibration solution in the middle of the expected range of measurements.
  2. reset the device to remove any previous calibration data.
  3. Submerge the probe in the calibration solution. Make sure there are no air bubbles on the probe's surface.
  4. Observe the values and wait for them to stabilize.
  5. Call calibrateProbe to perform the calibration.

After, you can use getCalibrateOffset to get the calibration data the device generated. As long as there is calibration data, it will be automatically used for subsequent measurements. setCalibrateOffset can be used to set the offset value to any provided value.

Dual Point

  • accurate over the entire measurement range of the device
  • requires two points to calibrate

Steps to calibrate

  1. Select a calibration solution near the low and high end of expected measurements.
  2. reset the device to remove any previous calibration data.
  3. Submerge the probe in the low calibration solution. Make sure there are no air bubbles on the probe's surface.
  4. Observe the values and wait for them to stabilize.
  5. Call calibrateProbeLow to perform the calibration.
  6. Repeat steps 3 - 4 for the high solution, then calling calibrateProbeHigh.
  7. Enable dual point calibration by calling useDualPoint.

After calibration, the calibration data as a pair of low/high points accessible by getCalibrateHighReference/getCalibrateHighReference and getCalibrateLowReading/getCalibrateHighReading.

All four points can be set directly, without the device measuring anything, by calling setDualPointCalibration.

Calibrating with temperature compensation

If you plan to use temperature compensation, make sure it is disabled (useTemperatureCompensation) prior to calibrating your device. Ensure you use the appropriate calibration value for the current temperature. For example, if you are calibrating with a 1.413 mS/cm solution and it is currently 20 C, use the value of 1.278 mS/cm. The value should be on your calibration solution. Then enable temperature compensation and ensure the temperature sensor is connected. Your measurements will now be adjusted to the value set by setTempConstant.

Avoid using a small plastic container to calibrate your probe. Ideally, a large amount of calibration solution will be poured into a glass jar to calibrate. The solution shouldn't be reused.


Once the probe has been calibrated, a reading can be taken.

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

If temperature compensation is enabled:

Temperature Compensation

Conductivity naturally changes with temperature. When the temperature drops, the solution condenses, molecules move closer together and allow a charge to more easily pass. The opposite occurs when the temperature increases.

Temperature compensation is used to calculate what the measured conductivity would be if it were measured at a different temperature. This is just an approximation and is determined by a standard formula which is implemented in the device's firmware.

To set the temperature used for compensation, call setTempConstant and pass the temperature to use; the temperature probe must be connected for this measurement. Calling measureEC will also call measureTemp before taking a measurement if usingTemperatureCompensation has been set to true.

The values passed to setTempConstant and useTemperatureCompensation will be saved and used automatically.

Using Temperature from Another Device

If there is an alternate source of temperature information, it can be provided to the device by calling setTemp. It should be called before each call to measureEC. Be sure to call measureEC(false). Passing false indicates not to take a temperature reading from the on-board sensor.

Measurement Time

Each EC measurement takes 250ms. A temperature measurement takes 750ms.

More Help

If you have any questions, find a bug, or have any suggestions, go to this project's GitHub page and submit an Issue or Pull Request. Or you can send an email to [email protected].