ESP8266 – WiFi controlled light

In home control systems you often need a simple remote controllable relay. Just to switch on or off some lights or appliances. Here is my solution where I used an ESP8266 to control a relay over TCP socket communication. It is a real simple and easy to build system, but special care has to be taken here, because we are dealing with 220V or 110V, depending on the region you live. In hardware description below I show the two option, first where a relay and the AC connectors are mounted on the same PCB as the ESP8266 and a second one where an external relay module (like this one) is used.

For safety reasons I would suggest you to use an external relay module, that is the safer way.

Also if you are not trained to connect high voltage AC (220V or 110V) wires, stay away from these DIY project and buy a ready made module like this one.



Relay hardware

The hardware is really simple. I used in this project an Adafruit Huzzah ESP8266 Breakout which I used in most of my projects. It can be supplied with either 5V or 3.3V and works reliable for me.

So to supply the board there are two connectors for 5V input. The 5V can be either supplied through screw connector X1 or, what I do quite often, a USB power supply can be soldered to connector X7. I buy often very cheap USB power supplies that are able to supply 5V 1A. Then I take the electronics of the PSU out of the case, replace the USB connector with 4 pins and solder the whole PSU to connector X7. But that is again something for the more experiences users. X7 can be as well used to solder an USB female port to it, so you can use a standard USB cable to supply the unit.

One problem I often experienced with these cheap USB power supplies is that the 5V voltage is not very good. It is full with noises from the PSU itself and often the PSU’s have a problem to keep the voltage stable when there is a sudden request for a higher current. The ESP8266 can request such higher currents usually during switching on.

To avoid these problems and as well smoothen the 5V, I added an 100uF and a 10nF capacitor to the power lines.

The second part in the schematic is the ESP32 module. In this application only one GPIO is used to control the relay. Here I use GPIO5.

The relay (or relay module) I usually use expect 5V to be activated. So I build up a simple Darlington transistor, because the ESP8266 supplies only 3.3V on the GPIO’s and in addtion it is not designed to supply enough current to drive a relay. In the schematic you can see the two options, solder a relay directly onto the board, or connect a relay module to connector X2. If you decide to go with a relay mounted on the PCB, don’t forget to mount diode D1 as well. This is necessary because the coil of the relay can produce negative current while switching, which can damage the transistors.

Here is the complete schematic, click on it to see it in full resolution:

I created as well a PCB for it.

You can see that below the relay I added an air gap between the high voltage pins of the relay and the coil pins to improve the isolation between high voltage AC and the 5V logic on the board.

5V logic lines and the ground plane keep their distance to the AC lines as well.

In order to make the capacitors on the power line effective, I separated the power lines before the capacitors and after the capacitors as well.

To keep the board as a single layer, I needed to add one jumper wire (J1).

Here is an image of the PCB:

Relay software

The software to control this WiFi relay board is of course adapted to work within my Home Control System.

As I am using PlatformIO and Visual Studio Code I can structure the software into several files for easier maintenance. The complete software can be found in my Github repo.

The header files

  • setup.h – loads all required Arduino core and library header files
  • declarations.h declares all global variables
  • globals.h – declares all globally used variables as extern
  • functions.h – declares all functions in the project used globally

To make life easier I use in this project as well my own library ESP8266-MyLib. In this library I put the code that is used by all my ESP8266 projects. This includes the WiFi manager to setup and automatically reconnect to a WiFi, NTP functions to update the internal time of the device, led functions to control the two on board LEDs of the Huzzah board and my WiFi credentials and passwords (this part is of course not shared in the Github repo). Details about the functions in this private library are explained in another post here.


The first part includes all standard Arduino libraries needed for WiFi, OTA, UDP and TCP communication

The next part includes public libraries that I need to create and parse JSON objects and the WiFiManager for the setup of the WiFi connection

And then it loads the parts of my personal library

To keep you awake and not getting too bored, I skip the detailed explanations of the other header files. You can find them in the Github repo

So I go into more details of Setup.cpp contains the setup() with the initialization functions.

First we initialize the LED’s, the GPIO used to activate the relay and the serial port

Next is to initialize the SPIFFS file system. If the initialization fails it is formatted.

The file system is used to store some configuration values I use in my home control system. At the moment there are 4 configuration values

  • loc = location where the device is mounted
  • light = the IP address of a second light that should be controlled
  • cam – the IP address of a still camera that should be triggered to take a photo
  • sec – the IP address of another security system that should receive informations

After initializing the SPIFFS we check if any of these configuration values are stored. As the access to the SPIFFS is the same for all my devices the function to check and read the values is in my ESP8266 library.

