Bootswatch offers free Bootstrap themes. We continue the series of articles on the programming of the Web Server part of the ESP8266. In this tutorial we will see how to customize the default theme (replace the default CSS style sheet) of the Bootstrap framework. If you take the train running, in the previous tutorial, we saw how to add the Bootstrap framework in HTML to get a modern view. The Bootstrap style sheet (CSS file) can easily be modified to meet your needs but the easiest way is still to use the many themes (for the most part free) available online.
How do I change the Bootstrap default theme?
It is possible to modify the CSS style sheet of the Bootstrap framework or to add new styles by writing them oneself but it is a shame to have to do everything by hand while there are very many styles Available on the internet (very often free of charge). Can not test them all. I suggest you try the themes of BootstrapCND.
To change the style of our page, simply point to another style sheet, ie we will replace this link in the code of the page.
link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'
Like Bootstrap, it is not necessary to download files locally, we will simply point to the CND (file server) of BootstrapCND. This will give for example for the Cosmo theme the following link.
<link href='https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/cosmo/bootstrap.min.css' rel='stylesheet'>
BootstrapCND also puts a key to verify that the recovered file has not been corrupted (it contains no malicious code). I did not include this check in this example.
BootstrapCDN provides 16 themes found in the Bootswatch section. If you need it, BootstrapCDN also provides a link to retrieve Font Awesome symbols.
How to send dropdown selection by HTTP request?
I take advantage of this tutorial to introduce a new component proposed by Bootstrap, the button drop down (which is also known as the combo) which allows to create a selection list. As usual there are several methods to send the selection made in the list. Since we do not necessarily want to embed javascript code, I suggest you go through a little trick that consists of creating an invisible field in which we will copy not possible before calling the submit () function.
We will create a form in which the drop down button and the invisible field (an input field) will be placed. For this to work, it is important to correctly name the HTML elements and assign them an identifier (id). Let’s see how it works:
- form method = ‘POST’ name = ‘selecttheme’ id = ‘selecttheme’: a form is created that has the name and the selecttheme id. This is not a problem. The form data will be sent by an HTTP request using the POST method.
- input class = ‘span’ id = ‘choicetheme’ name = ‘theme’ type = ‘hidden‘: the invisible input field (if you want to see what happens, change the type by text). The name parameter will be the key that will contain the value of the selected theme in the list
- onclick=’$(\”#choicetheme\”).val (\”bootstrap\”); $(“#selecttheme\”).submit() ‘: for each choice of the list, one executes a jquery command when a click is detected. In the hidden field (#choixtheme) we copy the value that corresponds to the section (this will be the value that will be sent to the Arduino code). Then we run a submit() which will send the form data (#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é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>
To see what happens, open the development tools on your browser is select a theme to view the query sent to Arduino code.
Change theme in dynamics
Now it only remains to add a treatment to refresh the page each time the user selects a theme. To do this, we need to add a test on the theme argument in the handleRoot() function.
A theme variable is used to store the value selected by the user. Then, when creating the HTML page, it is enough to make a test on the value of the theme to know if the user wishes the default theme or one of 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'>"; }
Project Code
I adapted the code from the previous tutorial that recovers the temperature and humidity of a DHT22 probe and the atmospheric pressure of a BMP180. The following material was used.
Any ESP8266 ESP-12 module, for example Wemos D1 Mini | |
|
Atmospheric pressure |
Temperature and humidity sensor | |
Jumper Dupont | |
Breadboard | |
Led (optional, follow the WiFi activity) | |
Resistor 220Ω (optional) |
Wiring
Here is a table of pin marking and correspondence between Arduino and ESP8266.
Sensor | Pin | ESP8266 Pin (Wemos D1 mini) |
DHT22 | VCC | 5V |
GND | G | |
Data | D4 | |
BMP180 | VCC | 5V |
GND | G | |
SDA | D2 | |
SCL | D1 |
It remains to change the ssid as well as the password of the WiFi network before uploading the code in the 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='https://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); }
Take advantage of new Bootswatch themes
The program starts with Bootstrap theme. The selection list is at the bottom of the page. With each change of theme, it is downloaded (unless it is already cached on the ESP8266) and the page is rebuilt with the new style sheet. Here are some examples. I let you discover the other themes by yourself.
What give a very pro and very well finished aspect to all your projects DIY ESP8266! Feel free to share screen impressions of your achievements in the comments.
Note. All the tutorial was developed on the IDE Arduino 1.8.1 installed on an Orange Pi Plus 2e (presented here) running under Armbian (Ubuntu 16.04 LTS). Follow this tutorial to learn how to install the Arduino IDE on Linux (ARM or x86).
- How to store data on a micro SD card. Arduino code compatible ESP32, ESP8266
- Getting started Arduino. Receive commands from the serial port (ESP32 ESP8266 compatible)
- C++ functions print•println•printf•sprintf for Arduino ESP32 ESP8266. Combine•format → serial port
- C++ String functions. concat•c_srt•indexOf•replace•subString… for Arduino, ESP32, ESP8266
- How to assign a fixed IP to an ESP32 ESP8266 or ESP01 project
- ESP01. Get started with the Arduino or PlatformIO IDE. Which module to choose? Pinout