ESP8266 WIFI Remote Controlled Mains Switch
Ever since getting Raspberry Pi Server able to monitor the power being produced by the solar system I’ve needed a way to turn the swimming pool filter on and off depending on whether the sun is shining.
The original plan was to run a wire down to the filter switch under the house, but having to interface it to the Pi was a pain, and the last thing I needed around here was more wires. With the advent of the cheap ESP8266 WIFI modules the new plan became obvious: a WIFI controlled mains relay.
1. Hardware

1.1. Schematic
The schematic is simple enough:
- The ESP8266’s GPIO2 line is used to switch the relay and is active low.
- The 10k resistor and 100n capacitor on the RST pin ensure a nicely rising voltage for reliable reset.
- The 10n power-supply bypass capacitor between Vcc and ground turned out to be critical component as will be discussed later on.
1.2. Construction
An old mains filter unit from the junk pile provided the box, and had the 240V output socket conveniently already mounted. A piece of particle board, held in by screws from the side, provided some mass and for easy mounting of the various parts.
Using a knot to keep the mains cable from being pulled out of the box used to be pretty much standard practice, but is frowned upon these days with the reasoning being that the knot puts tension on the wires and can cause them, or the insulation, to break.
A cable clamp (and a rubber grommet in the hole) should be used instead, but lacking these items at the time and during development, I deemed that the risk was acceptable.
1.3. ESP8266 Module
An ESP8266 module type ESP-01 was used.
A mount was made by glueing together to 0.1” female pin-headers into which the ESP8266 module is inserted. The resistors, capacitors and other wiring were soldered directly to the pin-header’s pins, and then the assembly was attached with hot-melt glue to a screw that was drilled into the wooden board. This is visible in the adjacent photo as bottom left.
In initial testing and without the 10n bypass capacitor, it was found that, with no load attached to the relay, everything would work reliably. However, with a load attached, even a 10W lightbulb, switching the relay on or off would often cause the ESP8266 to reset. With the capacitor fitted it works reliably, even with a 1000W load. Apparently the ESP8266 module has no bypass capacitor, and so the 10n should probably be fitted as a matter of course when using this module.
1.4. Power Supply
The 240V to 5V switchmode power supply came from the charger for a cheap mobile phone. It is rated at 500mA, and although being rudimentary, it appears to be of reasonable quality and with good isolation.
The 3.3V for the ESP8266 module comes from a tiny ’3A DC-DC converter module’ as shown at left. This was attached to the 5V power supply’s output and worked fine when set to 3.3V output from the 5V input.
Note that many of the really cheap 240V power supplies coming from China are of very low quality, have poor isolation, radiate lots of EMI, and, in my opinion, are downright dangerous. Don’t use these; throw them in the bin.
The power supply used here came for a major local retailer and was marked with the C-Tick symbol, meaning that it was approved for use in Australia.
1.5. Relay Board
The relay board proved to be a bit of a challenge. The first attempt was to simply connect the GPIO2 pin to the NPN transistor’s base resistor, the problem with this was that, on startup, the intrinsic diode between base and emitter pulls the pin low and hence causes the ESP8266 to enter bootloader mode.
The second attempt was to use a PNP transistor with base pullup resistor on the relay high side; pulling GPIO2 low would turn the transistor and relay on, and by setting it to high impedance would cause the pullup to turn the transistor off. This failed though because, no matter what I tried, the GPIO2 line would not go into high impedance state. With a choice of either 3.3 or zero volts on the base, the transistor was always turned on. I suspect that the inability to set high impedance state may have been a bug in the early version of NodeMCU that I was using.
The solution was the circuit shown. The forward drop across the red LED puts the PNP’s emitter at 3.2 or so volts, and allows 3.3V logic-high on the GPIO2 pin to turn it off, and 0V logic-low to turn it on.
The relay circuit was made on a piece of matrix board. The LED lights when the relay is energised and could be used as a bonus relay-on indicator.
To provide for isolation and separation between the mains and low voltage sides, the copper pads were removed and a slot was cut.
2. Software
- The software is available here.
- The application presented here is written in lua for the NodeMCU interpreter. See here for an installation guide.
The ESP8266 creates a socket on TCP port 2323 and listens. When a connection is made and a line of data received, it is treated as a command which is looked up in a table and, if found, the code corresponding to this command is executed.
Here’s an example of connecting via telnet and issuing some commands:
>telnet 192.168.0.100 2323
Trying 192.168.0.100…
Connected to esp8266-0.
Escape character is ‘^]’.
#0:0
version
#0:0
ver
#1:1.0
status
#1:0
1
#1:1
status
#1:1
0
#1:1
status
#1:0
heap
#1:6944
The lines that I have typed are in italics and the output from the ESP8266 is in bold.
The ESP8266 uses the prompt to indicate the status of the last command and and that it is waiting for the next command. The digit following the # indicates whether the typed command was a valid one. version was invalid and hence 0, ver and the other commands are valid and hence 1.
The text after the : is the status value as returned by the command, so here, ver returns the software version of 1.0, for example. The status command returns 0 if the relay is off, and 1 if it is on. The 1 command turns the really on, and 0 turns it off. There are 6944 bytes available on the heap, and you get the picture. A command may also output one or more lines of arbitary text (except ones beginning with #) which will be displayed prior to the prompt.
Doing it this way means it is relatively simple to write utilities that can issue and respond to commands automatically, while still allowing for humans to manually type commands
2.1. The LUA Command
The lua command is special in that it sets the interpreter to read input and write output to the socket. This allows for arbitary code to be issued. For example:
#0:0
lua
> print(node.heap())
6648
> lua
#1:0
heap
#1:6944
2.2. Adding New Commands
If you look at the source code, you will see that when the telnet
class defined in telnet.lpp
reads a line of text, it passes it to the Shell
class defined in shell.lpp
, which then looks up the command in the ShellCMDs
table in cmd.lpp
and, if found, calls the associated function.
Adding new commands is simply a matter of defining them in cmd.lpp
ShellCMDs={
-- display size of heap
["heap"] = function(param)
return node.heap()
end,
-- restart the device
["reboot"] = function(param)
node.restart()
return 1
end,
-- return seconds since startup
["time"] = function(param)
return tmr.time()
end,
-- return value of counter
["now"] = function(param)
return tmr.now()
end,
}
The param
function parameter is the rest of the command line, excluding the command itself.
2.3. The lttool.pl
Command Line Utility.
lttool.pl
is a perl utility intended to make controlling the ESP8266 from Unix scripts easy, or to be used as the basis of a more application specific script. It works as follows:
- Opens socket to specified host and port which is assumed to be an esp8266 running telnet shell firmware
- Timeout on all socket reads etc
- Expects to get initial prompt from esp: #0:0 or fails
- Then, for each command specified on command line:
- Writes command to socket
- Optionally displays command output lines, until command status line is received
- When command status line is received (eg #1:1 ) it is displayed as described below
The perl IO::Socket::Timeout
module is used to ensure that the utility does not hang for long periods in case of the ESP8266 not responding. It will likely need to be installed prior to use.
The command status line is considered to be the first line received that matches: ^#[01]:
and using the convention that for a status line of #1:1
the first 1 means that the command was found and executed (ie a valid command), it will be zero otherwise. The second 1 is the returned command status, ie the value that the command returned, it could be an integer, float, string, or whatever
When a status line is received, it is displayed as :command:cmd_ok:cmd_status
For example:
> ./lttool.pl -i 192.168.0.100 -p 2323 status
:status:1:1
Usage:
usage: lttool.pl [options] cmd1 cmd2 "cmd 3" ...
Where cmd arguments are the commands that will be executed on the remote system.
options:
-h display this help message and exit
-p port remote host port
-i hostname remote host ip numer or host name
-v display log messages. These are printed to STDERR
-s do not display command status line
-l do not display lines returned by command
-q use quotes on status line
3. Conclusion
The device described here has been installed and working switching the 1KW pump for over four weeks now, and without even a single problem. The lua software was modified with a timeout function to turn the pump off after one hour if no command has been received, but besides that, is as presented here.