September 22, 2018

Raspberry Pi Soil Moisture Tester

This is another basic Raspberry Pi project I put together to test out a soil moisture tester. My idea was to plant some vegetables at my parents house (I don’t get enough sun) and use this system to keep them watered. I wanted the entire system to run off a solar/battery set like I used on my garden fountain but so far I’m still only in the testing phase so the solar part will probably have to wait until next year. I used the same system image from my Petfeeder and just changed the code a bit to work for this. It checks the moisture twice a day emails me the results and then adds water if needed.

These are the four main parts of the system (besides the 3D printed case). A Raspberry Pi 3B, a SainSmart 2-Channel Relay Module, a Soetta Soil Moisture Sensor, and a Bluewo Mini Water Pump.



Here is everything mounted in the 3D printed case.

Everything is wired with jumpers.

(these pictures are just so I remember how I wired everything.)

The moisture sensor board just sits on a post in the case and is held in place by the one lid screw when the lid is mounted.

Once the lid is mounted I can still see the power and sensor Leds.

I super glued a piece of screen over the vent holes just to keep anything from falling in the case.

I had to make a special two piece wire stay for the sensor wire because of the plug on the end required a square hole in the case to fit through.

These two pieces just fit around the wire and then fit into square hole in the main case.

A single small zip tie then holds everything securely in place.

The water sensor is then stuck into the soil were it can monitor the moisture. I chose this sensor over the other cheap ones out there because it’s suppose to be corrosion resistant (but only time will tell). Right now I am only using it as a digital sensor so it’s either wet or dry. My goal for later is to learn how to use it as an analog sensor but I have to learn how to use a ADC MCP3008 first. You can also so the 3D printed spray nozzle in this picture.

This is a cross section of the simple printed nozzle.

For testing the nozzle is just stuck in the dirt with a peg and the hose is clipped to the planter.

The pump is just dunked in a bucket for now. I had a 5 gallon water cooler jug I was planning on using but the pump wouldn’t fit down the spout so this is it for now.

This is my test subject…. I have no idea what it is (some kind of grass?) I planted zucchini seeds in this planter but I think the squirrels dug them up and then this thing starter sprouting so I left it be to see what it turned into. What ever it is it’s getting tall.

Here the pump is on and spraying out the nozzle. It’s not the best spray pattern but for testing it works.

The pump is just hooked to a 12v battery through the relay for now.

I have everything stuck under the table but I also covered it with a plastic food container to protect the electronics from the rain. Now the testing begins…..

The (Digital) Code



(insert downloadable SoilDigital.py here)

#!/usr/bin/python2

import schedule #setup running tasks at certian time

import time #set up the time (internet required)

import smtplib #something to do with sending emails

import RPi.GPIO as GPIO #sets up the GPIO pins

from datetime import datetime #get the time from the internet?

now = datetime.now()

channel = 21 #signal pin for sensor board (pin40 GPIO 21)

GPIO.setmode(GPIO.BCM) #set GPIO to Broadcom SOC channel number

GPIO.setup(channel, GPIO.IN) #set as input

print ‘%s/%s/%s %s:%s:%s’ % (now.month, now.day, now.year, now.hour, now.minute, now.second) #prints time on screen

# start of soil check process

def soiltest(t):

print(“Checking Soil”)

if GPIO.input(channel): #if soil sensor is not triggered (no water detected) turn water on and send email

GPIO.setwarnings(False) #disables warnings

print “Start Water”

GPIO.setup(17, GPIO.OUT) #signal for relay board (pin11 GPIO 17)

GPIO.output(17, GPIO.LOW) #turn motor on

time.sleep(10) # run motor for x seconds

GPIO.output(17, GPIO.HIGH) # turn motor off

from email.mime.multipart import MIMEMultipart #Start of Email

from email.mime.text import MIMEText

from email.mime.base import MIMEBase

from email import encoders

fromaddr = “YourEmailAddress” #I just email my self

toaddr = “YourEmailAddress” #so these are the same

msg = MIMEMultipart()

msg[‘From’] = fromaddr

msg[‘To’] = toaddr

msg[‘Subject’] = “Soil Test Results” #what the email subject line says.

