Domotique et objets connectés à faire soi-même

Interface HTML pour ESP8266. Améliorer le design avec Bootstrap

Améliorer l'interface HTML des projet Web Serveur ESP8266 avec Bootstrap
Meilleur deal à : banggood.com

Dans le tutoriel précédent, nous avons vu comment créer un interface HTML pour accéder au serveur web embarqué sur un ESP8266. Dans ce tutoriel, nous allons découvrir comment utiliser le framework Bootstrap pour donner un aspect moderne à l’interface HTML. 

 

Tutoriel actualisé le 7 août 2020

L’objectif de ce tutoriel est de mettre en place le framework et tester quelques éléments (bouton, table, badge, interaction avec le code Arduino depuis l’interface Web). Pour aller plus loin dans l’HTML, voici quelques blogs très bien fait : Alsa Création (en Français), w3schools.

Remarque. Vous pouvez suivre la même démarche si vous utilisez un Shield Ethernet ou WiFi pour Arduino. Les librairies sont identiques

Bootstrap, c’est quoi ?

Bootstrap est un framework HMTL responsive, c’est à dire que l’affichage s’adapte à la dimension de l’écran. Bootstrap est un projet Open Source très populaire chez les concepteurs de sites internet et webapp (il a été le projet le plus populaire sur GitHub en 2014). Il a été initié par un développeur travaillant chez Tweeter.

Cela peut paraître difficile à première vue mais en fait c’est très simple à mettre en oeuvre. N’oubliez pas que la puissance d’un ESP8266 est très limitée, on ne va pas faire des interfaces très compliquée. L’essentielle de la “programmation” HTML va être d’indiquer les classes (class en anglais) que le moteur de rendu doit utiliser pour afficher un élément.

Prenons l’exemple d’un bouton. Pour afficher un bouton, on doit faire comme ceci en HTML

<button type="button">ON</button>

Ce qui donne ceci. Pas difficile de faire mieux !

Maintenant, allez sur le site de bootstrap et descendez jusqu’aux exemples de la section glyphicon. Pour modifier l’aspect d’un élément HTML, il faut lui attribuer des classes Bootstrap.

Par exemple ici, on va lui adjoindre les classes btn, btn-success (couleur verte), btn-lg (large). Voici ce que ça donne. Plus sympa non !

Voilà, rien de très compliqué. Ce qui est le plus difficile, c’est d’avoir une vue d’ensemble de toutes les classes disponibles et du rendu obtenu. Heureusement, il y a des sites qui permettent de faire des maquettes en ligne.

Comment réaliser un maquette d’interface HTML en ligne ?

En googlelisant boostrap builder online, vous allez trouver de nombreux sites qui permettent de réaliser des maquettes et d’obtenir le code HTML. Impossible de tous les tester, j’ai testé layouit!. Vous pouvez également lire cet article qui présente rapidement 7 solutions en ligne.

Layouit! est simple et efficace, on dépose les éléments sur la page. C’est un service gratuit, il est donc assez limité. Tous les éléments ne sont pas configurables. Par exemple, il est impossible de changer le nombre de lignes et de colonne d’une table, ajouter un badge dans une cellule… Il faudra le faire à la main au moment du codage.

Pour chaque élément, les différents styles disponibles sont accessibles depuis des menus et options à cocher

Une fois votre maquette terminée, pour récupérer le code HTML, cliquez sur Download (dans la barre supérieure) puis Continue non logged. Inutile de récupérer le code et les ressources puisqu’on va les télécharger directement sur internet. Copiez simplement le code dans le presse papier.

Attention, vous n’allez pas obtenir le code HTML tout fait pour votre application, simplement la structure avec les classes qui correspondent à vos choix. Ensuite il faudra adapter le code et “peupler” les éléments avec vos données.

Comment adapter le code HTML pour le programme Arduino ?

Voici un petit exemple d’interface composée uniquement d’un jumbotron, un cadre contenant titre, un résumé et un bouton d’action.

Voici le code HTML généré par layoutit!

<div class="container-fluid"> 
  <div class="row"> 
    <div class="col-md-12"> 
      <div class="jumbotron"> 
        <h2> Demo Bootstrap ESP8266 </h2> 
        <p> Il fait 22°C </p> 
        <p> <a class="btn btn-primary btn-large" href="#">Nouvelle mesure</a> </p> 
      </div> 
     </div> 
  </div> 
</div>

L’idée est ici de construire en dynamique le code HTML de la page en y intégrant la valeur des variables.

