Over the recent bank holiday weekend we decided to plant a few vegetables in the garden. Nothing too radical, a few Onions, Peppers, Chilli and re-plant our little strawberry plant. We invested in one of those small polythene tunnels too, so help give them a bit of a boost start and see if we can get the plants happy despite the miserable cold weather we've been having. To this end I finally thought up a use for my Arduino I've had kicking around. So I started to think about a soil moisture and poly-tunnel temperature sensor type array that would feed back to the Arduino and tell us when it wanted watering, and also what the highs and lows are for temperature.
So I set about making a soil moisture sensor. My basic background in electronics helped and I knew the simplest method is to use a resistance type technique, so provide +DC to a probe, and them measure the resistance between that and a second probe. This should be easy for the Arudino as it has a few analogue inputs that I can use. The second input was temperature, so I used a temperature sensor I'd previously bought from oomlout.co.uk (TMP36) which has a nice range of -40oC up to 125oC so perfect for this. It's supplied in a standard semiconductor package so first I had to make it a little more weatherproof. Soldering up the 3 wires to a long length of cable (burglar alarm cable, which is my fave cable for general purpose as its 6-core and pretty reasonable in terms of weathering and loss of signal). I then coated each of the exposed wires with a bit of heatgun glue, followed by electricians tape round each leg, so each were electrically insulated and also hopefully sufficiently weather proofed. (Not sure how well that would survive totally exposed to the elements, but as this was going in the poly-tunnel I thought it would be fine).
Next was to build the soil moisture sensor. There are a few different ones on the net, so I went with one that seemed a common way of doing it. Set two probes (in my case 2 brass screws) in a piece of electrically non-conducting packing foam. So this lets the screws push down into the earth whilst keeping the rest of the screws insulated. At the top I then wire wrapped the alarm wire around the screws and added a few blobs of solder for good measure. A bit more electricians tape finished them off.
Here is the sensor that I came up with. You can see the TMP36 semiconductor sticking out the top, and the screw probes out the bottom.
After the probe was sorted, I ran it round outside into the poly-tunnel:
So the final part was the circuit into the Arduino, and also the code to make it function. The breadboard I had already connected to my Arduino had the LCD attached so I left that to give constant output on the Temperature and soil moisture, so all I had to add was the connections out to the remote probes. I found an interesting issue, at first the soil moisture values varied wildly, even when the probe wasn't in the soil, until I realised that this was because nothing was 'pulling' the input to ground, adding a quick resistor into the setup so that it pulled the input to 0 when it didn't have any other value sorted that out and I got values depending on the resistance being presented. At first I'd connected the soil moisture probe to +5ve and the other probe to my analogue input. One problem I'd found reported quite frequently online was about electrolysis of the metal probes (where electricity passing through them caused corrosion to take place). To counter this I moved the moisture probe to an output pin, and so I'll only switch on current to the probe when I wish to measure it, hopefully reducing the amount of electrolysis taking place.
A few iterations of code were required to get the reading showing anything sensible, and de-bouncing the inputs was essential. I found 4 iterations of the sensor read loop followed by an average calculation stopped a lot of the large fluctuations taking place and gave me what appear to be stable readings, and when compared with the temperature read-out from the car, the outdoor temperature looked around the right values. Soil moisture though is still a bit of an issue as I need to self-calibrate the sensor by checking the soil and deciding when it gets too dry to set that as the lower threshold.
I'll post the final code up here shortly as I'm still fine-tuning it, and also hopefully connecting my Arduino up so I can access the readings remotely and graph them using my rrd graphing system I use for other things around the house (heating, electricity, etc).
Here are a few images of the Arduino in its plastic (indoor) enclosure in operation. The Green LED lights up when the soil moisture drops below the triggered limit, and the Red LED lights when the readings are being taken. Debugging is also output via the serial port.
Aruino code + schematics to follow!
Update 10/May/2012 - Here is the current Arduino code I've been using, sorry about the messy code!
<pre>
// AndyB 7-may-2012 Outdoor temperature and soil moisture circuit
// ---------------------------------------------------------------
// include the library code:
#include <LiquidCrystal.h>
#include <EEPROM.h>
#include <Time.h>
#include <TimeAlarms.h>
// PINS
// 2 3 4 5 11 12 - LCD
// 9 = watering LED
// 10 = soil power pin
// 13 = Data collection LED
// 8 = LCD display light
// 6 = LCD display button light up
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int sensorPin = A0; // Temperature sensor
int soilMoisturePin = A1; // soil moisture
int sensorValue1 = 0;
int addr = 0;
int SoilValue1 = 0;
float temperatureC = 0;
const int buttonPin = 7; // the number of the pushbutton pin
int buttonState = 0; // variable for reading the pushbutton status
const int ledPin = 13; // the number of the LED pin
const int waterPin = 9; // the LED for watering
const int soilPowerPin = 10; // Pin to power up the soil moisture sensor
const int lcdPowerPin = 8; // Pin to light up the LCD
const int lcdButtonPin = 6; // Switch to light up the LCD for a few loops
int lcdlightloop=0;
int logloop = 0;
int debug = 0; // Debug setting
void setup() {
Serial.begin(9600);
if (debug == 1) {Serial.println("Program initialised");};
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT);
pinMode(waterPin, OUTPUT);
pinMode(soilPowerPin, OUTPUT);
pinMode(lcdPowerPin, OUTPUT);
pinMode(lcdButtonPin, INPUT);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
digitalWrite(lcdPowerPin, HIGH);
// Print a message to the LCD.
// lcd.print(" oC");
// write a 0 to all 512 bytes of the EEPROM
// for (int i = 0; i < 512; i++) {
// EEPROM.write(i, 0);
// };
// do not reset the EEprom, find the last value and set the pointer, avoids power resets loosing old data
if (debug == 1) {Serial.println("reading EEPROM");};
for (int i = 0; i < 512; i++) {
byte tmp_val = EEPROM.read(i);
Serial.print("EEPROM ");
Serial.print(i);
Serial.print(": ");
Serial.println(tmp_val, DEC);
int tmp_val_int=tmp_val;
if (tmp_val_int == 0) {
addr=i;
if (debug == 1) {Serial.print("EEPROM empty at ");};
if (debug == 1) {Serial.println(i);};
break;
};
};
digitalWrite(soilPowerPin, LOW);
// sensorValue1 = analogRead(sensorPin);
// float voltage = sensorValue1 * 5.0;
// voltage /= 1024.0;
// temperatureC = (voltage - 0.5) * 100;
// SoilValue1 = analogRead(soilMoisturePin);
// Serial.println("Back from reading sensors");
// logdatatoeeprom();
if (debug == 1) {Serial.println("Setup completed");};
// Alarm.timerRepeat(9000, logdatatoeeprom); // every 15 minutes
// Alarm.timerRepeat(15, logdatatoeeprom);
digitalWrite(lcdPowerPin, LOW);
}
void loop() {
if (debug == 1) {Serial.println("Starting main loop");};
// check the button status
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
// Button pressed - Reset all of the EEPROM values
if (debug == 1) {Serial.println("Button pressed - resetting EEPROM");};
digitalWrite(ledPin, HIGH);
// write a 0 to all 512 bytes of the EEPROM
for (int i = 0; i < 512; i++) {
EEPROM.write(i, 0);
};
Serial.println("EEPROM ERASED");
addr=0;
logdatatoeeprom();
digitalWrite(ledPin, LOW);
};
// check for LCD display button
if (digitalRead(lcdButtonPin) == HIGH) {
// LCD button was pressed
lcdlightloop=1;
};
if (lcdlightloop > 0) {
lcdlightloop += 1;
digitalWrite(lcdPowerPin, HIGH);
};
if (lcdlightloop > 20) {
lcdlightloop=0;
digitalWrite(lcdPowerPin, LOW);
};
if (debug == 1) {Serial.println("Button checks complete");};
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
digitalWrite(ledPin, HIGH);
if (debug == 1) {Serial.println("Read temp sensor");};
// READ SENSORS HERE
temperatureC = readinput_temp();
SoilValue1 = readinput_soil();
if (SoilValue1 < 40) {
digitalWrite(waterPin, HIGH);
} else {
digitalWrite(waterPin, LOW);
};
if (debug == 1) {Serial.println("Display to LCD");};
Serial.println("Temp: ");
Serial.println(temperatureC);
Serial.println("Moisture: ");
Serial.println(SoilValue1);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("temp: ");
lcd.print(temperatureC,1);
lcd.print(" oC");
lcd.setCursor(0,1);
lcd.print("soil: ");
lcd.print(SoilValue1);
if (debug == 1) {Serial.println("Done writing LCD");};
// we write to the eeprom every 10 iterations
logloop=logloop+1;
if (debug == 1) {Serial.print("Testing for logloop ");};
if (debug == 1) {Serial.println(logloop);};
if (logloop > 9) {
logdatatoeeprom();
logloop = 0;
};
digitalWrite(ledPin, LOW);
if (debug == 1) {Serial.println("LED off");};
Alarm.delay(10000); // wait ten second between clock display
if (debug == 1) {Serial.println("Delay set");};
}
void logdatatoeeprom(){
digitalWrite(ledPin, HIGH);
lcd.setCursor(15,1);
lcd.print("E");
if (debug == 1) {Serial.println("logdatatoeeprom started");};
if (debug == 1) {Serial.print("writing to eeprom ");};
if (debug == 1) {Serial.print(addr);};
if (debug == 1) {Serial.print(" : ");};
if (debug == 1) {Serial.println(temperatureC);};
EEPROM.write(addr, temperatureC);
if (debug == 1) {Serial.println("back from writing to eeprom");};
digitalWrite(ledPin, LOW);
digitalWrite(ledPin, HIGH);
digitalWrite(ledPin, LOW);
// advance to the next address. there are 512 bytes in
// the EEPROM, so go back to 0 when we hit 512.
addr = addr + 1;
if (addr == 512) {
addr = 0;
};
if (debug == 1) {Serial.println("end logdatatoeeprom");};
}
float readinput_temp() {
// Read inputs and de-bounce, so read 3 times and take an average
if (debug == 1) {Serial.println("Readinputs (Temperature) function started");};
float temp1 = 0;
float temp2 = 0;
int soil1 = 0;
int soil2 = 0;
int lpcount=0;
while (lpcount<6) {
sensorValue1 = analogRead(sensorPin);
float voltage = sensorValue1 * 5.0;
voltage /= 1024.0;
temp2 = (voltage - 0.5) * 100;
temp1 += temp2;
if (debug == 1) {Serial.print("Temp loop :");};
if (debug == 1) {Serial.print(lpcount);};
if (debug == 1) {Serial.print(" : ");};
if (debug == 1) {Serial.println(temp2);};
lpcount=lpcount+1;
};
temperatureC = temp1/6;
if (debug == 1) {Serial.print("Temperature average: ");};
if (debug == 1) {Serial.println(temperatureC);};
if (debug == 1) {Serial.println("Exit Readinputs (Temperature)");};
return temperatureC;
};
int readinput_soil() {
if (debug == 1) {Serial.println("Readinputs (Soil) function started");};
// Switch power pin on for the soil sensor
digitalWrite(soilPowerPin, HIGH);
float temp1 = 0;
float temp2 = 0;
int soil1 = 0;
int soil2 = 0;
int lpcount=0;
if (debug == 1) {Serial.println("Read soil moisture sensor");};
lpcount=0;
while (lpcount<4) {
soil2 = analogRead(soilMoisturePin);
soil2 = (soil2 / 2);
soil1 += soil2;
if (debug == 1) {Serial.print("Soil loop :");};
if (debug == 1) {Serial.print(lpcount);};
if (debug == 1) {Serial.print(" : ");};
if (debug == 1) {Serial.println(soil2);};
lpcount=lpcount+1;
};
SoilValue1 = soil1/4;
if (debug == 1) {Serial.print("Soil average: ");};
if (debug == 1) {Serial.println(SoilValue1);};
digitalWrite(soilPowerPin, LOW);
if (debug == 1) {Serial.println("Exit Readinputs (Soil)");};
return SoilValue1;
}
</pre>
----------------------------------------------------------------------------------
Update: Tuesday 15th May 2012
I'm now graphing the output as I've hooked the Arduino up to a laptop via USB, below are the graphs (updated every hour):