body = ‘Water Was Added.’ #what the email message says

msg.attach(MIMEText(body, ‘plain’))

part = MIMEBase(‘application’, ‘octet-stream’)

encoders.encode_base64(part)

server = smtplib.SMTP(‘YourEmailSeverInfo’, 587) #outgoing email server field and port#

server.ehlo

server.starttls()

server.login(fromaddr, “YourEmailPassword”) #email password

text = msg.as_string()

server.sendmail(fromaddr, toaddr, text)

server.quit()

print(“Email Sent”) #End of Email

else: #if soil sensor is triggered (water is detected) just send email

print “Water Detected”

from email.mime.multipart import MIMEMultipart #Start of Email

from email.mime.text import MIMEText

from email.mime.base import MIMEBase

from email import encoders

fromaddr = “YourEmailAddress” #I just email my self

toaddr = “YourEmailAddress” #so these are the same

msg = MIMEMultipart()

msg[‘From’] = fromaddr

msg[‘To’] = toaddr

msg[‘Subject’] = “Soil Test Results” #what the email subject line says.

body = ‘Moisture Level OK.’ #what the email message says

msg.attach(MIMEText(body, ‘plain’))

part = MIMEBase(‘application’, ‘octet-stream’)

encoders.encode_base64(part)

server = smtplib.SMTP(‘YourEmailSeverInfo’, 587) #outgoing email server field and port#

server.ehlo

server.starttls()

server.login(fromaddr, “YourEmailPassword”) #email password

text = msg.as_string()

server.sendmail(fromaddr, toaddr, text)

server.quit()

print(“Email Sent”) #End of Email

return

# Run 1st Soil Test at time (6:30am) 24 hour format

schedule.every().day.at(“6:30”).do(soiltest,’Running Soil Test’)

# Run 2nd Soil Test at time (6:00pm) 24 hour format

schedule.every().day.at(“18:00”).do(soiltest,’Running Soil Test 2′)

while True:

schedule.run_pending()

time.sleep(60) # wait one minute

11-21-18 = UPDATE



It turns out using the sensor in Digital mode wasn’t really accurate enough and it wanted to water almost every day even if the soil was visibly wet. I probably could have adjusted it with the onboard pot but I really want to learn how to do it use it as an Analog sensor so I bough this ADS1115 Analog-to-Digital converter to try.

I got the converter all solder up and wired in (except for the soil sensor) at this point now I just need to figure out the new code.

I finally figured out most of the new code and modified the case to fit the new sensor and finished wiring everything up. The code still needs a little bit of work… I need to figure out how to change the value to a percentage and I also need to determine if the sensor is even calibrated correctly.

The (Analog) Code

(insert downloadable SoilAnalog.py here)

#!/usr/bin/python2

import schedule # setup running tasks at certian time

import time # sets up the time (internet required)

import smtplib # something to do with sending emails

import RPi.GPIO as GPIO # sets up the GPIO pins

import Adafruit_ADS1x15 # Import the ADS1x15 module.

adc = Adafruit_ADS1x15.ADS1115() # Create an ADS1115 ADC (16-bit) instance.

from datetime import datetime # get the time from the internet?

now = datetime.now()

channel = 21 # signal pin for sensor board (pin40 GPIO 21)

GPIO.setmode(GPIO.BCM) # set GPIO to Broadcom SOC channel number

GPIO.setup(channel, GPIO.IN) # set as input

# Choose a gain of 1 for reading voltages from 0 to 4.09V.

# Or pick a different gain to change the range of voltages that are read:

# – 2/3 = +/-6.144V

# – 1 = +/-4.096V

# – 2 = +/-2.048V

# – 4 = +/-1.024V

# – 8 = +/-0.512V

# – 16 = +/-0.256V

# See table 3 in the ADS1015/ADS1115 datasheet for more info on gain.

GAIN = 1

# Start continuous ADC conversions on channel 0 using the previously set gain value.

adc.start_adc(0, gain=GAIN)

# Once continuous ADC conversions are started you can call get_last_result() to

# retrieve the latest result, or stop_adc() to stop conversions.

