HomeTutorials › Raspberry Pi Irrigation Control – Part 3

Raspberry Pi Irrigation Control – Part 3

In the past two Raspberry Pi Irrigation Controller Tutorials we have assembled the board, connected the Pi, added sensors, and tested all of the components. In the third part of this four part tutorial we are going to figure out how to write a program that will run our sprinklers, monitor a few of our sensors, and email us once it has finished running. We will also figure out how to schedule this program so it only runs when we want it to – what good would a sprinkler system be if it can’t follow your watering schedule! There are several different ways to approach this on the Raspberry Pi and we will go into the design logic and reason for our choices in this tutorial as well as writing the code.

Overview:
Before we get started, you should have completed the previous two sections of this tutorial. We are going to start by setting up a Gmail account to send our emails and then it is straight back to coding as we will have to modify our Python program. We want our program to operate the sprinklers for the correct duration, read the sensors, and email us once it has run. After that we will look at how to schedule it to run at the right time on the right date. As always, we will break it down into a bunch of easier steps to get through this!

Requirements:

parts

This tutorial requires several items:

  • A completed assembly from Part 1 of this tutorial set
  • Completed code from Part 2 of this tutorial set
  • 1 x Pi 3 / 3+ capable power supply
  • A USB Keyboard & Mouse
  • A HDMI compatible monitor
  • Internet access

Step 1 – Emails

Step4

It is always nice knowing when your scheduled devices are running – and sending out an email when they do is a great way to keep track! In this project we are going to send an email every time the program runs to let us know it has run as well as a few readings from our sensors. Sending an email with the Pi using Python isn’t that difficult, but there are a few steps as we need to use an external email provider to handle sending the emails. In this example we are going to use Google / Gmail. We are effectively going to create an email account for this device and give the Pi permission to send using this account. Do not use your normal email address – the email address and password are stored in plain text!

Step 2 – Create a Gmail Account

Step4

Head on over to Gmail and create a new email account. You may need to sign out if you are already logged in with an existing account. Once that is completed – move on to step 3!

Step 3 – Set Gmail Permissions

Step4

To access a Gmail account using an external device like the Raspberry Pi, permission for “less secure apps” will have to be enabled. When logged in as your new email account you can jump over to https://myaccount.google.com/lesssecureapps and enable this setting. Since most people have several Google accounts, just be sure you are using the right account when you enable this setting!

Step 4 – Back to Coding

step10

Ok, back to coding on the Pi. We are going to start by opening Idle3 again to edit our previous Python program. The program was great for testing our sensors and solenoids, but it won’t run your sprinklers / irrigation in a way that would be desired. Once you have the program from Part 2 of this tutorial series loaded up, move on to the next step!

Step 5 – Remove un-needed code

This code was great for testing our components in the last part of the tutorial. Now that we know everything works, we can get on with converting it into something a little more useful. Start by removing the lines highlighted below:

import time
from w1thermsensor import W1ThermSensor
 
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
 
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
 
import RPi.GPIO as GPIO
 
ds18b20 = W1ThermSensor()
 
ads = ADS.ADS1015(i2c)
ads.gain = 1
 
interval = 5  #How long we want to wait between loops (seconds)
waterTick = 0   #Used to count the number of times the flow input is triggered
 
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
 
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
 
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
 
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
 
 
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
 
GPIO.add_event_callback(24, flowtrig)
 
