Conductivity via Analog Electrical Resistance

# Conductivity via Analog Electrical Resistance module

Classes for measuring conductivity using a simple analog voltage divider.

## Introduction

This is for a very basic conductivity circuit built with a single resistor and an old power cord. DC power is briefly supplied across the power cord causing the water to act as one of the resistors on a voltage divider. Knowing the voltage of the other resistor in the divider, we can calculate to resistance from the water (and then its electrical conductivity) based on the drop in volage across the divider.

For this to work, the power across the circuit MUST be turned off between readings. If the power to the circuit is left on the water will become polarized and the values will not be valid. The water temperature (if used) must be suplied separately for a calculation.

## The Circuit

One pole of the power cord wire is connected to the ground of the main logger board. The other pole is connected to the sensor power supply via a resistor of known resistance (R1) and then to an analog pin to measure the voltage.

So the circuit is:

```Vin (sensor power) --- R1 --- power cord  --- Vout
|
|
water between prongs (Rwater)
|
|
ground```

The above diagram and the calculations assume the reistance of the analog pins themselves on the Arduino is negligible.

## Calculating the Conductivity

First, we need to convert the bit reading of the ADC into volts based on the range of the ADC (1 bit more than the resolution):

`meas_voltage = (analog_ref_voltage * raw_adc_bits) / ANALOG_EC_ADC_RANGE`

Assuming the voltage of the ADC reference is the same as that used to power the EC resistor circuit we can replace the reference voltage with the sensor power voltage:

`meas_voltage = (sensor_power_voltage * raw_adc_bits) / ANALOG_EC_ADC_RANGE`

Now we can calculate the resistance of the water, knowing the resistance of the resistor we put in the circuit and the voltage drop:

`Rwater_ohms = (meas_voltage * Rseries_ohms) / (sensor_power_voltage - meas_voltage)`

Combining the above equations and doing some rearranging, we get:

`Rwater_ohms = Rseries_ohms / ((ANALOG_EC_ADC_RANGE / raw_adc_bits) - 1)`

The conductivity is then the inverse of the resistance - multiplied by a measured cell constant and a 10^6 conversion to µS/cm.

`water_conductivity = 1000000 / (Rwater_ohms * sensorEC_Konst)`

The real cell constant will vary based on the size of the "cell" - that is, the size of the plug on the power cord. You can calculate the cell constant for each power cord sensor you use following the calibration program.

For one AC Power Cord 12t with male IEC 320-C8 connector the cell constant was 2.88.

## Build flags

• `-D ANALOG_EC_ADC_RESOLUTION=##`
• `-D ANALOG_EC_ADC_REFERENCE_MODE=xxx`

## Sensor Constructor

### AnalogElecConductivity(int8_t powerPin, int8_t dataPin, float Rseries_ohms = RSERIES_OHMS_DEF, float sensorEC_Konst = SENSOREC_KONST_DEF, uint8_t measurementsToAverage = 1)

Construct a new AnalogElecConductivity object.

Parameters
powerPin The port pin providing power to the EC probe. Needs to be switched, and assumed to be same V as the dataPin's ADC.
dataPin

The processor ADC port pin to read the voltage from the EC probe. Not all processor pins can be used as analog pins. Those usable as analog pins generally are numbered with an "A" in front of the number

• ie, A1.
Rseries_ohms The resistance of the resistor series (R) in the line; optional with default value of 499.
sensorEC_Konst The cell constant for the sensing circuit; optional with default value of 2.88 - which is what has been measured for a typical standard sized lamp-type plug.
measurementsToAverage The number of measurements to average; optional with default value of 1.

## Example Code

The analog electrical conductivity sensor is used in the menu a la carte example.

