Bootswatch permet de personnaliser le thème par défaut (remplacer la feuille de style CSS par défaut) du framework Bootstrap. 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 sur bootstrapCDN.
Tutoriel actualisé le 7 août 2020
Nous allons modifier le code de l’interface HTML développé dans le tutoriel précédent.
Comment changer le thème par défaut de Bootstrap avec Bootswatch ?
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 changer le thème par une requête HTTP ?
On va profiter 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).
Et le code javascript correspondant
<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é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 et sélectionnez un thème pour visualiser la requête envoyée au serveur Web de l’ESP8266.
Comment changer le thème Bootstrap dynamiquement ?
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
Nous allons reprendre le code de notre petite station météo développé dans le tutoriel précédent. On récupère la température et l’humidité d’une sonde DHT22 et la pression atmosphérique d’un BMP180.
Wemos D1 Mini (module ESP8266 ESP-12) | |
Alimentation 5/3A micro-usb
Une alimentation de qualité est recommandée. |
|
Capteur de pression atmosphérique | |
Capteur de température et d’humidité
DHT22 (ou DHT11) |
|
Jumper Dupont | |
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été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érature</a>";
page += "</li><li>";
page += "<a href='#'> <span class='badge pull-right'>";
page += h;
page += "</span> Humidité</a>";
page += "</li><li>";
page += "<a href='#'> <span class='badge pull-right'>";
page += p;
page += "</span> Pression atmosphé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écédente</th></tr></thead>"; //Entête
page += "<tbody>"; // Contenu du tableau
page += "<tr><td>DHT22</td><td>Température</td><td>"; // Première ligne : température
page += t;
page += "°C</td><td>";
page += "-</td></tr>";
page += "<tr class='active'><td>DHT22</td><td>Humidité</td><td>"; // 2nd ligne : Humidité
page += h;
page += "%</td><td>";
page += "-</td></tr>";
page += "<tr><td>BMP180</td><td>Pression atmosphé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é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='http://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);
}
Test de quelques thèmes proposés par bootstrapCDN
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 en testant directement depuis votre projet ESP8266.
De quoi donner un aspect très pro et très bien fini à tous vos projets DIY ESP8266 !
- Stocker des données sur une carte micro SD. Code Arduino compatible ESP32, ESP8266
- Débuter Arduino. Recevoir des commandes depuis le port série (compatible ESP32 ESP8266)
- Fonctions C++ print•println•printf•sprintf pour Arduino ESP32 ESP8266. Combiner•formater → port série
- String C++. concat•c_srt•indexOf•replace•subString… pour Arduino ESP32 ESP8266
- ESP01. Débuter avec l’IDE Arduino ou PlatformIO. Quel module choisir ? Repérage des broches
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
<<