Projects Research & Design Hub

Smart Gardening System Uses Raspberry Pi

Written by Richard Sears

The GardenPi Project

There are many advantages to growing your own food. Embedded technology can help manage the process. With that in mind, Richard shares how he crafted a system to manage and monitor his garden, worm farm, irrigation and fish tanks. The system is designed to manage, monitor and control a series of sprinkler valves and a multitude of sensors for pretty much any size of project.

  • How to build a system to monitor and control multiple garden functions

  • How to partition the tasks

  • How to interface with the Raspberry PI 4 SBC

  • How to build the enclosure

  • How to build the front panel display

  • How to do the code design

  • How to implement the water source control

  • How to set up the back up and recovery system

  • Raspberry PI 4 SBC

  • Bosch BME280 environmental sensor

  • Maxim Integrated DS18B20 temp probes

  • SainSmart relays

  • Samsung SD cards

  • Polycase enclosure

  • Python3

  • Vantage PRO2 system from Davis Instruments

  • Front Panel Express’ Front Panel Designer

  • Adafruit 7″ touchscreen

Today more than ever people are turning to ways to become more self-sufficient, growing more of their own foods and controlling what they put on their tables. Home grown food tastes better, has less of an environmental impact than processed foods and is generally better for us. In this article, I discuss how my own family’s movement toward sustainability led to the design and development of a comprehensive platform to manage and monitor our garden, worm farm, irrigation and fish tanks.

Called “GardenPi,” this system is designed to manage, monitor and control a series of sprinkler valves and a multitude of sensors for pretty much any sized irrigation/hydroponics /aquaponics project. It can be scaled (using the hardware I used) from 1 to 32 zones for water, and 7 zones for power. It is built almost entirely in Python3 with a Flask web interface and relies on CSS to make the web interface very fast. It is written and designed to run on the Raspberry Pi 4, although it should run on pretty much any Pi because there is very little overhead.

GardenPi was designed around my family’s desire to get more heavily into gardening, hydroponics and eventually full aquaponics. Because we have several fish tanks and do large water changes weekly, we wanted to be able to use that nutrient-rich water for the garden instead of just dumping it down the drain. Every week my wife would have to cart fish tank water around the garden beds to water the plants manually and then switch back to a hose. This was getting very time consuming and tedious. Because of that, the basic concept of GardenPi was born.


The gardening part by itself would have been pretty easy. We could have used a simple irrigation controller, but we needed several water sources, in our case freshwater (street water) and water from our fish tanks. Standard irrigation controllers did not provide the functions we needed to manage the process of moving back and forth between water sources automatically and on demand. So, I designed a new irrigation layout and with my sons help dug up the yard and installed six separate garden irrigation zones separate from our lawn irrigation. This new system included the necessary valves so we could switch between street water and a large tank filled with fish tank water as needed. Now I just needed the software to manage it all.

As with any project, I wanted to look more long term. Our family sat down and talked about what we really wanted to do. One was to get into much larger fish tanks (around 200 to 500 gallons), to greatly expand our garden to provide more space for year-around growing (we live in Phoenix, AZ), start to dabble in full hydroponics and eventually move into full aquaponics and eliminate street water gardening altogether—relying 100% on old fish tank and aquaponic wastewater instead. Street water would be managed by a RODI (reverse osmosis and deionization) system and feed into the fish and aquaponic tanks and all plant water would come from our fish tanks. As you can imagine, this would require quite a bit of management and thinking ahead and out-of-the-box.

The system designed for our needs ended up with the following configuration: 27 “water” zones expandable to 32, 8 “power” zones of which 7 are user-controlled with the 8th power zone managing our sprinkler transformer, 6 temperature sensors including one to monitor the temperature of the dirt in our worm farm, 3 humidity sensors, 1 barometric sensor, DC current and voltage sensors, AC current and voltage sensors, 4 ultrasonic water level sensors, 4 non-contact liquid level sensors and a 7” touchscreen for local control of the system.


