Pour faire suite à mon précédent article ,je vous propose de rendre l’interface Web du module , plus agréable, de la rendre « responsive » grâce à l’utilisation du framework Bootstrap. En prenant comme exemple la fabrication d’un thermomètre / hydromètre .
Dans cet article nous allons voir comment :
- Manipuler la bibliothèque DHT
- Utiliser les fonctions jquery , pour afficher les infos de mon module sur ma page web
- Mettre en place un serveur web JSON
- intégrez un framework comme Bootstrap , afin de dynamiser le tout et avoir une belle interface « responsive »
Pour cela il nous faut :
- un module WEMOS ou compatible ESP8266
- une sonde DHT 11 ou DHT 22 ( dans mon exemple j’utilise la sonde DHT 11 )
Branchement de la sonde DHT , au module WEMOS .
Plus d’info sur le branchement de ce composante et l’utilisation.
Pour info , on trouve souvent des modules dht , montés sur des circuits prêts à l’emploi avec résistance inclus ( ce qui facilite le montage ). C’est ce que j’utilise . Sur ce type de circuit , le brochage n’est pas dans le même ordre .
Voir photos ci dessous.
Sur le photo ci contre , j’ai indiqué l’ordre des 3 PIN restantes .
Face coté soudure , on du haut vers le bas :
- GND : A brancher sur une broche GND du module
- VCC : A brancher sur le 3,3 V du module
- Data : A brancher sur une entrée logique , sur mon Wemos D1 , je l’ai branché à la prise D2 , qui correspond en notation GPIO à la broche 16
Plus d’information sur les brochages du Wemos voir mon article suivant .
La Bibliothéque DHTesp :
Pour utiliser la bibliothèque DHTesp , il suffit d’ajouter les lignes suivantes :
// bibliotheque DHT pour module ESP
#include <DHTesp.h>
Puis on crée une instance DHT et on indique le numéro de PIN ou est relié la data
// init dht
DHTesp dht;
// PIN ou est connecté l’info data du dht
int dhtPin = 16; // GPIO16 egale a D2 sur WEMOS D1R1 ou D0 pour les autres ( averifier selon esp )
et enfin dans la boucle setup , on demarre la lecture du DHT.
// Initialize temperature sensor
dht.setup(dhtPin, DHTesp::DHT11); // si vous avez un DHT22 , remplacé DHT11 par DHT22
Puis dans la fonction loop , je récupère les valeurs de température et d’humidité .
Dans un premier temps , je vais me contenter de lire les valeurs du DHT , et les afficher dans la console.
les differents valeurs récupérés sont les suivantes :
humidity = dht.getHumidity(); Récupére le pourcentage de l’humidité ( % )
temperature = dht.getTemperature(); la température en degrés celcius
heatIndex = dht.computeHeatIndex(temperature,humidity); L’index de chaleur , un peu comme la température ressentis , celui ci est basé sur l’humidité et la température ( en Europe , le vent est pris en charge aussi )
dewPoint = dht.computeDewPoint(temperature,humidity); Le point de rosée
cr = dht.getComfortRatio(cf,temperature,humidity); un indice de confort
per = dht.computePerception(temperature, humidity); un indice de perception humaine ( voir la doc pour plus de détails )
J’ai mis un délai de 10 secondes , pour récupérer les infos , il n’est pas utile de descendre plus bas , le module DHT auras du mal à suivre.
Ci dessous le script complet .
/* * tuto thermometre partie 1 * Affichage des infos DHT dans console */ // bibliotheque temperature #include <DHTesp.h> unsigned long previousMillis=0 ; unsigned long interval = 10000; #define led 2 // init dht DHTesp dht; ComfortState cf; // PIN ou est connecté l'info data du dht int dhtPin = 16; // GPIO16 egale a D2 sur WEMOS D1R1 ou D0 pour les autres ( averifier selon esp ) float humidity ; float temperature; void setup() { Serial.begin(115200); Serial.println(); pinMode(led, OUTPUT); // Initialize temperature sensor dht.setup(dhtPin, DHTesp::DHT11); delay(1000); humidity = dht.getHumidity(); temperature = dht.getTemperature(); } void loop() { // recup temp if( millis() - previousMillis >= interval) { previousMillis = millis(); humidity = dht.getHumidity(); temperature = dht.getTemperature(); float heatIndex = dht.computeHeatIndex(temperature,humidity); float dewPoint = dht.computeDewPoint(temperature,humidity); float cr = dht.getComfortRatio(cf,temperature,humidity); byte per = dht.computePerception(temperature, humidity); String comfortStatus; switch(cf) { case Comfort_OK: comfortStatus = "Comfort_OK"; break; case Comfort_TooHot: comfortStatus = "Comfort_TooHot"; break; case Comfort_TooCold: comfortStatus = "Comfort_TooCold"; break; case Comfort_TooDry: comfortStatus = "Comfort_TooDry"; break; case Comfort_TooHumid: comfortStatus = "Comfort_TooHumid"; break; case Comfort_HotAndHumid: comfortStatus = "Comfort_HotAndHumid"; break; case Comfort_HotAndDry: comfortStatus = "Comfort_HotAndDry"; break; case Comfort_ColdAndHumid: comfortStatus = "Comfort_ColdAndHumid"; break; case Comfort_ColdAndDry: comfortStatus = "Comfort_ColdAndDry"; break; default: comfortStatus = "Unknown:"; break; } Serial.println(" T:" + String(temperature) + "°C H:" + String(humidity) + "% I:" + String(heatIndex) + " D:" + String(dewPoint) + " " + comfortStatus+" et per :"+ String(per)); } }
Vous obtenez ceci :
La console , c’est pratique , pour « debugger » , mais un affichage déporté dans une interface WEB serait mieux
[adsense]Afficher l’info sur l’interface WEB :
afin que mon serveur Web , récupère l’info de la température , je crée un service web getInfo .
server.on(« /getInfo »,handleInfo); //Rpour récupérer info module
Qui envoie les requêtes sur la fonction handleInfo() . dans cette exemple , cette fonction est très simple , elle ne fait que renvoyer la valeur température .
void handleInfo() {
String Info=String(temperature);
server.send(200, « text/plane »,Info);
}
Il ne me reste plus qu’a modifier ma page web , afin de récupérer cette valeur .
Dans mon dossier data , je modifie mon fichier html ( de mon service WEB ) , je reprend le même fichier que dans l’article précédent , et j’ajoute les lignes suivantes :
Une balise HTML :
<div>Temperature : <span id= »Temp »></span> </div>
et un bout de code javascript ( jquery) ,afin d’aller renseigner l’info récupéré par l’appel à getInfo.
Ci dessous le code javascript :
function loadInfo(){ $( "#Temp" ).load( "/getInfo"); } loadInfo(); // premiere execution setInterval(function(){ loadInfo() // rappel toutes les 10 secondes }, 10000);
$( « #Temp » ).load( « /getInfo »); : permet de récupérer les données envoyés par la page /getInfo du module
La fonction setInterval , me permet de relancer cette fonction toutes les 10 secondes , afin d’actualiser la valeur de la température.
Ci dessous le script complet de la page HTML.
<!DOCTYPE html>
<html>
<head>
<script src="jquery.min.js"></script>
</head>
<body>
<center>
<h1>Tuto SPIFFS + jquery - Byfeel</h1>
<p>Demo jquery , pour on/off led interne</p>
<div>Temperature : <span id="Temp"></span> </div>
<form action='/setLED' method='POST' id='FormLED'>
<fieldset>
<legend>LED interne 2 :</legend>
<INPUT type='radio' name='LED' value='on' id='on' checked><label for='on'>ON</label>
<INPUT type='radio' name='LED' value='off' id='off'><label for='off'>OFF</label>
</fieldset>
<INPUT type='submit' value='Envoyer' id='bouton_LED'/>
</form>
<a href="https://byfeel.info">byfeel.info</a><br>
<div id="info"></div>
<script>
$("#FormLED").submit(function(){
$.ajax({
url: '/setLED',
type: 'post',
data: $("#FormLED").serialize(),
success: function(data) {
$("#info").html("<p> Commende LED : "+data+"</p>");
}
});
return false;
});
function loadInfo(){
$( "#Temp" ).load( "/getInfo");
}
loadInfo(); // premiere execution
setInterval(function(){
loadInfo() // rappel toutes les 10 secondes
}, 10000);
</script>
</body>
</html>
fichier à coller dans le dossier data de votre script arduino . Ainsi que le fichier jquery.min.js
Voila a quoi ressemble votre structure :
Il faut copier l’intégralité du dossier data , dans votre wemos , par le biais de la fonction « ESP8266 sketch data upload » de votre interface Arduino ( voir article SPIFFS ). Puis uploader par la suite votre sketch , par la méthode traditionnel.
Dans le script , j’ai conservé la mise en place du serveur FTP , afin de faciliter les test avec le fichier html , et ainsi recharger uniquement le fichier index.html ( plus rapide ) , que de passer par la fonction inclus dans l’interface arduino.
Script complet : ( pensez à renseigner vos identifiants WIFI )
/* * webserver dynamique - temperature */ #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> #include <FS.h> //Include SPIFFS // bibliotheque temperature #include <DHTesp.h> // FTPserver #include "ESP8266FtpServer.h" unsigned long previousMillis=0 ; unsigned long interval = 10000; //WiFi Connection configuration const char *ssid = "nomduwifi"; const char *password = "mdp wifi"; #define led 2 // init dht DHTesp dht; ComfortState cf; // PIN ou est connecté l'info data du dht int dhtPin = 16; // GPIO16 egale a D2 sur WEMOS D1R1 ou D0 pour les autres ( averifier selon esp ) float humidity ; float temperature; // on crée l'instance FtpServer ftpSrv; //*********************************** //************* Gestion du serveur WEB //*********************************** ESP8266WebServer server(80); void handleRoot(){ server.sendHeader("Location", "/index.html",true); //Redirige vers page index.html sur SPIFFS server.send(302, "text/plane",""); } // fonction de traitement SETLED void handleLED(){ String SetLED = server.arg("LED"); Serial.println(SetLED); if (SetLED=="on") digitalWrite(led, LOW); else digitalWrite(led, HIGH); server.send(200, "text/plane",SetLED); } // fonction de traitement SETLED void handleInfo() { String Info=String(temperature); server.send(200, "text/plane",Info); } void handleWebRequests(){ if(loadFromSpiffs(server.uri())) return; String message = "File Not Detected\n\n"; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET)?"GET":"POST"; message += "\nArguments: "; message += server.args(); message += "\n"; for (uint8_t i=0; i<server.args(); i++){ message += " NAME:"+server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; } server.send(404, "text/plain", message); Serial.println(message); } //********** fin serveur web void setup() { Serial.begin(115200); Serial.println(); // Conexion WIFI WiFi.begin(ssid, password); Serial.println(""); // Wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } //demarrage file system if (SPIFFS.begin()) { Serial.println("Demarrage file System"); ftpSrv.begin("esp8266","pass"); // demarrage serveur ftp avec user = esp8266 et mdp = password } else Serial.println("Erreur file System"); //Si connexion affichage info dans console Serial.println(""); Serial.print("Connection ok sur le reseau : "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); //Initialize Webserver server.on("/",handleRoot); server.on("/setLED",handleLED); //Rpour gerer led server.on("/getInfo",handleInfo); //Rpour récupérer info module server.onNotFound(handleWebRequests); //Set setver all paths server.begin(); pinMode(led, OUTPUT); // Initialize temperature sensor dht.setup(dhtPin, DHTesp::DHT11); delay(1000); humidity = dht.getHumidity(); temperature = dht.getTemperature(); } void loop() { //service web server.handleClient(); ftpSrv.handleFTP(); // recup temp if( millis() - previousMillis >= interval) { previousMillis = millis(); humidity = dht.getHumidity(); temperature = dht.getTemperature(); float heatIndex = dht.computeHeatIndex(temperature,humidity); float dewPoint = dht.computeDewPoint(temperature,humidity); float cr = dht.getComfortRatio(cf,temperature,humidity); byte per = dht.computePerception(temperature, humidity); String comfortStatus; switch(cf) { case Comfort_OK: comfortStatus = "Comfort_OK"; break; case Comfort_TooHot: comfortStatus = "Comfort_TooHot"; break; case Comfort_TooCold: comfortStatus = "Comfort_TooCold"; break; case Comfort_TooDry: comfortStatus = "Comfort_TooDry"; break; case Comfort_TooHumid: comfortStatus = "Comfort_TooHumid"; break; case Comfort_HotAndHumid: comfortStatus = "Comfort_HotAndHumid"; break; case Comfort_HotAndDry: comfortStatus = "Comfort_HotAndDry"; break; case Comfort_ColdAndHumid: comfortStatus = "Comfort_ColdAndHumid"; break; case Comfort_ColdAndDry: comfortStatus = "Comfort_ColdAndDry"; break; default: comfortStatus = "Unknown:"; break; } Serial.println(" T:" + String(temperature) + "°C H:" + String(humidity) + "% I:" + String(heatIndex) + " D:" + String(dewPoint) + " " + comfortStatus+" et per :"+ String(per)); } } // gestion du not found. bool loadFromSpiffs(String path){ String dataType = "text/plain"; if(path.endsWith("/")) path += "index.htm"; if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf(".")); else if(path.endsWith(".html")) dataType = "text/html"; else if(path.endsWith(".htm")) dataType = "text/html"; else if(path.endsWith(".css")) dataType = "text/css"; else if(path.endsWith(".js")) dataType = "application/javascript"; else if(path.endsWith(".png")) dataType = "image/png"; else if(path.endsWith(".gif")) dataType = "image/gif"; else if(path.endsWith(".jpg")) dataType = "image/jpeg"; else if(path.endsWith(".ico")) dataType = "image/x-icon"; else if(path.endsWith(".xml")) dataType = "text/xml"; else if(path.endsWith(".pdf")) dataType = "application/pdf"; else if(path.endsWith(".zip")) dataType = "application/zip"; File dataFile = SPIFFS.open(path.c_str(), "r"); if (server.hasArg("download")) dataType = "application/octet-stream"; if (server.streamFile(dataFile, dataType) != dataFile.size()) { } dataFile.close(); return true; }
Ce qui donne :
Pour récupérer une donnée , c’est bien , mais si je désire récupérer plusieurs données , cela devient vite un peu plus compliquer . Heuresement jquery , permet de récupérer facilement des données graces aux format JSON.
Parlons de JSON
JSON (que l’on peut prononcer à l’anglaise comme le prénom Jason ( par exemple jason bourne ) signifie JavaScript Object Notation . C’est une façon d’écrire les informations dans un fichier qui soit facilement compréhensible pour le Javascript.A quoi ça ressemble ?
Voici un exemple de syntaxe JSON :
{ "cle":"valeur", "Temperatue" : "25" }
Je pourrais créer le fichier par une simple chaine de caractére , mais je cherche une solution simple pour écrire le fichier , afin d’envoyer plus d’info.
Pour cela , je vais utiliser la bibliotheque JSON . ( récuperer la bibliotheque sur le site Github , en version 5.13.2 au moment ou j’écris cet article ).
Sur la doc officiel de la librairie JSON , il y a un avertissement qui indique que l’interface Arduino à tendance a vouloir installer la version 6 , mais cette dernière étant en beta , il est recommandé de rester sur une version 5.
Une fois récupéré , copier le dossier arduino.json-master , dans votre dossier librairies .
Comment utiliser cette bibliothèque :
#include <ArduinoJson.h>
//json – création de l’instance JSONInfo
StaticJsonBuffer<200> jsonBuffer;
JsonObject& JSONInfo = jsonBuffer.createObject();
Puis pour ajouter des données , il suffit d’utiliser l’instance créé , en indiquant la clé puis la valeur . Par exemple dans ce script , j’enregistre 3 infos ( la température avec la clé T , l’humidité avec la clé H et l’info confortstatus avec la clé confort.
JSONInfo[« T »] = temperature;
JSONInfo[« H »] = humidity;
JSONInfo[« Confort »] = comfortStatus;
Puis pour finaliser ma chaine JSON , j’utilise la fonction printTO(nom-de-la-sortie) .
JSONInfo.printTo(Info);
Il ne me reste plus qu’a envoyer le tout
server.send(200, « application/json »,Info); }
Ci dessous le script final , à téléverser dans votre module Wemos.
/* * webserver dynamique - temperature */ #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> #include <FS.h> //Include SPIFFS // bibliotheque temperature #include <DHTesp.h> #include <ArduinoJson.h> // FTPserver #include "ESP8266FtpServer.h" unsigned long previousMillis=0 ; unsigned long interval = 10000; //WiFi Connection configuration const char *ssid = "mon wifi"; const char *password = "mon mot de passe"; #define led 2 // init dht DHTesp dht; ComfortState cf; // PIN ou est connecté l'info data du dht int dhtPin = 16; // GPIO16 egale a D2 sur WEMOS D1R1 ou D0 pour les autres ( averifier selon esp ) float humidity ; float temperature; float heatIndex; float dewPoint; byte per; String comfortStatus; // on crée l'instance FtpServer ftpSrv; //json StaticJsonBuffer<200> jsonBuffer; JsonObject& JSONInfo = jsonBuffer.createObject(); //*********************************** //************* Gestion du serveur WEB //*********************************** ESP8266WebServer server(80); void handleRoot(){ server.sendHeader("Location", "/index.html",true); //Redirige vers page index.html sur SPIFFS server.send(302, "text/plane",""); } // fonction de traitement SETLED void handleLED(){ String SetLED = server.arg("LED"); Serial.println(SetLED); if (SetLED=="on") digitalWrite(led, LOW); else digitalWrite(led, HIGH); server.send(200, "text/plane",SetLED); } // fonction de traitement getInfo void handleInfo() { String Info; JSONInfo["T"] = temperature; JSONInfo["H"] = humidity; JSONInfo["Confort"] = comfortStatus; JSONInfo.printTo(Info); server.send(200, "application/json",Info); } void handleWebRequests(){ if(loadFromSpiffs(server.uri())) return; String message = "File Not Detected\n\n"; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET)?"GET":"POST"; message += "\nArguments: "; message += server.args(); message += "\n"; for (uint8_t i=0; i<server.args(); i++){ message += " NAME:"+server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; } server.send(404, "text/plain", message); Serial.println(message); } //********** fin serveur web void setup() { Serial.begin(115200); Serial.println(); // Conexion WIFI WiFi.begin(ssid, password); Serial.println(""); // Wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } //demarrage file system if (SPIFFS.begin()) { Serial.println("Demarrage file System"); ftpSrv.begin("esp8266","pass"); // demarrage serveur ftp avec user = esp8266 et mdp = password } else Serial.println("Erreur file System"); //Si connexion affichage info dans console Serial.println(""); Serial.print("Connection ok sur le reseau : "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); //Initialize Webserver server.on("/",handleRoot); server.on("/setLED",handleLED); //Rpour gerer led server.on("/getInfo",handleInfo); //Rpour récupérer info module server.onNotFound(handleWebRequests); //Set setver all paths server.begin(); pinMode(led, OUTPUT); // Initialize temperature sensor dht.setup(dhtPin, DHTesp::DHT11); delay(1000); humidity = dht.getHumidity(); temperature = dht.getTemperature(); JSONInfo["T"] = temperature; JSONInfo["H"] = humidity; } void loop() { //service web server.handleClient(); ftpSrv.handleFTP(); // recup temp if( millis() - previousMillis >= interval) { previousMillis = millis(); humidity = dht.getHumidity(); temperature = dht.getTemperature(); heatIndex = dht.computeHeatIndex(temperature,humidity); dewPoint = dht.computeDewPoint(temperature,humidity); float cr = dht.getComfortRatio(cf,temperature,humidity); per = dht.computePerception(temperature, humidity); switch(cf) { case Comfort_OK: comfortStatus = "OK"; break; case Comfort_TooHot: comfortStatus = "Trop Chaud"; break; case Comfort_TooCold: comfortStatus = "Trop froid"; break; case Comfort_TooDry: comfortStatus = "Trop Sec"; break; case Comfort_TooHumid: comfortStatus = "Trop humide"; break; case Comfort_HotAndHumid: comfortStatus = "Chaud et Humide"; break; case Comfort_HotAndDry: comfortStatus = "Chaud et Sec"; break; case Comfort_ColdAndHumid: comfortStatus = "Froid et humide"; break; case Comfort_ColdAndDry: comfortStatus = "Froid et Sec"; break; default: comfortStatus = "Unknown:"; break; } Serial.println(" T:" + String(temperature) + "°C H:" + String(humidity) + "% I:" + String(heatIndex) + " D:" + String(dewPoint) + " " + comfortStatus+" et per :"+ String(per)); } } // gestion du not found. bool loadFromSpiffs(String path){ String dataType = "text/plain"; if(path.endsWith("/")) path += "index.htm"; if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf(".")); else if(path.endsWith(".html")) dataType = "text/html"; else if(path.endsWith(".htm")) dataType = "text/html"; else if(path.endsWith(".css")) dataType = "text/css"; else if(path.endsWith(".js")) dataType = "application/javascript"; else if(path.endsWith(".png")) dataType = "image/png"; else if(path.endsWith(".gif")) dataType = "image/gif"; else if(path.endsWith(".jpg")) dataType = "image/jpeg"; else if(path.endsWith(".ico")) dataType = "image/x-icon"; else if(path.endsWith(".xml")) dataType = "text/xml"; else if(path.endsWith(".pdf")) dataType = "application/pdf"; else if(path.endsWith(".zip")) dataType = "application/zip"; File dataFile = SPIFFS.open(path.c_str(), "r"); if (server.hasArg("download")) dataType = "application/octet-stream"; if (server.streamFile(dataFile, dataType) != dataFile.size()) { } dataFile.close(); return true; }
Si vous executez sur une page web , la requete suivante :
http://adresseIP-du_module/getInfo , vous obtiendrez l’info suivante :
{"T":28,"H":43,"Confort":"OK"}
La chaine JSON , à bien été construite.
Afin de l’exploitez , il va falloir modifier le script jquery de votre page index html.
Pour tester tout de suite , il suffit de remplacer la ligne : $( « #Temp » ).load( « /getInfo »); par la fonction $.getJSON qui permet de lire un fichier JSON.
Ce qui donne :
$.getJSON(‘/getInfo’, function(jthermo) {
$(‘#Temp’).text(jthermo.T); // permet de récupérer la clé T qui correspond à la température
});
Je vous laisse modifier le fichier , afin d’afficher les autres infos.
Améliorer le rendu HTML
Afin de donner une touche plus moderne , a notre page WEB , je vais expliquer comment activer bootstrap sur le module.
Bootstrap est un framework WEB , qui permet de construire rapidement des sites web dynamique et full responsive.
Pour cela , il suffit de téléchargé , vous allez récupérer les dossiers et fichiers suivants :
Il y a deux fichiers à récuperer et à coller dans votre dossier data :
bootstrap.min.css et bootstrap.min.js
Uploader le tout avec la commande arduino ide : ESP8266 sketch data upload . Par la suite pour la modification de votre fichier HTML , je vous conseille de passer par le serveur FTP. Ça sera plus rapide.
Comment integrez bootstrap ,dans votre fichier html
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Demo ESP8266 dynamique</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<link rel="stylesheet" href="bootstrap.min.css" media="screen">
</head>
Retrouvez les script sur mon github.
2 commentaires sur “WEMOS ( EP8266 ) et serveur Web Dynamique”
Encore un bel article bien détaillé 🙂
Merci 🙂