I have a hard time keeping herbs alive in my windowsill and as a software engineer I have a deep felt love for automation. And I like designing and building stuff. So I would like to build an automated plant monitoring and watering machine for my kitchen windowsill.

Hardware Requirements

These are the requirements I setup for the hardware of the machine:

  • Controlled by Raspberry Pi Zero – as I had one already
  • Moisture sensors to monitor individual plants
  • Valves to control watering of individual plants
  • Water tank to minimize potential flooding and for ability to add fertilizer
  • Float switch to alert about empty water tank
  • Sensors for each plant to detect flooding
  • Light, Temperature, and humidity sensors
  • Relays to enable control of 12V components
  • ADC to allow analog sensors
  • RC filters to prevent noise from long wires for analog sensors
  • Timer chip (555) to prevent pump from keep pumping even if SW crashes
  • Optional: LED growlight

Software Requirements

These are the requirements I setup for the software of the machine – being a software engineer this is somewhat over-designed:

  • To be implemented in Python 3
  • Object Oriented approach
  • Modular design to isolate implementation of different components and to allow putting subparts in production as they are completed
  • Event driven system based on mails between the different components
  • Database backend for data collection
  • Web interface to monitor sensors, watering schedule, and water consumption
  • Optional: Command line interface to control machine manually

Software Design

I will not present the entire design in full detail, but will give examples that illustrate how the software is structured. The structure is illustrated in the image below and detailed explanation of the components is given in the sections below.

Main Component

The main component is responsible for creating all other objects in the system. This is where system pin configuration is entered – which sensor is connected to which hardware pin on the Raspberry Pi. So changing how the system is wired should only cause changes in this file. When objects have been created the main component enters the scheduler loop where it: deliver mails to other components in the system; sleeps waiting for timed mails to expire; or simply sits idle waiting for new mails to deliver.

A mail in this context is not an e-mail, so I don’t setup a SMTP or POP3 for my system. A mail as an object that holds an integer describing the sender and an integer describing the receiver – all addresses are defined in a common enumerate. Each receiver has defined an enumerate of subjects or commands that it is able to respond to. The sender must give one of the valid subjects that the receiver supports and pass the object along in the system – sending the mail.



The Main Component described above could be considered like a mail man who delivers mails. But the PostOffice is where mail is stored until it can be delivered. All components that send mail will send them to the PostOffice which will put the mail on one of two queues: Mails ready to be delivered; or Mails to be delivered after a specified delay – timed mails. Once the mails are on queue nothing happens until the mail man comes by to deliver the mail. Only one PostOffice can exist in the system so this is a Singleton Class.

This way of implementing allows for very flexible execution – the execution of one part of the code is in no way linked to the execution of others. Each component can request execution at any time they want, and will be assigned execution by a best effort approach. Components are contained in their own objects and development on one component does in no way interfere with other components. And lastly, debugging is easy as it is either in a single component or an analysis of mails will reveal all that is going on.


The TempHumSensor is responsible for reading the DHT22 sensor – the details of how this is done follows in a later section. The reading of this sensor is quite slow – sometimes multiple seconds. With the system design employed the mail execution cannot be blocked for that long – nothing else in the system can execute while waiting for this sensor reading to finish. If the water pump is on it could cause a flooring if the software has an unintended delay like this. Therefore, TempHumSensor will launch a separate thread of execution that does the actual reading of the sensor. This way the execution can return immediately and when the read thread finishes it can send a mail to the PostOffice with the result before terminating and in that way make the result available to the main execution thread.

TempHumSensor is made responsible for periodically reading the air temperature and humidity. To achieve this the module sends a timed mail to itself each time it has done a reading. This way it will keep reading the temperature and humidity at a fixed interval.

The operation of Main Component, PostOffice, and TempHumSensor is illustrated in the flow chart below.


Storage module is responsible for storing data. All data to be stored is received by mail and the rest of the system does not care how the storing is done. During development data is posted via a call to a PHP-page to a web server database. Another PHP-page is available for reading and presenting said data on a web page in the form of a graph.

Due to the containerisation in the design the Storage module can be rewritten to store data directly in a MySQL database or in an array in memory without it affecting the remaining system.


Log is not an object, but kind of a utility function. It handles all printing to the user. It makes sure to add timestamps to output along with color coding. If all output from the system needs to be redirected e.g. to a file then this provides a centralised point to do this.