After that we start connecting to the WiFi. For the connection I use the WiFiManager, a library for ESP8266 maintained by jakerabid. The WiFiManager helps you to initialize the WiFi credentials on a newly flashed ESP device. If no WiFi configuration is stored, the WiFiManager opens a captive portal on an WiFi access point. In this captive portal you can not only setup the WiFi configuration, but as well setup some of the configuration values mentioned above.

First step is to create a unique device identifier by using the MAC address of the device. Then we tell the WiFiManager which configuration values we want to be setup, then we try to connect. The function connectWiFi() is again in my library, because it is the same for all my devices. After trying to connect some debug messages ae printed over the serial port, but that is only useful during development and debugging, because later there will nothing connected to the serial.

Now we should be connected to a WiFi network. From this point on I use sendDebug() function to send debug messages over TCP to a predefined local IP address. I use my Android tablet to display these debug messages.

If the initialization of the file system failed, we report this. Then we try to find out what the last reboot reason was. After that we check if WiFiManager changed any of the configuration values and store them in a file.

Next is to update the internal clock of the ESP with the help of a NTP server. Again, this common routine is in my library. Then a timer i started which will send every 1 minute a status message over UDP broadcasting. This way other devices in My Home Control system know that the device is alive. And last in this code extract some debug information is sent. sendAlarm() is the function to send out the current status the first time after restart. This function is located in LanSubs.cpp and I will talk about it later.

The rest of setup() start the TCP socket server, which will listen to incoming commands, initializes the ArduinoOTA functionality and adds some serves text to the MDNS service.

The last step initializes the ESP8266 internal watchdog. From this point on, the watchdog needs to be reset every 8 seconds, otherwise the ESP will reboot. This helps to recover from endless loops or other coding errors.

loop() function

In the loop() function the different events are handled.

First we check if an OTA request was received. If this is the case, the boolean otaRunning is set to true. To avoid disturbance of the OTA process, in this case the main loop() is doing nothing anymore than triggering the watchdog.

If no OTA is active, we update the internal time if necessary, check if any device tries to send commands to the TCP socket server and send a message if the light was switched off (either by timer or by a command).

loop() handles as well the frequently sending of the status messages over UDP broadcasting. In this code block we check as well if we lost contact to the WiFi access point and reconnect if necessary.

Inside the loop() you can see as well that I frequently retrigger the watchdog with wdt_reset();


Handling of the communication

The LanSubs.cpp contains two functions. The first one (sendAlarm) sends a JSON object containing the status of the device as UDP broadcast. The second one (socketServer) handles incoming commands from other devices. I wrote about this in detail in my other post Communication between devices that doesn’t require a server. For a detailed explanation check out the TCP and the UDP examples.

The only part I want to go into more details is the TCP command “b“. If this command is received, the light should be switched on for 5 minutes. To achieve this, the lights are switched on and at the same time a timer is started which will call the function relayOff after 5 minutes. Inside the function relayOff the light is switched off again.

And here is the complete code:



Other functions

Subs.cpp contains the routines that are called by timer for the status message and the automatic light off timer.

If it is time to send a status message, the timer started in setup() is calling the function triggerHeartBeat. Here we just send a flag that is handled in the main loop().

If the light was switched on by the command “b” the associated timer will call relayOff after 5 minutes where the relay is switched off and a flag for the main loop() is set.

The other functions here handle the status of the file which is written frequently into the file status.txt in the SPIFFS file system. This status is used after reboot and as well for the 1 minute status messages sent over UDP broadcast.

The first function createStatus creates a JSON object which contains the current status of the device. The status values stored in the JSON object are

  • lo – shows if the light is on (1) or off (0)
  • bo – a flag if the status is sent from the setup routine

And if requested the status message can be extended by

  • rs – shows the current RSSI value of the WiFi
  • bu – a string showing the compile date of the app
  • re – a string with the last reboot reason
  • dt – the current internal time and date of the device
  • db – a flag if the debug output is enabled

The next function writeStatus stored the current status in the file system. It first creates the status as JSON object by calling createStatus and then writes it into the file status.txt

writeRebootReason() stores the last detected reboot reason inside the status.txt file

And the last function readStatus() reads the last stored values from the file status.txt. It is called from the setup() routine and restores the last status before the device was rebooting or started. That means that if the last light status was ON, then the light is switched on. And if the last debug status was ON, then debug output is enabled.

That’s it, all the different functions used in the WiFi controlled light application are explained.

Now you can of course say, that could be done simpler, but keep in mind that this device is part of a whole system and that many things here seems to be overloaded. But this is necessary to integrate the device into My Home Controlsystem.


Leave a Reply

Your email address will not be published. Required fields are marked *

Free Link Directory