IoT Irrigation

ESP8266-based Garden Watering Controller

Gamal Labib

Issue 75, October 2023

This article includes additional downloadable resources.
Please log in to access.

Log in

Instead of replacing a broken garden sprinkler controller, Gamal integrated an Arduino board and electronics for some smart IoT control.

The motivation for building this project was due to the breakdown of my garden's watering X-Core timer from Hunter.

A trivial crack in the timer's selector rotary switch had a catastrophic effect as it crippled the timer's management capabilities. You can see the loose copper contact on the photo shown below.

Unfortunately, replacing the defective switch was not an option due to the lack of spare parts in my region.

A manual makeshift activation of the watering sprinklers had to be adopted till I commissioned this project.

As the timer's main task was to power up to six sprinkler zones with 24VAC voltage according to the setup weekly programs, my improvised temporary solution was to release my current three sprinkler zones wiring from the timer's terminal block and to power up them at will using a wire terminated with crocodile connectors at both ends.

Direct wiring of zone #3 line to 24VAC source

The broad overview

In this project, I simply integrated an ESP8266-12E module with a 5V/10A 4-relay module to remotely control my four sprinkler zones using an Android smartphone.

I made use of the 220VAC to 24VAC transformer in the X-Core to power the sprinklers, which helped me contain the project circuitry in a small waterproof electric distribution box.

This arrangement outperformed the X-Core in many terms. It enabled me to control garden irrigation from within my home network using the WiFi facility of the ESP module. The X-Core, on the other hand, is a standalone box that requires face-to-box interaction. The wear-and-tear drawback of the mechanical parts is no longer an issue for my project and all spare parts of the project's components are within my reach.

The tricky part of the project was designing and implementing the Android App that can mimic the functionality of the X-Core.

In order to put boundaries to the project so that it does not slip away with the endless achievable programming capabilities, I set the following targets for the project:

  1. It would support four watering zones and four programs per weekly day;
  2. Powering the watering zones using modular electrical relay breakout boards.
  3. Dedicate one watering zone circuitry to drive a 12V/1.5W flood LED instead for off garden testing purposes and for scheduled lighting of my project whereabouts.

electronic components required


The project uses a 12V step-up module, 3.3V regulator and 5V 1A power adaptor

Top and bottom view of the Step-up DC-DC power module.
3.3V Voltage Regulator Module
Optional 5VDC Power Adaptor


The brains of the operation is via an ESP12 board, which is mounted to an IO breakout board (shown below).

ESP-12 Board options.
WiFi Module Breakout Board


4-Way Relay Module


I used a 12V/1.5W 3030 SMD flood LED module, which gets attached to the front of the enclosure.

The Electronics Build:

Parts Required:JaycarAltronicsCore Electronics
1 x ESP12 Board%XC3802-ADA2491
1 x ESP12 AdapterNot required--
1 x 4-Way Relay Module^XC4440Z6327CE05279
1 x 12V Step-up Module^-Z6338POLOLU-4945
1 x 3.3V Regulator^-Z6334CE08495
1 x 12V 1.5W 3030 SMD Flood LED ModuleZD0594--
1 x 5VDC 1A Power SupplyMP3144M8903AAM8904
2 x 220Ω Resistors*RR0556R7542COM-10969
1 x Green 5mm LED (For Activity Indicator)*ZD0170Z0801CE05104
1 x Blue 5mm LED (For Power indicator)*ZD0183Z0806ACEO5103

Parts Required:

Refer to the wiring diagram shown and build photos to assemble the project.

For building the project, I used ESP pins D1, D2, D5, D6, representing GPIO 5, 4, 14, and 12 which were wired to relay driver pins 3, 4, 2, 1 respectively, and D0 of GPIO16 to drive the project activity flashing green LED. The 5V input has a blue LED indicator ensuring that AC power is reaching the circuit.

As noted earlier, Relay #4 was temporarily dedicated to driving the LED flood light, while the remaining relays control the watering sprinklers.

The Code

The code is available to download from the resources section of this article.

As the ESP boots, the setup() section of the Arduino sketch will attempt to connect to either internet router available out of four declared routers. This is due to the crucial requirement of synching time with a Web NTP server using the ntp_setup() function; in my case, I chose it to be "", since the project relies on ESP's internal timers that support the millis() function rather than having an RTC external module installed.