Le mieux est de créer des chaines qui contiennent des éléments récurrents, ce n’est pas obligatoire et peu rendre la mise au point plus difficile car il est plus difficile de conserver l’arborescence du code. Il faudra également remplacer tous les double guillemets (“) par un simple (‘).

String row = "<div class='row'>";
String col_md_12 = "<div class='col-md-12'>";

Il ne reste plus qu’à assembler la chaine en y incluant la variable de température. On peut compacter certaines chaines, mais il faut garder à l’esprit qu’en cas de modification ou ajout d’élément, ce sera plus difficile à mettre au point. Toute balise ouverte devant être refermée, il est préférable de garder une certaine arborescence pour s’y retrouver.

String page = "<div class='container-fluid'>";
page += "<div class='row'";
page +=   "div class='col-md-12'";
page +=     "<div class='jumbotron'>";
page +=       "<h2>Demo Bootstrap ESP8266</h2>";
page +=          "<p>Il fait";
page +=          t;
page +=          "°C</p>";
page +=          <p><a class='btn btn-primary btn-large' href='#'>Nouvelle mesure</a></p>";
page +=     "</div>";
page +=   "</div>";
page += "</div>";

Comment ajouter le framework Bootstrap à un projet ESP8266

Nous allons maintenant rentrer un peu plus dans le vif du sujet.

Il y a deux façons de procéder pour ajouter le framework Bootstrap à un projet Arduino/ESP8266. La première est d’embarquer tout le nécessaire dans la mémoire de l’ESP8266. Ce sera le thème du prochain tutoriel sur le sujet. Ici, nous allons récupérer les sources depuis internet. Cela oblige l’ESP8266 a être connecté à internet. Pour que cela fonctionne, on ne pourra donc pas l’utiliser comme un point d’accès.

On va donc indiquer dans l’entête de la page HTML les ressources à télécharger sur internet.

Remarque. Il n’y a que la feuille de style bootstrap.min.css qui est obligatoire,mais si vous avez besoin de créer des interactions avec le code Arduino ou utiliser certains composants de Bootstrap avec du Javascript, il faut tout installer.
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>"

Si la sécurité est une obsession, vous pouvez même ajoutez un contrôle d’intégrité des sources.

<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' integrity='sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js' integrity='sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

Ensuite, il faut indiquer au navigateur internet que le site est adapté pour le mobile. Tous les autres réglages sont expliqués ici (paragraphe Mobile First).

<meta name="viewport" content="width=device-width, initial-scale=1">

C’est terminé pour l’entête. Passons au body de la page HTML. Tout le contenu de la page doit être inclus dans un container. On va donc ajouter une div et indiquer la classe container-fluid . L’option fluid signifie que la page prend toute la place disponible à l’écran

<div class="container-fluid">
  ...
</div>

Bootstrap a été conçu pour gérer les blocs d’affichage sous la forme de grille. On peut avoir par exemple un affichage sur trois colonnes. En fonction du type d’écran, ou si on redimensionne la fenêtre du navigateur, les colonnes vont commencer par se réduire puis se placer l’une en dessous de l’autre (sur un smartphone). Pour cela, on doit créer un ligne (row) dans laquelle on placera nos éléments, par exemple une table.

Ici, je vous propose de créer un ligne qui contient

Le code HTML qui permet d’afficher. Il reste à intégrer les mesures dans le code final.

<div class='container-fluid'>
 <div class='row'>
  <div class='col-md-12'>
    <h1>Demo Webserver ESP8266 + Bootstrap</h1>
    <h3>Mini station météo</h3>
    <ul class='nav nav-pills'>
      <li class='active'>
        <a href='#'> <span class='badge pull-right'>22°C</span> Température</a>
      </li>
      <li>
        <a href='#'> <span class='badge pull-right'>33%</span> Humidité</a>
      </li>
      <li>
        a href='#'> <span class='badge pull-right'> 980mbar</span> Pression atmosphérique</a>
      </li>
    </ul>
  </div>
 </div>
</div>

Interaction avec le code Arduino depuis l’interface HTML

Il y a trois grandes méthodes pour interagir avec le code Arduino. La première méthode consiste à ajouter du code javascript dans la page HTML. C’est une très bonne solution mais vous devrez maîtriser un language supplémentaire. On va donc laisser de coter cette solution.

