ESP32/ESP8266 Relay Module Web Server using Arduino IDE (1, 2, 4, 8, 16 Channels)

This tutorial is a step-by-step guide that covers how to build a standalone ESP32 or ESP8266 NodeMCU Web Server that controls any relay module. We’ll create an ESP32/ESP8266 Web Server that is mobile responsive and it can be accessed with any device with a browser in your local network.

ESP8266 NodeMCU Relay Module Web Server using Arduino IDE 1 2 4 8 16 Channels

Introducing Relays

A relay is an electrically operated switch and like any other switch, it that can be turned on or off, letting the current go through or not. It can be controlled with low voltages, like the 3.3V provided by the ESP GPIOs and allows us to control high voltages like 12V, 24V or mains voltage (230V in Europe and 120V in the US).

There are different relay modules with a different number of channels. You can find relay modules with one, two, four, eight and even sixteen channels. The number of channels determines the number of outputs you can control.

Relay modules with different number of channels

Get a relay module:

For an in-depth guide on how a relay module works, read:

Control Relay Modules with Multiple Channels using an ESP32/ESP8266 Web Server

Control Multiple Relays with ESP8266 Web Server

With this web server code, you can control as many relays as you want via web server whether they are configured as normally opened or as normally closed. You just need to change a few lines of code to define the number of relays you want to control and the pin assignment.

To build this web server, install the ESP32/ESP8266 board add-on and the next libraries in your Arduino IDE.

Installing the ESPAsyncWebServer, AsyncTCP, and ESPAsyncTCP Libraries

  1. Click here to download the ESPAsyncWebServer library
  2. Click here to download the AsyncTCP library (ESP32)
  3. Click here to download the ESPAsyncTCP library (ESP8266 NodeMCU)

In your Arduino IDE, to install the libraries go to Sketch > Include Library > Add .ZIP library… and select the library you’ve just downloaded.

After installing the required libraries, copy the following code to your Arduino IDE.

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp8266-relay-module-ac-web-server/
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

// Import required libraries
#include "ESP8266WiFi.h"
#include "ESPAsyncWebServer.h"

// Set to true to define Relay as Normally Open (NO)
#define RELAY_NO    true

// Set number of relays
#define NUM_RELAYS  5

// Assign each GPIO to a relay
int relayGPIOs[NUM_RELAYS] = {5, 4, 14, 12, 13};

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

const char* PARAM_INPUT_1 = "relay";  
const char* PARAM_INPUT_2 = "state";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 3.0rem;}
    p {font-size: 3.0rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px} 
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 34px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 68px}
    input:checked+.slider {background-color: #2196F3}
    input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
  </style>
</head>
<body>
  <h2>ESP Web Server</h2>
  %BUTTONPLACEHOLDER%
<script>function toggleCheckbox(element) {
  var xhr = new XMLHttpRequest();
  if(element.checked){ xhr.open("GET", "/update?relay="+element.id+"&state=1", true); }
  else { xhr.open("GET", "/update?relay="+element.id+"&state=0", true); }
  xhr.send();
}</script>
</body>
</html>
)rawliteral";

// Replaces placeholder with button section in your web page
String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons ="";
    for(int i=1; i<=NUM_RELAYS; i++){
      String relayStateValue = relayState(i);
      buttons+= "<h4>Relay #" + String(i) + " - GPIO " + relayGPIOs[i-1] + "</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"" + String(i) + "\" "+ relayStateValue +"><span class=\"slider\"></span></label>";
    }
    return buttons;
  }
  return String();
}

