WiFi relay controller (ESP8266, ESP32)

This article describes a WiFi connected remote relay controller implemented in ESP8266 and ESP32 microcontrollers.

Fig 1: 

Fig 1 shows a basic ESP8266 single channel relay module which can be bought online for less than $5. I might note that the 5V relay is run from the regulated 3.3V supply.

 The controller logic is contained in program stored in flash memory, and parameters that control its operation are stored in a config file in flash hosted LittleFS file system. This means that the same flash module can be used for a range of applications, and operation tailored by field programming of the config file with appropriate data.


Web interface

The controller uses a Wifi accessible RESTful interface than can be used on a browser or using any command line tool that can submit a GET mode URL (eg curl).

Fig 2: 

Fig 1 shows the web form for a 4 channel controller.

The url to update the above settings to also turn on the flood lights is http://EspFactory01.lan/?n00=on&n02=on&update= .


The firmware is flexible and caters for a number of use cases. Controller operation is configured with a configuration file stored in the file system resident in the controller flash memory.

Configuration is read from file config.json in the LittleFS on-board file system.

Config file v1

At this point in time, all published firmware requires config file v1.

"cfgver": 1,
"wifi": {
"ssid": "",
"pwd": ""
"hostname": "LilyGo-TRelay",
"login": {
"user": "",
"pwd": ""
"outputs": [
"inputs": [
["GPIO 21 K1",21,false],
["GPIO 19 K2",19,false],
["GPIO 18 K3",18,false],
["GPIO 6 K4",5,false],
["GPIO 12 (9)",12,false],
["GPIO 32 (11)",32,false],
["GPIO 35 (13)",35,false],
["GPIO 39 (15)",39,false]

Above is the prototype configuration file built for code testing, it is in strict JSON format. Some sections are optional as detailed later.

Hint: check the validity of your JSON syntax using an online tool such as ArduinoJSON Assistant. The currently allocated JSONDocument size is 4096B, check that your JSON does not overrun that size.

"version" required An integer value for the config file layout version. This is not a user choice, it relates to firmware / config file compatibility.
"outputs" optional This is an array of 1 to n elements, each being an array comprising a string type label, an integer GPIO pin to be driven, a boolean inversion flag, a boolean value of true or false for the state of the relay at power up. If the array is given, all elements of the array are requried.
"inputs" optional This is an array of 1 to n elements, each being an array comprising a string type label, an integer GPIO pin to be read, a boolean inversion flag, and a boolean pullup flag (on supported pins). If the array is given, all elements of the array are requried.
"hostname" required Unique string hostname that will present on the network.
"wifi" optional An array of strings for SSID and password to access the network access point (overriding the flexible discovery configuration facility). If the array is given, all elements of the array are requried.
"login" optional An array of strings for user and password to require user authentication to the controller web server.  If the array is given, all elements of the array are requried.
"static ip" optional Static IP parameters which will be used instead of DHCP (if the IP address is non-zero). Note the octets are JSON array elements and separated by commas. If the array is given, all elements of the array are requried.
"wificfgpin" optional GPIO pin number for on-demand WiFi config portal, has pullup enabled, LOW active. (ESP8266: Consider one of GPIOs 0,4-5,12-14. ESP32: Consider one of GPIOs 0,3-4,13-14,16-33)

The order of JSON object names (keys) is not important, the order JSON array elements is important.

"cfgver": 1,
"hostname": "Minimal",
"outputs": [
"wificfgpin": 5

Above, a minimal 4 channel configuration where WiFi is configured though the captive configuration portal using a web browser, and defaulting to DHCP and no web server access security.

Fig 5: 

Fig 5 shows the WiFi configuration screen. (The warning message advises that it cannot connect to the previously configured AP.

If the captive portal does not cause a popup in your device, browse to

GPIO overview

Some GPIO pins on the ESP8266 and ESP32 are not fully available during boot, though some of those can be used once the system has started.

ESP8266 GPIO Function State Restrictions 

GPIO Function State Restrictions
0 Boot mode select 3.3V No Hi-Z
1 TX0 - Not usable during Serial transmission
2 Boot mode select
3.3V (boot only) Don’t connect to ground at boot time. Sends debug data at boot time.
3 RX0 - Not usable during Serial transmission
4 SDA (I²C) - -
5 SCL (I²C) - -
6-11 Flash connection x Not usable, and not broken out
12 MISO (SPI) - -
13 MOSI (SPI) - -
14 SCK (SPI) - -
15 SS (SPI) 0V Pull-up resistor not usable
16 Wake up from sleep - No pull-up resistor, but pull-down instead. Should be connected to RST to wake up.

wificfgpin: If GPIO00 is used, it must not be pulled low at boot, but can be pulled low (or the boot/00 button if fitted pressed) after startup.

ESP32: GPIO34-39 can only be set as input mode and do not have software-enabled pullup or pulldown functions.


The project is developed in Arduino IDE (C++) with ESP8266 and ESP32 packages installed, and a bunch of needed libraries.

This area is one where there is a lot of parallel developement of libraries, and often with little regard for backwards and cross compatiblity, so it becomes a nightmare finding a combinarion that works, and even worse, fixing incompatiblities in time. For this reason, there are no plans to release the source code, but firmware binaries will be published once stabilised and updated from time to time.

Building the LittleFS file system image

Get the mklittlefs program for your operating system from mklittlefs repository.

mklittlefs -d 2 -c data -b 4096 -p 256 -s 0x10000 littlefs.ESP8266.bin
echo littlefs.ESP8266.bin loads at address 0x0eb000
mklittlefs -d 2 -c data -b 4096 -p 256 -s 0x30000 littlefs.ESP32.bin
echo littlefs.ESP32.bin loads at address 0x03d0000

Above, the commands to make the LittleFS images, they assume the configuration files are in subdirectory data.

Downloading firmware and filesystem


Some relay boards have a USB interface for the tx and rx data lines, some also implement control of the RESET and BOOT lines for automated programming, the latter is a very convenient way of working.

Boards that do not have an integrated USB programming interface will require an external adapter jumpered to the appropriate pins. Be aware that many relay boards to not put the RESET line on header pins, though they may have the EN pin available which may work as a substitute.

Beware that some boards with a USB connector that is NOT for direct USB connection, eg the LILYGO TTGO T-Relay requires a special adapter or an external adapter wired to the header pins on the board.


There are options for software to download the firmware and filesystem images, this article describes one: Espressif's own GUI tool for windows: Espressif flash download tool.

Fig 7: 

Fig 7 shows the download tool after downloading firmware and LittleFS images.

I recommend that before you first use the microcontroller for this project, that you erase the whole memory (Erase button in the dialogue). Thereafter you can just load the firmware and filesystem as changed. To change the config file, just build a new filesystem image and download it alone.

In summary, the LittleFS is different for the ESP8266 and ESP32 modules (and dependent on the partition scheme used by this firmware):

Programming adapters

Fig 8: 

Fig 8 shows a basic adapter for programming the ESP01S modules. There are plenty that look like this, but this one has the auto programming interface on the board, the two transistors near the top edge of the board, and is superior to the others. This module could be patched to a relay control board with jumper wires. Note that the Vcc presented to the header is 3.3V, if you need 5V, you can self power the target and just connect ground, txd, rxd, EN (RST) and IO00.

Fig 9: 

Fig 9 shows Espressif's Esp-Prog adapter for programming the ESP modules, it contains the auto-prog electronics. These are available on Aliexpress for <$25 shipped.

Command line access

The relay controller can be access not only using a browser, but also by command line tools or any utility that can send RESTful requests to a webserver.

D:\src\EspRelay\data>wget --user=user --password=pwd "espfactory01.lan/?n02=on&n03=on&update="
--2022-04-02 17:21:57-- http://espfactory01.lan/?n02=on&n03=on&update=
Resolving espfactory01.lan...
Connecting to espfactory01.lan||:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1267 (1.2K) [text/html]
Saving to: `index.html@n02=on&n03=on&update='

100%[==============================================================================>] 1,267 --.-K/s in 0.05s

2022-04-02 17:21:57 (24.0 KB/s) - `index.html@n02=on&n03=on&update=' saved [1267/1267]

D:\src\EspRelay\data>curl --user user:pwd "espfactory01.lan/?n02=on&n03=on&update="
<html><body><h1>ESP Relay controller (v0.1)</h1><h2>EspFactory01</h2>
<!-- status={"outputs":[0,0,1,1],"inputs":[0,0]} -->
<form method="get" action="/">
<input type='button' value='Check all' onClick="javascript:f=this.form;for(x=0;x<f.elements.length;x++){if (f.elements[x].type=='checkbox'){f.elements[x].checked=true;}}">
<input type='button' value='Uncheck all' onClick="javascript:f=this.form;for(x=0;x<f.elements.length;x++){if (f.elements[x].type=='checkbox'){f.elements[x].checked=false;}}">
<input type="checkbox" id="r00" name="n00"><label for="r00">Flood lights</label><br>
<input type="checkbox" id="r01" name="n01"><label for="r01">Interior lights</label><br>
<input type="checkbox" id="r02" name="n02" checked><label for="r02">Security lights</label><br>
<input type="checkbox" id="r03" name="n03" checked><label for="r03">Ventilation</label><br>
<input type="checkbox" id="i00" name="ni00" disabled><label for="i00">GPIO 0 (Flood lights)</label><br>
<input type="checkbox" id="i01" name="ni01" disabled><label for="i01">GPIO 3 (Interior lights)</label><br>
<input type="hidden" id="update" name="update" value="">
<input type="button" value="Read" onclick="location.href='/';">
<input type="submit" value="Write">

Above are examples of use of wget and curl to access a server protected by user and password.

The above example shows in the response to the curl command, a comment line with "status=" followed by a JSON structure of the status of the outputs and inputs suitable for programmatic interpretation.

A work in development...

To do:

Fig 1: 

Fig 1 shows




Fig 1: 

Fig 1 shows





13/05/2022 v0.2:


Version Date Description
1.01 27/03/2022 Initial.
1.02 06/04/2022 Updated for new ESP32 partition model.
1.03 14/05/2022 Enhanced input pin pullup handling, wificfgpin. (v.02)

© Copyright: Owen Duffy 1995, 2021. All rights reserved. Disclaimer.