while True:
 
    time.sleep(interval)
 
    #Pull Temperature from DS18B20
    temperature = ds18b20.get_temperature()
 
    #Measure Analog Input 0 
    chan = AnalogIn(ads, ADS.P0) #ADS.P1 , P2, P3 for channels 1, 2, 3
    val = chan.value #Pull the raw ADC data from Channel 0
 
    waterFlow = waterTick * 2.25
    waterTick = 0
 
    #Test Solenoids by turning each on for a half second
    GPIO.output(s1, GPIO.HIGH) #turn solenoid 1 on
    time.sleep(0.5) #Wait for a half second
    GPIO.output(s1, GPIO.LOW) #turn solenoid 1 off
    time.sleep(0.5)
 
    GPIO.output(s2, GPIO.HIGH) #turn solenoid 2 on
    time.sleep(0.5) #Wait for a half second
    GPIO.output(s2, GPIO.LOW) #turn solenoid 2 off
    time.sleep(0.5)
 
    GPIO.output(s3, GPIO.HIGH) #turn solenoid 3 on
    time.sleep(0.5) #Wait for a half second
    GPIO.output(s3, GPIO.LOW) #turn solenoid 3 off
    time.sleep(0.5)
 
    GPIO.output(s4, GPIO.HIGH) #turn solenoid 4 on
    time.sleep(0.5) #Wait for a half second
    GPIO.output(s4, GPIO.LOW) #turn solenoid 4 off
    time.sleep(0.5)
 
    GPIO.output(s5, GPIO.HIGH) #turn solenoid 5 on
    time.sleep(0.5) #Wait for a half second
    GPIO.output(s5, GPIO.LOW) #turn solenoid 5 off
    time.sleep(0.5)
 
    GPIO.output(s6, GPIO.HIGH) #turn solenoid 6 on
    time.sleep(0.5) #Wait for a half second
    GPIO.output(s6, GPIO.LOW) #turn solenoid 6 off
    time.sleep(0.5)
 
    #Print the results
    print( 'Temperature: ' , temperature)
    print( 'Soil Moisture: ' , val)
    print( 'Flow Rate: ' , waterFlow)
    print( ' ')

Step 5 – No Loop, No Problem!

You may have noticed we removed the loop in this program – well since this program isn’t running all the time and we have an external method of triggering this program to run – we don’t need it! Instead, this program is going to run from start to finish once, and that’s it. Our external trigger will run it again the next time we need it. We will go a little further into how this works later on.

Step 6 – Starting The New Code

Our new “loopless” design is going still use the majority of the original code. All of our pin assignments are the same and we still need all of the libraries. However, we do need to add a few pieces of user defined information at the top.

We are going start by creating variables to store the run duration of each sprinkler zone. The values here are going to vary wildly depending on what you are controlling. If you are watering your lawn, it may be 10 minutes, if you are running drip irrigation it could be a lot longer. So keep in mind, these variables will be something you will probably be adjusting as time goes on.

Start by adding the code highlighted below:

import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)
  • (20-25) Run time for each zone (measured in minutes)

Step 7 – SMTP Library

Ok time to start setting up the Email portion of the program. We are going to add one more library to make this a little easier – add the SMTP reference to the top of the file:

import smtplib
import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)
  • (1) Import smtplib library for sending emails

Step 8 – Email Variables

We need to store a bunch of information for sending out emails as well. Be sure to update the email address and password in your code to match what you set up for your email account earlier.

You will also define where you want the notification to be sent to. This can be sent to any email address or, if you prefer, you can also send to a phone via email to text message. The information for this will be location and provider specific. In Canada this is typically done by emailing your phone number including area code @ your provider. This website has a listing for all major Canadian providers (External Link). Depending on your plan, charges may apply!

import smtplib
import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)

#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'youremail@email.com' #change this to match your gmail account
GMAIL_PASSWORD = 'yourPassword'  #change this to match your gmail password

recipient = 'sendToEmail@email.com' #change this to your destination email account

  • (60) Email Server Address (Don’t Change)
  • (61) Email Server Port (Don’t Change)
  • (62) Your Gmail Username that was just created
  • (63) Your Gmail Password that was just created
  • (65) Where you want the email to be sent

Step 9 – Let’s Do Something!

Ok enough setup! Now that everything is defined, we can create the bulk of the program… the part that does everything! In order we are going to: read the initial ground moisture, run each of our sprinklers for the right amount of time, read the moisture sensor once again to see what it is at after the sprinklers have run, calculate the total water consumed, grab the temperature, and email out our information.

Step 10 – Initial Moisture Reading

Before we start throwing water on everything, we want to read the Soil Moisture Sensor to get an initial value. Add the highlighted code at the very bottom:

import smtplib
import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)

#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'youremail@email.com' #change this to match your gmail account
GMAIL_PASSWORD = 'yourPassword'  #change this to match your gmail password

recipient = 'sendToEmail@email.com' #change this to your destination email account

#Start
smStart = AnalogIn(ads, ADS.P0) #Read initial value from soil moisture sensor connected to A0
  • (68) Read the analog pin we connected our soil moisture sensor to

