We recently moved offices at work as we have been expanding over the last 12 months, and outgrew our old office. Our new office is fantastic and has its very own Garden Room, where we have a picnic bench, Astro-turf flooring and, until recently, two office plants (named Sven and Elizabeth).

The "Garden Room" at the Silktide offices in Derby

However, as with any office there are times where people neglect the simple things and our office plants were the unfortunate victims here. Everybody was under the assumption that somebody else was watering the plants, which sadly was not the case. After a number of weeks without water, one of our office plants died and ended up in the compost heap in the sky but the other (Sven) was clinging to life, and with some watering has slowly returned to its former glory.

Now when you work for a software development company, if there is an excuse to use something involving tech or code then we often will. Throwing myself into the mix, and my passion for Raspberry Pi, then if I have the excuse to use one at work then I will!

 

Raspberry Pi soil moisture sensor

I’ve come across Raspberry Pi soil moisture sensors before, but I’ve never really had the use-case to buy one. Until now. Our office plants plant means a lot to our MD’s wife – so much so that each plant was named Elizabeth and Sven respectively (as you do) – as they used to be in her office and she donated them to us when she moved out. As you can imagine, she was not best pleased that one of the plants died (Elizabeth) and I thought that this would be the perfect case to whip out a spare office Raspberry Pi and put together a simple Python script that uses our preferred in-work chat tool, Slack.

For those of you who have not heard of Slack, it is a chat system that is perfect for in-office communication, especially if you want to cut out the unnecessary email conversations. It is also useful for collaboration – in fact, I’m in a Slack group with other Raspberry Pi fans and coders including the talented likes of Alex Ellis, Richard Gee and Ryan Walmsley to name but a few and it is really helpful when bouncing ideas of each other.

 

Inspiration for the project

After a quick search online for Raspberry Pi soil moisture sensors projects, I came across a relatively old post on ModMyPi about using the soil moisture sensor in combination with email to notify you when to water the plant.

I thought that this would be a great place to start, so I took parts from their script and added my own parts to help call a Slack webhook to send notifications to our office Slack channel whenever the soil moisture is too low.

Update: 13 September 2017

Having ran the initial code for at least 24 hours, I soon realised that the Slack webhook was being triggered almost constantly for some reason. This happened at 5AM, so there were hundreds of Slack messages about the plant needing to be watered, which not only annoyed my colleagues, but it also made me think about how often the script should be run.

This morning, I re-wrote the script to use a simpler approach to triggering the Slack webhook – in short, I made use of the Raspberry Pi Zero W’s internal pull-up resistors and simply wrote the Python script to check if this changed, rather than looking for a complete state change either way. I also felt that almost constant checking of the soil moisture was over-the-top, so I have tweaked the sleep times to every 15 minutes if the soil is moist, and every 45 minutes if the soil is dry. I’m not too fussed if the plant doesn’t need watering, so I thought this would make more sense.

The code has now been updated to reflect these changes.

Required parts

For my project, I used the following parts:

  • Raspberry Pi Zero W (for size and convenience)
  • A soil moisture sensor (available on ModMyPi and eBay)
  • A  Slack account
  • A custom webhook integration on Slack
  • My code on  GitHub.

Connect the soil moisture sensor

This is very simple. There are two connections on the fork shaped part (that goes into the soil) marked positive and negative – simply connect these to the sensor module part. It doesn’t matter which of the two pins you use. Whilst there are 4 pins on the part of the sensor module that connects to your Raspberry Pi, you only need 3: VCC, GND and D0. Connect these as follows:

  • VCC to pin 1 on the Pi (3.3v)
  • GND to pin 9 on the Pi (Ground)
  • D0 to pin 11 on the Pi (GPIO 17).

If connected correctly, you should see at least 1 LED light up on the sensor module. If you then stick the fork end into a glass of water (only the prongs, not the entire module!), you should then see a second LED light up on the sensor module. If you don’t, go back and check your connections – try swapping the two from the fork to the sensor module around first.

