Bootstrap (Serveur Web ESP8266) : utiliser les thèmes Bootswatch (code Arduino)

Bootswatch propose des thèmes gratuits pour Bootstrap. Nous continuons la série d’articles sur la programmation de la partie Web serveur des ESP8266. Dans ce tutoriel nous allons voir comment personnaliser le thème par défaut (remplacer la feuille de style CSS par défaut) du framework Bootstrap. Si vous prenez le train en marche, dans le tutoriel précédent, nous avons vu comment ajouter le framework Bootstrap dans le code HTML pour obtenir un affichage moderne. La feuille de style (fichier CSS) de Bootstrap peut facilement être modifiée pour répondre à vos besoins mais le plus facile est encore d’utiliser les nombreux thèmes (pour la plupart gratuits) disponibles en ligne.

Comment changer le thème par défaut de Bootstrap ?

Il est possible de modifier la feuille de style CSS du framework Bootstrap ou d’ajouter de nouveaux styles en les écrivant soi-même mais c’est un peu dommage de devoir tout faire à la main alors qu’il y a de très nombreux styles de qualité disponibles sur internet (très souvent gratuitement). Impossible de tous les tester. Je vous propose d’essayer les thèmes de BootstrapCND.

Pour modifier le style de notre page, il suffit simplement de pointer vers une autre feuille de style, c’est à dire qu’on va remplacer ce lien dans le code de la page.

link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'

Tout comme Bootstrap, il n’est pas nécessaire de télécharger les fichiers en local, on va simplement pointer vers le CND (serveur de fichier) de BootstrapCND. Ce qui va donner par exemple pour le thème Cosmo le lien suivant.

<link href='https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/cosmo/bootstrap.min.css' rel='stylesheet'>

BootstrapCND met également une clé pour vérifier que le fichier récupéré n’a pas été altéré (il ne contient aucun code malicieux). Je n’ai pas intégré cette vérification dans cet exemple.

BootstrapCDN met à disposition 16 thèmes que l’on trouve dans la section Bootswatch. Si vous en avez besoin, BootstrapCDN met également à disposition un lien pour récupérer les symboles de Font Awesome.

Comment envoyer la sélection du dropdown par requête HTTP ?

Je profite de ce tutoriel pour introduire un nouveau composants proposé par Bootstrap, le bouton drop down (que l’on connait aussi sous le nom combo) qui permet de créer une liste de sélection. Comme d’habitude il existe plusieurs méthodes pour envoyer la sélection faite dans la liste. Comme on ne veut pas forcément embarquer du code javascript, je vous propose de passer par une petite astuce qui consiste à créer un champ invisible dans lequel on va recopier pas possible avant d’appeler la fonction submit().