Step 11 – Run the Sprinklers

There are many ways to do this – but we are going to keep it nice and simple. In the past we have used time.sleep() to pause a program. Since our program doesn’t do anything while each solenoid is triggered we can use this as a very primitive (and easy) way to run our sprinkler zones for the correct amount of time. It is as simple as triggering one of our solenoids, sleeping the program for the amount of time we want the water flowing, and then shutting off that solenoid.

What about the water flow? In the last tutorial we created a background process that counts the flow even while the program is paused. We left this code in place so the flow will be counted already, no need to change anything.

The times we want each of our zones to run for have already been defined. These times are stored in minutes for our own convenience, but the sleep command is expecting seconds. We will make the conversion at the time of operation by simply multiplying our minutes by 60 to get seconds.

import smtplib
import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)

#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'youremail@email.com' #change this to match your gmail account
GMAIL_PASSWORD = 'yourPassword'  #change this to match your gmail password

recipient = 'sendToEmail@email.com' #change this to your destination email account

#Start
smStart = AnalogIn(ads, ADS.P0) #Read initial value from soil moisture sensor connected to A0

#Run Zone 1
GPIO.output(s1, GPIO.HIGH) #turn zone 1 on
sleepTime = zone1 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s1, GPIO.LOW) #turn zone 1 off




  • (70) Trigger Solenoid ON
  • (71) Convert sleep time in minutes to seconds
  • (72) Sleep program for our defined number of seconds
  • (73) Trigger Solenoid OFF

Step 12 – Run the Sprinklers Part 2

Now that we have the first zone done, just copy it for each of the remaining zones:

import smtplib
import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)

#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'youremail@email.com' #change this to match your gmail account
GMAIL_PASSWORD = 'yourPassword'  #change this to match your gmail password

recipient = 'sendToEmail@email.com' #change this to your destination email account

#Start
smStart = AnalogIn(ads, ADS.P0) #Read initial value from soil moisture sensor connected to A0

#Run Zone 1
GPIO.output(s1, GPIO.HIGH) #turn zone 1 on
sleepTime = zone1 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s1, GPIO.LOW) #turn zone 1 off

#Run Zone 2
GPIO.output(s2, GPIO.HIGH) #turn zone 2 on
sleepTime = zone2 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s2, GPIO.LOW) #turn zone 2 off

#Run Zone 3
GPIO.output(s3, GPIO.HIGH) #turn zone 3 on
sleepTime = zone3 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s3, GPIO.LOW) #turn zone 3 off

#Run Zone 4
GPIO.output(s4, GPIO.HIGH) #turn zone 4 on
sleepTime = zone4 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s4, GPIO.LOW) #turn zone 4 off

#Run Zone 5
GPIO.output(s5, GPIO.HIGH) #turn zone 5 on
sleepTime = zone5 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s5, GPIO.LOW) #turn zone 5 off

#Run Zone 6
GPIO.output(s6, GPIO.HIGH) #turn zone 6 on
sleepTime = zone6 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s6, GPIO.LOW) #turn zone 6 off

  • (76-104) Trigger remaining zones

Step 13 – Read the moisture level again

Now that everything has run, lets go grab the moisture level again.

import smtplib
import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)

#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'youremail@email.com' #change this to match your gmail account
GMAIL_PASSWORD = 'yourPassword'  #change this to match your gmail password

recipient = 'sendToEmail@email.com' #change this to your destination email account

#Start
smStart = AnalogIn(ads, ADS.P0) #Read initial value from soil moisture sensor connected to A0

#Run Zone 1
GPIO.output(s1, GPIO.HIGH) #turn zone 1 on
sleepTime = zone1 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s1, GPIO.LOW) #turn zone 1 off

#Run Zone 2
GPIO.output(s2, GPIO.HIGH) #turn zone 2 on
sleepTime = zone2 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s2, GPIO.LOW) #turn zone 2 off

#Run Zone 3
GPIO.output(s3, GPIO.HIGH) #turn zone 3 on
sleepTime = zone3 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s3, GPIO.LOW) #turn zone 3 off