La seconde solution consiste à passer les paramètres dans une URL, cela revient donc à brancher une fonction qui surveille la page concernée. Dans le cas présent, cette solution n’est pas très bien adaptée car il va falloir gérer le rechargement de la page principale, ce qui ne va pas donner quelque chose de très élégant.

On va donc utiliser la troisième méthode qui consiste à envoyer une requête HTTP de type POST.

Les requêtes POST sont envoyés par exemple lorsqu’on appel la fonction submit. C’est la fonction qui est appelée lorsqu’on valide le remplissage d’un formulaire (création de compte, paiement par carte bancaire…). On peut très facilement voir ce qui se passe en ouvrant les outils de développement proposés par Chrome (l’équivalent existe sur tous les navigateurs).

On va donc commencer par modifier les éléments avec lesquels ont souhaite interagir dans le code Arduino. Ce sont surtout les boutons en général. Pour qu’un bouton puisse “poster” une information, il faut l’inclure dans un formulaire. Le formulaire a deux paramètres :

<form action='/' method='POST'>
 ...
</form>

Ensuite pour un bouton, on lui ajoute le type submit. Pour changer l’état d’une sortie, on donnera le même nom aux deux boutons (ON et OFF), et on chargera la value (valeur). Ce qui donne le code suivant.

<form action='/' method='POST'>
  <button type='button submit' name='D6' value='1' class='btn btn-success btn-lg'>ON</button>
</form>  
<form action='/' method='POST'>
  <button type='button submit' name='D6' value='0' class='btn btn-danger btn-lg'>OFF</button>
</form>

Dans les outils de développement, allez sur l’onglet Network et appuyez sur un bouton ON qui active la sortie D6 pour voir la requête envoyée par la page. La section Form Data en bas de page contient les données envoyées par la page lorsqu’on appuie sur le bouton.

Et lorsqu’on la désactive

Maintenant coté Arduino, on doit faire 3 choses :

Dans la fonction handleRoot, on ajoute un test qui vérifie la présence de l’argument D6. S’il est présent, on exécute la fonction handleD6.

void handleRoot(){ 
  if ( server.hasArg("D6") ) {
    handleD6();
  } else {
    server.send ( 200, "text/html", getPage() );
  }  
}

On récupère la valeur de l’argument et on lance le traitement associé

void handleD6() {
  String D6Value = server.arg("D6"); 
  updateGPIO(1, D6Value); 
}

Affichage des caractères accentués ou spéciaux

Si vous utilisé Firefox ou Safari (sur iOS) vous allez très certainement rencontrer des problèmes d’affichage des caractères accentués. j’ai pas mal cherché comment résoudre le problème aussi bien du coté ESP8266 (paramètres lang et charset) que du coté du navigateur (option d’affichage des caractères accentués), sans succès. La seule solution qui fonctionne (je suis preneur si vous trouvez autre chose) est d’utiliser le code HTML correspondant au caractère accentué ou spécial. Voici un petit tableau avec les plus courants. Il y a plein de sources sur internet.

Code HTML Caractère
&agrave; à
&acirc; â
&eacute; é
&egrave; è
&ecirc; ê
&ugrave; ù
&ucirc; û
&ccedil; ç
&deg; °

Ce qui donne par exemple pour température, Temp&eacute;rature . Le mieux est de faire un chercher / remplacer lorsque tout est terminé.

Assemblage du projet

Il est temps de passer aux choses sérieuses et réaliser notre première interface.

Matériel utilisé

Pour cette démo, j’ai simplement assemblé un DHT22 et un BMP180 pour récupérer des mesures. J’ai également branché des Led pour simuler l’activation des GPIO.

Module ESP8266 ESP-12. Par exemple Wemos D1 Mini
Alimentation 5/3A micro-usb
Capteur de pression atmosphérique

BMP180

Capteur de température et d’humidité

DHT11 ou DHT22

Jumper Dupont
Breadboard

Circuit

Voici un tableau de repérage et de correspondance des broches entre Arduino et ESP8266. Vous pouvez également ajouter un écran OLED SDSD1306.

Composant Broches Repérage Arduino Equivalence ESP8266 (Wemos D1 mini)
DHT22 VCC 5V 5V
GND GND G
Data GPIO-14 D4
BMP180 VCC 5V 5V
GND GND G
SDA GPIO-4 D2
SCK GPIO-5 D1

Code du projet

Voici le code final du projet qu’il vous suffit de coller dans un nouveau sketch Arduino. Il faut définir les variables ssid et password pour accéder à votre réseau WiFi.

#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <DHT.h>
#include <Adafruit_BMP085.h>

