fbpixel
Etiquetas: , ,

Neste tutorial, vamos criar uma interface web para controlar 8 relés individualmente. Este tutorial pode ser aplicado a qualquer microcontrolador com uma ligação WiFi ou Ethernet. Terá de ter o cuidado de modificar o esquema de ligação e o código de acordo com a sua utilização.

Este tutorial dá continuidade ao projeto Controlar 8 relés utilizando um ESP32 e o monitor série.

Hardware

  • NodeMCU 32S
  • Prancheta
  • Cabo de ligação
  • Módulo de 8 relés
  • 74hc595 registo de deslocação

Princípio

No tutorial anterior, vimos como controlar 8 relés de forma independente utilizando o monitor série do IDE Arduino. Quando está disponível uma ligação à Internet, como no NodeMCU ESP32, pode transformar o seu microcontrolador num servidor e alojar uma página Web. Esta página web pode ser utilizada como uma interface gráfica para controlar o seu projeto. Vamos criar uma página Web, acessível através da rede local, com botões para ativar os relés.

Código

Vamos pegar no código do tutorial anterior e combiná-lo com o código para criar uma interface Web para o ESP32 NodeMCU. Na página Web, vamos criar uma tabela com 16 botões que correspondem às acções “Ativar” e “Reiniciar” dos 8 relés. Para visualizar determinadas acções, vamos acrescentar um insert para visualizar as mensagens do 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();
}

Quando a página Web estiver a funcionar, tudo o que precisamos de fazer é gerir os pedidos do browser na função 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");
    }
  }
}

Abaixo encontra-se o código completo que integra a gestão da página web e o registo de turnos visto nos artigos anteriores. Não se esqueça de atualizar as variáveis ssid e password com os valores da sua rede.

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

Resultados

Quando o código tiver sido carregado no microcontrolador, introduza o endereço IP apresentado no monitor de série (neste caso, 192.168.1.64) no seu navegador Web. Deve ser apresentada a seguinte página.

nodemcu32s-74hc595-relay-webinterface-1280x500 Controlo de 8 relés utilizando um ESP32 e uma interface Web

Pode então fechar os relés com os botões “Ativar” e abri-los com os botões “Repor” correspondentes.

nodemcu32s-74hc595-relay-webinterface-monitor Controlo de 8 relés utilizando um ESP32 e uma interface Web

Não hesite em deixar-nos um comentário para nos dar a sua opinião, feedback e sugestões de melhoria para este tutorial.

Fontes