#Run Zone 4
GPIO.output(s4, GPIO.HIGH) #turn zone 4 on
sleepTime = zone4 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s4, GPIO.LOW) #turn zone 4 off

#Run Zone 5
GPIO.output(s5, GPIO.HIGH) #turn zone 5 on
sleepTime = zone5 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s5, GPIO.LOW) #turn zone 5 off

#Run Zone 6
GPIO.output(s6, GPIO.HIGH) #turn zone 6 on
sleepTime = zone6 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s6, GPIO.LOW) #turn zone 6 off

smEnd = AnalogIn(ads, ADS.P0) #Read end value from soil moisture sensor connected to A0

  • (106) Read soil moisture sensor again

Step 14 – Temperature

We will grab the temperature for fun while we are at it.

import smtplib
import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)

#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'youremail@email.com' #change this to match your gmail account
GMAIL_PASSWORD = 'yourPassword'  #change this to match your gmail password

recipient = 'sendToEmail@email.com' #change this to your destination email account

#Start
smStart = AnalogIn(ads, ADS.P0) #Read initial value from soil moisture sensor connected to A0

#Run Zone 1
GPIO.output(s1, GPIO.HIGH) #turn zone 1 on
sleepTime = zone1 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s1, GPIO.LOW) #turn zone 1 off

#Run Zone 2
GPIO.output(s2, GPIO.HIGH) #turn zone 2 on
sleepTime = zone2 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s2, GPIO.LOW) #turn zone 2 off

#Run Zone 3
GPIO.output(s3, GPIO.HIGH) #turn zone 3 on
sleepTime = zone3 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s3, GPIO.LOW) #turn zone 3 off

#Run Zone 4
GPIO.output(s4, GPIO.HIGH) #turn zone 4 on
sleepTime = zone4 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s4, GPIO.LOW) #turn zone 4 off

#Run Zone 5
GPIO.output(s5, GPIO.HIGH) #turn zone 5 on
sleepTime = zone5 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s5, GPIO.LOW) #turn zone 5 off

#Run Zone 6
GPIO.output(s6, GPIO.HIGH) #turn zone 6 on
sleepTime = zone6 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s6, GPIO.LOW) #turn zone 6 off

smEnd = AnalogIn(ads, ADS.P0) #Read end value from soil moisture sensor connected to A0

temperature = ds18b20.get_temperature() #Get temperature from the temperature sensor

  • (108) Read DS18B20 to get temperature

Step 15 – Water Consumed

Just about done collecting data – let’s figure out how much water we used. Our flow sensor counts up by one every time ~2.25ml of water flows by. So we can take our count and simply multiply it by 2.25. To convert that to liters, we would then divide by 1000.

import smtplib
import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)

#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'youremail@email.com' #change this to match your gmail account
GMAIL_PASSWORD = 'yourPassword'  #change this to match your gmail password

recipient = 'sendToEmail@email.com' #change this to your destination email account

#Start
smStart = AnalogIn(ads, ADS.P0) #Read initial value from soil moisture sensor connected to A0

#Run Zone 1
GPIO.output(s1, GPIO.HIGH) #turn zone 1 on
sleepTime = zone1 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s1, GPIO.LOW) #turn zone 1 off

#Run Zone 2
GPIO.output(s2, GPIO.HIGH) #turn zone 2 on
sleepTime = zone2 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s2, GPIO.LOW) #turn zone 2 off

#Run Zone 3
GPIO.output(s3, GPIO.HIGH) #turn zone 3 on
sleepTime = zone3 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s3, GPIO.LOW) #turn zone 3 off

#Run Zone 4
GPIO.output(s4, GPIO.HIGH) #turn zone 4 on
sleepTime = zone4 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s4, GPIO.LOW) #turn zone 4 off

#Run Zone 5
GPIO.output(s5, GPIO.HIGH) #turn zone 5 on
sleepTime = zone5 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s5, GPIO.LOW) #turn zone 5 off

#Run Zone 6
GPIO.output(s6, GPIO.HIGH) #turn zone 6 on
sleepTime = zone6 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s6, GPIO.LOW) #turn zone 6 off

smEnd = AnalogIn(ads, ADS.P0) #Read end value from soil moisture sensor connected to A0

