fbpixel
Etiquetas: , ,

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

  • NodeMCU 32S
  • Breadboard
  • Jumper cable
  • Módulo de 8 relés
  • Registro de desplazamiento 74hc595

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.

nodemcu32s-74hc595-relay-webinterface-1280x500 Controla 8 relés mediante ESP32 y una interfaz web

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

nodemcu32s-74hc595-relay-webinterface-monitor Controla 8 relés mediante ESP32 y una interfaz web

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

Fuentes