A capacitive soil humidity sensor: Part 3

Initially I imagined the probe as a sleek device with a wire coming out that one would stick in the soil, if necessary completely. This would mean that the circuit pcb would be an integral part of the PCB that  formed the plates, but as always, things go different in practice. For one thing , the piece of PCB I intended to use just wasnt long enough. Second, my PCB etching possibilites are temporarily impeded. Third, I wanted to add an LDR, meaning that I had to have some sort of  translucent cover.

I constructed the circuit on a small piece of stripboard:

IMG_20160225_163125 i2cprobe

With regard to the LDR, sure that is nonsense. If I want to add an LDR I  could just as well add it to my base station. But I am not adding it because I have to, I am adding it because I can and because I wanted to get some experience in programming an I2C slave. An LDR might not be so usefull but in future I may want another sensor, e.g.a sensor that reads if there is really water flowing from the irrigation tube.

Also I had decided on putting the capacitor plates back to back, but as I did not have double sided PCB I just used two pieces glued and  soldered(!) together.
So My BOM was rather simple:

  • 2 equal size pieces of PCB  Size depends on what you have, but do not make them too small. I used 12×3 cm.
  • 1 piece of 0.5-1.0 cm plastic for a baseboard. I used an old cutting board.
  • 1 clear/translucent cover, I used the lid of a whipped cream spray can.
  • 1 piece of  thin 4 wire cable, length depending on your need

pcbprobe2I glued the two piecesof pcb back to back. drilled a hole in all 4 corners and through soldered a piece of wire through each hole, thus anchoring the plates together. I removed some copper around the solder so it would become an island isolated from the rest of the plate. (See picture).

Soldering the plates together in the corners may not be necessary if you decide to electrically isolate them from the soil with e.g. shrink tube.

Eventually I will place an NTC on the plate as well after it is covered with Shrink tube. Esthetically it might be better to put the NTC under the shrink tube, but that could create an air pocket.

 

baseplateI made the base plate from a o.5 cm thick piece of soft plastic. Cut out a round shape  with a 5.5 cm diameter to fit the base of my  clear dome and  made a slit 3 x0.3 cm into that in which the PCB  fits snug. made a round hole for the connecting cable.

IMG_20160226_133820

 

 

I soldered two wires on the top of the PCB, one on each side. Soldered wires on an NTC, insulated those, attached the NTC to the bottom of the PCB with the wires leading to the top and then covered the PCB with shrink wrap.

probeEventually the probe looks like this (picture)probe3

 

A capacitive soil humidity sensor: Part 2

capacitive74HC14_attinyIn a previous article I presented a simple way to read a capacitive moisture sensor with a simple RC generator.
In this post I will present a sensor with some added functionality that can be read through I2C. The circuit (figure) doesnt need much explanation: the RC generator we saw in the previous article and the two variable resistors in a voltage divide read by analogue inputs. The Attiny 45 (NOTE, it is NOT an Attiny25) is the heart, or rather the brains of the sensor. As the Attiny  functions as an I2C slave we will need the TinyWireS library. The library comes with some examples and one example was quite easy to rework to what I needed. The code is as follows.

#define I2C_SLAVE_ADDRES 0x4
#include <TinyWireS.h>//https://github.com/rambo/TinyWire
#ifndef TWI_RX_BUFFER_SIZE
#define TWI_RX_BUFFER_SIZE ( 16 )
#endif

volatile uint8_t i2c_regs[] =
{
	0x00,
	0x00,
	0x00,
	0x00,
};
// Tracks the current register pointer position
volatile byte reg_position;
const byte reg_size = sizeof(i2c_regs);

byte LDRvalue;
byte NTCvalue;
int MoistPulse;

/**
 * This is called for each read request we receive, never put more than one byte of data (with TinyWireS.send) to the
 * send-buffer when using this callback
 */
void requestEvent()
{
	TinyWireS.send(i2c_regs[reg_position]);
	// Increment the reg position on each read, and loop back to zero
	reg_position++;
	if (reg_position >= reg_size)
	{
		reg_position = 0;
	}
}

/**
 * The I2C data received -handler
 *
 * This needs to complete before the next incoming transaction (start, data, restart/stop) on the bus does
 * so be quick, set flags for long running tasks to be called from the mainloop instead of running them directly,
 */