temperature = ds18b20.get_temperature() #Get temperature from the temperature sensor

waterUsed = waterTick * 2.25 #convert our sensor count to millilitres 
waterUsed = waterUsed / 1000 #convert to liters 
  • (110) Convert our sensor count to millilitres
  • (111) Convert water used in millilitres to liters

Step 16 – Let’s Write An Email

In this step we are going to assemble our Email and send it! First we need to give it a subject. Next we will have to assemble all of our text for the email into one string. We don’t really want one long line of text in our email so it has been broken up into 5 pieces – one for each line of text. The “\n” is used to input a carriage return to force the next chunk of text to a new line. Finally we structure the email header, connect to Gmail and send the email.

import smtplib
import time
from w1thermsensor import W1ThermSensor
  
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
  
import adafruit_ads1x15.ads1015 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
  
import RPi.GPIO as GPIO
  
ds18b20 = W1ThermSensor()
  
ads = ADS.ADS1015(i2c)
ads.gain = 1
  
waterTick = 0   #Used to count the number of times the flow input is triggered

zone1 = 6  #Zone 1 run duration in minutes
zone2 = 10 #Zone 2 run duration in minutes
zone3 = 8  #Zone 3 run duration in minutes
zone4 = 5  #Zone 4 run duration in minutes
zone5 = 7  #Zone 5 run duration in minutes
zone6 = 10 #Zone 6 run duration in minutes
  
#Assign channels to variables to keep track of them easier (these BCM pin numbers were listed in part 1 of the tutorial)
s1 = 13
s2 = 16
s3 = 19
s4 = 20
s5 = 26
s6 = 21
  
#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
  
#Set digital pin 24 to an input
GPIO.setup(24, GPIO.IN) 
  
#Set solenoid driver pins to outputs:
GPIO.setup(s1, GPIO.OUT) #set Solenoid 1 output 
GPIO.setup(s2, GPIO.OUT) #set Solenoid 2 output
GPIO.setup(s3, GPIO.OUT) #set Solenoid 3 output 
GPIO.setup(s4, GPIO.OUT) #set Solenoid 4 output
GPIO.setup(s5, GPIO.OUT) #set Solenoid 5 output 
GPIO.setup(s6, GPIO.OUT) #set Solenoid 6 output
  
  
#Event to detect flow (1 tick per revolution)
GPIO.add_event_detect(24, GPIO.FALLING) 
def flowtrig(self):
    global waterTick
    waterTick += 1
  
GPIO.add_event_callback(24, flowtrig)

#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'youremail@email.com' #change this to match your gmail account
GMAIL_PASSWORD = 'yourPassword'  #change this to match your gmail password

recipient = 'sendToEmail@email.com' #change this to your destination email account

#Start
smStart = AnalogIn(ads, ADS.P0) #Read initial value from soil moisture sensor connected to A0

#Run Zone 1
GPIO.output(s1, GPIO.HIGH) #turn zone 1 on
sleepTime = zone1 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s1, GPIO.LOW) #turn zone 1 off

#Run Zone 2
GPIO.output(s2, GPIO.HIGH) #turn zone 2 on
sleepTime = zone2 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s2, GPIO.LOW) #turn zone 2 off

#Run Zone 3
GPIO.output(s3, GPIO.HIGH) #turn zone 3 on
sleepTime = zone3 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s3, GPIO.LOW) #turn zone 3 off

#Run Zone 4
GPIO.output(s4, GPIO.HIGH) #turn zone 4 on
sleepTime = zone4 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s4, GPIO.LOW) #turn zone 4 off

#Run Zone 5
GPIO.output(s5, GPIO.HIGH) #turn zone 5 on
sleepTime = zone5 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s5, GPIO.LOW) #turn zone 5 off

#Run Zone 6
GPIO.output(s6, GPIO.HIGH) #turn zone 6 on
sleepTime = zone6 * 60 #multiply our run time (minutes) by 60 to get seconds
time.sleep(sleepTime ) # sleep for our duration with the solenoid open 
GPIO.output(s6, GPIO.LOW) #turn zone 6 off

smEnd = AnalogIn(ads, ADS.P0) #Read end value from soil moisture sensor connected to A0

temperature = ds18b20.get_temperature() #Get temperature from the temperature sensor