Calibrate the soil moisture sensor

There is a small potentiometer on the sensor module that allows you to calibrate the sensitivity of the sensor. The best way to do this is to insert the prongs of the fork into the soil and then slowly turn the potentiometer until 1 LED goes off, this means that when the soil moisture drops to this level, the script will be triggered and alert you via Slack that it needs watering.

Clone my code on GitHub

Obviously you’ll need the code, so clone this on your Raspberry Pi by running this in your command line:

$ git clone git://github.com/raspberrycoulis/plant-hydro-slack.git

If you want to see what the code looks like, then you can also use this:

#!/usr/bin/python

#####################################################################################
# This was inspired by a guide on ModMyPi (http://bit.ly/mmpsms).                   #
#                                                                                   #
# Use a soil moisture sensor and a Raspberry Pi to monitor the soil in a plant pot  #
# and warn the office, via Slack, that it needs watering.                           #
#                                                                                   #
# By Wesley Archer (@raspberrycoulis - https://www.raspberrycoulis.co.uk)           #
#####################################################################################

# Import the necessary libraries:
import RPi.GPIO as GPIO
import time
import httplib, urllib
import urllib2
import json

# Set the GPIO mode:
GPIO.setmode(GPIO.BCM)

# Define the GPIO pin that the moisture sensor (D0 on the sensor) is connected to:
channel = 17

# Set the GPIO pin above as an input and set the internal pull-up resistor down:
GPIO.setup(channel, GPIO.IN,pull_up_down=GPIO.PUD_DOWN)

# Slack webhook - get this from https://api.slack.com/custom-integrations/incoming-webhooks
webhook_url = "ADD_HERE"

# This is the function that calls the Slack webhook to notify you:
def postToSlack():
    data = '{"attachments":[{"fallback":"Water plant!","pretext":"The soil is too dry!","color":"#cc0000","fields":[{"title":"The Garden Room plant needs watering!","short":false}]}]}'
    slack = urllib2.Request(webhook_url, data, {'Content-Type': 'application/json'})
    post = urllib2.urlopen(slack)
    post.close()

# Run the code in an infinite loop. If the soil is dry, a Slack notification is triggered:
while True:
    if GPIO.input(channel)==False:
        #print('Soil is moist')     # Uncomment to print console commands
        time.sleep(900)             # Sleep for 15 minutes (900 seconds)
    else:
        #print('Soil is dry!')      # Uncomment to print console commands
        postToSlack()               # Trigger the Slack webhook notification
        time.sleep(2700)            # Sleep for 45 minutes (2700 seconds)

Edit the code to use your Slack webhook

After you have created your webhook integration in Slack, you’ll need to edit the Python script accordingly:

$ cd plant-hydro-slack
$ nano dry-to-slack.py

Find the variable on line 29 called webhook_url and then replace "ADD_HERE" with your Slack webhook URL. Then save and exit:

$ CTRL+X
$ Y

Your script is now good to go.

When your plant needs watering, the Pi will send you Slack messages accordingly

When your plant needs watering, the Pi will send you Slack messages accordingly

Running the script automatically on boot using systemd

In order to run the script automatically when the Raspberry Pi boots, I recommend using systemd to run it as a service:

$ sudo nano /lib/systemd/system/moisturesensor.service

Then add the following:

[Unit]
Description=Plant soil moisture sensor service
After=multi-user.target

[Service]
Type=idle
ExecStart=/usr/bin/python /home/pi/plant-hydro-slack/dry-to-slack.py > /home/pi/plant-hydro-slack/dry-to-slack.log 2>&1
Restart=always

[Install]
WantedBy=multi-user.target

The parts to check are the ExecStart command as this assumes the dry-to-slack.py script is located in /home/pi/plant-hydro-slack so please update accordingly if you have installed the script in a different location.

Once you have done this, Ctrl+X to exit and Y to save then run:

