Automatic Garden Watering using a BeagleBone Black

Automatic Garden Watering using a BeagleBone Black

The sun is back. It is time to start thinking about a good automated watering system for the garden if you need to have it up and running when it's going to get real warm. Do you already have a central garden watering system ? Then let's see how you could make it smarter using a BeagleBone Black and a Yoctopuce device.

A time-based automated watering system is not bad. The right time to water a garden is mainly defined by the time of day: either at daybreak, at dawn or during the night. This reduces the water loss due to evaporation, which can otherwise sum up to 60% of the water spent. But the question is, how often does a garden need watering ? That varies a lot depending on past and upcoming weather.

One could decide to blindly water every two days, and use longer watering periods during the warmest months. But there are days where this would be literally pouring water down the drain. In a temperate climate, watering 200 square meters of cultivation (flowers, vegetables, etc) may use up to 100'000 liters water! There is plenty of room for significant water savings by properly choosing when watering can be skipped.

The standard solution

One standard method is to disable the watering system after every "significant rain", using a rain sensor.

A low-tech rain sensor: the Mini-Click made by Hunter. When the cork rings become thicker from absorbing humidity, they press on the simple switch at the bottom of the socket.

Automated watering system often have an input for an external rain sensor like this one. When there is no such input, it is also possible to connect the sensor directly on the 24V supply of the solenoid valves, as central watering system use normally closed solenoids. So when one of the wires connecting the valve gets disconnected by the switch, watering is disabled even if the watering controller is still running.

Two ways to connect a rain sensor to a watering controller
Two ways to connect a rain sensor to a watering controller

The advantage of this system is its simplicity. The problem is, the turn-on and turn-off thresholds are not well defined, and they cannot properly be adjusted at will. That's not good for automation, in particular when your only quality check is to look for water drops coming out of a dripping hose during a rainy night. Not to speak about upcoming summer storms, that this sensor will not be able to anticipate.

The IoT solution

There is a better solution, which does not cost much. The idea is to replace the low-tech rain sensor by a simple USB relay driven by a tiny PC, which will decide to water or not to water in a deterministic way. This system will allow us to simply pull out our mobile phone to know wether or not there will be watering tonight, and for what reason. As controller, we will use a BeagleBone Black for this project, but a Raspberry Pi would do as well in this case since the USB stack will only be mildly solicited.

We replace the 'normally closed' water sensor by a 'normally closed' USB relay
We replace the 'normally closed' water sensor by a 'normally closed' USB relay

Better safe than sorry, we connect the output A (normally closed) of the Yocto-PowerRelay to the rain detector input. In this way, in case our IoT control system goes astray, the watering system will return to a basic daily time-based watering until the problem is solved.

Our automated watering system, driven by a BeagleBone Black
Our automated watering system, driven by a BeagleBone Black

In order to decide if watering is needed or not, a simple solution is to use a weather data and forecast service. Unless you live in a highly singular place, there is a good chance that the interpolation of data from nearby weather stations will provide more relevant information than a wet cork in your garden. Moreover, this makes it possible to use weather forecast for the next 24h, which are also very likely to be good at telling if there is going to be a real good rain or not.

Retrieving weather data

We use the free API from OpenWeatherMap, but you can easily reuse similar code for another one like if you are looking for more elaborate data: all these API work on the exact same principle.

We get the most recent weather measurements using a simple HTTP query, providing as only parameter our location. The result comes as JSON structure, which can easily be parsed in most languages. In this example, we have chosen Python:

LOCATION = "Cartigny,ch" # Use the nearest place or city
URL = ""

