Automated Greenhouse project
Automated Greenhouse project
References
Materials
List of material per experiment.
- Mini greenhouse
- Heatpad
- Seeds / soil
- Temperature probe one-wire
- Soil moisture sensor
- 2 x 10k Ohm high precision resistors (0.1%)
- Arduino Uno + solderless breadboard
- Raspberry Pi
- 2 x 5V Raspberry Pi power supply
- 60mm 12VDC Cooling Fan
- Pololu Basic SPDT Relay Carrier with 5VDC Relay (Assembled) [1]
Soil Moisture sensor
Applications
Soil moisture of trees, garden beds and agriculture. Sensors originally deployed in Africa to educate farmers about irrigation of food crops
How the Soil moisture sensor works
- Soil moisture sensor circuit explained by Matthew Driver
Components
- Arduino Uno microcontroller using two analog pins with 10 bit analog to digital converters (ADC).
- Two 10K 0.1% precision resistors
- Chameleon soil moisture sensor
Power supply voltage (Vcc)
- This design is independent of voltage.
- In this circuit the power will be supplied from a Raspberry Pi using the Serial USB cable.
Analog pins
- The analog pins on the Arduino microcontroller are dual purpose.
- They can measure a voltage between 0 and Vcc (when their mode is set as an analog INPUT)
- They can also but can also output Vcc (5V) or drain to ground (when their mode is set to digital OUTPUT)
- Both of these states are set using the pinmode function in the Arduino code.
States
- To measure the soil moisture the sensor circuit is placed into three different states:
- Forward current
- Reverse current (the flow of electrons is reversed from the perspective of the sensor)
- No current
Schematic
- A schematic of the soil moisture sensor circuit is shown below.
- Pin X and Pin Y are the two analog input pins on the Arduino microcontroller.
Excitation and and measuring (states applied)
Forward current flow
- To begin the measurement a Forward current is applied to the sensor
- The Forward current state is applied for 250us (microseconds) by setting the pins to:
- pinMode(Pin_Y,INPUT)
- pinMode(Pin_X,OUTPUT)
- digitalWrite(Pin_X, HIGH)
- The voltage is then measured at pin Y and stored in a variable.
No current flow
- A No current state is then applied for 250us by setting the pins to:
- digitalWrite(Pin X, LOW)
- The result is all pins are at ground so no current flows.
- Note that the INPUT pin acts as a drain, which is equivalent to ground.
Reverse current flow
- Then Reverse current state is then applied for 250us (microseconds) by setting the pins to:
- pinMode(Pin_X,INPUT)
- pinMode(Pin_Y,OUTPUT)
- digitalWrite(Pin_Y, HIGH)
- The voltage is then measured at pin X and stored in variable.
Sensor resistance calculation
- The Arduino convert the analog input to a digital value between 0 and 1023 where 1023 is the Vcc voltage (5V), *
- Sensor Resistance = Rs = 10K * (1023 - Vs) / Vs
- where Vs is the average of forward and reverse current values
- Temperature will change the measured value
- A simple formula allows for soil temperature changes to be corrected.
- Formula: Ra = R0 * (1 + (Temp - 22) * 0 .018 )
- Lastly, the soil moisture resistance is converted to soil moisture tension and expressed in kPa pressure units.
- This is a measure of how much pressure plants need to apply to extract moisture from soil.
- The relationship is shown in the graph below.
Interpreting Soil Moisture sensor values
- The Soil Moisture Sensor measures how hard it is for plants to draw water from soil.
- This soil moisture sensor measures resistance and then converts this value to soil moisture tension in kPa.
- This higher the soil moisture tension (kPa), the harder it is for plants to extract moisture from the soil.
- Soil moisture tension from 20 - 30 kPa - 2-6 kOhms - signifies wet soil - ideal for broccoli, celery, lettuce and onion
- Soil moisture tensions from 30 - 45 kPa - 6-21 kOhms - is moist soil - ideal for beans, cabbage, carrot, capsicum, corn, cucumber, eggplant, melons, potato and tomato.
- Soil moisture tenions from 45 - 60 kPa - 20-50 kOhms - is dry soil - ideal for beet, peas, sweet potato and pumpkin.
Water content
Software installation requirements
- Atom installed on PC or Mac with Pymakr package installed
- Arduino IDE installed
Arduino IDE Code
const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0; // Constant value of known resistor in k Ohms
float ReadSensor();
int sensorVoltageX, sensorVoltageY; // Measured sensor voltage from 0 to 1023
float sensorVoltage;
void setup() {
Serial.begin(9600);
}
void loop(){
// Average of sensorVoltageX, sensorVoltageY
//Forward current through sensor
pinMode(sensorYAnalogPin, INPUT); //Set the pin as drain
pinMode(sensorXAnalogPin, OUTPUT);
digitalWrite(sensorXAnalogPin, HIGH); // set the voltage supply on
sensorVoltageY = analogRead(sensorYAnalogPin); // read the sensor voltage takes 125us
Serial.println("sensorY "+String(sensorVoltageY));
digitalWrite(sensorXAnalogPin, LOW); // set the voltage supply off
delayMicroseconds(250); //small delay before reversing polarity (mandatory according to Matt!)
//Reverse current through sensor
pinMode(sensorXAnalogPin, INPUT); //Set the pin as drain
pinMode(sensorYAnalogPin, OUTPUT);
digitalWrite(sensorYAnalogPin, HIGH); // set the voltage supply on
sensorVoltageX = analogRead(sensorXAnalogPin); // read the sensor voltage takes 125us
Serial.println("sensorX "+String(sensorVoltageX));
digitalWrite(sensorYAnalogPin, LOW); // set the voltage supply off
sensorVoltage = (sensorVoltageY + sensorVoltageX) / 2;
delayMicroseconds(250); //small delay before reversing polarity (mandatory according to Matt!)
//Calculate resistance
float value= float(knownResistor) * ( 1023 - sensorVoltage ) / sensorVoltage;
Serial.println("---resistance: "+String(value)+" kOhms\n");
delay(300000); // 5 minutes - use one number
}
Serial Monitor output
Multi-Tab Sketch for Soil Moisture
- Main file auto-soil1
Code for Multi-Tab Sketch
const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0; // Constant value of known resistor in k Ohms
float resistance;
void setup() {
Serial.begin(9600);
}
void loop() {
resistance = ReadSensor();
Serial.print("Resistance: ");
Serial.print(resistance);
Serial.println(" kOhms");
delay(5000); // Use one number for delay
}
- Tab created and named readSoil
Code for Multi-Tab Sketch for Soil Moisture
float ReadSensor() {
int sensorVoltageX, sensorVoltageY; // Measured sensor voltage from 0 to 1023
float sensorVoltage; // Average of sensorVoltageX, sensorVoltageY
//Forward current through sensor
pinMode(sensorYAnalogPin, INPUT); //Set the pin as drain
pinMode(sensorXAnalogPin, OUTPUT);
digitalWrite(sensorXAnalogPin, HIGH); // set the voltage supply on
sensorVoltageY = analogRead(sensorYAnalogPin); // read the sensor voltage takes 125us
digitalWrite(sensorXAnalogPin, LOW); // set the voltage supply off
//Reverse current through sensor
pinMode(sensorXAnalogPin, INPUT); //Set the pin as drain
pinMode(sensorYAnalogPin, OUTPUT);
digitalWrite(sensorYAnalogPin, HIGH); // set the voltage supply on
sensorVoltageX = analogRead(sensorXAnalogPin); // read the sensor voltage takes 125us
digitalWrite(sensorYAnalogPin, LOW); // set the voltage supply off
sensorVoltage = (sensorVoltageY + sensorVoltageX) / 2;
//Calculate resistance
return float( float(knownResistor) * ( 1023 - sensorVoltage ) / sensorVoltage );
}
Delays in Arduino
- To make delays more human readable add the following code to your Arduino sketch.
- Try this example obtained from O'Rielly creating delays
const long oneSecond = 1000; // a second is a thousand milliseconds
const long oneMinute = oneSecond * 60;
const long fiveMinute = oneMinute * 5;
const long oneHour = oneMinute * 60;
const long oneDay = oneHour * 24;
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.println("delay for 1 millisecond");
delay(1);
Serial.println("delay for 1 second");
delay(oneSecond);
Serial.println("delay for 1 minute");
delay(oneMinute);
Serial.println("delay for 5 minutes");
delay(fiveMinute);
Serial.println("delay for 1 hour");
delay(oneHour);
Serial.println("delay for 1 day");
delay(oneDay);
Serial.println("Ready to start over");
}
Combining both Temperature and Soil Moisture Codes
Arduino code for Soil and Temperature readings - image
Arduino code for Soil and Temperature readings - code
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 4 // Data wire is conntected to the Arduino digital pin 4
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature sensor
const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0; // Constant value of known resistor in k Ohms
float resistance;
void setup() {
Serial.begin(9600);
// Start up the library
sensors.begin();
}
void loop() {
// Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
sensors.requestTemperatures();
Serial.print("Celsius temperature: ");
Serial.println(sensors.getTempCByIndex(0)); //0 refers to the first IC on the wire
delay(1000);
resistance = ReadSensor();
Serial.print("Resistance: ");
Serial.print(resistance);
Serial.println(" kOhms");
delay(300000);
}
Arduino code to support Soil Moisture readings - image
Arduino code to support Soil Moisture readings - code
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 4 // Data wire is conntected to the Arduino digital pin 4
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature sensor
const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0; // Constant value of known resistor in k Ohms
float resistance;
void setup() {
Serial.begin(9600);
// Start up the library
sensors.begin();
}
void loop() {
// Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
sensors.requestTemperatures();
Serial.print("Celsius temperature: ");
Serial.println(sensors.getTempCByIndex(0)); //0 refers to the first IC on the wire
delay(1000);
resistance = ReadSensor();
Serial.print("Resistance: ");
Serial.print(resistance);
Serial.println(" kOhms");
delay(300000);
}
Example output of data in Serial Monitor
Sending Temperature and Soil Moisture data to Raspberry Pi
- Data can be sent from the Arduino to the Raspberry pi using the Serial cable.
- Data is separated by commas, without spaces.
- In this example temperature and soil moisture data will be sent.
- A typical output will look like this 13.5, 0.85
- 13.5 degress Centigrade
- 0.85 k Ohms resistance for the Soil moisture sensor
Arduino code to send data to Raspberry Pi - image
Arduino code to send data to Raspberry Pi - code
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 4 // Data wire is conntected to the Arduino digital pin 4
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature sensor
const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0; // Constant value of known resistor in k Ohms
float resistance;
float temperature;
void setup() {
Serial.begin(9600);
// Start up the library
sensors.begin();
}
void loop() {
// Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
sensors.requestTemperatures();
Serial.print(sensors.getTempCByIndex(0)); // new line character removed
resistance = ReadSensor();
Serial.print(",");
Serial.println(resistance);
delay(5 * 60 * 1000);
}
Example output using Serial Monitor
Python code on the Raspberry Pi to read Arduino data
Python - code to constantly collect data from Arduino - code
#!/usr/bin/env python3
import serial
if __name__ == '__main__':
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.reset_input_buffer()
while True:
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8').rstrip()
print(line)
Python - code to constantly collect data from Arduino - image
Python - Collecting data from Arduino and adding a datestamp - code
#!/usr/bin/env python3
import serial
import datetime
if __name__ == '__main__':
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.reset_input_buffer()
while True:
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8').rstrip()
print(line)
now = datetime.datetime.now()
date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
print(f'This python program was run at this time {date_stamp}')
data = str(line) + "," + date_stamp + "\n"
print(f'The data is = {data}')
Python - Collecting data from Arduino and adding a datestamp - image
Python - saving data to a file - image
Python - saving data to a file - code
#!/usr/bin/env python3
import serial
import datetime
import time
if __name__ == '__main__':
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.reset_input_buffer()
while True:
time.sleep(0.1) # added a small delay in loop
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8').rstrip()
print(line)
now = datetime.datetime.now()
date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
print(f'This python program was run at this time {date_stamp}')
data = str(line) + "," + date_stamp + "\n"
print(f'The data is = {data}')
f = open('/home/pi/auto-greenhouse/greenhouse_data.txt','a')
f.write(data)
f.close()
Python - with graphing in Plotly
Plotting Soil Moisture data
#!/usr/bin/env python3
import serial
import datetime
import plotly
import plotly.graph_objects as go
import pandas
import time
if __name__ == '__main__':
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.reset_input_buffer()
while True:
time.sleep(0.1)
if ser.in_waiting > 0:
print("Data in")
line = ser.readline().decode('utf-8').rstrip()
print(line)
now = datetime.datetime.now()
date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
data = str(line) + "," + date_stamp + "\n"
print(f'The data is = {data}')
f = open('/home/pi/auto-greenhouse/greenhouse_data.txt','a')
f.write(data)
f.close()
print("Data saved")
# import and plot data text file
df = pandas.read_csv('/home/pi/auto-greenhouse/greenhouse_data.txt')
fig = go.Figure(data = go.Scatter(mode='markers',
x=df.date_time,
y=df.soil_moisture,
marker=dict(
color="LightSkyBlue",
size=20)
)
)
fig.update_layout(title="Soil Moisture in Greenhouse",
xaxis_title = 'Time',
yaxis_title = 'Soil Moisture',
font=dict(
size=20,
color="RebeccaPurple"
)
)
plotly.offline.plot(fig,
filename="/home/pi/auto-greenhouse/soil_moisture_plot.html",
auto_open=False)
print("Soil Moisture data plotted")
Plotting Temperature data
#!/usr/bin/env python3
import serial
import datetime
import plotly
import plotly.graph_objects as go
import pandas
import time
if __name__ == '__main__':
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.reset_input_buffer()
while True:
time.sleep(0.1)
if ser.in_waiting > 0:
print("Data in")
line = ser.readline().decode('utf-8').rstrip()
print(line)
now = datetime.datetime.now()
date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
data = str(line) + "," + date_stamp + "\n"
print(f'The data is = {data}')
f = open('/home/pi/auto-greenhouse/greenhouse_data.txt','a')
f.write(data)
f.close()
print("Data saved")
# import and plot data text file
df = pandas.read_csv('/home/pi/auto-greenhouse/greenhouse_data.txt')
fig = go.Figure(data = go.Scatter(mode='markers',
x=df.date_time,
y=df.soil_moisture,
marker=dict(
color="LightSkyBlue",
size=20)
)
)
fig.update_layout(title="Soil Moisture in Greenhouse",
xaxis_title = 'Time',
yaxis_title = 'Soil Moisture',
font=dict(
size=20,
color="RebeccaPurple"
)
)
plotly.offline.plot(fig,
filename="/home/pi/auto-greenhouse/soil_moisture_plot.html",
auto_open=False)
print("Soil Moisture data plotted")
fig = go.Figure(data = go.Scatter(mode='markers',
x=df.date_time,
y=df.temperature,
marker=dict(
color="LightSkyBlue",
size=20)
)
)
fig.update_layout(title="Temperature in Greenhouse",
xaxis_title = 'Time',
yaxis_title = 'Temperature',
font=dict(
size=20,
color="RebeccaPurple"
)
)
plotly.offline.plot(fig,
filename="/home/pi/auto-greenhouse/temperature_plot.html",
auto_open=False)
print("Temperature data plotted")
Arduino code - Soil Moisture with Temperature Compensation - image
Arduino code - Soil Moisture with Temperature Compensation - code
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 4 // Data wire is conntected to the Arduino digital pin 4
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature sensor
const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0; // Constant value of known resistor in k Ohms
float resistance;
float resistanceCorrect;
float temperature;
void setup() {
Serial.begin(9600);
sensors.begin();
}
void loop() {
// Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
sensors.requestTemperatures();
temperature = sensors.getTempCByIndex(0);
resistance = ReadSensor();
resistanceCorrect = resistance * (1.0 + (temperature - 22.0) * 0.018);
Serial.print(temperature);
Serial.print(",");
//Serial.print(resistance);
//Serial.print(",");
Serial.println(resistanceCorrect);
delay(300000); // 300000 five minutes,
}
Arduino - Soil Moisture with Temperature Compensation - Serial Monitor
Python - Splitting data and using try and except statements
#!/usr/bin/env python3
# split data
import serial
import datetime
import plotly
import plotly.graph_objects as go
import pandas
import time
if __name__ == '__main__':
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.reset_input_buffer()
while True:
time.sleep(0.1)
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8').rstrip()
print(f"Data separated using commas {line.split(',')}")
mylist = line.split(',')
try:
temperature = float(mylist[0])
soilMoisture = float(mylist[1])
except:
temperature = 0.0
soilMoisture = 0.0
print(f'temperature is {temperature} and soil moisture is {soilMoisture}')
time.sleep(1)
now = datetime.datetime.now()
date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
data = str(line) + "," + date_stamp + "\n"
print(f'The data is = {data}')
data = ""
data = date_stamp + "," + str(temperature) + "," + str(soilMoisture) + '\n'
f = open('/home/pi/auto-greenhouse/greenhouse_data.txt','a')
f.write(data)
f.close()
print("Data saved")
# import and plot data text file
df = pandas.read_csv('/home/pi/auto-greenhouse/greenhouse_data.txt')
fig = go.Figure(data = go.Scatter(mode='markers',
x=df.date_time,
y=df.soil_moisture,
marker=dict(
color="LightSkyBlue",
size=20)
)
)
fig.update_layout(title="Soil Moisture in Greenhouse",
xaxis_title = 'Time',
yaxis_title = 'Soil Moisture',
font=dict(
size=20,
color="RebeccaPurple"
)
)
plotly.offline.plot(fig,
filename="/home/pi/auto-greenhouse/soil_moisture_plot.html",
auto_open=False)
print("Soil Moisture data plotted")
fig = go.Figure(data = go.Scatter(mode='markers',
x=df.date_time,
y=df.temperature,
marker=dict(
color="LightSkyBlue",
size=20)
)
)
fig.update_layout(title="Temperature in Greenhouse",
xaxis_title = 'Time',
yaxis_title = 'Temperature',
font=dict(
size=20,
color="RebeccaPurple"
)
)
plotly.offline.plot(fig,
filename="/home/pi/auto-greenhouse/temperature_plot.html",
auto_open=False)
print("Temperature data plotted")
Python - Dweeting Data
#!/usr/bin/env python3
# dweet data
import serial
import datetime
import plotly
import plotly.graph_objects as go
import pandas
import time
import requests
if __name__ == '__main__':
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.reset_input_buffer()
while True:
time.sleep(0.1)
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8').rstrip()
print(f"Data separated using commas {line.split(',')}")
mylist = line.split(',')
try:
temperature = float(mylist[0])
soilMoisture = float(mylist[1])
except:
temperature = 0.0
soilMoisture = 0.0
print(f'temperature is {temperature} and soil moisture is {soilMoisture}')
time.sleep(1)
now = datetime.datetime.now()
date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
data = str(line) + "," + date_stamp + "\n"
print(f'The data is = {data}')
data = ""
data = date_stamp + "," + str(temperature) + "," + str(soilMoisture) + '\n'
f = open('/home/pi/auto-greenhouse/greenhouse_data.txt','a')
f.write(data)
f.close()
print("Data saved")
# import and plot data text file
df = pandas.read_csv('/home/pi/auto-greenhouse/greenhouse_data.txt')
fig = go.Figure(data = go.Scatter(mode='markers',
x=df.date_time,
y=df.soil_moisture,
marker=dict(
color="LightSkyBlue",
size=20)
)
)
fig.update_layout(title="Soil Moisture in Greenhouse",
xaxis_title = 'Time',
yaxis_title = 'Soil Moisture',
font=dict(
size=20,
color="RebeccaPurple"
)
)
plotly.offline.plot(fig,
filename="/home/pi/auto-greenhouse/soil_moisture_plot.html",
auto_open=False)
print("Soil Moisture data plotted")
fig = go.Figure(data = go.Scatter(mode='markers',
x=df.date_time,
y=df.temperature,
marker=dict(
color="LightSkyBlue",
size=20)
)
)
fig.update_layout(title="Temperature in Greenhouse",
xaxis_title = 'Time',
yaxis_title = 'Temperature',
font=dict(
size=20,
color="RebeccaPurple"
)
)
plotly.offline.plot(fig,
filename="/home/pi/auto-greenhouse/temperature_plot.html",
auto_open=False)
print("Temperature data plotted")
try:
print("Preparing dweet")
dweet_dict = {}
dweet_dict.update({"temperature": str(temperature)})
dweet_dict.update({"soilMoisture": str(soilMoisture)})
dweet_dict.update({"Time": str(date_stamp)})
url = "https://dweet.io/dweet/for/3083-auto-greenhouse1?"
x = requests.post(url, json=dweet_dict)
print(x.text)
except:
print("Dweet failed")
# check dweet with - https://dweet.io/get/latest/dweet/for/3083-auto-greenhouse1