```#include <sensors/AnalogElecConductivity.h>

const int8_t ECpwrPin   = A4;  // Power pin (-1 if unconnected)
const int8_t ECdataPin1 = A0;  // Data pin (must be an analog pin, ie A#)

// Create an Analog Electrical Conductivity sensor object
AnalogElecConductivity analogEC_phy(ECpwrPin, ECdataPin1);

// Create a conductivity variable pointer for the analog sensor
Variable* analogEc_cond = new AnalogElecConductivity_EC(
&analogEC_phy, "12345678-abcd-1234-ef00-1234567890ab");

// Create a calculated variable for the temperature compensated conductivity
// (that is, the specific conductance).  For this example, we will use the
// temperature measured by the Maxim DS18 saved as ds18Temp several sections
// above this.  You could use the temperature returned by any other water
// temperature sensor if desired.  **DO NOT** use your logger board temperature
// (ie, from the DS3231) to calculate specific conductance!
float calculateAnalogSpCond(void) {
float waterTemp       = ds18Temp->getValue();
float rawCond         = analogEc_cond->getValue();
float temperatureCoef = 0.019;
// ^^ Linearized temperature correction coefficient per degrees Celsius.
// The value of 0.019 comes from measurements reported here:
// Hayashi M. Temperature-electrical conductivity relation of water for
// environmental monitoring and geophysical data inversion. Environ Monit
// Assess. 2004 Aug-Sep;96(1-3):119-28.
// doi: 10.1023/b:emas.0000031719.83065.68. PMID: 15327152.
if (waterTemp != -9999 && rawCond != -9999) {
// make sure both inputs are good
spCond = rawCond / (1 + temperatureCoef * (waterTemp - 25.0));
}
return spCond;
}

// Properties of the calculated variable
// The number of digits after the decimal place
const uint8_t analogSpCondResolution = 0;
// This must be a value from http://vocabulary.odm2.org/variablename/
const char* analogSpCondName = "specificConductance";
// This must be a value from http://vocabulary.odm2.org/units/
const char* analogSpCondUnit = "microsiemenPerCentimeter";
// A short code for the variable
const char* analogSpCondCode = "anlgSpCond";
// The (optional) universallly unique identifier
const char* analogSpCondUUID = "12345678-abcd-1234-ef00-1234567890ab";

// Finally, Create the specific conductance variable and return a pointer to it
Variable* analogEc_spcond = new Variable(
calculateAnalogSpCond, analogSpCondResolution, analogSpCondName,
analogSpCondUnit, analogSpCondCode, analogSpCondUUID);```

## Classes

class AnalogElecConductivity
Class for the analog Electrical Conductivity monitor.
class AnalogElecConductivity_EC
The variable class used for electricalConductivity measured using an analog pin connected to electrodes submerged in the medium.

## Defines

Default resolution (in bits) of the voltage measurement.

The default for all boards is 10, use a build flag to change this, if necessary.

The maximum possible value of the ADC - one less than the resolution shifted up one bit.
The maximum possible range of the ADC - the resolution shifted up one bit.
The voltage reference mode for the processor's ADC.

For an AVR board, this must be one of:

• `DEFAULT`: the default built-in analog reference of 5 volts (on 5V Arduino boards) or 3.3 volts (on 3.3V Arduino boards)
• `INTERNAL`: a built-in reference, equal to 1.1 volts on the ATmega168 or ATmega328P and 2.56 volts on the ATmega32U4 and ATmega8 (not available on the Arduino Mega)
• `INTERNAL1V1`: a built-in 1.1V reference (Arduino Mega only)
• `INTERNAL2V56`: a built-in 2.56V reference (Arduino Mega only)
• `EXTERNAL`: the voltage applied to the AREF pin (0 to 5V only) is used as the reference.

If not set on an AVR board `DEFAULT` is used.

For the best accuracy, use an `EXTERNAL` reference with the AREF pin connected to the power supply for the EC sensor.

For a SAMD board, this must be one of:

• `AR_DEFAULT`: the default built-in analog reference of 3.3V
• `AR_INTERNAL`: a built-in 2.23V reference
• `AR_INTERNAL1V0`: a built-in 1.0V reference
• `AR_INTERNAL1V65`: a built-in 1.65V reference
• `AR_INTERNAL2V23`: a built-in 2.23V reference
• `AR_EXTERNAL`: the voltage applied to the AREF pin is used as the reference

If not set on an SAMD board `AR_DEFAULT` is used.

For the best accuracy, use an `EXTERNAL` reference with the AREF pin connected to the power supply for the EC sensor.

The voltage reference mode for the processor's ADC.

For an AVR board, this must be one of:

