Smart Sprinkler Controller

by peternickerson2006 in Outside > Backyard

699 Views, 4 Favorites, 0 Comments

Smart Sprinkler Controller

FinalCover.png
Full Testing Video

Introduction

According to the US EPA, a household with an automatic landscape irrigation system that isn't properly maintained and operated can waste up to 25,000 gallons of water per year — and as much as 50% of all outdoor water use is lost to wind, evaporation, and runoff from inefficient irrigation. That statistic hit me hard when I looked at my water bill and realized my dumb timer-based sprinkler system was running rain or shine, every day, whether the lawn needed it or not.

So I built a smarter one.

This project combines an ESP32 microcontroller, a capacitive soil moisture sensor, a gorgeous 1.28" round GC9A01 display, and live weather data from OpenWeatherMap to create a sprinkler controller that actually thinks before it waters. But the part I'm most proud of isn't the hardware — it's the math. Instead of just checking whether the soil is dry and turning on a relay, this system implements the FAO-56 Penman-Monteith evapotranspiration equation, the same scientific standard used by agronomists and water utilities worldwide, to calculate exactly how much water evaporated from your lawn today — and therefore exactly how long to run the sprinkler to put it back.

The result is a wall-mounted controller with a sleek round display showing four different information screens (soil moisture, weather, schedule, and the ET math), a live web dashboard accessible from any phone or browser on your network, and a relay output ready to connect to a real sprinkler valve. It skips watering automatically if it's raining, if rain is forecast, if the wind is too high, or if the soil is already wet enough. And if you hold the button for five seconds, it activates a secret screensaver. You're welcome.

This is currently a proof-of-concept. The relay wiring and valve integration are straightforward to add, but in my build the software and control logic are fully complete and operational — I just haven't plumbed it into an actual valve yet. Everything described here works exactly as written.


What This Project Does

  1. Reads soil moisture every 5 seconds via a capacitive sensor
  2. Fetches current weather and 12-hour forecasts from OpenWeatherMap every 10 minutes
  3. Calculates daily evapotranspiration using the FAO-56 Penman-Monteith equation (temperature, humidity, wind, cloud cover, latitude, elevation, day of year)
  4. Applies a monthly seasonal grass coefficient (Kc) to determine actual crop water need
  5. Calculates exactly how many minutes to run the sprinkler to replace what evaporated
  6. Skips watering if: it's raining, rain probability > 50%, rain forecast > 2mm, wind > 15 mph, soil already wet, or not in the scheduled watering window
  7. Displays four information screens on a round GC9A01 display, navigated with a potentiometer knob
  8. Serves a live web dashboard over WiFi with WebSocket real-time updates
  9. Controls a relay to switch a sprinkler valve (active-low, connects to any standard 24V sprinkler valve via a relay module)
  10. Manual override via physical button or web dashboard
  11. Adjustable watering window and weather override from the web UI
  12. Secret screensaver easter egg (hold button 5 seconds)

Built in Fort Collins, Colorado — elevation 1,525 meters, semi-arid high plains, where water is expensive and the sun will absolutely evaporate your lawn if you don't pay attention.

Supplies

esp32.jpg
screen.jpg
sensor.jpg
button.jpg


Electronics

Components, Notes, and Approx. Cost

ESP32 development board (any generic 30-pin or 38-pin):

WROOM-32 or similar ($16, 3 pack)

Waveshare 1.28" Round LCD, GC9A01 driver, 240×240px

Must be GC9A01 — not all round displays are ($20)

Capacitive soil moisture sensor

Capacitive, NOT resistive — resistive ones corrode ($3–5)

Capacitive Touch Sensor (Button)

Any tactile button, 12mm panel-mount looks clean ($1)

10K potentiometer (linear)

For the view-switching knob ($10 pre-soldered)

Micro-USB

At least 1A ($5–8)

Jumper wires + breadboard (for prototyping)

($3–5)

Enclosure material

See Step 9 (Varies)

Total: approximately $33–$49 for electronics alone, $50–$75 with enclosure materials.

Accounts & API Keys (Free)

  1. OpenWeatherMap API key — free tier supports 1,000 calls/day, well within our 10-minute polling interval. Sign up at openweathermap.org.

Software & Tools

  1. Arduino IDE (2.x recommended) or PlatformIO
  2. ESP32 board support package (by Espressif)
  3. Soldering iron + solder
  4. Wire strippers

Files are attached below