$ sudo chmod 644 /lib/systemd/system/moisturesensor.service
$ sudo systemctl daemon-reload
$ sudo systemctl enable moisturesensor.service

You can sudo reboot or simply run sudo systemctl start moisturesensor.service to start the script. Check the status by running sudo systemctl status moisturesensor.service.

Taking it further

Now I have an understanding of how webhooks can be triggered by a Python script on a Raspberry Pi, I thought about how else they could be used. One idea that I currently use in the script in our office is to utilise the IFTTT Maker Webhooks channel to trigger an update to a Google Sheet, keeping track of all the time the soil is moist but without annoying everybody with constant Slack messages.

Create the IFTTT Maker Webhook

You’ll need a free IFTTT (If This Then That) account first, then you’ll need to activate the Maker Webhook channel. Once you do this, create a new applet and use the Maker Webhook as the “If This” part. Set a trigger keyword, as this will form the basis of the webhook and then set an action for the “Then That” section of your applet. In my example, I chose to add a new row to a Google Sheet under the Google Drive action.

You can add variables if you want, but I didn’t see the need for this so I just continued without them. You can find the webhook you’ll need by clicking on the “Documentation” button on the Maker Webhook channel, and test it works by substituting your trigger word in the appropriate box. For example:

curl -X POST https://maker.ifttt.com/trigger/{event}/with/key/{your_unique_key}

You actually want only the URL, so ignore the “curl -X POST” part. Copy this as you’ll need it later.

Modify the code

To use the new webhook, I edited my code and added a new function:

$ nano dry-to-slack.py

The code now looks like this:

#!/usr/bin/python

import RPi.GPIO as GPIO
import time
import httplib, urllib
import urllib2
import json
import requests

GPIO.setmode(GPIO.BCM)
channel = 17
GPIO.setup(channel, GPIO.IN,pull_up_down=GPIO.PUD_DOWN)

# Slack webhook
webhook_url = "ADD_HERE"

# IFTTT Maker Webhook
ifttt_url = "ADD_HERE"

def postToSlack():
    data = '{"attachments":[{"fallback":"Water plant!","pretext":"The soil is too dry!","color":"#cc0000","fields":[{"title":"The Garden Room plant needs watering!","short":false}]}]}'
    slack = urllib2.Request(webhook_url, data, {'Content-Type': 'application/json'})
    post = urllib2.urlopen(slack)
    post.close()

def IFTTT():
    requests.post(ifttt_url)

while True:
    if GPIO.input(channel)==False:
        print('Soil is moist')
        IFTTT()
        time.sleep(900)
    else:
        print('Soil is dry!')
        postToSlack()
        time.sleep(2700)

To get this working, you’ll also need to install a couple of modules on your Pi:

$ sudo apt-get install python-pip
$ sudo pip install requests

This will install the PIP Python module, and then use PIP to install the requests module required for the IFTTT webhook to be triggered. You can then test it out by running:

$ ./dry-to-slack-ifttt.py

You should then see wither “Soil is moist” or “Soil is dry!” in the console, and if it is the former you will see a new row on a new Google Sheet with the date and time, as well as the trigger keyword, or if it is the latter, you should get a Slack notification telling you to water the plant.

Running the systemd service at set times

One other issue I noticed was that the code would run constantly, and could trigger Slack messages during the early hours or over a weekend when nobody is about. This would soon become annoying, so one simple method I used is to trigger the service to start and stop using crontab:

$ sudo crontab -e

Then add the following two lines:

# Start Plant Soil Moisture Monitoring Service every weekday at 8:00am
00 08 * * 1-5 systemctl start plant-moisture.service

# Stop Plant Soil Moisture Monitoring Service every weekday at 9:30pm
30 21 * * 1-5 systemctl stop plant-moisture.service

I always find Crontab Guru really useful when creating cron jobs as I can be 100% confident they will run at the time I want.

Exit (CTRL+X) and save (Y) to install the new crontab.

There may be more elegant ways of running the script at designated times, but I didn’t know how. I’m happy to take any suggestions though!