Étiquettes : , ,

Dans ce tutoriel, nous allons créer une interface web permettant de piloter 8 relais individuellement. Ce tutoriel fait suite au projet Pilotez 8 relais à l’aide d’un ESP32 et du moniteur série.

Ce tutoriel peut être appliqué à n’importe quel microcontrôleur avec une connexion Wifi ou Ethernet. Il faudra faire attention à modifier le schéma de connexion et le code pour qu’ils correspondent à votre usage.

Matériel

  • NodeMCU 32S
  • Breadboard
  • Jumper cable
  • Module 8 relais
  • Registre à décalage 74hc595

Principe

Nous avons vu, dans le tutoriel précédent, comment piloter 8 relais indépendamment à l’aide du moniteur série de l’IDE Arduino. Lorsqu’une connexion internet est disponible, comme sur le NodeMCU ESP32, il est possible de transformer votre microcontrôleur en serveur et d’héberger une page web. Cette page web peut servir d’interface graphique pour piloter votre projet. Nous allons créer une page Web, accessible via le réseau local, avec des boutons permettant d’activer les relais.

Code

Nous allons reprendre le code du tutoriel précédent et le combiner au code permettant de créer une Interface Web pour le NodeMCU ESP32. Sur la page Web, nous allons créer un tableau contenant 16 boutons correspondant aux actions « Activate », et « Reset » des 8 relais. Afin de visualiser certaines actions, nous ajoutons un encart afin d’afficher des messages provenant du microcontrôleur.

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

Une fois la page web fonctionnelle, il ne nous reste plus qu’à gérer les requêtes du navigateur dans la fonction 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");
    }
  }
}

Vous trouverez ci-dessous, le code complet intégrant la gestion de la page web ainsi que du registre à décalage vue dans les articles précédents. N’oubliez pas de mettre à jour les variables ssid et password avec les valeurs de votre réseau.

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

Résultat

Une fois le code chargé dans le microcontrôleur, entrez l’adresse IP qui s’affiche dans le moniteur série (ici 192.168.1.64), dans votre navigateur web. La page suivante devrait s’afficher.

Vous pouvez ensuite fermer les relais avec les boutons « Activate » et les ouvrir avec les boutons « Reset » correspondants.

N’hésitez pas à nous laisser un commentaire pour nous donner votre avis, vos retours et pistes d’améliorations pour ce tutoriel.

Sources