Keep your garden growing with this Photon-powered Wi-Fi irrigation project

Learn how to build an automated irrigation system using a Particle Photon and a bit of code.

Michael Greene article author avatarMichael GreeneSeptember 24, 2018
Keep your garden growing with this Photon-powered Wi-Fi irrigation project

Editor’s note: this post was originally published on Michael Greene’s project blog and explains step-by-step how to build a Wi-Fi irrigation system using a Photon. It is republished here with the permission of the author.

This project really traces its roots back to 2012, when I built a temporary irrigation solution for a small herb garden on our apartment balcony. That particular solution was designed to simply water our herbs while we were away on vacation, and was certainly limited by the fact that the 5 gallon bucket only held a 5-6 day water supply, and contained no smarts of any kind—outside of the MacGyver’d pond pump connected to a Christmas light timer.

In 2016 we moved into our house — outgrowing the 5-gallon bucket solution, and starting our home automation journey. After building a new herb and vegetable planter, and planting new shrubs this year, it was time to upgrade our irrigation solution.

Table of contents

Parts list
Hardware & assembly
Software & automation
Next steps

The top priority is obviously to keep the plants alive (my wife and I are notorious for forgetting to water things). On top of that, I wanted this project to integrate with our existing SmartThings-based home automation, to water at ideal times of day, and to only water if necessary. Lastly, I decided early on that this project would be open source.

All source code, parts lists, build guides, etc. are available on GitHub. Copy it, hack it, do what you want with it, and post a comment with photos of your version if you build one.

Parts List


  • Particle Photon
  • Particle Relay Shield
  • IP66 rated enclosure
  • 3-Outlet grounded adapter
  • U.FL mini PCI to RP-SMA antenna pigtail
  • 7dBi RP-SMA Wi-Fi antenna
  • 7-Pin Waterproof connector
  • Orbit 24VAC transformer
  • 12VDC, 2A power adapter
  • 20′ Outdoor extension cord
  • PG9 waterproof cable connector
  • P3 wire nuts (x3)
  • Pipe strap (x2)


  • Garden reel leader hose
  • 3/4″ Garden hose to 1/2″ PVC Adapter
  • 3/4″ Sprinkler valve (x2)
  • Rain bird drip irrigation 4-Port manifold (x2)
  • 90° Elbow connector, 1/2″ Schedule 40 PVC fitting (x2)
  • 3/4″ MIPT to PVC adapter (x2)
  • 3/4″ to 1/2″ Reducer bushing (x2)
  • 3/4″ to 1/2″ Threaded reducer bushing (x2)
  • 1/2″ x 1-1/2″ Nipple (x2)
  • 1/2″ PVC pipe cut to 2″ in length (x5)
  • Tee connector, 1/2″ Schedule 40 PVC fitting
  • PVC primer & cement
  • Teflon/PTFE tape

Irrigation Components

  • 1/4″ Rain bird distribution tubing
  • Assorted drip irrigation emitters

Hardware & Assembly

Irrigation Manifold

We’ll begin by assembling the irrigation manifold. The manifold splits a standard garden hose connection to the two valves, and adapts those valves down to 1/4” drip irrigation hose.

The parts list references all of the parts you’ll need. In addition to being available at the provided links, they’re also readily available at most hardware stores. It’s important to note that not all 3/4” threads are the same.

Due to the differences between hose pipe threads, NPT threads, etc. it may be wise to take a trip to your local hardware store, so you can test fit while you’re there and be sure you have the correct components. Plus, running around Home Depot, grabbing and testing parts for a project like this is always a good time!