I broke the project down into four distinct tasks. The first task was to design a high-level overview of the system and the components we would need to make it work. Second was the hardware design. We would need to come up with all of the necessary components that would make the hardware side of the project work. Third was the coding stage of the project. We would need to actually write and test the code to run the hardware. And finally, we would need to install all of the necessary irrigation components to actually run the system. Because the project was so large, we decided to initially focus on the irrigation, power and automatic wastewater management side of the project along with RODI control, environmental monitoring and notifications. We could then expand the system into hydroponic and aquaponic control as we continue to build out our infrastructure.

In addition to the sensors we would be using, the system also interacts directly as shown in Figure 1 with our already installed power and water monitoring systems. This system was built on Emoncms, which is part of the OpenEnergyMonitor project. We use our Emoncms data to gather water utilization information for GardenPi via smart water meters on our property and do the same for monitoring the AC circuit utilization for GardenPi. Other data such as outside temperature and humidity are likewise drawn from an outside source, namely our Vantage PRO2 system from Davis Instruments that we have installed on the property. This information is written directly to the Neptune database automatically by outside scripts. If you don’t use these readings, you can set them to “0” in the database and that’s all that will be displayed in the web interface. In future versions I plan to make this easier via a setup script.

GardenPi architecture

Before I started buying the parts or writing the code for the project, I laid out an overview of what I needed the system to be able to accomplish. I started out by drawing my plans and thinking very long term. Initially, we just needed to get the irrigation and fish water systems online, but I knew the system would grow. I wanted to design the hardware from the beginning to meet all of our needs from day one. The coding could come later.

After a lot of discussion on various gardening, hydroponic, aquaponic and aquarium forums, I ended up with the water flow system setup as diagrammed in Figure 2. The diagram consists of everything that we want to do with our setup. The hardware build that I’m sharing in this article handles all of this! The coding for much of it still has to be written, but the hardware is 100%. The electrical schematics for the GardenPi system are rather large and complex. Therefore, instead of including them in the article, they are available for download from Circuit Cellar’s article code and files webpage.

Water flow diagram (Click to enlarge)

Once I had the physical design figured out, I started buying parts. As you can see from the parts list, there are a lot of them! Of course, the nice thing about this project is that someone could scale it up or down to suit their needs. In fact, my hardware configuration actually supports a total of 32 water zones even though I am only using 27 of them myself. The core system is built on the Raspberry Pi 4B SBC with 4GB of RAM. It could have easily run on something less powerful, however, I wanted to make sure I had enough power to pretty much do anything that I wanted to do with it.

I have several other projects so I stuck with devices that have worked well for me in the past: SainSmart relays, RaspberryPi, Samsung SD cards and so forth. I am sure there could be cost savings going with less expensive products, but this will be outside in the Phoenix heat, so I tried to only use parts that I had used before or that had good reviews.

So, the first thing was to connect everything up and get it talking to the Pi SBC. I tried to stick with I2C as much as I could for most things, but I used Onewire for the Maxim Integrated DS18B20 temp probes and GPIO for all of my relays and a couple of interrupts for my serial expanders. In Figure 3, you can see the hardware layout of the system while I was testing it and before I started putting it all together.

Hardware during testing

Once I had the hardware figured out and working, I moved on to working on my ultrasonic tank level monitors. I wanted to be able to see how much water was available in each of our tanks, along with the temperature of the water. This required figuring out how to use the sensors—which I had not used before—and to figure out how accurate they actually were for day-to-day use. I needed to make sure they were accurate because I would be making water switching decisions based on how much (or little) water was available in my tanks. And no one wants an angry wife if you miscalculate the water and kill all of her plants!

So, I rigged up a five-gallon bucket and started playing around with the code to figure out how much water was available. Initially, I was using a different ultrasonic sensor, but it did not have the range I needed because we were going to be using 275 – 330-gallon IBC tanks for water storage.

In the end, I tried several different sensors until I found one that worked just fine and worked with different sized tanks. I didn’t want to have to write code for each size tank, so I ended up keeping all the information about each tank in the database. When needed, GardenPi reads the tank information and uses it to determine how much water is left in the tank. A portion of the database that stores this information can be seen in Figure 4.

Tank database information (Click to enlarge)

I used a website to help me figure out the math and then wrote my code (Listing 1) to figure it out on the fly. One thing that I decided early on is that the ultrasonic sensors, while cool, did lack the exactness that I needed in making timely decisions. As a result, GardenPi never uses the ultrasonic sensors to make decisions, only to provide information. The decision part I leave to tried-and-true non-contact liquid level sensors that I’ve used in my pool projects for a number of years.

