So we’ve reinvented the EC meter, or CF meter as we call it around here. When gardening hydroponically, you usually use an “EC Truncheon” to measure the concentration of nutrient salts in the hydroponic fluid. If you just pass a current through the liquid and see how much passes, you start to change the chemistry and your reading goes inaccurate very quickly. So these devices measure the conductivity in short pluses, swapping direction each time they make a measurement. The electronics, we thought, was an unnecessarily complex collection of op amps, comparators and other fancy bits. So we designed a way of making an Arduino do the hard work so you can build one into your own automated systems.

Start by extracting the carbon rod from the centre of an AA zinc/carbon battery. Clean it, clean everything else, then boil the rod in water for a few minutes to get the last of the battery compounds out of it. Break into two pieces, notch the ends with a file, wrap and tightly twist a wire into each notch, then embed the whole thing in your favourite waterproof plastic leaving a couple of millimetres of each rod sticking out about 10mm apart. That’s your sensor.IMAG0021

The wiring is easy: One electrode is wired to A0 on the Arduino, the other to A1. A 2K2 resistor (red, red, red bands) is used to connect D2 to A0, and another connects D3 and A1. That’s it. Load the code below into your Arduino and you’re off.

Calibration is a bit tricky, and unless you have EC buffer solutions handy, you’ll want to borrow a real EC meter. We use CF units rather than EC units because they’re largish integers which both the Arduino and humans find easier to relate to. Run the code below and call up the Arduino serial monitor (under “Tools” in the IDE) to see the sensor values your setup is measuring. Dunk sensor in pure water to get the ‘baseValue’, and edit it in. The ‘cfTable’ consists of a series of [sensor reading,CF reading] pairs. Mix up slightly salty water, measure it’s CF value with the real EC meter, and then test it with your homemade probe. Use a range of CF values as we have, and if you expand or reduce the table do remember to alter the TABLE_ENTRIES count.

Feel free to hack the code to return more convenient data for your automation system, or use A0-A3 for the pins to simplify the wiring. The important thing is to remember to call the neutral() routine as soon as the measurements are done to turn off the sensor and prevent chemistry happening. It’s all GPL’d. Enjoy.

/*
  CF Meter (or EC meter) for aqueous nutrient measurement.
 
 Reads an alternate analogue pins, reversing polarity on each
 read so that electrolytic effects even out.
 
 The circuit:
 * A0 connected to electrode Alpha
 * D2 Connected to electrode Alpha via a 10K resistor
 * A1 connected to electrode Beta
 * D3 connected to electrode Beta via a 10K resistor
 
 created 17th Feb 2015
 (C) Vik Olliver
 
 This code is licenced under the GPLv3
 
 */

// These constants won't change.  They're used to give names
// to the pins used:
const int alphaAnalogue = A0;
const int betaAnalogue = A1;
const int alphaOut = 2;
const int betaOut = 3;

// These bits you change to configure the sensor
const int baseValue=340;  // Value of pure water
const int cfTable[]={
340,0,
595,5,
749,10,
840,15,
860,20,
923,34};
#define TABLE_ENTRIES  6

int alphaSensor;
int betaSensor;

// Stop the outputs from doing anything electrolytic.
void neutral() {
  pinMode(alphaAnalogue,INPUT);
  pinMode(betaAnalogue,INPUT);
  pinMode(alphaOut,OUTPUT);
  pinMode(betaOut,OUTPUT);
  digitalWrite(alphaOut,LOW);
  digitalWrite(betaOut,LOW);
}

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);
  neutral();
}

// Use the table to average out the sensor data and calculate the corresponding CF.
// This may not be the best way, but it is the most legible!

int calcCf(int sens) {
  int i;
  int sensDiff;
  int sensDelta;
  int cfDiff;
  // Spot very low values
  if (sens <= cfTable[0]) return cfTable[1];
  // Spot very high values
  if (sens >= cfTable[(TABLE_ENTRIES*2)-2]) return cfTable[(TABLE_ENTRIES*2)-1];
  // OK, we're in range. Trot through the table until we overtake our sensor reading
  i=1;
  while (cfTable[(i*2)]<sens) i++;
  // Find the difference between the last 2 table entries;
  sensDiff=cfTable[(i*2)]-cfTable[(i*2)-2];
  // Find the difference between the last 2 CF readings;
  cfDiff=cfTable[(i*2)+1]-cfTable[(i*2)-1];
  // Now the amount our sensor passed the previous table entry by
  sensDelta=sens-cfTable[(i*2)-2];
  // Now we can work out a ratio,and calculate the corresponding CF
  return cfTable[(i*2)-1]+((cfDiff*sensDelta+1)/sensDiff);
}

void loop() {
  int cf;
  
  // Reset for the analogue alpha pin
  pinMode(alphaAnalogue,INPUT);
  pinMode(betaAnalogue,OUTPUT);
  digitalWrite(alphaOut,LOW);
  digitalWrite(betaAnalogue,HIGH);
  digitalWrite(betaOut,HIGH);
  delay(2);  // Wait for the ADC to settle.
  alphaSensor = analogRead(alphaAnalogue);
  
  // Reset for the analogue beta pin
  pinMode(betaAnalogue,INPUT);
  pinMode(alphaAnalogue,OUTPUT);
  digitalWrite(betaOut,LOW);
  digitalWrite(alphaAnalogue,HIGH);
  digitalWrite(alphaOut,HIGH);
  delay(2);  // Wait for the ADC to settle.
  betaSensor = analogRead(betaAnalogue);

  // Now stop buggering around.
  neutral();

  // Calculate the CF from the average sensor value
  cf=calcCf((alphaSensor+betaSensor)/2);

  // print the results to the serial monitor:
  Serial.print("Sensor = " );                       
  Serial.print((alphaSensor+betaSensor)/2);
  if (alphaSensor>baseValue) {
    Serial.print("\tCF = ");
    Serial.print(cf);
  }
  Serial.println("");  

}

« »