#define ssid      "xxx"       // WiFi SSID
#define password  "xxxxxxxx"  // WiFi password
#define DHTTYPE   DHT22       // DHT type (DHT11, DHT22)
#define DHTPIN    D4          // Broche du DHT / DHT Pin
const uint8_t GPIOPIN[4] = {D5,D6,D7,D8};  // Led
float   t = 0 ;
float   h = 0 ;
float   p = 0;
String  etatGpio[4] = {"OFF","OFF","OFF","OFF"};

// Création des objets / create Objects
DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP085 bmp;
ESP8266WebServer server ( 80 );

String getPage(){
  String page = "<html lang='fr'><head><meta http-equiv='refresh' content='60' name='viewport' content='width=device-width, initial-scale=1'/>";
  page += "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'><script src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script><script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>";
  page += "<title>ESP8266 Demo - www.projetsdiy.fr</title></head><body>";
  page += "<div class='container-fluid'>";
  page +=   "<div class='row'>";
  page +=     "<div class='col-md-12'>";
  page +=       "<h1>Demo Webserver ESP8266 + Bootstrap</h1>";
  page +=       "<h3>Mini station m&eacute;t&eacute;o</h3>";
  page +=       "<ul class='nav nav-pills'>";
  page +=         "<li class='active'>";
  page +=           "<a href='#'> <span class='badge pull-right'>";
  page +=           t;
  page +=           "</span> Temp&eacute;rature</a>";
  page +=         "</li><li>";
  page +=           "<a href='#'> <span class='badge pull-right'>";
  page +=           h;
  page +=           "</span> Humidit&eacute;</a>";
  page +=         "</li><li>";
  page +=           "<a href='#'> <span class='badge pull-right'>";
  page +=           p;
  page +=           "</span> Pression atmosph&eacute;rique</a></li>";
  page +=       "</ul>";
  page +=       "<table class='table'>";  // Tableau des relevés
  page +=         "<thead><tr><th>Capteur</th><th>Mesure</th><th>Valeur</th><th>Valeur pr&eacute;c&eacute;dente</th></tr></thead>"; //Entête
  page +=         "<tbody>";  // Contenu du tableau
  page +=           "<tr><td>DHT22</td><td>Temp&eacute;rature</td><td>"; // Première ligne : température
  page +=             t;
  page +=             "&deg;C</td><td>";
  page +=             "-</td></tr>";
  page +=           "<tr class='active'><td>DHT22</td><td>Humidit&eacute;</td><td>"; // 2nd ligne : Humidité
  page +=             h;
  page +=             "%</td><td>";
  page +=             "-</td></tr>";
  page +=           "<tr><td>BMP180</td><td>Pression atmosph&eacute;rique</td><td>"; // 3ème ligne : PA (BMP180)
  page +=             p;
  page +=             "mbar</td><td>";
  page +=             "-</td></tr>";
  page +=       "</tbody></table>";
  page +=       "<h3>GPIO</h3>";
  page +=       "<div class='row'>";
  page +=         "<div class='col-md-4'><h4 class ='text-left'>D5 ";
  page +=           "<span class='badge'>";
  page +=           etatGpio[0];
  page +=         "</span></h4></div>";
  page +=         "<div class='col-md-4'><form action='/' method='POST'><button type='button submit' name='D5' value='1' class='btn btn-success btn-lg'>ON</button></form></div>";
  page +=         "<div class='col-md-4'><form action='/' method='POST'><button type='button submit' name='D5' value='0' class='btn btn-danger btn-lg'>OFF</button></form></div>";
  page +=         "<div class='col-md-4'><h4 class ='text-left'>D6 ";
  page +=           "<span class='badge'>";
  page +=           etatGpio[1];
  page +=         "</span></h4></div>";
  page +=         "<div class='col-md-4'><form action='/' method='POST'><button type='button submit' name='D6' value='1' class='btn btn-success btn-lg'>ON</button></form></div>";
  page +=         "<div class='col-md-4'><form action='/' method='POST'><button type='button submit' name='D6' value='0' class='btn btn-danger btn-lg'>OFF</button></form></div>";
  page +=         "<div class='col-md-4'><h4 class ='text-left'>D7 ";
  page +=           "<span class='badge'>";
  page +=           etatGpio[2];
  page +=         "</span></h4></div>";
  page +=         "<div class='col-md-4'><form action='/' method='POST'><button type='button submit' name='D7' value='1' class='btn btn-success btn-lg'>ON</button></form></div>";
  page +=         "<div class='col-md-4'><form action='/' method='POST'><button type='button submit' name='D7' value='0' class='btn btn-danger btn-lg'>OFF</button></form></div>";
  page +=         "<div class='col-md-4'><h4 class ='text-left'>D8 ";
  page +=           "<span class='badge'>";
  page +=           etatGpio[3];
  page +=         "</span></h4></div>";
  page +=         "<div class='col-md-4'><form action='/' method='POST'><button type='button submit' name='D8' value='1' class='btn btn-success btn-lg'>ON</button></form></div>";
  page +=         "<div class='col-md-4'><form action='/' method='POST'><button type='button submit' name='D8' value='0' class='btn btn-danger btn-lg'>OFF</button></form></div>";
  page +=       "</div>";
  page +=     "<br><p><a href='http://www.projetsdiy.fr'>www.projetsdiy.fr</p>
  page += "</div></div></div>";
  page += "</body></html>";
  return page;
}
void handleRoot(){ 
  if ( server.hasArg("D5") ) {
    handleD5();
  } else if ( server.hasArg("D6") ) {
    handleD6();
  } else if ( server.hasArg("D7") ) {
    handleD7();
  } else if ( server.hasArg("D8") ) {
    handleD8();
  } else {
    server.send ( 200, "text/html", getPage() );
  }  
}