I used a website to help me figure out the math and then wrote my code to figure it out on the fly.

tank[tank_name].set_dis_range(dis_min, dis_max)
check_distance = (tank[tank_name].getDistance() / 25.4) #25.4 is mm to inches conversion factor
distance = int(check_distance)
if distance >= tank[tank_name].tank_empty_depth:
tank_level = 0
tank_level = int(abs((distance * tank[tank_name].gallons_per_inch) - tank[tank_name].max_tank_volume))
return tank_level

Once I had all of my hardware figured out, I had to figure out how I was going to get all of that equipment into a nice enclosure that I could mount in our garden shed. I also needed to make sure I had adequate enclosure ventilation as well—I didn’t want anything overheating in the hot Phoenix weather. I installed a Bosch BME280 environmental sensor in the enclosure itself. This sensor provides us with enclosure temperature, humidity and barometric pressure. I use this information for monitoring, notifications, and for the enclosure fan control.

For the enclosure itself, I chose to go with Polycase, a company whose products I’ve used in the past. They have a large selection of enclosures with pretty much every possible option and they are priced well for the quality that you get. The first thing I needed to do was to cut out all of the holes for the electrical outlets. I needed seven exposed 120V, 3-prong outlets, a 15A circuit breaker and a power switch with a removable cord. I printed out a little template that I designed and attached it to the enclosure so I could mark all the spots I needed to cut out as shown in Figure 5.

Enclosure electrical outlet template

After I marked everything, I tried various different ways to make the holes using a test piece of plastic. I tried drilling, my Dremel tool and so on—nothing seemed to work well. The enclosure was very thick and getting through it with a razor blade just did not seem doable. That was until I decided to heat that razor up with a blow torch! Then it was just a simple matter of cutting out all the holes I needed and cleaning everything up. In the end, it all turned out very nicely. I did end up having to epoxy my outlets in the enclosure because it was too thick for the tabs to hold them in place. As you can see in Figure 6, it really turned out well.

Enclosure electrical outlets completed

Once the whole electrical part was completed, the next arduous task was to figure out how to lay everything out in the enclosure. I wanted to keep my 120V stuff separate (as much as possible) from my 5V stuff, so I designed it in two layers. On the bottom would be all of my outlets, transformers, power supplies—anything that required 120V power. On the top would be everything else. I had to keep in mind the hundreds of wires that would need to be inside the box as well, and how to route them and keep it all clean.

Starting on my AC shelf, I tried a bunch of different layouts until I found one that I was happy with and drilled and mounted all the lower parts. The completed lower level can be seen in Figure 7. One I was done with the bottom, I had to do the same thing on the top as shown in Figure 8, where all my 5V equipment resides—including relays, Raspberry Pi, temperature and ultrasonic sensors.

Enclosure 120V bottom level completed
Enclosure 5V/relay top level completed

Next, we needed to solder and connect all of our wiring plugs. We had a lot of zone wires, sensors and probes to get out of the box in a clean and intelligent way, so I went with waterproof high density plugs to make this happen. Figure 9 is what each plug looked like after soldering everything up.

Wiring plug completed

In the end I had four 9-wire plugs for the water zones (8 zone wires and one common), two 8-wire plugs for the ultrasonic liquid level sensors, two 8-wire plugs for the non-contact liquid level sensors and a single 3-wire plug for our Onewire temperature probe bus. I also added in a waterproof Ethernet jack since I would be hard wiring the network connection to the Pi (Figure 10).

Enclosure wiring connections completed

The last piece to the hardware puzzle was mounting the 7″ touchscreen to the front panel. Because of the way the touchscreen was built, this was going to be difficult. However, I had an idea to design a stainless steel or aluminum front plate to hold the screen. After doing some research I came across a company that would make custom one-off designs. They even had their own software to design the parts. Of course, the problem was that you had to be exactly right the first time, otherwise you would be spending a lot of money to do it again.