print ‘%s/%s/%s %s:%s:%s’ % (now.month, now.day, now.year, now.hour, now.minute, now.second) #prints time on screen

# start of soil check process

def soiltest(t):

print(“Checking Soil”)

start = time.time()

while (time.time() – start) <= 10.0: # Poll the sensor for 10 seconds.

value = adc.get_last_result() # Read the last ADC conversion value and print it out.

print(‘Channel 0: {0}’.format(value)) # Prints the value to screen

time.sleep(1.0) # Sleep for a second (so it reads the sensor tens times.)

while True:

# Read the last ADC conversion value and print it out.

value = adc.get_last_result()

print(‘Channel 0: {0}’.format(value))

if value > 20000: # if soil sensor is not triggered (no water detected) turn water on and send email

GPIO.setwarnings(False) # disables warnings

print “Start Water”

GPIO.setup(17, GPIO.OUT) # signal for relay board (pin11 GPIO 17)

GPIO.output(17, GPIO.LOW) # turn motor on

time.sleep(20) # run motor for 20 seconds

GPIO.output(17, GPIO.HIGH) # turn motor off

from email.mime.multipart import MIMEMultipart #Start of Email

from email.mime.text import MIMEText

from email.mime.base import MIMEBase

from email import encoders

fromaddr = “YourEmail@WhereEver.net” # I just email my self

toaddr = “YourEmail@WhereEver.net” # so these are the same

msg = MIMEMultipart()

msg[‘From’] = fromaddr

msg[‘To’] = toaddr

msg[‘Subject’] = “Soil Test Results – Water Added” # what the email subject line says.

body = (‘Channel 0: {0}’.format(value)) # what the email message says (sends last sensor value)

msg.attach(MIMEText(body, ‘plain’))

part = MIMEBase(‘application’, ‘octet-stream’)

encoders.encode_base64(part)

server = smtplib.SMTP(‘YourServer.WhereEver.net’, 587) # outgoing email server field and port#

server.ehlo

server.starttls()

server.login(fromaddr, “YourPassword”) # email password

text = msg.as_string()

server.sendmail(fromaddr, toaddr, text)

server.quit()

print(“Email Sent”) # End of Email

else: # if soil sensor is triggered (water moister is detected) just send email

print “Water Detected”

from email.mime.multipart import MIMEMultipart # Start of Email

from email.mime.text import MIMEText

from email.mime.base import MIMEBase

from email import encoders

fromaddr = “YourEmail@WhereEver.net” # I just email my self

toaddr = “YourEmail@WhereEver.net” # so these are the same

msg = MIMEMultipart()

msg[‘From’] = fromaddr

msg[‘To’] = toaddr

msg[‘Subject’] = “Soil Test Results – Water OK” # what the email subject line says.

body = (‘Channel 0: {0}’.format(value)) # what the email message (sends last sensor value)

msg.attach(MIMEText(body, ‘plain’))

part = MIMEBase(‘application’, ‘octet-stream’)

encoders.encode_base64(part)

server = smtplib.SMTP(‘YourServer.WhereEver.net’, 587) # outgoing email server field and port#

server.ehlo

server.starttls()

server.login(fromaddr, “YourPassword”) # email password

text = msg.as_string()

server.sendmail(fromaddr, toaddr, text)

server.quit()

print(“Email Sent”) # End of Email

return

# Run 1st Soil Test at time (6:30am) 24 hour format

schedule.every().day.at(“6:30”).do(soiltest,’Running Soil Test’)

# Run 2nd Soil Test at time (6:00pm) 24 hour format

schedule.every().day.at(“18:00”).do(soiltest,’Running Soil Test 2′)

while True:

schedule.run_pending()

time.sleep(60) # wait one minute

I recently bought a Palette 2 Pro for my 3D printers which allows me to print up to 4 colors at once so I made a new cover with colored text while testing the new features.

All Information, Pictures, and Material is copyright © 2018 by Stephen Thone and may not be used for any personal or commercial purposes without the consent of the author. All rights reserved. The Author makes no guarantees or warranties as to the accuracy or completeness of, or results to be obtained from accessing and using the Information herein.