String relayState(int numRelay){
  if(RELAY_NO){
    if(digitalRead(relayGPIOs[numRelay-1])){
      return "";
    }
    else {
      return "checked";
    }
  }
  else {
    if(digitalRead(relayGPIOs[numRelay-1])){
      return "checked";
    }
    else {
      return "";
    }
  }
  return "";
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  // Set all relays to off when the program starts - if set to Normally Open (NO), the relay is off when you set the relay to HIGH
  for(int i=1; i<=NUM_RELAYS; i++){
    pinMode(relayGPIOs[i-1], OUTPUT);
    if(RELAY_NO){
      digitalWrite(relayGPIOs[i-1], HIGH);
    }
    else{
      digitalWrite(relayGPIOs[i-1], LOW);
    }
  }
  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP8266 Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Send a GET request to <ESP_IP>/update?relay=<inputMessage>&state=<inputMessage2>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    String inputParam;
    String inputMessage2;
    String inputParam2;
    // GET input1 value on <ESP_IP>/update?relay=<inputMessage>
    if (request->hasParam(PARAM_INPUT_1) & request->hasParam(PARAM_INPUT_2)) {
      inputMessage = request->getParam(PARAM_INPUT_1)->value();
      inputParam = PARAM_INPUT_1;
      inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
      inputParam2 = PARAM_INPUT_2;
      if(RELAY_NO){
        Serial.print("NO ");
        digitalWrite(relayGPIOs[inputMessage.toInt()-1], !inputMessage2.toInt());
      }
      else{
        Serial.print("NC ");
        digitalWrite(relayGPIOs[inputMessage.toInt()-1], inputMessage2.toInt());
      }
    }
    else {
      inputMessage = "No message sent";
      inputParam = "none";
    }
    Serial.println(inputMessage + inputMessage2);
    request->send(200, "text/plain", "OK");
  });
  // Start server
  server.begin();
}
  
void loop() {

}

View raw code

Define Relay Configuration

Modify the following variable to indicate whether you’re using your relays in normally open (NO) or normally closed (NC) configuration. Set the RELAY_NO variable to true for normally open os set to false for normally closed.

#define RELAY_NO true

Define Number of Relays (Channels)

You can define the number of relays you want to control on the NUM_RELAYS variable. For demonstration purposes, we’re setting it to 5.

#define NUM_RELAYS 5

Define Relays Pin Assignment

In the following array variable you can define the ESP GPIOs that will control the relays.

int relayGPIOs[NUM_RELAYS] = {5, 4, 14, 12, 13};

The number of relays set on the NUM_RELAYS variable needs to match the number of GPIOs assigned in the relayGPIOs array.

Network Credentials

Insert your network credentials in the following variables.

const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Wiring a Relay Module to an ESP Board

For demonstration purposes, we’re controlling 5 relay channels. Wire the ESP boards to the relay module as shown in the next schematic diagrams.

ESP32 Wiring

ESP32 8-channel relay module wiring diagram schematic

ESP8266 NodeMCU Wiring

ES8266 8-channel relay module wiring diagram schematic

Warning: most relay projects deal with mains voltage. Misuse can result in serious injuries. If you’re not familiar with mains voltage ask someone who is to help you out. While programming the ESP or wiring your circuit make sure everything is disconnected from mains voltage. Alternatively, you can use a 12V power source to control 12V appliances.

Demonstration

After making the necessary changes, upload the code to your ESP. Open the Serial Monitor at a baud rate of 115200 and press the ESP RST button to get its IP address (make sure that you circuit is unplugged from mains voltage).

Open a browser in your local network and type the ESP IP address to get access to the web server. You should get something as follows with as many buttons as the number of relays you’ve defined in your code.

ESP32 ESP8266 NodeMCU Relay Web Server Mobile web browser

Now, you can use the buttons to control your relays remotely using your smartphone.

ESP32 ESP8266 Relay Module Control Web Server using Arduino IDE

Enclosure for Safety

For a final project, make sure you place your relay module and ESP inside an enclosure to avoid any AC pins exposed.

Enclosure for Relay Module

Watch the Video

Watch the following video for a complete tutorial and live demonstration.

Wrapping Up

Using relays with the ESP is a great way to control AC household appliances remotely. You can also read our complete guides to learn more about relay modules:

Learn more about the ESP boards with our resources:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