I took the Adafruit display and measured everything very carefully. Because of how it is designed, you could not just bolt it to something. I measured where I wanted the front panel to meet the screen and designed the front panel around those measurements. I wanted the entire touchscreen visible but not the area outside the screen itself. To make matters worse, the distances from the mounting posts to where the touchscreen part of the display actually started was different side-to-side. I spent many hours with my trusty calipers and the design software before committing to the purchase.

The company I chose was Front Panel Express and it has its own software called Front Panel Designer. The software was pretty easy to use and included both 2D and 3D (Figure 11) renderings of the product. Once I was satisfied with my 80th remeasure, I sent the part off to be made. It was not inexpensive, but it did have different colors, engraving, counter-sunk holes and posts. It ended up running about $140 in total.

3D rendering of front panel display holder

When it arrived in less than a week, I held my breath as I mated up the display. I was very happy that it all lined up exactly as I intended. Figure 12 shows just how nice the front panel turned out. The end result (Figure 13) was (IMHO) very much worth the extra time and cost. Not only does it look nice, but it gave my project that final touch that truly made it amazing!

Screen installed in front panel display holder
Screen installed in enclosure

From the enclosure, we will connect into the zone and sensor boxes. These boxes make it easy to have the enclosure mounted in our garden shed and have all of the other boxes mounted outside where we need them. All of the connections are IP68 rated, so having those boxes outside is not be an issue. In Figure 14 we are working on our first zone plug, which controls our freshwater zone, our fish water zone and irrigation zones 1 through 6.

Zone controller extension box

Likewise, we have sensor boxes for our temperature probes, boxes for our non-contact sensors and our ultrasonic sensors. We custom make the wiring harness to length once we determine where the boxes will be placed. I am also currently working on a “remote” sensor box as well that does not need to be hard-wired to the main unit but communicates via a Flask API back and forth.


GardenPi is designed to automate, as much as possible, our garden irrigation, hydroponic and aquaponic systems (still in the works). It also manages in a semi-automated fashion, our fish tank water changes with the old water being pumped into a 275-gallon IBC holding tank. When water is added to that tank, a sensor on the tank tells GardenPi that water is available and, during the next scheduled irrigation run, it will use that water.

The end user has the ability to toggle this capability on or off at will, or to set it to automatic. In automatic mode, the system will start and stop external pumps, open and close a variety of sprinkler valves, manage the scheduling of jobs for all zones (including power zones) and provide a wide variety of notifications with very granular settings. GardenPi also monitors a variety of temperature, humidity, pressure, electrical and liquid level sensors and provides graphical visibility into all of those readings as well as making updates and providing system notifications based on those readings.

GardenPi as a whole is powered by a series of Python scripts—the main one being The basic premise of the system is that every 60 seconds (it could be less if needed) a CronJob utility kicks off Neptune and does a few things. It checks to see if there are any scheduled jobs to run both irrigation and power, it checks all of the various temperature probes and updates our database with that information, it checks the level and temperature of water in our various tanks we are monitoring, checks our enclosure temperature and starts or stops our ventilation fans as necessary, checks the temperature of our Raspberry Pi CPU and more.

There is also a threaded application that runs all the time called that monitors our water tank levels in real time and makes decisions based on the various water levels. I created the threaded app because I wanted to be able to make immediate decisions based on my water levels as opposed to waiting 60 seconds for Neptune to query the tank levels. We don’t want any of our external pumps running dry.

The core system is programmed in Python3, Flask, SQLAlchemy and CSS. Most of the settings needed for the zones, monitoring, notifications and other parameters are stored in a Marina SQL database and read or updated when necessary. There is a system information file called that stores some information not practical to store in the database, but over time I plan on rewriting code so that everything is in the database. I won’t get into installation and basic configuration of the code since that is already done on my Github repository [1].


In version 1 of our code, we have six irrigation zones and seven power zones that we can directly control. Other zones are automatically managed by Neptune. While we name our zones different things for the sake of grouping (zones, power and hydroponics) functionally a “zone” is attached to a relay. Each zone has the following capabilities:

• Enable/Disable the Zone
• Run/Stop/Manage 2 separate job schedules per zone (More can be added if necessary)
• Manually Run/Stop zones
• Individual Notification Settings for Email, SMS and Pushbullet (More coming)
• Water Usage Tracking for “Water” zones
• Query zone or job status (enabled/disabled/running/stopped)