On va donc créer un formulaire (form) dans lequel sera placé le bouton drop down ainsi que le champ invisible (un champ de type input). Pour que cela fonctionne, il est important de bien nommer les éléments HTML et leur attribuer un identifiant (id). Voyons en détail comment ça fonctionne :

  • form method=’POST’ name=’selecttheme’ id=’selecttheme’ : on créé un formulaire qui possède le nom et l’id selecttheme. Ce n’est pas un problème. Les données du formulaire seront envoyée par une requête HTTP avec la méthode POST.
  • input class=’span’ id=’choixtheme’ name=’theme’ type=’hidden’ : le champ de saisie invisible (si vous voulez voir ce qui se passe, changer le type par text). le paramètre name sera la clé qui contiendra la valeur du thème sélectionné dans la liste
  • onclick=’$(\”#choixtheme\”).val(\”bootstrap\”); $(\”#selecttheme\”).submit()’ : pour chaque choix de la liste, on exécute une commande jquery lorqu’un click est détecté. On recopie dans le champ caché (#choixtheme) la valeur qui correspond à la section (ce sera la valeur qui sera envoyé au code Arduino). Ensuite on exécute un  submit() qui va envoyer les données du formulaire (#selecttheme).
<form method='POST' name='selecttheme' id='selecttheme'/> 
  <input class='span' id='choixtheme' name='theme' type='hidden'>
  <div class='btn-group'>
    <button class='btn btn-default'>Choisir un th&eacute;me</button>
    <button data-toggle='dropdown' class='btn btn-default dropdown-toggle'><span class='caret'></span></button>
      <ul class='dropdown-menu'>
        <li onclick='$(\"#choixtheme\").val(\"bootstrap\"); $(\"#selecttheme\").submit()'><a href='#'>Boostrap</a></li>
        <li onclick='$(\"#choixtheme\").val(\"cosmo\"); $(\"#selecttheme\").submit()'><a href='#'>Cosme</a></li> 
      </ul>
  </div>
  </input>     
</form>

Pour voir ce qui se passe, ouvrez les outils de développements sur votre navigateur est sélectionnez un thème pour visualiser la requête envoyée au code Arduino.

demo webserver esp8266 drop down request http post theme selected

Changer le thème en dynamique

Maintenant il ne reste plus qu’à ajouter un traitement pour actualiser la page à chaque fois que l’utilisateur sélectionne un thème. Pour cela il nous suffit de rajouter un test sur l’argument theme dans la fonction handleRoot().

Une variable theme permet de stocker la valeur sélectionnée par l’utilisateur. Ensuite, lorsqu’on créé la page HTML, il suffit de faire un test sur la valeur du thème pour savoir si l’utilisateur souhaite le thème par défaut ou un de BootstrapCDN.

if ( theme == "bootstrap" ) {
  page += "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>";
} else {
  page += "<link href='https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/";
  page += theme;
  page += "/bootstrap.min.css' rel='stylesheet'>";
}

Code du projet

J’ai adapté le code du tutoriel précédent qui récupère la température et l’humidité d’une sonde DHT22 et la pression atmosphérique d’un BMP180. Le matériel suivant a été utilisé.

esp8266 Wemos D1 mini Wemos D1 Mini (module ESP8266 ESP-12)
chargeur raspbery pi 3 5v 3000ma Alimentation 5/3A micro-usb

Une alimentation de qualité est recommandée.

BMP180 Capteur de pression atmosphérique

BMP180

Broches DHT22 Capteur de température et d’humidité

DHT22 (ou DHT11)

jumper dupont Jumper Dupont
breadboard Breadboard

Il reste à changer le ssid ainsi que le mot de passe du réseau WiFi avant de téléverser le code dans l’ESP8266.

#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"};
String  theme = "bootstrap";

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

String getPage(){
  String page = "<html charset=UTF-8><head><meta http-equiv='refresh' content='60' name='viewport' content='width=device-width, initial-scale=1'/>";
  page += "<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>";
  if ( theme == "bootstrap" ) {
    page += "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>";
  } else {
    page += "<link href='https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/";
    page += theme;
    page += "/bootstrap.min.css' rel='stylesheet'>";
  }
  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 +=   "<div class='row'>";
  page +=     "<div class='col-md-4'>";
  page +=       "<form method='POST' name='selecttheme' id='selecttheme'/>"; 
  page +=       "<input class='span' id='choixtheme' name='theme' type='hidden'>";
  page +=       "<div class='btn-group'>";
  page +=         "<button class='btn btn-default'>Choisir un th&eacute;me</button>";
  page +=         "<button data-toggle='dropdown' class='btn btn-default dropdown-toggle'><span class='caret'></span></button>";
  page +=         "<ul class='dropdown-menu'>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"bootstrap\"); $(\"#selecttheme\").submit()'><a href='#'>Boostrap</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"cerulean\"); $(\"#selecttheme\").submit()'><a href='#'>Cerulean</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"cosmo\"); $(\"#selecttheme\").submit()'><a href='#'>Cosmo</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"cyborg\"); $(\"#selecttheme\").submit()'><a href='#'>Cyborg</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"darkly\"); $(\"#selecttheme\").submit()'><a href='#'>Darkly</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"flatly\"); $(\"#selecttheme\").submit()'><a href='#'>Flatly</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"journal\"); $(\"#selecttheme\").submit()'><a href='#'>Journal</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"lumen\"); $(\"#selecttheme\").submit()'><a href='#'>Lumen</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"paper\"); $(\"#selecttheme\").submit()'><a href='#'>Paper</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"readable\"); $(\"#selecttheme\").submit()'><a href='#'>Readable</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"sandstone\"); $(\"#selecttheme\").submit()'><a href='#'>Sandstone</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"simplex\"); $(\"#selecttheme\").submit()'><a href='#'>Simplex</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"slate\"); $(\"#selecttheme\").submit()'><a href='#'>Slate</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"spacelab\"); $(\"#selecttheme\").submit()'><a href='#'>Spacelab</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"superhero\"); $(\"#selecttheme\").submit()'><a href='#'>Superhero</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"united\"); $(\"#selecttheme\").submit()'><a href='#'>United</a></li>";
  page +=           "<li onclick='$(\"#choixtheme\").val(\"yeti\"); $(\"#selecttheme\").submit()'><a href='#'>Yeti</a></li>";
  page +=         "</ul>";
  page +=       "</div>";
  page +=       "</form></div>";
  page +=       "<div class='col-md-8'>";
  page +=         "<p><a href='https://www.projetsdiy.fr'>Version francaise : www.projetsdiy.fr</p>";
  page +=         "<p><a href='http://www.diyprojects.io'>English version : www.diyprojects.io</p>";
  page +=       "</div>";
  page +=   "</div>"; 
  page += "</div></div></div>";
  page += "</body></html>";
  return page;
}
void handleRoot(){ 
  if ( server.hasArg("theme") ) {
    handleTheme();
  } else 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 handleTheme(){
  theme = server.arg("theme");
  Serial.println("Update theme : "); Serial.print(theme);  
  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);
}

Profiter des nouveaux thèmes Bootswatch

Le programme démarre avec le thème Bootstrap. La liste de sélection se trouve en bas de page. A chaque changement de thème, celui-ci est téléchargé (sauf s’il est déjà en cache sur l’ESP8266) et la page est reconstruite avec la nouvelle feuille de style. Voici quelques exemples. Je vous laisse découvrir les autres thèmes par vous-même.

demo webserver esp8266 theme bootstrap

Thème Bootstrap 3.3.7 par défaut

 

demo webserver esp8266 theme bootstrapCDN darkly

Thème Bootswatch Darkly

 

demo webserver esp8266 theme bootstrapCDN readable

Thème Bootswatch Readable

 

demo webserver esp8266 theme bootstrapCDN superhero

Thème Bootswatch Superhero

 

demo webserver esp8266 theme bootstrapCDN united

Thème Bootswatch United

De quoi donner un aspect très pro et très bien fini à tous vos projets DIY ESP8266 ! N’hésitez pas à partager des impressions d’écran de vos réalisations dans les commentaires.

Remarque. Tout le tutoriel a été développé sur l’IDE Arduino 1.8.1 installé sur un Orange Pi Plus 2e (présenté ici) fonctionnant sous Armbian (Ubuntu 16.04 LTS). Suivez ce tutoriel pour savoir comment installer l’IDE Arduino sur Linux (ARM ou x86).

 

Inscrivez-vous à la newsletter hebdomadaire

Aucun spam et aucun autre usage ne sera fait de votre email. Vous pouvez vous désinscrire à tout moment.

Promos à ne pas louper

1
Poster un Commentaire

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

  S’abonner  
Notifier de
Werley Brito

J’utilise un ESP8266 avec une mémoire flash en mode DIO (en utilisant les entrées GPIO9 et GPIO10), mais l’erreur suivante est générée:

Soft WDT reset

ctx: cont
sp: 3fff11b0 end: 3fff13c0 offset: 01b0

>>>stack>>>
3fff1360: feefeffe 3fff0360 3fff0360 40202fa4
3fff1370: feefeffe feefeffe feefeffe feefeffe
3fff1380: feefeffe feefeffe feefeffe feefeffe
3fff1390: feefeffe feefeffe feefeffe 3fff038c
3fff13a0: 3fffdad0 00000000 3fff0384 40207f2c
3fff13b0: feefeffe feefeffe 3fff03a0 40100710
<<

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