At this stage, a local ESP web server is initialised to accept watering programs' settings from remote clients. I chose to preset the web server to a fixed IP of, which the reader may alter as required and the Android App supports such flexibility. I like to fix servers IP addresses rather than leaving them to be dynamically allocated by a DHCP server since this strategy simplifies server and clients' communication setup.

In the loop() section, the current time is maintained by the time_now() function, while the server polls the client's requests using web_access() function to receive their watering schemes and to apply them using the
run_programs() function.

I normally slice down large sketches into subsidiary files alongside the main sketch ino file, where each file contains the code of specific functionality in the project. This helps the reader follow up the flow of sketch code, and provides for code modularity and reusability.

The table below summarises the details of the sketch files in terms of functions declarations and the number of statements. Note that the order of the include header statements in the main ino file needs to be maintained to avoid compiler errors for missing declarations.

Sketch File




setup(), loop() are main sketch sections



ntp_setup() sets local NTP port, sendNTPpacket() communicates with NTP server,
get_time() queries NTP server for current time using sendNTPpacket()



time_now() calculates the current time of day using millis()



watering program data structure, activate_sprinkler() activates ESP pin of watering program for the preset duration,run_programs() activates scheduled watering programs using run_programs()



web_access() for polling client data



wlan_setup() connects ESP to an accessible one out of four Internet routers


Now let's dive into key segments of the code starting with the current time calculation.

Code snippet 1

Current time calculation

I use the millis() function which gives us the elapsed milliseconds since the last ESP boot. When we add this period to the number of minutes of the day, called epoch, at boot time which is derived from the NTP server, we end up with the current number of minutes of the day. Such simple coding relieves us from the burden of using an RTC module especially when precise timing is crucial for the kind of application. The user should set his time zone correctly in rtc.h in order to get these calculations work properly.