void handleD5() {
  String D5Value; 
  updateGPIO(0,server.arg("D5")); 
}

void handleD6() {
  String D6Value; 
  updateGPIO(1,server.arg("D6")); 
}

void handleD7() {
  String D7Value; 
  updateGPIO(2,server.arg("D7")); 
}

void handleD8() {
  String D8Value; 
  updateGPIO(3,server.arg("D8")); 
}

void updateGPIO(int gpio, String DxValue) {
  Serial.println("");
  Serial.println("Update GPIO "); Serial.print(GPIOPIN[gpio]); Serial.print(" -> "); Serial.println(DxValue);
  
  if ( DxValue == "1" ) {
    digitalWrite(GPIOPIN[gpio], HIGH);
    etatGpio[gpio] = "On";
    server.send ( 200, "text/html", getPage() );
  } else if ( DxValue == "0" ) {
    digitalWrite(GPIOPIN[gpio], LOW);
    etatGpio[gpio] = "Off";
    server.send ( 200, "text/html", getPage() );
  } else {
    Serial.println("Err Led Value");
  }  
}

void setup() {
  for ( int x = 0 ; x < 5 ; x++ ) { 
    pinMode(GPIOPIN[x],OUTPUT);
  }  
  Serial.begin ( 115200 );
  // Initialisation du BMP180 / Init BMP180
  if ( !bmp.begin() ) {
    Serial.println("BMP180 KO!");
    while(1);
  } else {
    Serial.println("BMP180 OK");
  }
  
  WiFi.begin ( ssid, password );
  // Attente de la connexion au réseau WiFi / Wait for connection
  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 ); Serial.print ( "." );
  }
  // Connexion WiFi établie / WiFi connexion is OK
  Serial.println ( "" ); 
  Serial.print ( "Connected to " ); Serial.println ( ssid );
  Serial.print ( "IP address: " ); Serial.println ( WiFi.localIP() );

  // On branche la fonction qui gère la premiere page / link to the function that manage launch page 
  server.on ( "/", handleRoot );

  server.begin();
  Serial.println ( "HTTP server started" );
  
}

void loop() {
  // put your main code here, to run repeatedly:
  server.handleClient();
  t = dht.readTemperature();
  h = dht.readHumidity();
  p = bmp.readPressure() / 100.0F;

  delay(1000);
}

Voici l’interface que vous allez obtenir en saisissant l’adresse IP de la Wemos D1 Mini. La page est configurée pour s’actualiser automatiquement toutes les 60 secondes (paramètre content=’60’ dans l’entête de la page).

Il reste encore énormément à découvrir, il est impossible de tout détailler dans un tutoriel, mais vous avez maintenant les clés pour réaliser de belles interfaces HTML pour vos projets ESP8266.

Il est possible d’aller encore plus loin et de changer dynamiquement le thème de l’interface HTML de vos projets ESP8266 à l’aide de Bootswatch détaillé dans le prochain tutoriel

Mises à jour

7/08/2020 Mise à jour du thème de l’article

Avez-vous aimé cet article ?
[Total: 0 Moyenne: 0]
Exit mobile version