void receiveEvent(uint8_t howMany)
{
	if (howMany < 1) { // Sanity-check return; } if (howMany > TWI_RX_BUFFER_SIZE)
	{
		// Also insane number
		return;
	}

	reg_position = TinyWireS.receive();
	howMany--;
	if (!howMany)
	{
		// This write was only to set the buffer for next read
		return;
	}
	while(howMany--)
	{
		i2c_regs[reg_position] = TinyWireS.receive();
		reg_position++;
		if (reg_position >= reg_size)
		{
			reg_position = 0;
		}
	}
}


void setup()
{
	pinMode(1, INPUT);
	TinyWireS.begin(I2C_SLAVE_ADDRESS);
	TinyWireS.onReceive(receiveEvent);
	TinyWireS.onRequest(requestEvent);
}

void loop()
{
	readSensors();
	/**
	 * This is the only way we can detect stop condition (http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=984716&sid=82e9dc7299a8243b86cf7969dd41b5b5#984716)
	 * it needs to be called in a very tight loop in order not to miss any (REMINDER: Do *not* use delay() anywhere, use tws_delay() instead).
	 * It will call the function registered via TinyWireS.onReceive(); if there is data in the buffer on stop.
	 */
	TinyWireS_stop_check();
}

void readSensors()
{
	LDRvalue = (analogRead(A2)) >>2; // max value is 1023/4=255 is 1 byte (FF) physical pin3=PB4=A2
        i2c_regs[0] = LDRvalue;
	NTCvalue = (analogRead(A3)) >>2; // max value is 1023/4=255 is 1 byte (FF) pin2=PB3=A3
	i2c_regs[1] = NTCvalue;
	//pulsepin= pin6=PB1
	MoistPulse = pulseIn(1, HIGH);
	i2c_regs[2] = highByte(MoistPulse); // or use = MoistPulse >> 8;
	i2c_regs[3] = lowByte(MoistPulse); // or use = MoistPulse & 0xFF;
}

I have called the pulseIn function with an integer rather than with a long. This means that you will have to choose a value for R3 that gives a reasonable range for the type of soil that you use. A range of say 0-200 uS is very reasonable. Once you have done that you can also add a timeout to the pulseIn function. This should be abt 2 times the pulselength you expect (but depends on the duty cycle). With regard to the two variable resistors, they are in pull up so their value is Rntc = Rseries/((1023/ADC) – 1));  For the NTC this could be substituted in a Steinhart Hart approximation

The code to call the values (and that is loaded onto the master arduino) is even simpler:

#include <Wire.h>
void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {
  for (byte i=0;i<4;++i){
  Serial.print("0x");
  Serial.println(readRegister(i),HEX); // print the character
}
Serial.println(" ");
    delay(1000);
  }

uint8_t readRegister(uint8_t regaddress)
{
	Wire.beginTransmission(4);
	Wire.write((byte)regaddress);
	Wire.endTransmission();
	Wire.requestFrom(4, 1);
	return Wire.read();


 delay(500);
}

This code only prints out the values, you still need to combine the LSB and MSB from the cycle time and e.g. switch a pump based on the value. You could do that with this function:

int combine (byte lsbv, byte msbv)
{
	int value = msbv << 8;
	value = value | lsbv;
	//value= msbv<<8 | lsvb;//if you want to do it in one go
	return value;
}

Don’ t forget that the I2C lines need pull-up resistors of 4.7-10k.

In a next article I will present the construction of the probe itself.

A capacitive soil humidity sensor: Part 1

An automated plant/garden watering system is a popular application for  the Arduino and other microcontrollers. The humidity sensor that is often used is  often a resistance meter: 2 probes in the soil form a resistor and as part of a voltage divider that gives info on the amount of water in the soil.
The major shortcoming of these sensors is that since there is a current flowing, the probe is sensitive to electrolytic corrosion. Another shortcoming is that it doesnt really give information on the soil humidity, but more on the  ion concentration in the soil. Remember: pure water is a bad conductor, It are the ions in the water that make it a conductor.

To get around the corrosion, people have started to feed the probe with AC rather than DC current, but at best the circuits for this supply a pulsating DC current. Another  possibility to minimize corrosion is to  switch off  the current to the probe and only switch it on when a measurement is taken, say every 5  minutes.

All these measures help but even then, just due to the contact with moist soil, the metal on the probe will corrode and weather.
Another method is to encase the probes in plaster. Some people swear by it but I  think it is bothersome. Also the plaster encased probe has a lag as it will retain water  for some time after the  soil is already dry and it will still be dry for some time after the soil is already moist.

Capacitive measuring is a way to avoid these problems. With capacitive measurement the ‘plates’ of the capacitor can be electrically isolated from the soil and the soil in fact forms the dielectrum of the capacitor. Water makes a good dielectrum, whereas ions dont. Capacitive measuring therefore will have a better correlation with the actual amount of water in the soil than resistive measuring and, as said, ideally there will not be any corrosion.

Although the Arduino can measure capacity, it needs 3 pins for that. Also, it isnt really practical to have a long wire as part of your soil probe capacitor go to your Arduino as the wire and whether it is  straight or curly or in a loop, will act as a parasitic capacity and affect the measurement.
Putting your Arduino right onto your plantbed might also not be the best solution.

capacitive74hc14
RC Oscillator With 74HCT14

A better way to do this is to introduce an RC oscillator in which the soil functions as a capacitor and the Arduino measures the frequency that comes out of the oscillator. More water  will increase the value of the ‘capacitor’ which usually will lower the frequency (or increase the cycle time) and that can be measured with the arduino.