Once you have your parts in hand, simply use the drawing above as a guide, being sure to prime and glue all slip and socket joints, and wrap all threaded joints in PTFE/Teflon tape prior to assembly. You’ll need a total of 5 pieces of 1/2″ PVC pipe cut to approximately 2″ in length (though you’ll likely end up buying a 5 or 6 foot piece).

  1. Using one of your pieces of 2” PVC, connect the garden hose adapter to one end, and the tee connector to the other.
  2. With another piece of 2” PVC, connect an elbow connector to one side, and insert the other side into one of the 3/4″ to 1/2″ reducers (this is the reducer without any threads). Then insert the end with the reducer into one of the 3/4″ to MIPT threaded adapters (which will later screw into one of the valves). Repeat this step so we have a second one for the second valve.
  3. Take one of the 1/2″ x 1-1/2” nipples, and screw one side into one of the 3/4″ to 1/2” threaded reducer bushing. Screw one of the 4-port drip irrigation manifolds onto the other side of the nipple. Repeat this step so we have a second one for the second valve.
  4. With one of the valves, connect one of the elbow subassemblies to the source side of the valve (as noted by the direction of the arrow). Connect one of the 4-port manifold subassemblies to the output side of the valve. Repeat this step with the second valve.
  5. Lastly, using the two remaining 2” pieces of PVC, connect the elbow ends of the valve subassemblies to each side of the tee connector.


The controller assembly is one of the most simple electronics projects you’ll do. Many of the components are plug and play, with minimal soldering required. I used a 7-pin waterproof connector for my build to give me flexibility of adding additional valves in the future, but if you know you’re going to only have 2 for the long term, feel free to switch to a 3-pin or 4-pin connector (as long as it’s rated to be waterproof). The biggest electronic challenge with the controller is that the valves operate on 24 VAC, while the relay shield (and thus the Particle Photon) is DC powered.

  1. Start by drilling all of the holes in the waterproof enclosure. You’ll need to drill a hole for the PG9 connector, one for the RP-SMA antenna connector, one for the 7-pin waterproof connector, and 4 holes for the pipe straps to mount the manifold to the enclosure.
  2. Mount the PG9 connector, RP-SMA antenna connector, and 7-pin waterproof connector.
  3. Using 4 machine screws with lock washers, mount the manifold to the top of the controller box using two pipe straps, being sure to place silicon sealant around the inside of the bolts to help ensure the waterproofing of the box.
  4. Cut your extension cord, approximately 2 feet from the female end, and thread the male end through the PG9 connector. Clamp down the outside of the connector, then strip and reconnect the two ends of the extension cord. Cap the wires with wire nuts. Connect the 3-outlet grounded adapter to the female end of the extension cord inside the enclosure. Connect the 24VAC transformer and 12VDC transformer to the 3-outlet adapter.
  5. Solder the valve wires to the male end of the 7-pin waterproof connector. I used pins 1/2 for valve 1 and 3/4 for valve 2. Also solder 4 wires to the matching pins of the female side of the 7-pin connector, and wire them (along with the 24VAC transformer) as shown in the schematic below. We’ll use the “MAIN” (center) terminal on the relay and the “NO” (normally open) side of the relay to control the valves.
  6. Connect the 12VDC transformer’s barrel jack to the connector on the relay shield, and insert the Particle Photon into its place on the relay shield.
  7. Connect the pigtail from the u.Fl to RP-SMA antenna connector to the u.Fl connector on the Particle Photon, and screw the external wifi antenna to the lid of the enclosure.
  8. Lastly, use a piece of double-sided tape to mount the relay shield to the underside of the enclosure’s lid.

Software & Automation

Setting up the Photon

Configuring the Photon and flashing the software is relatively simple. Refer to the Particle documentation for instructions on setting up a Photon if you’ve never done it before. Once your Photon has been associated with your Particle account, use the Web IDE to flash the wifi-irrigation project’s firmware to your Photon.

  1. Create a new Particle app.
  2. Paste the contents of the wifi-irrigation.ino sketch into the IDE’s code window, and save the app as “wifi-irrigation.”
  3. Click the “Libraries” icon at the bottom left of the screen, and include the “RelayShield” library in the project by selecting it, then clicking the blue “Include in Project” button, then select the “wifi-irrigation” app. Click the blue “Confirm” button to continue.
  4. Save the app, and click the checkmark icon to verify the sketch.
  5. Assuming no errors are discovered, click the devices icon at the lower left of the screen, click the star icon next to the Photon you wish to flash the code to, then click the lightening bolt at the top left corner to flash the code to the Photon.
  6. Within the IDE, identify the Device ID and Access Token as we’ll need to provide these to SmartThings in the next section. The Device ID can be found by clicking the devices icon at the lower left, then the right arrow icon next to the Particle device that we flashed the code to. Your Access Token can be found by clicking the settings gear at the lower left.

