In this tutorial, we will create a web interface to control 8 relays individually. This tutorial is a continuation of the project Pilot 8 relays using an ESP32 and the serial monitor.
This tutorial can be applied to any microcontroller with a Wifi or Ethernet connection. You will have to be careful to modify the connection scheme and code to suit your use.
Material
- NodeMCU 32S
- Breadboard
- Jumper cable
- 8 relay module
- Shift register 74hc595
Principle
In the previous tutorial, we saw how to drive 8 relays independently using the Arduino IDE serial monitor. When an internet connection is available, as on the ESP32 NodeMCU, it is possible to turn your microcontroller into a server and host a web page. This web page can be used as a graphical interface to drive your project. We will create a web page, accessible via the local network, with buttons to activate the relays.
Code
We will take the code from the previous tutorial and combine it with the code to create a Web Interface for the ESP32 NodeMCU. On the web page we will create a table with 16 buttons corresponding to the “Activate” and “Reset” actions of the 8 relays. In order to visualize some of the actions, we add an insert to display messages from the microcontroller.
void webpage(WiFiClient client) { //Send webpage to browser client.println("<!DOCTYPE HTML>"); client.println("<html>"); client.println("<head>"); client.println("<title> AranaCorp </title>"); client.println("<meta name='apple-mobile-web-app-capable' content='yes' />"); client.println("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />"); client.println("<meta charset='UTF-8'>"); client.println(""); client.println(""); client.println(""); client.println("</head>"); client.println("<body> "); client.println("<div id='page'>"); client.println("<div id='content'>"); client.println("<hr/><hr>"); client.println("<h1><center> AranaCorp - Relay Controller Web Interface </center></h1>"); client.println("<hr/><hr>"); client.println("<br><br><div id='m' class='box'><center><table>"); client.println("<tr><td>Console </td><td><input id='senM1' class='sensor' value='" + String(console) + "' readonly></input></td></tr> </table>"); client.println(" <table><tr><td>Relay 0</td>"); client.println(" <td><a href='/light0on' class='button activate'> Activate </a>"); client.println(" <a href='/light0off' class='button reset'> </a></td><td>Relay 1</td>"); client.println(" <td><a href='/light1on' class='button activate'> Activate </a>"); client.println(" <a href='/light1off' class='button reset'> Reset </a></td><td>Relay 2</td>"); client.println(" <td><a href='/light2on' class='button activate'> Activate </a>"); client.println(" <a href='/light2off' class='button reset'> Reset </a></td><td>Relay 3</td>"); client.println(" <td><a href='/light3on' class='button activate'> Activate </a>"); client.println(" <a href='/light3off' class='button reset'> Reset </a></td></tr><tr><td>Relay 4</td>"); client.println(" <td><a href='/light4on' class='button activate'> Activate </a>"); client.println(" <a href='/light4off' class='button reset'> Reset </a></td><td>Relay 5</td>"); client.println(" <td><a href='/light5on' class='button activate'> Activate </a>"); client.println(" <a href='/light5off' class='button reset'> Reset </a></td><td>Relay 6</td>"); client.println(" <td><a href='/light6on' class='button activate'> Activate </a>"); client.println(" <a href='/light6off' class='button reset'> Reset </a></td><td>Relay 7</td>"); client.println(" <td><a href='/light7on' class='button activate'> Activate </a>"); client.println(" <a href='/light7off' class='button reset'> Reset </a></td></tr>"); client.println("</table></center></div>"); client.println("</div><footer><div><p style='text-align:left;float:left;padding:0px;margin:0px;'>© Copyright 2020 AranaCorp. All rights reserved</p><p style='float:right;padding:0px;margin:0px;'><a style='color:#3aaa35;' href='https://www.aranacorp.com/fr/evidence'>www.aranacorp.com</a><p></div></footer></div></body></html>"); delay(1); client.stop(); }
Once the web page is up and running, all we have to do is handle the browser requests in the loop() function.
void loop() { WiFiClient client = server.available(); if (client) { if (client.connected()) { String request = ""; if (client.available()) { request = client.readStringUntil('\r'); if (request != "") { client.print( header ); handleRequest(request); webpage(client);//Return webpage } } } } } void handleRequest(String request) { /* function handleRequest */ ////Handle web client request String pwmCmd; //Digital Ouputs for (int i = 0; i < 16; i++) { if (request.indexOf("/light" + String(i) + "on") > 0) { Serial.print("Relay n° "); Serial.print(i); +Serial.println(" ON "); setRegisterPin(i, 0); writeRegisters(); console = String("Relay n°" + String(i) + " ON"); } if (request.indexOf("/light" + String(i) + "off") > 0) { Serial.print("Relay n° "); Serial.print(i); +Serial.println(" OFF "); setRegisterPin(i, 1); writeRegisters(); console = String("Relay n°" + String(i) + " OFF"); } } }
You will find below, the complete code integrating the management of the web page as well as the shift register seen in the previous articles. Don’t forget to update the ssid and password variables with the values of your network.
//Libraries #include <WiFi.h>//https://www.arduino.cc/en/Reference/WiFi //Constants #define number_of_74hc595s 2 #define numOfRegisterPins number_of_74hc595s * 8 #define SER_Pin 25 #define RCLK_Pin 33 #define SRCLK_Pin 32 //Variables boolean registers[numOfRegisterPins]; char* ssid = "*****"; char* password = "*****"; String nom = "ESP32"; //Objects WiFiServer server(80); WiFiClient client; String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"; String console = "System initialized"; void setup() { //Init Serial USB Serial.begin(115200); Serial.println(F("Initialize System")); //Init ESP32Wifi Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); // Connect to Wifi network. while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print(F(".")); } server.begin(); Serial.println(); Serial.println(F("ESP32Wifi initialized")); Serial.print(F("IP Address: ")); Serial.println(WiFi.localIP()); //Init register pinMode(SER_Pin, OUTPUT); pinMode(RCLK_Pin, OUTPUT); pinMode(SRCLK_Pin, OUTPUT); clearRegisters(); writeRegisters(); delay(500); } void loop() { WiFiClient client = server.available(); if (client) { if (client.connected()) { String request = ""; if (client.available()) { request = client.readStringUntil('\r'); if (request != "") { client.print( header ); handleRequest(request); webpage(client);//Return webpage } } } } } void handleRequest(String request) { /* function handleRequest */ ////Handle web client request String pwmCmd; //Digital Ouputs for (int i = 0; i < 16; i++) { if (request.indexOf("/light" + String(i) + "on") > 0) { Serial.print("Relay n° "); Serial.print(i); +Serial.println(" ON "); setRegisterPin(i, 0); writeRegisters(); console = String("Relay n°" + String(i) + " ON"); } if (request.indexOf("/light" + String(i) + "off") > 0) { Serial.print("Relay n° "); Serial.print(i); +Serial.println(" OFF "); setRegisterPin(i, 1); writeRegisters(); console = String("Relay n°" + String(i) + " OFF"); } } } void webpage(WiFiClient client) { //Send webpage to browser /* client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("");*/ client.println("<!DOCTYPE HTML>"); client.println("<html>"); client.println("<head>"); client.println("<title> AranaCorp </title>"); client.println("<meta name='apple-mobile-web-app-capable' content='yes' />"); client.println("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />"); client.println("<meta charset='UTF-8'>"); //client.println("<meta http-equiv='refresh' content='1'>"); client.println(""); client.println("<style>"); client.println(""); client.println(" body {"); client.println(" font-size:100%;"); client.println(" color:white;"); client.println(" background-color: #111111;"); client.println(" margin:0px;"); client.println(" } "); client.println(" "); client.println(" #page {"); client.println("width:100%;"); client.println(" position: relative;"); client.println(" min-height: 99vh;"); client.println("}"); client.println(""); client.println("#content {"); client.println(" padding-bottom: 2.5rem; /* Footer height */"); client.println("}"); client.println(""); client.println("footer {"); client.println(" background-color:black;"); client.println(" position: absolute;"); client.println(" bottom: 0;"); client.println(" width: 100%;"); client.println(" height: 2.5rem; /* Footer height */"); client.println("}"); client.println(" "); client.println(" h1 {color: #3AAA35;}"); client.println(" p { text-align:center; }"); client.println(""); client.println(" table {"); client.println(" width=80%;"); client.println("margin: 40px 40px 40px 40px;"); client.println(" }"); client.println(" tr{"); client.println("border: 1px solid black;"); client.println("padding: 20px 20px 20px 20px;"); client.println(" }"); client.println(" td{"); client.println("padding: 10px 10px 10px 10px;"); client.println(" }"); client.println(" .wrapper {"); client.println("display: grid;"); client.println("/*grid-template-columns: 1fr 1fr 1fr;*/"); client.println(""); client.println("margin: 60px 20px 60px 20px;"); client.println("}"); client.println(".box {"); client.println(" border-radius: 10px;"); client.println(" border: 2px solid white;"); client.println(" font-size: 150%;"); client.println(" /*height: 120px;*/"); client.println(" margin: 5px 5px 5px 5px;"); client.println("}"); client.println(""); client.println(".button {"); client.println(" background-color: #111111; /* Green */"); client.println(" border: none;"); client.println(" color: white;"); client.println(" padding: 16px 32px;"); client.println(" text-align: center;"); client.println(" text-decoration: none;"); client.println(" display: inline-block;"); client.println(" font-size: 16px;"); client.println(" margin: 4px 2px;"); client.println(" transition-duration: 0.4s;"); client.println(" cursor: pointer;"); client.println(" border-radius: 12px;"); client.println("}"); client.println(""); client.println(".activate {"); client.println(" color: white; "); client.println(" border: 2px solid #3AAA35;"); client.println("}"); client.println(""); client.println(".activate:hover {"); client.println(" background-color: #3AAA35;"); client.println(" color: white;"); client.println("}"); client.println(""); client.println(".reset { "); client.println(" color: white; "); client.println(" border: 2px solid #f44336;"); client.println("}"); client.println(""); client.println(".reset:hover {"); client.println(" background-color: #f44336;"); client.println(" color: white;"); client.println("}"); client.println(""); client.println(" .sensor {"); client.println(" background-color: #ffffff;"); client.println(" border: none;"); client.println(" color: black;"); client.println(" padding: 16px 32px;"); client.println(" text-align: center;"); client.println(" text-decoration: none;"); client.println(" display: inline-block;"); client.println(" font-size: 16px;"); client.println(" margin: 4px 2px;"); client.println(" transition-duration: 0.4s;"); client.println(" cursor: pointer;"); client.println(" border-radius: 2px;"); client.println("}"); client.println(""); client.println(".status {"); client.println(" color: white; "); client.println(" border: 5px solid #3AAA35;"); client.println(" border-radius: 12px;"); client.println("}"); client.println(""); client.println(""); client.println("@media (min-width: 1050px){ /*screen and*/"); client.println(" .wrapper {"); client.println("grid-template-columns: repeat(2, 1fr);"); client.println(" }"); client.println(" "); client.println("@media (min-width: 1500px){"); client.println(" .wrapper {"); client.println("grid-template-columns: repeat(3, 1fr);"); client.println(" }"); client.println("}"); client.println(""); client.println(" </style>"); client.println(""); client.println(""); client.println("</head>"); client.println("<body> "); client.println("<div id='page'>"); client.println("<div id='content'>"); client.println("<hr/><hr>"); client.println("<h1><center> AranaCorp - Relay Controller Web Interface </center></h1>"); client.println("<hr/><hr>"); client.println("<br><br><div id='m' class='box'><center><table>"); client.println("<tr><td>Console </td><td><input id='senM1' class='sensor' value='" + String(console) + "' readonly></input></td></tr> </table>"); client.println(" <table><tr><td>Relay 0</td>"); client.println(" <td><a href='/light0on' class='button activate'> Activate </a>"); client.println(" <a href='/light0off' class='button reset'> Reset </a></td><td>Relay 1</td>"); client.println(" <td><a href='/light1on' class='button activate'> Activate </a>"); client.println(" <a href='/light1off' class='button reset'> Reset </a></td><td>Relay 2</td>"); client.println(" <td><a href='/light2on' class='button activate'> Activate </a>"); client.println(" <a href='/light2off' class='button reset'> Reset </a></td><td>Relay 3</td>"); client.println(" <td><a href='/light3on' class='button activate'> Activate </a>"); client.println(" <a href='/light3off' class='button reset'> Reset </a></td></tr><tr><td>Relay 4</td>"); client.println(" <td><a href='/light4on' class='button activate'> Activate </a>"); client.println(" <a href='/light4off' class='button reset'> Reset </a></td><td>Relay 5</td>"); client.println(" <td><a href='/light5on' class='button activate'> Activate </a>"); client.println(" <a href='/light5off' class='button reset'> Reset </a></td><td>Relay 6</td>"); client.println(" <td><a href='/light6on' class='button activate'> Activate </a>"); client.println(" <a href='/light6off' class='button reset'> Reset </a></td><td>Relay 7</td>"); client.println(" <td><a href='/light7on' class='button activate'> Activate </a>"); client.println(" <a href='/light7off' class='button reset'> Reset </a></td></tr>"); client.println("</table></center></div>"); client.println("</div><footer><div><p style='text-align:left;float:left;padding:0px;margin:0px;'>© Copyright 2020 AranaCorp. All rights reserved</p><p style='float:right;padding:0px;margin:0px;'><a style='color:#3aaa35;' href='https://www.aranacorp.com/fr/evidence'>www.aranacorp.com</a><p></div></footer></div></body></html>"); delay(1); client.stop(); } //SR void clearRegisters() { /* function clearRegisters */ //// Clear registers variables for (int i = numOfRegisterPins - 1; i >= 0; i--) { registers[i] = HIGH; } } void writeRegisters() { /* function writeRegisters */ //// Write register after being set digitalWrite(RCLK_Pin, LOW); for (int i = numOfRegisterPins - 1; i >= 0; i--) { digitalWrite(SRCLK_Pin, LOW); digitalWrite(SER_Pin, registers[i]); digitalWrite(SRCLK_Pin, HIGH); } digitalWrite(RCLK_Pin, HIGH); } void setRegisterPin(int index, int value) { /* function setRegisterPin */ ////Set register variable to HIGH or LOW registers[index] = value; } void printRegisters() { /* function clearRegisters */ //// Clear registers variables for (int i = 0; i < numOfRegisterPins; i++) { Serial.print(registers[i]); Serial.print(F(" ,")); } Serial.println(); }
Result
Once the code has been loaded into the microcontroller, enter the IP address displayed in the serial monitor (here 192.168.1.64) into your web browser. The following page should be displayed.
You can then close the relays with the “Activate” buttons and open them with the corresponding “Reset” buttons.
Don’t hesitate to leave us a comment to give us your opinion, feedback and ideas for improvement for this tutorial.
Can we do this in below project, actually we are doing that as a collage project and want to increase switches, thanks
https://www.iotstarters.com/esp32-nodemcu-web-server-physical-switch/
Of course you can. You just need to increase the number of variables and graphical switches to match the number of relays
// set the LED:
digitalWrite(relay1, relay_1_status);
digitalWrite(relay2, relay_2_status);
hello we can add physical buttons for the relays to be controlled both by the internet and by physical buttons
Hello! Thanks for your code! It seems great! I will try to use yours code to adapt for this board: http://www.chinalctech.com/cpzx/513.html that use an ESP8266 controller (ESP12F). Can you suggest modifications to your code for using with that board? I’m quite sure that also the SER, RCLK, SRCLK are a little bit different.
Thanks in advance.
Thanks!
you need to change Library name for ESP8266
And rename pins on which SR is hooked up like D6, D7, D8