Icono del sitio AranaCorp

Controla 8 relés mediante ESP32 y una interfaz web

En este tutorial, crearemos una interfaz web para controlar 8 relés individualmente. Este tutorial es una continuación del proyecto Piloto 8 relés utilizando un ESP32 y el monitor de serie.

Este tutorial se puede aplicar a cualquier microcontrolador con conexión Wifi o Ethernet. Tendrá que tener cuidado de modificar el esquema de conexión y el código para que se ajuste a su uso.

Hardware

Principio

En el tutorial anterior, vimos cómo accionar 8 relés de forma independiente utilizando el monitor serie de Arduino IDE. Cuando se dispone de una conexión a Internet, como en el ESP32 NodeMCU, es posible convertir el microcontrolador en un servidor y alojar una página web. Esta página web puede utilizarse como interfaz gráfica de usuario para impulsar su proyecto. Crearemos una página web, accesible a través de la red local, con botones para activar los relés.

Código

Tomaremos el código del tutorial anterior y lo combinaremos con el código para crear una Interfaz Web para el ESP32 NodeMCU. En la página web, crearemos una tabla que contenga 16 botones correspondientes a las acciones «Activar» y «Reiniciar» de los 8 relés. Para visualizar algunas acciones, añadimos un inserto para mostrar los mensajes del microcontrolador.

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;'>&copy; 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();
}

Una vez que la página web es funcional, todo lo que tenemos que hacer es manejar las peticiones del navegador en la función loop().

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");
    }
  }
}

A continuación encontrará el código completo que integra la gestión de la página web así como el registro de turnos visto en los artículos anteriores. No olvides actualizar las variables ssid y password con los valores de tu red.

//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;'>&copy; 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();
}

Resultado

Una vez cargado el código en el microcontrolador, introduzca la dirección IP que aparece en el monitor serie (aquí 192.168.1.64) en su navegador web. Debería aparecer la siguiente página.

A continuación, puede cerrar los relés con los botones «Activar» y abrirlos con los correspondientes botones «Reiniciar».

No dudes en dejarnos un comentario para darnos tu opinión, comentarios e ideas para mejorar este tutorial.

Fuentes

Salir de la versión móvil