• `DEFAULT`: the default built-in analog reference of 5 volts (on 5V Arduino boards) or 3.3 volts (on 3.3V Arduino boards)
• `INTERNAL`: a built-in reference, equal to 1.1 volts on the ATmega168 or ATmega328P and 2.56 volts on the ATmega32U4 and ATmega8 (not available on the Arduino Mega)
• `INTERNAL1V1`: a built-in 1.1V reference (Arduino Mega only)
• `INTERNAL2V56`: a built-in 2.56V reference (Arduino Mega only)
• `EXTERNAL`: the voltage applied to the AREF pin (0 to 5V only) is used as the reference.

If not set on an AVR board `DEFAULT` is used.

For the best accuracy, use an `EXTERNAL` reference with the AREF pin connected to the power supply for the EC sensor.

For a SAMD board, this must be one of:

• `AR_DEFAULT`: the default built-in analog reference of 3.3V
• `AR_INTERNAL`: a built-in 2.23V reference
• `AR_INTERNAL1V0`: a built-in 1.0V reference
• `AR_INTERNAL1V65`: a built-in 1.65V reference
• `AR_INTERNAL2V23`: a built-in 2.23V reference
• `AR_EXTERNAL`: the voltage applied to the AREF pin is used as the reference

If not set on an SAMD board `AR_DEFAULT` is used.

For the best accuracy, use an `EXTERNAL` reference with the AREF pin connected to the power supply for the EC sensor.

#define RSERIES_OHMS_DEF = 499
The default resistance (in ohms) of the measuring resistor. This should not be less than 300 ohms when measuring EC in water.
#define SENSOREC_KONST_DEF = 2.88
Cell Constant For EC Measurements.

This should be measured following the calibration example on https://hackaday.io/project/7008-fly-wars-a-hackers-solution-to-world-hunger/log/24646-three-dollar-ec-ppm-meter-arduino.

Mine was around 2.9 with plugs being a standard size they should all be around the same. If you get bad readings you can use the calibration script and fluid to get a better estimate for K.

## Sensor Timing

The timing for analog conductivity via resistance.

#define ANALOGELECCONDUCTIVITY_WARM_UP_TIME_MS = 2
Sensor::_warmUpTime_ms; giving 2ms for warm-up.
Sensor::_stabilizationTime_ms; we give just a tiny delay for stabilization.
Sensor::_measurementTime_ms; we assume the analog voltage is measured instantly.

It's not really quite instantly, but it is very fast and the time to measure is included in the read function. On ATmega based boards (UNO, Nano, Mini, Mega), it takes about 100 microseconds (0.0001 s) to read an analog input, so the maximum reading rate is about 10,000 times a second.

## Electrical Conductance

The humidity variable from an AOSong DHT

• Range: low 100's when open air, for short circuit: a high number
• Accuracy: needs determining for each combination of ADC. ADC_REF, and series R. its designed as a very simple relative EC measurement

### AnalogElecConductivity_EC(AnalogElecConductivity* parentSense, const char* uuid = "", const char* varCode = ANALOGELECCONDUCTIVITY_EC_DEFAULT_CODE)

Construct a new AnalogElecConductivity_EC object.

Parameters
parentSense The parent AnalogElecConductivity providing the result values.
uuid A universally unique identifier (UUID or GUID) for the variable; optional with the default value of an empty string.
varCode A short code to help identify the variable in files; optional with a default value of "anlgEc".

#define ANALOGELECCONDUCTIVITY_EC_RESOLUTION = 1
Decimals places in string representation; EC should have 1.

Range of 0-3V3 with 10bit ADC - resolution of 0.003 = 3 µS/cm.

#define ANALOGELECCONDUCTIVITY_EC_VAR_NUM = 0
Sensor vensor variable number; EC is stored in sensorValues.
#define ANALOGELECCONDUCTIVITY_EC_VAR_NAME = "electricalConductivity"
Variable name in ODM2 controlled vocabulary; "electricalConductivity".
#define ANALOGELECCONDUCTIVITY_EC_UNIT_NAME = "microsiemenPerCentimeter"
Variable unit name in ODM2 controlled vocabulary; "microsiemenPerCentimeter" (µS/cm)
#define ANALOGELECCONDUCTIVITY_EC_DEFAULT_CODE = "anlgEc"
Default variable short code; "anlgEc".