Make sure that in the same folder as the Smart_Sprinkler.ino you also include a folder called "data" where you include the index.html (Instructables doesn't allow html uploads so you will have to change the file type). The data folder is also where you will include your 240x240 .bmp photos for the screen saver.

Understanding the System

Picture1.jpg

Before touching a single wire, it helps to understand how the pieces fit together. Here's the full data flow:

Sensors In: The capacitive soil sensor outputs an analog voltage on GPIO 34. The ESP32's ADC reads this and maps it to a 0–100% moisture percentage using calibration values you set once (dry reading and wet reading). Ten readings are averaged to smooth out the ESP32's notoriously noisy ADC.

Weather In: Every 10 minutes the ESP32 calls two OpenWeatherMap endpoints — the current weather API (temperature, humidity, wind, cloud cover) and the 5-day forecast API limited to 4 entries (covering the next 12 hours). It extracts rain probability and forecasted precipitation in mm.

The Brain: The ET calculation runs after every weather update. It takes temperature, humidity, wind speed, cloud cover, your latitude, elevation, and the current day of year, and runs the full FAO-56 Penman-Monteith formula to produce a daily evaporation value in mm/day. That gets multiplied by the monthly grass coefficient and converted to inches, then divided by your sprinkler's application rate (inches/hour) to get the exact number of minutes to run.

Decisions: Every 5 seconds the code checks all conditions: Is the soil dry? Are we in the allowed time window? No rain? Wind okay? Has enough time passed since the last watering? If all five conditions are true, the relay turns on for the calculated number of minutes (bounded between 2 and 30 minutes as a safety cap).

Output: GPIO 26 drives the relay module (active-low — LOW = relay energized = valve open). The round display shows one of four screens. The web dashboard updates via WebSocket every second.

Wiring It Up

All connections use 3.3V logic (the ESP32 is not 5V tolerant on most pins). The relay module is powered from the ESP32's 5V pin and takes a 3.3V-compatible control signal just fine.

Display (GC9A01, SPI)

You must configure TFT_eSPI to match these exact pin assignments before the display will work (see Step 3).

  1. GC9A01 MOSI / SDA → ESP32 GPIO 23 (SPI data)
  2. GC9A01 SCLK / SCL → ESP32 GPIO 18 (SPI clock)
  3. GC9A01 CS → ESP32 GPIO 5 (Chip select)
  4. GC9A01 DC / RS → ESP32 GPIO 2 (Data/command)
  5. GC9A01 RST / RES → ESP32 GPIO 4 (Reset)
  6. GC9A01 BL → ESP32 GPIO 22 (Backlight — HIGH = on)
  7. GC9A01 VCC → ESP32 3.3V
  8. GC9A01 GND → ESP32 GND

Soil Sensor

  1. Sensor AOUT → ESP32 GPIO 34 (Analog input, ADC1 — do NOT use ADC2 pins, those conflict with WiFi)
  2. Sensor VCC → ESP32 3.3V
  3. Sensor GND → ESP32 GND

Relay Module

  1. Relay IN → ESP32 GPIO 26 (Control signal)
  2. Relay VCC → ESP32 5V (VIN pin — the relay coil needs 5V)
  3. Relay GND → ESP32 GND

The relay's COM and NO terminals connect between your 24V sprinkler transformer and the valve solenoid. When GPIO 26 goes LOW, the relay closes and the valve opens.

Button

  1. One leg → ESP32 GPIO 27
  2. Other leg → GND

The code uses INPUT_PULLUP so no external resistor is needed.

Potentiometer

  1. Left outer leg → ESP32 3.3V
  2. Right outer leg → ESP32 GND
  3. Center wiper leg → ESP32 GPIO 32 (ADC1 channel 4)

If the knob cycles screens backwards, just swap the 3.3V and GND legs — no code change needed.

Configuring TFT_eSPI

TFT_eSPI requires you to edit its User_Setup.h file directly — this is not done through code but through the library file itself.

  1. Install TFT_eSPI through the Arduino Library Manager (search "TFT_eSPI" by Bodmer).
  2. Navigate to your Arduino libraries folder. On Windows this is typically Documents/Arduino/libraries/TFT_eSPI/. On Mac it's ~/Documents/Arduino/libraries/TFT_eSPI/.
  3. Open User_Setup.h in any text editor.
  4. Find and comment out any existing driver definition (look for lines like #define ILI9341_DRIVER), then add:
#define GC9A01_DRIVER
  1. Find the pin definition section and set:
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS 5
#define TFT_DC 2
#define TFT_RST 4
#define TFT_BL 22
  1. Make sure SPI frequency is set appropriately (40MHz works well for this display):
#define SPI_FREQUENCY 40000000
  1. Save User_Setup.h. These settings are now baked into the library for this project.
Important: Every time you update TFT_eSPI through the library manager, User_Setup.h gets overwritten. Keep a copy of your settings somewhere so you can re-apply them quickly.


Getting Your OpenWeatherMap API Key

This project uses OpenWeatherMap to fetch current weather conditions and a 12-hour rain forecast. The free tier is completely sufficient — it allows 1,000 API calls per day, and this project only makes about 144 calls per day (one every 10 minutes). You will never hit the limit.

Creating Your Account

Go to openweathermap.org and click "Sign In" in the top right, then choose "Create an Account." Fill in your name, email, and a password. After confirming your email you will be logged into the dashboard.

Getting Your API Key

Once logged in, click your username in the top right corner and select "My API Keys" from the dropdown. You will see a default key has already been created for you — it will be a long string of letters and numbers that looks something like this:

eaff286c9668a4059029849f2706f85

Copy that key. This is what you will paste into the weatherApiKey field in the code. If you prefer to create a named key for this project, click the "Generate" button, give it a name like "smart-sprinkler," and use that one instead.

Important: New API keys take up to 2 hours to activate. If the ESP32 boots up and the weather fields on the display show dashes or the serial monitor shows HTTP error 401, just wait and try again. This is normal and not a bug in your code.

Finding Your City String

The city is passed to the API as a URL-encoded string. The format is:

CityName,CountryCode

For example:

  1. Fort Collins, Colorado → Fort%20Collins,US
  2. New York City → New%20York%20City,US
  3. Los Angeles → Los%20Angeles,US
  4. London, UK → London,GB

Spaces become %20 in the URL. The country code is the two-letter ISO code for your country (US, GB, CA, AU, DE, etc.). Find the right code by searching "ISO country codes" if you are outside the US.

Paste your formatted city string into the myCity field in the code:

const char* myCity = "Fort%20Collins,US";

Testing Your Key Before Flashing

You can verify your API key works by pasting this URL into any browser (replace YOUR_KEY and YOUR_CITY with your actual values):

http://api.openweathermap.org/data/2.5/weather?q=YOUR_CITY,US&appid=YOUR_KEY&units=imperial

If you see a wall of JSON data with temperature and weather information, your key is working. If you see an error message saying "Invalid API key," either the key hasn't activated yet or there is a typo — double-check that you copied the full key with no extra spaces.

Installing Libraries and Flashing the Code

Install These Libraries

Through Arduino IDE's Library Manager (Sketch → Include Library → Manage Libraries):

  1. TFT_eSPI by Bodmer — the display driver
  2. ArduinoJson by Benoit Blanchon — for parsing weather API responses
⚠️ Do NOT install "Arduino_JSON" (the one by Arduino). It has a naming conflict with ArduinoJson that causes compile errors on ESP32. If you have it installed, remove it.

These two must be installed manually from GitHub (Library Manager versions are outdated):

  1. ESPAsyncWebServer by me-no-dev — github.com/me-no-dev/ESPAsyncWebServer
  2. AsyncTCP by me-no-dev — github.com/me-no-dev/AsyncTCP

To install manually: download the ZIP from GitHub, then in Arduino IDE go to Sketch → Include Library → Add .ZIP Library.

WiFi, HTTPClient, SPI, LittleFS, time.h, and math.h are all included with the ESP32 board package and don't need separate installation.

Configure Your Settings

At the top of the main .ino file, fill in your details:

const char* wifiName = "YourWiFiName";
const char* wifiPassword = "YourWiFiPassword";

const char* weatherApiKey = "your_openweathermap_api_key";
const char* myCity = "Your%20City,US"; // URL-encoded city name

#define myTimezone "MST7MDT,M3.2.0,M11.1.0" // change to your POSIX timezone string

#define myLat 40.585f // your latitude (positive = north)
#define myElevationMeters 1525.0f // meters above sea level
#define sprinklerRate 0.50f // inches/hour your sprinkler heads deliver

For the timezone string, search "POSIX timezone string" + your city/region to find the correct value. For example: US Eastern is EST5EDT,M3.2.0,M11.1.0, US Pacific is PST8PDT,M3.2.0,M11.1.0.

Flash the Code

  1. Select your ESP32 board (Tools → Board → ESP32 Arduino → ESP32 Dev Module or your specific variant).
  2. Set Upload Speed to 921600.
  3. Click Upload.

On the first boot, watch the Serial Monitor at 115200 baud. You'll see the WiFi connection, NTP time sync, and the first weather fetch. The display will show a boot splash screen, then transition to the soil moisture screen.

Setting Up LittleFS (Dashboard + Screensaver Images)

The web dashboard (index.html) and the screensaver images are stored on the ESP32's flash filesystem using LittleFS. You upload them using the ESP32 Sketch Data Upload plugin.

Install the LittleFS Upload Plugin

  1. Download the ESP32 LittleFS Data Upload plugin from GitHub: github.com/lorol/arduino-esp32fs-plugin
  2. Place the downloaded .jar file in your Arduino tools folder: Documents/Arduino/tools/ESP32FS/tool/
  3. Restart Arduino IDE. You should now see Tools → ESP32 LittleFS Data Upload.

Prepare the /data Folder

Create a folder called data inside your sketch folder (same folder as your .ino file). Place these files inside it:

  1. index.html — the web dashboard (the full HTML file is provided with this project)
  2. Your screensaver images as 240×240 pixel 24-bit BMP files

Screensaver Image Requirements

  1. Format: 24-bit BMP (not 8-bit, not 32-bit)
  2. Size: exactly 240×240 pixels
  3. Filename: must match the array in the code (default: chud.bmp, monkey.bmp, angry.bmp, confused.bmp, horse.bmp, smile.bmp, ropopo.bmp)
  4. You can use any images you like — drop any 240×240 BMPs in and update the pics[] array in the code to match

To convert images to the right format in Photoshop or GIMP: resize to 240×240, then export/save as BMP with 24 bits per pixel selected.

Upload to the ESP32

  1. Make sure the correct ESP32 board and port are still selected.
  2. Go to Tools → ESP32 LittleFS Data Upload.
  3. Wait for the upload to complete (it will say "LittleFS Image Uploaded" when done).

You only need to re-upload the data folder if you change the HTML or swap screensaver images — regular code changes are uploaded normally through the main Upload button.

Calibrating the Soil Sensor

cap_soil_moisture_sensor_in_soil_BLOG.jpg

Capacitive soil sensors need two calibration readings: one for completely dry soil and one for saturated soil. These map the raw ADC output (0–4095) to the 0–100% scale shown on the display.

Getting Your Readings

  1. Flash the code and open the Serial Monitor at 115200 baud.
  2. Hold the sensor in open air, completely dry. Watch the serial output for lines like [sensor] soil: X% — or temporarily add a raw print. The raw value is what you need. With most sensors, the dry reading will be near 3000–4095.
  3. Submerge the sensor tip in a glass of water (don't submerge above the white line on the PCB — that's the electronics). The wet reading will typically be around 1200–1800.
  4. Update these lines in the code:
#define drySoilReading 4095 // replace with your actual dry reading
#define wetSoilReading 1500 // replace with your actual wet reading
  1. Re-flash. The constrain() call in the code ensures readings never go below 0% or above 100% even if your calibration isn't perfect.

Tip: Temporary Raw Reading Print

To see the raw ADC value, temporarily add this to the loop() function:

Serial.println(analogRead(soilPin));

Remove it before the final flash.

The Web Dashboard

Screenshot 2026-04-28 114506.png
Screenshot 2026-04-28 133001.png

Once the ESP32 is running and connected to WiFi, open a browser on any device on your network and navigate to the ESP32's IP address. You'll see it printed in the Serial Monitor on boot: [web] dashboard at http://192.168.x.x

The dashboard is a dark-themed, real-time control terminal that updates every second via WebSocket — no page refreshing needed.

What You'll See

Soil Moisture Card: A circular SVG gauge showing current moisture percentage with color coding (red below 30%, yellow 30–40%, green above 40%). When watering is active, the card pulses with a blue glow, animated water drops fall, and a progress bar shows time remaining.

Weather Card: Current temperature, conditions description, wind speed (turns red if above the 15 mph threshold), humidity, 12-hour rain probability (color coded green/yellow/red), and forecasted precipitation in mm. A "Raining Now" badge appears when the API reports active rain.

Watering Schedule Card: Shows the configured watering window, a countdown to when the window next opens, time since the last watering cycle, and cooldown status.

Conditions Card: Five LED indicators — each one glows green when its condition is met, red when it isn't. Conditions: soil dry enough, no rain expected, wind acceptable, in watering window, all systems ready. Real-time sensor values are shown next to each indicator.

Evapotranspiration Card: The nerd panel. Shows today's calculated ET₀ in inches/day, the seasonal crop coefficient (Kc), the resulting crop water need, a bar chart of all 12 months' Kc values with the current month highlighted in cyan, and the final calculated watering duration.

Controls Card: Three sections — Manual Water (Start/Stop buttons), Watering Window (dropdowns to set the start and end hour, with a Save button), and Weather Override (a toggle that disables all weather checks if you need to force a watering cycle regardless of forecasts).

Accessing It Remotely

The dashboard is only accessible on your local WiFi network — it's not exposed to the internet. If you want remote access, you can set up a VPN to your home network or use a reverse proxy, but that's outside the scope of this project.

The Science — FAO-56 Penman-Monteith Evapotranspiration

1741270262151.jpg

This is what separates this project from "soil sensor + timer." Most DIY smart sprinklers ask one question: is the soil wet enough? This one asks a better question: how much water did the lawn actually lose today, and exactly how much do we need to put back?

The answer comes from the FAO-56 Penman-Monteith equation, published by the United Nations Food and Agriculture Organization and used globally as the standard reference for irrigation scheduling.

What It Calculates

The equation combines four weather inputs (temperature, humidity, wind speed, cloud cover) with three location inputs (latitude, elevation, day of year) to produce ET₀ — the reference evapotranspiration in mm/day. This represents how much water would evaporate from a standard reference surface (well-watered grass) under today's conditions.

Here's what each input contributes:

Temperature drives the vapor pressure deficit — the difference between how much water vapor the air could hold at that temperature versus how much it actually holds. Hot, dry air has a high deficit and pulls more water out of the soil and leaves.

Humidity directly determines the actual vapor pressure. High humidity means the air is already nearly saturated, so less water evaporates from the lawn.

Wind speed (converted from the 10-meter measurement height to 2 meters, which is what the formula expects) increases evaporation by continuously replacing the humid air near the leaf surface with drier air from above.

Cloud cover determines how much solar radiation reaches the ground. The code calculates theoretical extraterrestrial radiation for your latitude and day of year using astronomical formulas, then scales it down by cloud cover to estimate actual net radiation at the surface.

Latitude and elevation affect the atmospheric pressure (used to calculate the psychrometric constant) and the solar geometry calculations.

From ET₀ to Minutes

Once ET₀ is calculated, two more steps convert it to an actual watering duration:

  1. Seasonal Kc (crop coefficient): Grass doesn't always need the same amount of water relative to ET₀. In peak summer it needs close to 100% of ET₀. In winter, much less. The code uses a table of monthly Kc values representing cool-season turf grass (like the bluegrass/fescue common in Colorado): the values range from 0.60 in December to 1.00 in June and July. Multiplying ET₀ by Kc gives the actual crop water need in mm/day.
  2. Application rate: Your sprinkler heads deliver water at a known rate — typically 0.4–0.6 inches/hour for rotor heads, 1.0–1.5 in/hour for fixed spray heads. Dividing the daily water need (in inches) by the application rate (inches/hour) and multiplying by 60 gives the minutes to run. The result is clamped between 2 and 30 minutes as a sanity check.

Why This Matters for Water Conservation

A simple soil-threshold system will water whenever the soil drops below its trigger point, regardless of conditions. On a hot, windy, sunny day it might need to run twice. On a cool, cloudy, calm day it might not need to run at all even if the soil reads on the low end. The ET approach automatically accounts for all of this — it waters less on days with low evaporation demand and more on days with high demand, matching the lawn's actual needs rather than approximating them.

The Enclosure

Screenshot 2026-04-28 115436.png

The enclosure in the project photos is a custom 3D-printed case with a brushed finish and a wood accent strip along the bottom — a small design detail that makes it look like something you'd actually want mounted on your wall rather than a prototype.

Dimensions to Design Around

  1. The round GC9A01 display is 37.5mm in diameter at the glass, with an overall PCB size of about 42mm × 42mm
  2. The button should be a panel-mount style with a nut that clamps through the front panel
  3. The potentiometer knob needs a D-shaft hole and enough depth behind the panel for the pot body
  4. The ESP32, relay module, and wiring need space behind the display — plan for at least 25–30mm of depth
  5. Leave a notch or hole on the back/side for the USB power cable and for the relay terminal wires

Material Suggestions

FDM 3D print (PLA or PETG): Easiest option. Use 20% infill, 3 walls. Sand and prime for a smooth finish. A brushed metallic spray paint over gray primer gives a convincing brushed aluminum look.

Laser-cut acrylic: Clean and professional-looking if you have access to a laser cutter. Stack layers to create depth. The round display cutout is easy to do precisely.

Repurposed project box: Any plastic or metal project enclosure from Amazon or a local electronics shop works. Cut the display opening with a step drill and a file.

Wood accent: Even a thin strip of bamboo or walnut veneer glued along the bottom edge of a printed enclosure dramatically elevates the look for minimal effort.

Mounting

The project is designed to be wall-mounted. Add two small keyhole slots on the back of the enclosure to hang it on screws, or use 3M Command strips for a no-holes solution.

The Secret Screensaver Easter Egg

Screen Saver
smile.bmp
ropopo.bmp
monkey.bmp
horse.bmp
confused.bmp
chud.bmp
angry.bmp

Because serious engineering projects should have at least one completely ridiculous feature:

Hold the physical button for 5 seconds while on any screen. The display will switch to a slideshow of 240×240 BMP images stored in LittleFS, cycling through them every 3 seconds. The default set are a collection of chaotic meme-style images. Tap the button once (short press) to exit the screensaver and return to the dashboard.

To customize the images, drop your own 240×240 24-bit BMP files into the /data folder, update the pics[] array in the code with your filenames, and re-upload via LittleFS Data Upload:

const char* pics[] = { "/yourimage.bmp", "/another.bmp", "/etc.bmp" };

The BMP loader handles both top-down and bottom-up encoded BMPs, loads them in 24-row chunks to avoid running out of RAM, and pushes them directly to the display using TFT_eSPI's pushImage() for speed.

This feature exists for two reasons: first, because it was fun to build, and second, because the ability to display arbitrary full-color images on a round screen opens up some genuinely useful possibilities — plant identification guides, watering zone maps, or QR codes linking to the web dashboard.

Tuning the Watering Logic

All the key parameters are defined at the top of the code with clear comments, so you don't need to dig through the logic to adjust things. Here are the ones most worth tuning for your setup:

#define startWateringWhen 40 // soil % below this → start watering
#define stopWateringWhen 72 // soil % above this → stop watering
#define maxWindSpeed 15.0f // won't water if wind is above this (mph)
#define skipIfRainOver 0.50f // skip if 12h rain chance is over 50%
#define skipIfMoreThanMm 2.0f // skip if 2+ mm of rain is forecast
#define defaultStartHour 0 // watering window opens at midnight
#define defaultEndHour 6 // watering window closes at 6am
#define cooldownTime 14400000UL // wait at least 4 hours between waterings

The watering window can also be adjusted live from the web dashboard without needing to reflash.

The Seasonal Kc Table

If you're growing something other than cool-season turf grass (fescue, bluegrass), adjust the monthly crop coefficient table in the code. Warm-season grasses like Bermuda and Zoysia have different seasonal patterns. Vegetable gardens, flower beds, and trees all have different Kc curves. The FAO publishes Kc values for hundreds of crops if you want to get precise.

static const float monthlyFactors[12] = {
0.60f, 0.60f, 0.70f, 0.85f, 0.95f, 1.00f,
1.00f, 0.95f, 0.85f, 0.75f, 0.65f, 0.60f
};


Conclusion

Main Image.jpg

This project started as a reaction to watching water run down the driveway during a rainstorm because the timer didn't know any better, and to a water bill that didn't need to be as high as it was. The EPA estimates improper irrigation wastes up to 25,000 gallons per household per year — and the frustrating part is that most of that waste is completely preventable with information that's freely available: soil readings, a weather forecast, and a bit of math.

The FAO-56 evapotranspiration approach elevates this beyond a simple "is the soil dry?" check into something that actually models what's happening in your lawn. It's the same science that professional irrigation engineers use, running on a $7 microcontroller.

What's Next

The natural next step is connecting the relay to an actual 24V sprinkler valve and running it through a full watering season to gather real data on water savings. Multiple soil sensors across a larger area would allow zone-by-zone decisions. A history log saved to LittleFS would enable tracking watering patterns over time. And the web dashboard could be extended with a chart of soil moisture and ET over the past 24 hours.

The hardware cost is intentionally low — under $75 for a complete system that makes genuinely intelligent watering decisions. That's a single month's water savings in a lot of parts of the country.