class WeatherInfo:
    def __init__(self, record):
        self.time = time.strftime("%Y-%m-%d %H:%M:%S",
        self.temp = math.ceil((record['main']['temp'] - 273.15) * 10) / 10
        self.rain = 0
        if 'rain' in record: self.rain = record['rain']['3h']

def currWeather():
    response = requests.get(URL+"/weather?q="+LOCATION)
    if response.status_code != 200: return None
    return WeatherInfo(response.json())

The result of this code is an object which looks like:

{'temp': 14.4, 'rain': 0, 'time': '2014-04-15 17:20:00'}

The function that returns the forecast for the next 24h is very similar:

def comingWeather():
    response = requests.get(URL+"/forecast?q="+LOCATION)
    res = []
    if response.status_code == 200:
        now = time.time()
        for entry in response.json()['list']:
            # only return forecasts for the coming 24h
            entryTime = entry['dt']
            if entryTime >= now+3600 and entryTime < now+86400:
    return res

The list of weather predictions looks like this:

{'rain': 0, 'temp': 11.2, 'time': '2014-04-15 20:00:00'}
{'rain': 0, 'temp': 8.8, 'time': '2014-04-15 23:00:00'}
{'rain': 0, 'temp': 7.0, 'time': '2014-04-16 02:00:00'}
{'rain': 0, 'temp': 4.3, 'time': '2014-04-16 05:00:00'}
{'rain': 0, 'temp': 3.2, 'time': '2014-04-16 08:00:00'}
{'rain': 0, 'temp': 10.2, 'time': '2014-04-16 11:00:00'}
{'rain': 0, 'temp': 14.1, 'time': '2014-04-16 14:00:00'}
{'rain': 0, 'temp': 15.2, 'time': '2014-04-16 17:00:00'}

The watering control loop

The final step is to add the control code to decide when watering should be disabled. We have chosen to record every quarter of an hour the last known weather conditions, and to decide just before the scheduled watering time of water is indeed needed. The decision is made by comparing the estimated evapotranspiration and the rainfall.

history = []
while True:
    # wait for approx 1 minute
    now = time.localtime()
    print("Now %02d:%02d" % (now.tm_hour, now.tm_min))

    # Archive weather event records every 15 min
    if (now.tm_min % 15) == 0:

    # Evaluate the need for water at 7:45PM
    if now.tm_hour == 19 and now.tm_min == 45:
        # Integrate water history from recent events
        sum_rain = 0
        sum_etp = 0
        for event in history:
            if event.rain > 0:
                sum_rain += event.rain / 12
            elif event.temp > 10:
                sum_etp += 0.01 * (event.temp - 10)

If rainfall is significantly higher than evapotranspiration, watering is disabled. If it is significantly lower, watering is enabled. In case of doubt, watering occurs every 3 days.

        if sum_rain < 2:
            dryDays += 1
        period = "last "+str(len(history)/4)+"h"
        if sum_rain - sum_etp > 3:
            watering = False
            reason = str(sum_rain)+"mm rain in "+period
        elif sum_etp - sum_rain > 5:
            watering = True
            reason = str(sum_rain)+"mm water missing in "+period
        elif dryDays >= 3:
            watering = True
            reason = "No water for last "+str(dryDays)+" days"
            watering = False
            reason = "Enough water for now"

Of course, watering is disabled if there is 3mm rainfall or more coming in the next 24h:

        forecast = comingWeather()
        if watering:
            new_rain = 0
            for event in forecast:
                new_rain += event.rain
            if new_rain >= 3:
                watering = False
                reason += " but "+str(new_rain)+"mm rain is coming"

Eventually, we switch the relay to open position (for 90 minutes) when watering must be disabled, and we send an email update about the system status. Thanks to the pulse function implemented by the Yocto-PowerRelay, we know that even if the control system stops for any reason, watering will only be disabled for one cycle.

        # Apply decision (open relay for 90 min to disable watering today)
        if not watering:

        # Keep me informed of what is going on
        subject = "Watering" if watering else "No watering"
        body = reason + ".\r\n\r\nWeather records:\r\n"
        for event in history:
            body += pprint.pformat(vars(event)) + "\r\n"
        body += "\r\nForecast:\r\n"
        for event in forecast:
            body += pprint.pformat(vars(event)) + "\r\n"
        sendMail(subject, body)

The full source code of this application is available on GitHub. And here is the result:

Here is a watering system that tells what it does!
Here is a watering system that tells what it does!

If you like tuning, you can enhance this system in many ways. You can adapt the duration of the watering by disconnecting it during the switched-on period. You can also add additional decision inputs using a Yocto-Meteo, an optic rain sensor, a pluviometer, a volumetric soil water content sensor connected to a Yocto-4-20mA-Rx... or even a Mini-Click connected to a Yocto-Button if you want to integrate the decision that would be taken by the cork. By replacing the opaque rain sensor by a determinist software, you will perfect your garden watering while saving precious natural ressources.

Add a comment No comment yet Back to blog

Yoctopuce, get your stuff connected.