void time_now() {
    timeNow = millis() / 1000;     
    // the no. of milliseconds passed since boot
    seconds = timeNow - timeLast;  
    //the number of seconds that have passed 
    //since the last time 60 seconds was reached.
    epoch +=  seconds;
    minutes = ((epoch % 86400L) / 60) + timeZone;
    Serial.print("Internal minutes of the day = ");

The watering program has a data structure depicted in the following code snipped that keeps its timing details which include activation days of the week, starting time in those days measured in day minutes, and the duration of irrigation for each sprinkler zone in the garden.

All data used here is numeric, and for water days it is Boolean (1/0). The sketch gets updates of those time pieces either from a web client which may be an internet explorer or a dedicated App as we shall see further on this article.

Code snippet 2

Watering Program Data Structure

typedef struct {
  int waterDay[7]; // days of week, 1 for on, 0 for off
  int waterStrt; // minute of the day to start program
  int waterDur[4]; // for 4 sprinklers, 0 for off
} schemePGM;

Now for the difficult part of any C coding job is the conversion between data types and structures and printing structured arrays.

The ESP receives the watering programs' settings in the form of a serial string of string values, 13 per program, separated by commas. We need to cast the 54 value string into the programs[] structure of numeric arrays (of type schemePGM as indicated in Snippet 2). Snippet 3 and Snippet 4 illustrates how this is done. Firstly, I segment the req string containing the 52 values into 4 rec[] arrays each having 13 values representing a particular watering program. Then, secondly, I map the intermediate rec[] array into the final programs[] array, element by element, while combining the time of the day in hh:mm into minutes of the day.

Code snippet 3

Updating intermediate watering programs structure with uploaded setting from Android App

// update programs[] array with incoming 
// settings from Android
  uint8_t i = 0, j = 0, k = 0, n = 0;
  int rec[4][13];
  String numVal = "00000000";
  req = req + ',';
  for (k = 0; k < 4; k++) {  // 4 programs loop
    i = 0;    // index of the number in program
    while (j < req.length() && i < 13) {
      if (req.charAt(j) == ',' || req.charAt(j) == '�') {
        numVal[n] = '�';
        rec[k][i] = numVal.toInt();
        n = 0;
        Serial.print(" ");
        numVal = "00000000";
      } else {
        numVal[n] = req[j];
    Serial.println(" ");

Code snippet 4

Mapping segmented sequence of received settings into the programs[] array.

for (i = 0; i < 4; i++) {
    for (j = 0; j < 7; j++) {
      programs[i].waterDay[j] = rec[i][j];
      Serial.print(" ");
    programs[i].waterStrt = rec[i][7] + (rec[i][8] * 60);
    // convert hh:mm time into minutes of the day
    Serial.print(" ");
    for (j = 0; j < 4; j++) {
      programs[i].waterDur[j] = rec[i][j + 9];
      Serial.print(" ");
    Serial.println(" ");


Watering program setup data structure

For example, a setup of {0,1,0,1,0,1,0,45,12,15,10,24,8} indicates that the program will run on Sundays, Tuesdays, and Thursdays, and will start at 12:45 in the evening. As for the 4 sprinklers' activation durations, that will last for 15, 10, 24, and 8 minutes respectively.








Program start(Min)

Program start (Hr)

Sprinker #1


Sprinker #2


Sprinker #3


Sprinker #4









0 - 60

0 - 23

0 - 60

0 - 60

0 - 60

0 - 60

The Android App

Remote controlling for the ESP8266 is made possible by launching a web server on it that accepts formatted input from web clients. The input can be setup manually in the URL of the server, and entered via a web browser, as depicted in the screenshot shown below.

The URL is composed of the ESP URL address followed by the 4 watering programs consecutive settings (green, yellow, cyan, and orange).,0,0,0,0,0,0,45,14,15,15,15,15,0,0,0,0,0,0,0,45,14,15,15,15,15,0,0,0,0,0,0,0,45,14,15,15,15,15,0,0,0,0,0,0,0,45,14,15,15,15,15

Another alternative is to build the formatted input via a web application run on a smartphone. I prepared an Android App that provides a graphical interface for building watering programs as described in the previous section.

Accessing the ESP8266 via a web browser

I used the MIT App Inventor online free tool for building a smartphone application that will simplify setting up the watering programs, and even facilitate the integration of this project in home IoT infrastructure.

ESP8266 response to URL sent from a web client

The App is composed of a single window containing the watering programs setup blocks: program selection buttons, time of day for running a program, sprinkler zone activation duration, and days of the week for running a program.

I included two message areas at the bottom of the App window for debugging purposes.

The App would maintain a dedicated text file on the phone for each watering program.

The setup of a program starts with clicking on the corresponding PGM button to retrieve its current settings. The button turns red indicating this mode of operation. The user can then alter the time of day for starting the program by clicking the Program Time button.

A popup window should appear enabling the setting of the hour and minute. Upon accepting the settings and exiting this window, a printout of the time in 24 hour format will show up beneath the button.

Setting sprinkler activation duration can be set using the corresponding slider whose value is momentary shown alongside.

As for the days of the week in which a program is to be run, seven checkboxes are provided for the week days. Checking a box indicates the day selection for the program.

Finally, the clicking the reddish PGM button would commit the changes to its setup in the program file. When the ESP module is online, the user can click the Commit to Garden button to send the four programs' setup to the module which instantly takes effect.

The App facilitates setting up the web server address of the ESP module in case it is altered in the Arduino sketch. At the bottom of the App window, four boxes for specifying the IP address segments are provided and they accept only numeric entries.

The App installation apk file is included with the Downloads of this article and can be installed on recent Android versions by skipping the Android warnings as shown here.

Note, the Android App installer notifications and warnings.

In popup screen, you will need to click on "More details" in order to get the screen in which you also need to click on "Install anyway". Clicking on button "Got it", cancels the installation process.

Programming the ESP-12E Module

Programming or flashing the ESP-12E module can be achieved by connecting it via USB to your computer supposedly running a supported IDE such as the Arduino IDE. Such connection requires a mediating hardware that links the serial interface of the ESP8266 to the USB port of the computer and also manipulates some other interface pins of the ESP in a specific sequence to achieve the flashing of the sketch code. I followed an interesting reference discussing several alternatives for flashing the ESP-12E, and I adopted method #3 in step #5.

I ended up with a programmer of my own depicted in the photos shown here. In flashing mode, I use NodeMCU board as the mediator whose GPIO0, 5, 15, and RST pins are wired to their equivalence in ESP-12E. The ESP-12E is also powered from the NodeMCU, so their 3.3V and GND are wired between modules as well. I added some extra features to the programmer such as powering up the ESP-12E module from USB in standalone mode without the presence of the NodeMCU, and provided some LEDs to indicate the state of pins GPIO12, 13, 14, 15, 16.