waterUsed = waterTick * 2.25 #convert our sensor count to millilitres 
waterUsed = waterUsed / 1000 #convert to liters 

#Send Email

#Email Subject
subject = 'Sprinkler Update'   

#Email Content
line0 = 'Run Completed \n'
line1 = 'Starting Moisture: ' + str(smStart.value) + '\n'
line2 = 'Ending Moisture: ' + str(smEnd.value) + '\n'
line3 = 'Water Consumed: ' + str(waterUsed) + '\n'
line4 = 'Temperature: ' + str(temperature)

emailText = line0 + line1 + line2 + line3 + line4

#Email Headers
headers = ["From: " + GMAIL_USERNAME, "Subject: " + subject, "To: " + recipient, "MIME-Version: 1.0", "Content-Type: text/html"]
headers = "\r\n".join(headers)

#Send Email
session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo

session.login(GMAIL_USERNAME, GMAIL_PASSWORD)

session.sendmail(GMAIL_USERNAME, recipient, headers + "\r\n\r\n" + emailText)
session.quit
#Done



  • (116) Give our email a subject line
  • (119-123) Generate our email content line by line. We use “str( )” to convert our numbers to text.
  • (125) Assemble our text lines into one string containing all of our email content
  • (128-129) Create and format our email headers
  • (132-135) Start connection with Gmail server
  • (137) Login to Gmail
  • (139) Send our email
  • (140) Close connection

Save your program – we saved it in the /home/pi/ directory to keep things simple later on. Once it is saved, feel free to test run the program with the “F5” key – however it may take a while to run through before emailing depending on the run times set for each zone. To speed it up, you could always set the zone durations shorter – just remember to set them back!

Step 17 – Schedule It To Run

Ok now that the program is done, we need to get it to run at the right times. There are many ways to write a program to run things on a schedule – we could have simply written python code to continuously check against predefined times or run every xxxxxx number of seconds. But that gets a bit clunky and complicated. Fortunately for us, the Raspberry Pi is a little more sophisticated and already has something that sort of takes care of this problem. We are going to use a really useful tool found within Raspbian (and all Unix-like operating systems) called a Cron Job. This is effectively a scheduling tool that allows you to run code / commands at predefined times or intervals – Exactly what we are looking to do! We are going to use this schedule to run a python script at our desired intervals. Once the program is done, it closes and the system will not run it again until it is scheduled. Pretty cool, huh?

Step 18 – Setting up Cron jobs on the Pi

Step4

This is actually quite easy – there is a handy scheduling program we can install to take all of the hard work out of setting up a cron job. Fire up terminal and punch in the following command and hit enter to install:

sudo apt-get install gnome-schedule

Step 19 – Scheduling A Task

Step4

Once this program has been installed, it can be found under the Raspberry Pi menu under System Tools. Open “Scheduled Tasks”.

Step 20 – Using The Scheduler

Step4

Setting up cron jobs on the Pi could be a tutorial in itself (it likely will be in the near future) but we will go through the basics quickly. Hit “New” to add a new task. We want a recurring task for our sprinkler program so we will select “A task that launches recurrently”. You should end up with a window as pictured.

Step 21 – Set Our Schedule

Step4

Give it a name to start – this is just for reference. The next line is the command we want to run. We saved our program as sprinkler.py in the default directory of “/pi/home” so our command to run the program is:

sudo python3 sprinkler.py

The remainder has to do with timing – how this is setup will largely depend on how often you want it to run. Select “Advanced” and start playing around with numbers and a preview will appear at the bottom indicating what will happen. You can put in multiple values, ranges, and even basic math into each of the boxes. Our pictured example will run on odd days at 7:15am and 6:15pm from May to September. If you wanted even days you could simply change the date to 2-30/2 .

Step 22 – That’s It!

Step4

We are just about there – Click “Add” in the bottom right corner and that is it. The new cron job will show up in the list. Test it out, make sure it runs (basically, wait for it to email you) and prepare for the next section – in Part 4 we are putting this Pi to work.

Have A Question?
If you have any questions, or need further clarification please post in the comments section below; this way future users of this tutorial can see the questions and answers!

Parts Used In This Tutorial:

Leave a Reply