An easy RC oscillator can be seen in the figure to the right. A 74HC14 or 74HCT14 Inverter Schmitt-trigger is all you need. For the HC version the Frequency is:
f=1/T=1/(0.8*R*C)
for the HCT version it is:
f=1/T=1/(0.67*R*C)

But in fact the frequency isn’t really that important because we are not making a frequency meter. We are only interested in changes of the frequency that relate to  dry, moist or wet soil. The workings of this type of Oscillator are explained here. and here. The theory behind the frequency calculation is explained here.

leydenjarI tested my set-up initially with a sort of Leyden Jar as a capacitor. Simply put I taped two pieces of alufoil on the outside of a glass jar, attached wires to it and tried if I could measure a change between an empty and a full jar. I started out using a 100k resistor, but only got a decent range after using a 2M2 resistor: an empty jar gave me 1uSec (maybe less, but I guess that was my lower limit) a half full jar around 50uS and a full jar about 100uS (measured with PulseIn). From using a known capacitor I knew I had to use the formula f=1/T=1/(0.67*R*C), but as said, neither the frequency or the actual capacitor value are of much importance as we are just looking for  changes.

Nevertheless the capacity of this jar (when full) could be calculated from
100*10⁻⁶=0.67*2.2*10⁶*C
C=(100*10⁻⁶)/(1.47*10⁶)
C=68*10⁻¹²
C=68pF
Now ofcourse this isn’t entirely correct as with PulseIn We only measured half a cycle, so in fact the Capacity is more likely to be 136pF, provided the dutycycle is 50% (some sources say it is 50% others say it is 33% with the space  2*mark).

byte pin = 8;
unsigned long duration;

void setup()
{
  pinMode(pin, INPUT);
  Serial.begin(115200);
}

void loop()
{
  duration = pulseIn(pin, HIGH);
  Serial.print("Time ");
  Serial.print(duration);
  Serial.print(" usec ");
  Serial.print(500/duration);
  Serial.print(" kHz ");
  Serial.print(500000/duration);
  Serial.println(" Hz");
  delay(500);
}

Then it was time to do some garden field tests. There are several ways to construct a probe: two pieces of PCB at a distance from eachother, One piece of PCB with two plates etched onto it, or a piece of double sided PCB. With regerd to the latter, we tend to think of a capacitor as two plates with a dielectrum in between, but in fact it can also be two  plates with a dielectrum around it.

capacitive probe-textMy garden field test, using two insulated plates of PCB (picture) kinda gave similar results as with the Leyden Jar so I knew I had a decent design.

Yet, two plates is not really very handy: you have a loose wire connecting the plates and as that forms part of the capacitor it introduces stray capacitance, so I decided to go for either a double sided PCB, or a single sided PCB with two plates etched onto it and then place the oscillator on that PCB as well. Frankly, that idea isnt new as many of the (semi)DIY (e.g. the ‘Chirp’) or commercial capacitive probes follow that design as well.

Now it is always good  that before you make a PCB to think through what you want. As I was using only 1/6th of the 74HCT14, I was thinking maybe I could use the other gates as RC oscillators as well  for e.g. an LDR or NTC. Now ofcourse I immediately dismissed that again as I could just as well use an analog read of those and I would have to run extra cables that would all carry a frequency signal, no doubt those would interfere, but it got me thinking. The ‘Chirp’ has a big brother that has an I2C interface, if I would add that, I could add some other sensors (light, temperature)  to my probe. With just the capacitive sensor I would have to run 3 wires (+Vcc, signal, ground). With I2C 4 wires (+Vcc, SDA, SCL, Ground) would be enough for a number of sensors on my probe. Also, as I have various plantbeds, having a number of I2C probes wouldnt require  more inputs on my Arduino.

Obviously, adding I2C meant adding a microcontroller, preferably a cheap one, which easily brings one to the attiny25/45/85 series. If you have good eye sight and a steady hand, you could consider the soic version that is available from 30-70 cts. If you go for a DIP version, that is around 0.9-1 euro. Considering one can get an entire Pro Mini for about 1.20, obviously that is a narrow gap and I (or you) could consider giving each plantbed a totally dedicated  Pro Mini microprocessor that could take care of the irrigation as well.
Having said that, in the past, I have made a self contained system for plantbeds, just using 1 Opamp, so maybe I should just consider this I2C excercise a ‘learning excercise’

capacitive74HC14_attinyI came up with the following circuit. It is easy to recognise the RC oscillator. The cycle signal is fed to PB1 whereas the analog inputs on PB3 and PB4
A3= PB3=physical pin2;A2=PB4=physical pin3.
Pin PB2 is used as SCL signal and PB0 as SDA signal for the I2C interface.

If for any reason you do not have a 74HCx14 in your toolbox, there are other ways of constructing an RC generator. E.g. a 555 or a 74HC00. Even a single transistor could be used. With this type of oscillator the dutycycle approaches 50%
rc-osc

555-oscillator-probe

In a next article I will discuss how to implement the I2C software.