Other components

Other components in the system are implemented following this design and the mechanisms described here. No more attention will be given to the software design. Code snippets showing how individual sensors are made to work will be presented in the appropriate sections.

Temperature and Humidity Sensor

For the sake of data collection and comparison to environment changes I like to include a temperature and humidity sensor into the setup. I set my heart on the DHT22 sensor. There are loads of examples on how to use this sensor so I just picked one:

It was easy to follow and the code was quite simple:

Moisture Sensor

Originally, I planned to use pretty standard moisture sensors and have bought a bunch of the type shown.

However, after receiving the sensors I stumbled upon this article Sensing Soil Moisture: You’re Doing it Wrong. I decided to setup an isolated test to verify the claim. My setup was a Raspberry Pi Zero connecting the sensor placed in the soil of a plant to a LED. So continuous monitoring and thus continuous power to the sensor. Below is the chocking result after just one week of operation.

Completely oxidized – I was chocked! This could probably be mitigated or delayed significantly by only powering on the sensor when sensing is needed. But the oxidizing will still happen at some point and the uncertainly of when is undesired in the system. I followed the suggestion from the article and built sensors from pencils but my experience is that these have lower sensitivity and thus difficult to get good results from.

Instead I opted for capacitive soil moisture sensors. This sensor gives an analog read-out and is thus connected to the ADC MCP3008 – the wiring of the ADC is covered in the next section.

ADC – MCP3008

An Analog to Digital Converter (ADC), as the name suggests, converts analog signals – current levels – into digital values. The MCP3008 converts current levels from 0v to 3v into corresponding digital values of 0 to 1024.

A problem with analog signals is that if they are transmitted over long wires they can pick up a lot of noise. This noise will be of a much higher frequency than the signals we are trying to read – in this system at least – so they can be filtered out using a lowpass filter implemented as an RC filter. But before solving any problem it is a good idea to determine if the problem is actually there!

I estimated that the longest I will need to run wires for will be 3 meters. So, I took two Capacitive Soil Moisture sensors and connected them to the ADC – one with about 0.1 meter of wire and the other with about 3 meter of wire. I placed them next to each other on a table and connected data for an hour – the results are seen in the plot below.

More noise is definitely present and a filter is probably a good idea. One approach could be to sample multiple times and do a mean value calculation in software. But as the sole purpose of this project is for me to learn, I’ll do a hardware filter – an RC Filter.

RC Filter

The RC filter consists of a resistor (R) and a capacitor (C) and is hence called RC. It is a low pass filter which allows signals of low frequency (slowly changing signals) to pass through while filtering out high frequency signals. The cut off frequency (fc) determines which frequencies pass and which are filtered out. The cut off frequency is configured by the values of R and C.

In my setup I am only interested in low frequency signals and thus aimed for a cut off frequency of 0.05Hz. I used an online tool to determine my R and C values:

After implementing the filter I repeated the test from above and the effect of the filter is evident.

The filter does however have an impact on the reaction time of the sensor. I expect the drying out of soil to be quite slow so for these measurements there will be no problem. But what about when I water the plant? I that case I would want to get an idea if enough water has been added quite quickly. But before redesigning the RC filter for a faster response time lets first do some testing.

I have two sensors wired up. One which is directly connected and one that has the RC filter implemented. Both sensors are dry at the beginning of the test and the put in a glass of water at the same time. An added bonus of this test is that the dynamic range of the sensor is determined, which we need to know in the software if we want to relate measurements to something human readable.

The count on the x-axis is number of samples at a sample rate of 2Hz. So 100 samples equals 50 seconds. 60 seconds of settle time is enough and is acceptable in my design.

The dynamic range of the sensor goes from 890 for completely dry to 450 for completely wet.

Pump Control

To pump water to the plants I have a 12V pump that is going to be controlled by a relay connected to a GPIO on the control computer – pretty straight forward. But as a firm believer in Murphy’s Law I am convinced that my computer will crash or go into a deadlock or whatever right when it has started the pump but before it turns it off again. Thus, resulting in flooding. To prevent this, I’ll implement a hardware cutoff/timeout mechanism using a 555 timer chip after the following design.

Below is a video of a prototype in action.