31 thoughts on “ESP32/ESP8266 Relay Module Web Server using Arduino IDE (1, 2, 4, 8, 16 Channels)”

  1. Lots of good info. Tks.

    One issue I’ve been grappling with is how to make projects like the above securely visible on the internet. I’ve tried building an https server but without any success.

    A tutorial on https would be greatly appreciated.

    Reply
  2. For ESP8266 expansion via MCP23017 for controlling 16 channel tutorial will be useful.

    Saving state to EEPROM during power cycle would be an added advantage.

    Also, 🤔 feedback of current relay status in webserver.

    Please help with tutorial for the above

    Thank a lot

    -Kumaran

    Reply
  3. Hi Rui and Sara
    Instead of on/off button if I want to control like push button.Once button pressed then relay trigger for 1 sec.
    What is code need to change?

    Reply
  4. Hi again.
    Its working fine, thanks.
    My board has 8 relay, can please help me witch 3 more GPIO can i add to make the 8 relay working in my ESP32.
    Thanks for everything.

    Reply
  5. I have noticed that on older devices such as android froyo 2.2 the button would only work one time and after that, would operate erratically if at all. I solved the problem by adding:

    after the tag.

    Reply
  6. I have noticed that on older devices such as android froyo 2.2 the button would only work one time and after that, would operate erratically if at all. I solved the problem by adding:

    meta http-equiv=’cache-control’ content=’no-cache’
    meta http-equiv=’expires’ content=’0′
    meta http-equiv=’pragma’ content=’no-cache’

    inside html tags, after the head tag.

    Reply
  7. Hi Sara.
    I have found that the no-cache tags are not as reliable as I had hoped for in older cell browsers. Adding Math.random() seems to be a more reliable way to prevent reusing cached responses such as:

    if(element.checked){ xhr.open(“GET”, “/update?relay=” + element.id + “&state=1” + Math.random(), true); xhr.send();

    I would be interested in learning better ways.

    Love your blog.
    Regards

    Reply
  8. Hola, muchas gracias por tu trabajo, en una hora tengo controlada mi casa.
    Solo una consulta que probablemente solo me suceda a mi, al conectarme a la web los botones parten encendidos, (en azul) al apagarlos activan los relés, quiere decir que están configurados al reves.
    ¿Me ayudas a corregiro por favor?
    Nuevamente muchas gracias y feliz año nuevo.

    Reply
  9. Hola.
    Quisiera añadirle un BMP180 al servidor.
    Lo que no sé es como inserto los datos obtenidos del BMP180 dentro de la definicion de la pagina web:
    const char index_html[] PROGMEM = R”rawliteral(

    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 3.0rem;}
    p {font-size: 3.0rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px}
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 34px}
    .slider:before {position: absolute; content: “”; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 68px}
    input:checked+.slider {background-color: #2196F3}
    input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}

    Control 4 zonas

    %BUTTONPLACEHOLDER%
    function toggleCheckbox(element) {
    var xhr = new XMLHttpRequest();
    if(element.checked){ xhr.open(“GET”, “/update?relay=”+element.id+”&state=1”, true); }
    else { xhr.open(“GET”, “/update?relay=”+element.id+”&state=0″, true); }
    xhr.send();
    }

    )rawliteral”;

    Gracias.

    Reply
  10. Hi Sara,

    I try to put two scripts in one. This one with “ESP32 DHT11/DHT22 Web Server – Temperature and Humidity using Arduino IDE”. I need to check dynamicaly temperature and remote control relay in one web page. I think ESPAsyncWebServer library is good for it, but I try many various and one thing is working and other not.
    The update function not working like before. Can you please explain how to put both scripts in one to work it good.
    Thank you very much

    Peter

    Reply
  11. hi Sara

    Why when i open serial montitor, it constantly saying connecting to wifi, then when i press reset button, it prints

    rll�r$�n�l�b|���rb�b�nnlnnbbp�$blrlp�n�l�bn�n��b��nn’l�l�nn$nr���nrr�p�n�r�bbn�nb��nn'l�nn$nr���nrlr��nrl�$rl��n�`

    Reply
  12. Please add auto update code. Like Toggle switch pressed in one device automatically updates on other device.

    Thanks and Regards..

    Reply
  13. Hi Sara,

    Thank you very very much for this code.

    I have made 2 esp8266 boards to control 2 relays each, total 4 relays.
    I have assigned IP address A to board 1 and IP address B to board 2.
    Those 2 boards are connected to my wifi router, and I access the IP A and IP B from my phone to control those relays.

    Now I am setting up a third esp8266 board (say that the IP Address will be C) to become Access Point and Webserver to control another 2 relays.
    I wish that board 1 and board 2 can connect to board 3, and expecting to see all 6 buttons when accessing board 3 through IP address C to control all 6 relays. So that I can get rid of the router.
    All 3 boards are placed in 3 different location.

    Would you please help me? I am still learning those html css and javascript but it may take few months untill I can write good code for this purpose.

    Thanks

    Javen

    Reply

Leave a Reply to Razu Ahmad Cancel reply

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.