wifi-irrigation project for Particle Photon
Copyright 2018 Michael Greene

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 
file except in compliance with the License. You may obtain a copy of the License at:
Unless required by applicable law or agreed to in writing, software distributed under 
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 
ANY KIND, either express or implied. See the License for the specific language governing 
permissions and limitations under the License.

// This #include statement was automatically added by the Particle IDE.
#include "RelayShield.h"

// create an instance of the RelayShield library for the valves
RelayShield valveRelays;

// create function methods and an array to track relay state
int relayOn(String command);
int relayOff(String command);
int relayState[] = {0, 0, 0, 0, 0};

// select the u.FL antenna

void setup() {
    Particle.function("relayOn", relayOn);
    Particle.function("relayOff", relayOff);

    Particle.variable("valve1", &relayState[1], INT);
    Particle.variable("valve2", &relayState[2], INT);
    Particle.variable("valve3", &relayState[3], INT);
    Particle.variable("valve4", &relayState[4], INT);

void loop() {
    // nothing needed here, functions will run when called via the API

// define relayOn method
int relayOn(String command){
    Particle.publish("Valve On", command);
    // convert char command to valve integer
    char inputStr[64];
    int i = atoi(inputStr);

    // turn the desired relay on
    relayState[i] = 1;

    // respond when successful
    return 1;

// define relayOff method
int relayOff(String command){
    Particle.publish("Valve Off", command);
    // convert char command to valve integer
    char inputStr[64];
    int i = atoi(inputStr);

    // turn the desired relay off;
    relayState[i] = 0;

    // respond when successful
    return 1;


Creating SmartThings Device Handler

Creating the SmartThings device handler can be done by manually importing the device handler’s Groovy file, or by connecting your SmartThings account to my SmartThings repo (where you’ll also find SmartApps and custom Device Handlers from other projects).

Either follow this guide to connect to my repo (owner: webdes03, name: SmartThingsPublic, branch: master), or follow the steps below to manually deploy the device handler.

  1. Login to your SmartThings IDE.
  2. From the top navigation bar, select “My Device Handlers”, then click “Create New Device Handler”.
  3. Select the “From Code” tab, and paste the contents of the wifi-irrigation Groovy Device Handler into the box, then click the “Create” button.
  4. Click the “Publish” button at the top right.
Wifi Irrigation Control for Particle Photon
Copyright 2018 Michael Greene

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 
file except in compliance with the License. You may obtain a copy of the License at:
Unless required by applicable law or agreed to in writing, software distributed under 
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 
ANY KIND, either express or implied. See the License for the specific language governing 
permissions and limitations under the License.

metadata {
	definition (name: "wifi-irrigation", namespace: "webdes03", author: "Michael Greene") {
		capability "Switch"
		capability "Refresh"
		capability "Polling"
	multiAttributeTile(name:"switch", type: "generic", width: 6, height: 4, canChangeIcon: true) {
		tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
			attributeState "unknown", label:'${name}', action:"refresh.refresh", icon:"st.Outdoor.outdoor12", backgroundColor:"#e5e5e5"
			attributeState "off", label:'${name}', action:"switch.on", icon:"st.Outdoor.outdoor12", backgroundColor:"#ffffff", nextState:"turningOn"
			attributeState "on", label:'${name}', action:"", icon:"st.Outdoor.outdoor12", backgroundColor:"#47bd18", nextState:"turningOff"
			attributeState "turningOn", label:'${name}', action:"switch.on", icon:"st.Outdoor.outdoor12", backgroundColor:"#e5e5e5", nextState:"on"
			attributeState "turningOff", label:'${name}', action:"", icon:"st.Outdoor.outdoor12", backgroundColor:"#e5e5e5", nextState:"off"
	standardTile("refresh", "capability.refresh", width: 2, height: 2,  decoration: "flat") {
		state ("default", label:"Refresh", action:"refresh.refresh", icon:"st.secondary.refresh")
	details(["switch", "refresh"])

preferences {
	input("deviceId", "text", title: "Device ID", required: true, displayDuringSetup: true) // particle device id
	input("authorizationToken", "text", title: "Authorization Token", required: true, displayDuringSetup: true) // particle authorization id
    input("relayNumber", "text", title: "Relay ID (1-4)", required: true, displayDuringSetup: true) // relay number (1-4) the valve is connected to

def installed() {
	log.debug "${}: Installed"

def updated() {
	log.debug "${}: Initializing"

def on() { "${}: Turning ON"

def off() { "${}: Turning OFF"

def refresh() { "Refreshing ${}"

def getParticleRelayStatus() {
	def queryVariable = "valve${relayNumber}" "Getting status of $queryVariable"
	def params = [
		uri: "$deviceId/$queryVariable?access_token=$authorizationToken"
	httpGet(params) { resp ->
		def status = "unknown"
		if ( == 0) {
			status = "off"
		} else if ( == 1) {
			status = "on"
		} "${}: Status: ${status}"
		sendEvent(name: "switch", value: status, isStateChange: true)
		runIn(300, getParticleRelayStatus)

def sendParticleRelayCommand(command){
	def params = [
		uri: "$deviceId/$command?access_token=$authorizationToken",
		body: [arg: relayNumber]
	httpPostJson(params) { resp ->
        if ( == 1) {
		} else {
			sendEvent(name: "switch", value: "unknown", isStateChange: true)

Creating SmartThings Devices

  1. Now that our device handler has been created, we need to create a device inside of SmartThings for each of our valves.
  2. From the top navigation of the SmartThings IDE, select “My Devices”.
  3. Click the “New Device” button.
  4. Enter a name for your valve (ie: Front Flower Beds), a device network id (this must be unique, so use something like “wifi-irrigation-1” where 1 is the number of the valve), and select “wifi-irrigation” at the bottom of the type list, then click the “Create” button.
  5. Select the device that you just created (ie: Front Flower Beds), and click the “edit” link in the preferences section.
  6. Enter the Device ID and Authorization Token that we noted when we setup the Photon, and the number of the valve/relay that this device is connected to, then click “Save”.
  7. With the device created, and configured, you should now see it listed in the SmartThings app.


There’s a number of ways to automate the watering of your plants using SmartThings and the wifi-irrigation project. I chose to use Webcore thanks to its ability to handle some pretty complex logic, but you could also choose to create simpler rules with SmartThings routines, or simply control your watering manually. If you wish to use Webcore, and have not previously configured the Webcore engine, follow the simple guide at to get started.

I’ve broken my watering routines into three separate Webcore pistons. One that starts the watering of my front flower beds twice a day (around sunrise and sunset), and one that starts the watering of my vegetable garden twice a day (around sunrise and sunset). I then use a third piston to actually control how long each zone gets watered for. This third piston runs every minute, checks whether each valve is open or not, and if it is open, counts how many minutes it has been open.

Once the valve has been open for a maximum amount of time (in my case, 10 minutes for my front flower beds, and 3 minutes for my vegetable garden) the valve is shut off automatically. Handling the timing and shutoff separately from the piston that starts the schedule also gives me flexibility to manually start watering any time I want to outside of the scheduled start, and the valves will still automatically be closed once the timed limits have been reached.

You may import my pistons by using the following codes. Simply replace each generic “switch” device with the appropriate SmartThings device.

Next steps

I have begun work on a follow-up to this project, a solar powered Particle Photon and SmartThings connected soil moisture sensor. This sensor will help add some additional intelligence around when to water and for how long, and I’ll also address including that data in the Grafana home dashboards that we built previously.