Since all of these capabilities apply to all zones, I decided to build classes for each of these functions that would allow us to use the same type command structure throughout the code. For example, the code in Listing 2 is the class for our primary irrigation zones.

This code is the class for our primary irrigation zones.

This code is the class for our primary irrigation zones:
class ZoneController:
def __init__(self, name, number, description, gpio, enabled, running, running_manually, mcp,
notifications, sms, pb, email):
self.zone_name = name
self.zone_number = number
self.description = description
self.gpio = gpio
self.enabled = enabled
self.running = running
self.running_manually = running_manually
self.mcp = mcp
self.notifications = notifications
self.sms = sms
self.pb = pb = email
def read_config(cls, zone_name):
with engine.begin() as conn:
stmt = select([zones]).where(zones.c.zone_name.in_(zone_name))
return [
for row in conn.execute(stmt).fetchall()
This class is instantiated whenever needed by calling it like this:
zones = {zone.zone_name: zone for zone in ZoneController.read_config([n for [n] in use_database.

We are using a database function (use_database.read_all_zone_names()) which returns a list of all zone names in our “zone” table. It then reads everything about that zone from the database and creates my class object for that zone. This includes everything the system needs to manage that zone including GPIO information, notification preferences for that particular zone, which GPIO expander the zone is located on and more. Adding more zones is a simple function of adding it to our database and it will immediately become available for use.

Because of how we have the classes defined, we have all the functionality we need built into the classes themselves. Listing 3 shows the class enable() function, how to call the enable_zone() function and more about zones.

The shows the class
enable() function, how to call the enable_zone() function and more about zones.

def enable(self):
with engine.begin() as conn:
enabled = (conn.execute(select([zones.c.enabled]).where(zones.c.zone_name == self.zone_name))).scalar()
if enabled:
log.debug(f'Zone {self.zone_number} ({self.zone_name}) is already enabled.')
self.enabled = True
with engine.begin() as conn:
conn.execute(zones.update().where(zones.c.zone_name == self.zone_name).
values({zones.c.enabled: True}))
log.debug(f'Zone {self.zone_number} ({self.zone_name}) has been enabled.')
Once we have the class object instantiated (regardless of type (power, water, hydroponic), we can then control it with a simple
set of commands. For example, to enable a zone we can call the enable_zone() function from anywhere else in the code:
def enable_zone(zone_name):
"""Enable a disabled zone."""
notify(zone_name, f'{zone_name} Enabled', f'{zone_name} has been Enabled.')
if zone_name in ([n for [n] in use_database.read_all_zone_names()]):
elif zone_name in ([n for [n] in use_database.read_all_power_zone_names()]):
elif zone_name in ([n for [n] in use_database.read_all_hydroponic_zone_names()]):
Likewise, we can do any of the following with a zone:
Of course, with the classes configured this way we can also query the state of any of the information. To find out if a zone is
enabled or currently running, we can just do one of the following:
return zones[zone_name].enabled
return zones[zone_name].running

One of the major reasons I wrote my own code was to automate the usage of our old fish water for irrigation purposes. The design is pretty simple, we have two “water inputs” to our irrigation system. The first is connected to a pump which is connected to a 275-gallon IBC tank. This IBC tank is where we put our old fish tank water. When there is water in the tank, a non-contact liquid level sensor notifies the system that Fish Tank water is available for use.


Advertise Here

The process of monitoring the level of water in the tank is managed by the threaded script This script watches our non-contact liquid level sensors and updates Neptune as to the availability of our old fish tank water. If there is fish tank water available and if the water source is set to “Automatic” or “Fish Tank”, then the next time the irrigation system runs, it will use the fish water. The system will start the external pump, apply power to our sprinkler valve transformer and open the sprinkler valve that connects this tank to the main irrigation line. One-way valves in the irrigation system prevent water backflow.

If the system is in automatic mode and you run out of fish water, the system will automatically and immediately switch to freshwater. If not, then the system will stop all jobs and disable itself until some type of water becomes available. Determining which water source to use is managed with a set of functions tied to our run_zone() function. These functions determine if there is fish water available (fish water is enabled and fish water is available) and then detects what source has been selected by the end user (Listing 4).

Determining which water source to use is managed with a set of functions tied to our
run_zone() function. These functions determine if there is fish water available and then
detects what source has been selected by the end user.

def run_zone(zone_name):
log.debug(f'run_zone() called with {zone_name}.')
notify(zone_name, f'{zone_name} is Running', f'{zone_name} is now Running.')
if zone_name in ([n for [n] in use_database.read_all_garden_zone_names()]):
source_selected_this_job = (get_water_source()['source_to_use'])
elif zone_name in ([n for [n] in use_database.read_all_power_zone_names()]):
elif zone_name in ([n for [n] in use_database.read_all_hydroponic_zone_names()]):

The main web interface allows you manually toggle between the three different water selection modes. Automatic, Fresh Water or Fish Water. You can also manually enable or disable the Fresh or Fish water sources if you choose. In automatic mode, the system will always choose Fish Water first if it is both enabled and available. If not, it will choose Fresh Water. If at any time both the Fresh Water and Fish Water are unavailable or disabled, all watering functions including manual watering are disabled systemwide with appropriate notifications. You can see additional interface screenshots in a special APPENDIX on Circuit Cellar’s article materials webpage.


Now let’s step through a variety of the systems monitoring and control capabilities.

User web interface: The main interface (Figure 15) is entirely web driven and very responsive thanks to extensive use of CSS to manage all of the buttons and graphics. From the main page and submenus we have the ability to manage almost all aspects of the GardenPi system— including all scheduling of jobs, manual running of zones and notification types and alerting limits.

GardenPi main web Interface

Zone control: When a zone is running, the system provides you with visual clues to let you know what is happening and provides you with different options including the ability to “force stop” a zone job. This is the same for both water/irrigation zones as well as power zones. The idea behind a “force stop” is that you want to immediately stop a scheduled job that is in progress. Once you force stop a job, it will not reenable until midnight. If you want to prevent the job from running again, you can disable the job, or disable the zone itself. There is no force stop for manual operations because you would be manually stopping the job anyway.

When a zone is running for any reason, the zone submenu changes automatically to remove items that we don’t want to be modifying while it is running. You lose your ability to do any schedule or notification changes when the zone is running since it could affect the currently running zone. There are other changes as well. When a water/irrigation job is running, we automatically add current gallons-per-minute and current total gallons to the already present total gallons used for that zone. In the “System Tools” section, we have a “Water Stats” page that shows all water zone total water usage information. Once the zone stops running, all context sensitive menu items return.

Notifications: No system would be complete without the ability to provide timely notifications. There are two places we need to set up to enable system notifications. Systemwide notification settings set what types of notifications are available systemwide. Depending on what you would like to use, you can choose between email, SMS text messaging (via Twilio) or Pushbullet. Each of these types of notifications can be enabled and disabled. Once enabled or disabled at the systemwide level, they become available on any zone or system that supports notifications such as temperature and power utilization.


Environmental monitoring: As we previously mentioned, we track a lot of different environmental sensors. To the extent necessary, we can also provide notifications on those readings. Environmental monitoring (for us) includes monitoring the shed temperature and humidity where the GardenPi system is physically located and monitoring the enclosure temperature, humidity and barometric pressure so that we can make decisions on starting or stopping our internal ventilation fans and monitoring the CPU temperature to make sure we are not going to damage our Raspberry Pi. It also includes monitoring the outdoor temperature for informational purposes and finally, monitoring the soil temperature in our worm farm. This prevents us from overheating or overcooling our worms—both of which can be fatal. We provide notifications on the worm farm, Pi CPU and enclosure maximum temperatures.

Power notification interface (Click to enlarge)

Power monitoring: Just like our environmental monitoring, we also monitor power utilization on both our 5V bus and our 120V circuit. We monitor both voltage and current, but also provide 5V bus power and DC shunt voltage readouts as well thanks to our DC wattmeter installed on the system. And again, we provide notification capability on those items we deem critical: 120V AC current and voltage and 5V DC current and voltage. We have the ability to set high and low alert levels. The low alert level allows us to have a device plugged in. As long as we know the current draw, if that device fails or shuts off unexpectedly, we can get an alert when the circuit goes below our predefined level. We can also set how often we will get a follow up alert for the same issue (Figure 16).

RODI control: The last thing available in version 1.0.0 of GardenPi is the ability to monitor the capacity and temperature of our RODI water tank. We use RODI water as the base water for our fish tank water changes. We can use the RODI control valve to make RODI water when necessary, as well as to backfill our fish water IBC tank to use as a nutrient base when we do not have fish water available for irrigation.


Once we had everything working on the hardware and software side, it was time to actually design, build and install the plumbing to make everything actually work. Figure 17 shows one of the manifolds with our two input valves, one for fish tank water and one for freshwater. Note the one-way valves that prevent water from flowing backward! Freshwater enters right below the hose bib in the middle and Ts off to our “Fresh Water” valve and to our RODI valve. The hose bib to the left connects to our RODI system. The valve on the right will be connected to our fish tank water pump. Figure 17 shows the manifold installed along with zones 1 and 2. Figure 18 shows zones 3 – 6 installed and ready to go.

Main water manifold
Zones 3 – 6 manifold

As with any system there exists the possibility of the unthinkable happening—the loss of an SD card, corruption of your SD card, your Pi going south. Included in my Github repo [1] is a 1,700+ line shell script that I have designed to be able to completely backup an operational GardenPi system and then completely restore that image in the event of a failure. However, instead of just making a “dd clone” image of the card (way too much space), I back up all critical directories, files, IP information, code—basically absolutely everything needed to make a new install. You can backup locally to a USB or to an NFS mount and restore from the same. See Figure 19.

Backup menu

In the event that you need to restore your backup, you install a brand new clean Raspbian image and then run the script in restore mode. It will do all updates, download and install all necessary software, restore all GardenPi files, apply all existing network settings and have you right where you were before you had your issues. Because we only store state and configuration data in the database, there is no “data” to lose.

Eventually this will become the basis for my install script, but for now I use it every single time I make a code change and want to test it operationally. I haven’t documented it very well, but the code is not difficult to read. One more thing for my to-do list!


Needless to say, anyone looking at this project and reading the code can tell there is a lot left to be done. I am a pilot by occupation and a programmer because I love building things and watching them work. I wanted to do so much more and clean up the code a lot more, but frankly, version 1 has to come at some point. We are currently running our GardenPi in production and I will continue to expand its capabilities as my family and I continue to grow in our knowledge of both gardening and programming.

I have a lot of error checking to add to the code and much more functionality, including my remote water valve and sensor boxes for other areas of our large yard to code and providing a unified setup tool. I need to build in a “system reset” feature that takes the entire system back to a known good state with all valves off. While I have a UPS with battery backup on the Pi itself, writing my own pool control software has shown me the value of a “reset” button!

Thank you for taking the time to read about our family project. From my wife building the gardens to my youngest son helping build the system enclosure, my middle son and youngest sons helping to dig up the yard to my daughters running to Home Depot even though they hate it, it has been a fun and interesting project. This is just the start of the project. As we grow in our gardening and add in our hydroponics and aquaponics, the software will continue to expand and grow to meet all of the capabilities of the hardware. 

Scroll down to the Article Materials webpage an APPENDIX with additional interface screenshots.

Get the Garden Pi schematic on the Circuit Cellar Code and Files Download page.


Advertise Here

[1] GardenPr GitHub repo:

Author’s note: Icons used in GardenPi: When you build an interface, how it looks it very important. I would not have been able to have such a nice-looking interface without a lot of cool icons. These icons were mostly free and came from Check them out!

Bosch Sensortec |
Adafruit |
Davis Instruments |
Emoncms |
Flaticon |
Front Panel Express |
Maxim Integrated |
Polycase |
Raspberry Pi Foundation |
SainSmart |
Samsung |


Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article
+ posts

Richard Sears is the former CTO for a large multi-state, multi-location data center colocation operation in California where he received the San Diego Business Journal’s IT Executive of the Year award. Having learned to fly while serving in the US Marine Corps, he is currently the Vice President and a flight instructor with a jet transition and training company. He is certified to fly the UH60 Blackhawk, Gulfstream G550, Citation Ultra 560 and the Citation CJ. He can be reached at

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

Smart Gardening System Uses Raspberry Pi

by Richard Sears time to read: 26 min