Nous continuons le développement de l’interface HTML de la station météo en ajouter le code nécessaire pour piloter le code Arduino depuis l’interface Web. Nous allons également mettre en place l’actualisation automatique de la table des mesures ainsi que des afficheurs.
Projet actualisé le 29 juillet 2020
Nous allons en profiter pour apprendre comment gérer les commandes pour activer et désactiver le GPIO de l’ESP8266 depuis l’interface Web. Nous verrons enfin comment optimiser le temps de chargement de la page en téléversant certaines ressources dans la zone SPIFFS (fichiers CSS et JS de Bootstrap et jquery) plutôt que d’aller les récupérer à chaque fois sur internet.
Si vous découvrez les modules ESP8266 et la programmation de l’interface HTML, vous pouvez commencer par lire quelques ces articles.
Découverte de JSON avant de commencer
JSON est un format qui permet de structurer les données pour le stockage et le transfert. Il remplace très avantageusement le format XML dans le langage Javascript (entre autre) ou il est supporté nativement. Il est également très simple de créer une structure JSON dans le code Arduino. Sans trop rentrer dans les détails, voici comment ça fonctionne :
- Les données sont stockées sous la forme clé:valeur
- Chaque couple doit être séparé par une virgule, sauf le dernier couple
- Plusieurs types de valeurs existent
- une chaine
- un tableau []
- une structure de données libre à condition de respecter le formaliste JSON clé:valeur, clé:{}, clé:[]
Ce qui donne par exemple
{
"cle1":"valeur1",
"cle2":"[1,2,3,4]",
"cle3":{
"clea":"valeura",
"cleb":"['a','b','c']"
}
}
Ou un tableau
[{
"temperature": "18",
"humidite": "52"
}, {
"temperature": "19",
"humidite": "52"
}]
Dernière remarque, le format JSON n’est rien d’autre qu’une chaîne de caractère, ce qui donne
[{"temperature":"18","humidite":"52"},{"temperature":"19","humidite":"52" }]
Super pratique pour envoyer des données ou des commandes à l’aide de messages MQTT ou Node-RED via le port série ! D’ailleurs, si le sujet vous intéresse, voici un projet complet
Pour tester vos JSON, je vous conseille jsonlint disponible en ligne et largement suffisant. Pour en savoir plus, vous pouvez consulter cet article en français de nos amis d’alsacreation.
Actualisation automatique de la table et des indicateurs
Autant laisser le navigateur actualiser régulièrement les mesures faites à l’aide de l’ESP8266. Nous allons reprendre le code de l’interface développée dans le tutoriel précédent et le faire évoluer pour que toutes les interactions soient gérées coté client (sur le navigateur internet).
Code HTML
Reprenons le code de l’onglet Mesures. On a ajouté 3 boutons de navigations qui permettent de créer 3 afficheurs. Chaque bouton possède un badge repéré par un identifiant #temperature #humidite #pa qui va nous permettre d’actualiser la mesure.
Ensuite un tableau composé de trois colonnes permet d’afficher le type de mesure, la valeur courante et une valeur précédente. Chaque colonne est associée à un champ de données (data-field) qui sera associé à un JSON envoyé par le code Arduino. Le contenu de chaque colonne est mis en forme par une fonction indiquée dans l’attribut data-formatter.
ul.nav.nav-pills li.active a(href='#') #temperature.span.badge.pull-right - | Température li a(href='#') #humidite.span.badge.pull-right - | Humidité li a(href='#') #pa.span.badge.pull-right - | Pression atmosphérique table(id='tab_mesures' data-toggle='table' data-show-colunns='true') thead tr th(data-field='mesure' data-align='left' data-sortable='true' data-formatter='labelFormatter') Mesure th(data-field='valeur' data-align='left' data-sortable='true' data-formatter='valueFormatter') Valeur th(data-field='precedente' data-align='left' data-sortable='true' data-formatter='vpFormatter') Valeur Précédente
Tout est prêt du coté de l’interface Web
Code Javascript
Le plugin Bootstrap-table va nous simplifier la vie pour actualiser les données. En effet, il va nous suffire d’appeler la méthode refresh et d’indiquer l’url de retour des données (toutes les fonctions sont présentée ici).
Par contre, il faut s’assurer que la table existe avant de pouvoir commencer, on doit donc inclure cet appel dans un test $(document).ready(). On en profitera également pour appeler une fonction qui permettra d’actualiser les afficheurs.
Voici le code
$(document).ready(function(){ var Timer_UdpateMesures; // Actualise les données dès que la page est chargée - Update data when the page is ready $('#tab_mesures').bootstrapTable('refresh',{silent: true, showLoading: false, url: '/tabmesures.json'}) updateMesures(); })
Petit problème, cette solution ne fonctionne pas dans le cas ou la table est intégrée dans un onglet (div tab-pane). Heureusement, la solution a été présentée dans l’issue 1760 sur Github.
Sur la classe shown.bs.tab de la balise a[data-toggle=’tab’], on test la présence de l’attribut tab_mesures, celui qui correspond à l’onglet contenant la table des mesures. Si c’est le cas, on lance l’actualisation de la table ainsi que les 3 afficheurs.
var Timer_UdpateMesures; $('a[data-toggle=\"tab\"]').on('shown.bs.tab', function (e) { //On supprime tous les timers lorsqu'on change d'onglet clearTimeout(Timer_UdpateMesures); var target = $(e.target).attr("href") console.log('activated ' + target ); // IE10, Firefox, Chrome, etc. if (history.pushState) { window.history.pushState(null, null, target); } else { window.location.hash = target; } if (target=='#tab_mesures') { $('#table_mesures').bootstrapTable('refresh',{silent:true, url:'/tabmesures.json'}); updatesMesures(); } });
Pour actualiser à intervalle régulier les données, on va donc créer un Timer qui va exécuter une fonction au bout d’une minute (6000ms) par exemple.
Pour que le Timer soit appelé de manière récurrente, il suffit de le relancer à chaque fois que la table des mesures vient d’être actualisée on.(‘load-success.bs.table’). On testera également que l’onglet de la table soit actif. On obtient le code suivant :
// Créé un timer qui actualise les données régulièrement - Create a timer than update data every n secondes
$('#tab_mesures').on('load-success.bs.table',function (e,data){
//console.log("tab_mesures loaded");
Timer_UdpateMesures=setTimeout(function(){
$('#tab_mesures').bootstrapTable('refresh',{silent: true, showLoading: false, url: '/tabmesures.json'});
updateMesures();
},6000);
});
Dans la fonction updateMesures(), on va donc faire un appel $.getJSON qui prend en paramètre une URL, ici /mesures.json .
Dès que les données sont réceptionnées, la fonction est exécutée. La méthode jquery html permet d’actualiser le contenu du badge.
Comme c’est un objet JSON, on accède à la valeur par data.nom_element. Pour faciliter la mise au point, on peut également appeler la fonction fail. On envoi vers la console l’objet JSON. Pour pouvoir le lire, on utilise la fonction Javascript JSON.stringify.
function updateMesures(){
$.getJSON('/mesures.json', function(data){
//console.log("Mesures envoyees : " + JSON.stringify(data) + "|" + data.t + "|" + data.h + "|" + data.pa) ;
$('#temperature').html(data.t);
$('#humidite').html(data.h);
$('#pa').html(data.pa);
}).fail(function(err){
console.log("err getJSON mesures.json "+JSON.stringify(err));
});
};
Il ne reste plus qu’à mettre en forme l’affichage des colonnes de la table. Pour bien comprendre ce qui se passe, voici le JSON renvoyé par l’ESP8266. Le plugin Bootstrap-table attend un tableau de données. Si ce n’est pas le cas, vous aurez une erreur renvoyée par la fonction .fail()
[{
"mesure": "Température",
"valeur": "21.40",
"unite": "°C",
"glyph": "glyphicon-indent-left",
"precedente": "0.00"
}, {
"mesure": "Humidité",
"valeur": "32.00",
"unite": "%",
"glyph": "glyphicon-tint",
"precedente": "0.00"
}, {
"mesure": "Pression Atmosphérique",
"valeur": "990.48",
"unite": "mbar",
"glyph": "glyphicon-dashboard",
"precedente": "0.00"
}]
Pour chaque ligne, on va donc récupérer :
- Le libellé de la mesure
- La valeur actuelle
- L’unité
- Le symbole, cela évite un test coté client (navigateur) et le code Arduino est plus vite téléchargé en cas de modification que les fichiers SPIFFS.
- Une valeur précédente
On va maintenant créer une fonction qui va s’occuper de créer le libellé qui va correspond à chaque colonne. Pour la colonne Mesure, on appel la fonction labelFormatter().
On réalise un test sur le libellé et on construit une chaine qui va contenir le libellé. On y ajoute une balise span qui va ajouter juste avant le libellé (pull-left) un icône (glyphicon) contenu dans le JSON (row.glyph). Vous trouverez tous les glyphicons proposés par Bootstrap ici. Sinon vous pouvez également utiliser la librairie Fontawesome.
function labelFormatter(value, row){
var label = "";
if ( value === "Température" ) {
label = value + "<span class='glyphicon " + row.glyph + " pull-left'></span>";
} else if ( value === "Humidité" ) {
label = value + "<span class='glyphicon " + row.glyph + " pull-left'></span>";
} else if ( value === "Pression Atmosphérique" ) {
label = value + "<span class='glyphicon " + row.glyph + " pull-left'></span>";
} else {
label = value;
}
return label;
}
valueFormatter() met en forme la mesure actuelle. Comme on dispose du contenu de la ligne, on peut faire un test pour déterminer si la valeur actuelle est supérieure ou inférieure à la précédente. On peut ainsi construire un libellé de la forme : valeur + unité + icône (haut / bas).
function valueFormatter(value, row){
//console.log("valueFormatter");
var label = "";
if ( row.valeur > row.precedente ) {
label = value + row.unite + "<span class='glyphicon glyphicon-chevron-up pull-right'></span>";
} else {
label = value + row.unite + "<span class='glyphicon glyphicon-chevron-down pull-right'></span>";
}
return label;
}
Enfin pour la valeur précédente, on assemble simplement value + unite.
function vpFormatter(value, row){
//console.log("valueFormatter");
var label = "";
if ( row.valeur > row.precedente ) {
label = value + row.unite
} else {
label = value + row.unite
}
return label;
}
Tout est prêt du coté de l’interface Web, passons au code Arduino coté ESP8266.
Code Arduino
On va ajouter des fonctions qui seront appelées lorsque le serveur reçoit une requête sur /mesures.json et tabmesures.json juste avant de démarrer le serveur.
server.on("/tabmesures.json", sendTabMesures);
server.on("/mesures.json", sendMesures);
Pour les deux fonctions, on va créer une chaine texte en respectant le formaliste JSON. Pour la fonction sendMesures(), ce sera objet simple dont voici un exemple
{“t”:”21.50″,”h”:”31.80″,”pa”:”990.53″}
ou sous une forme plus lisible
{
"t":"21.50",
"h":"31.80",
"pa":"990.53"
}
Comme vous pouvez le voir dans le code, chaque clé et valeur doit être encadrée par un double guillemet (“). Il faut donc le faire précéder du caractère échappatoire pour que la chaîne puisse être compilée (\”). On appel ensuite la méthode server.send() pour envoyer l’objet JSON au client.
void sendMesures() {
String json = "{\"t\":\"" + String(t) + "\",";
json += "\"h\":\"" + String(h) + "\",";
json += "\"pa\":\"" + String(pa) + "\"}";
server.send(200, "application/json", json);
Serial.println("Mesures envoyees");
}
Pour la table, on doit envoyer un objet constitué d’un tableau, chaque ligne étant un objet contenant les informations nécessaires (libellé, valeur actuelle, unité, symbole, valeur précédente). La chaine est plus délicate à construire. Pour le moment on envoi 0 pour la valeur précédente.
void sendTabMesures() {
double temp = 0; // Récupère la plus ancienne mesure (temperature) - get oldest record (temperature)
String json = "[";
json += "{\"mesure\":\"Température\",\"valeur\":\"" + String(t) + "\",\"unite\":\"°C\",\"glyph\":\"glyphicon-indent-left\",\"precedente\":\"" + String(temp) + "\"},";
temp = 0; // Récupère la plus ancienne mesure (humidite) - get oldest record (humidity)
json += "{\"mesure\":\"Humidité\",\"valeur\":\"" + String(h) + "\",\"unite\":\"%\",\"glyph\":\"glyphicon-tint\",\"precedente\":\"" + String(temp) + "\"},";
temp = 0; // Récupère la plus ancienne mesure (pression atmospherique) - get oldest record (Atmospheric Pressure)
json += "{\"mesure\":\"Pression Atmosphérique\",\"valeur\":\"" + String(pa) + "\",\"unite\":\"mbar\",\"glyph\":\"glyphicon-dashboard\",\"precedente\":\"" + String(temp) + "\"}";
json += "]";
server.send(200, "application/json", json);
Serial.println("Tableau mesures envoyees");
}
Petit conseil. N’hésitez pas à bien découper votre chaîne et allez dans le navigateur pour récupérer le JSON envoyé. Une virgule en trop ou manquante et rien ne fonctionne. Pensez également à jsonlint pour vérifier vos JSON.
Comment piloter le GPIO de l’ESP8266 depuis l’interface Web
Il n’y a rien de plus compliqué pour piloter le GPIO depuis l’interface WEB, on va envoyer une requête HTTP de type POST à l’aide de la commande jsquery $.post(). En retour, on actualisera l’affichage du GPIO (On ou Off) en fonction de la réponse envoyée par le serveur.
Code HTML
On va profiter de la puissance de la librairie jquery pour intercepter un click sur le bouton de commande (ON, OFF) de chaque GPIO. Le code HTML devient très simple.
.col-xs-6.col-md-4
#D5_On.button.btn.btn-success.btn-lg(type='button') ON
.col-xs-6.col-md-4
#D5_Off.button.btn.btn-danger.btn-lg(type='button') OFF
Code Javascript
Le code Javascript va se dérouler en deux temps :
Tout d’abord on intercepte un click sur le bouton à l’aide de la commande jquery $(‘#Dx_Etat’).
Dès qu’un clic est effectué, on appel la fonction setBouton() et on lui passe les paramètres GPIO et Etat. L’état prendra la valeur 1 pour activer la sortie et 0 pour l’arrêter !
$('#D5_On').click(function(){ setBouton('D5','1'); });
$('#D5_Off').click(function(){ setBouton('D5','0'); });
Ensuite la fonction setBouton envoi une requête HTTP de type POST. Pour cela on utilise la fonction $.post de jquery (documentation officielle). Voici sont fonctionnement :
- URL contient les paramètres et les valeurs. On pourrait aussi passer un JSON mais c’est plus facile de la gérer du coté Arduino avec la méthode server.arg(“xx”). L’URL prendra la forme gpio?id=id_gpio@etat=etat_gpio
- done permet d’exécuter du code dès qu’une réponse valide est réceptionnée du serveur
- fail permet de réaliser un traitement en cas d’erreur. Pratique pour la mise au point
Le traitement est très simple, par convention, le code Arduino renvoi la demande complétée par un drapeau (flag) success qui prend la valeur 1 si l’ESP8266 a correctement réalisée la requête demandée, 0 si ce n’est pas le cas. On pourrait imaginer d’autres états et modifier l’affichage en conséquence ou faire apparaître une notification…
function setBouton(id, etat){
$.post("gpio?id=" + id + "&etat=" + etat).done(function(data){
//console.log("Retour setBouton " + JSON.stringify(data));
var id_gpio = "#" + id + "_etat";
//console.log(id_gpio);
if ( data.success === "1" ) {
if ( data.etat === "1" ) {
$(id_gpio).html("ON");
} else {
$(id_gpio).html("OFF");
}
} else {
$(id_gpio).html('!');
}
}).fail(function(err){
console.log("err setButton " + JSON.stringify(err));
});
}
Code Arduino compatible ESP8266
C’est exactement la même chose que pour les mesures. On appel la fonction updateGpio() lorsque le serveur reçoit une demande sur /gpio.
server.on("/gpio", updateGpio);
On va donc récupérer les paramètres passés au serveur (gpio, etat) à l’aide de la méthode server.arg(“id”). Ensuite, c’est de la cuisine classique. On doit juste faire attention car on récupère une chaine de caractère, il faut donc faire une petite conversion pour pointer vers la bonne broche (Pin).
Peu importe le traitement, ce qui importe, c’est de répondre au client, car il attend sa réponse avec impatience.
On construit un JSON dans lequel on recopie la demande et on ajoute un succès (success) qui peut prendre 1 en cas de succès (OK), 0 en cas d’échec (KO), une autre valeur en cas d’erreur… et on envoi le tout en appelant la méthode server.send().
void updateGpio(){
String gpio = server.arg("id");
String etat = server.arg("etat");
String success = "1";
int pin = D5;
if ( gpio == "D5" ) {
pin = D5;
} else if ( gpio == "D7" ) {
pin = D7;
} else if ( gpio == "D8" ) {
pin = D8;
} else {
pin = D5;
}
Serial.println(pin);
if ( etat == "1" ) {
digitalWrite(pin, HIGH);
} else if ( etat == "0" ) {
digitalWrite(pin, LOW);
} else {
success = "1";
Serial.println("Err Led Value");
}
String json = "{\"gpio\":\"" + String(gpio) + "\",";
json += "\"etat\":\"" + String(etat) + "\",";
json += "\"success\":\"" + String(success) + "\"}";
server.send(200, "application/json", json);
Serial.println("GPIO mis a jour");
}
Ce qui donne par exemple pour activer la broche D7 l’objet JSON suivant
{"gpio":"D7","etat":"1","success":"1"}
Téléverser les ressources dans le système de fichier SPIFFS pour diminuer la vitesse de chargement
Il est possible d’éviter d’aller chercher les ressources sur internet (jquery, bootstrap, bootstrap-table…) à chaque fois qu’on se connecte à l’interface WEB de l’ESP8266 en embarquant toutes les resources dans le répertoire Data.
Remarque. Après avoir fait quelques tests, il n’y a pas vraiment de gain en terme de performance. L’ESP8266 ayant une puissance assez limitée, il ne délivre pas les ressources au navigateur plus rapidement que le CDN.
Par contre si vous ne souhaitez pas que l’ESP8266 puisse accéder à internet (connexion internet limitée ou réduction des data consommée d’un forfait 4G), il est nécessaire de tout embarquer dans la zone SPIFFS.
Pour récupérer les ressources, collez l’URL dans un navigateur et collez le texte obtenu dans un fichier texte. Il est préférable de placer les fichiers dans des dossiers séparés pour faciliter la maintenant.
Attention à ce que le chemin complet (repertoire/fichier.ext) ne dépasse pas 31 caractères (limitation SPIFFS)
Ressource | Répertoire | Nom du fichier |
bootstrap.min.css | css | bootstrap.min.css |
bootstrap-table.min.css | css | bootstrap-table.min.css |
Il ne reste plus qu’à modifier les chemins dans le Head, ce qui donne par exemple.
script(src="js/jquery.min.js")
script(src="js/bootstrap.min.js")
script(src="js/bootstrap-table.min.js")
link(rel="stylesheet" href="css/bootstrap-table.min.css")
Code complet du projet intermédiaire
Comme d’habitude, voici le code Pug permettant de générer le fichier de l’interface HTML. Vous pouvez le modifier en fonction de vos besoins et pour faire vos tests.
Template Pug de l’interface
html(charset='UTF-8')
head
meta( name='viewport')
script(src='js/jquery.min.js')
script(src='js/bootstrap.min.js')
script(src='js/bootstrap-table.min.js')
link(rel="stylesheet" href="css/bootstrap-table.min.css")
link(href='https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/superhero/bootstrap.min.css', rel='stylesheet' title="main")
title Demo ESP8266 SPIFFS + Boostrap - www.projetsdiy.fr
body
.container-fluid
h1 Demo Webserver ESP8266 + Bootstrap
ul#tab.nav.nav-tabs
li.active
a(href="#tab_mesures" data-toggle="tab") Mesures
li
a(href="#tab_graphs" data-toggle="tab") Graphiques
li
a(href="#tab_gpio" data-toggle="tab") GPIO
li
a(href="#tab_configuration" data-toggle="tab") Configuration
div.tab-content
div#tab_mesures.tab-pane.fade.in.active
h2 Mini station météo (DHT22 + BMP180)
ul.nav.nav-pills
li.active
a(href='#')
#temperature.span.badge.pull-right -
| Température
li
a(href='#')
#humidite.span.badge.pull-right -
| Humidité
li
a(href='#')
#pa.span.badge.pull-right -
| Pression atmosphérique
br
table(id='table_mesures' data-toggle='table' data-show-colunns='true')
thead
tr
th(data-field='mesure' data-align='left' data-sortable='true' data-formatter='labelFormatter') Mesure
th(data-field='valeur' data-align='left' data-sortable='true' data-formatter='valueFormatter') Valeur
th(data-field='precedente' data-align='left' data-sortable='true' data-formatter='vpFormatter') Valeur Précédente
div#tab_graphs.tab-pane.fade
h2 Graphs
div#tab_gpio.tab-pane.fade
h2 GPIO
.row
.col-xs-6.col-md-4
h4.text-left
| D5
#D5_etat.span.badge OFF
.col-xs-6.col-md-4
#D5_On.button.btn.btn-success.btn-lg(type='button') ON
.col-xs-6.col-md-4
#D5_Off.button.btn.btn-danger.btn-lg(type='button') OFF
.col-xs-6.col-md-4
h4.text-left
| D6
#D6_etat.span.badge OFF
.col-xs-6.col-md-4
#D6_On.button.btn.btn-success.btn-lg(type='button') ON
.col-xs-6.col-md-4
#D6_Off.button.btn.btn-danger.btn-lg(type='button') OFF
.col-xs-6.col-md-4
h4.text-left
| D7
#D7_etat.span.badge OFF
.col-xs-6.col-md-4
#D7_On.button.btn.btn-success.btn-lg(type='button') ON
.col-xs-6.col-md-4
#D7_Off.button.btn.btn-danger.btn-lg(type='button') OFF
.col-xs-6.col-md-4
h4.text-left
| D8
#D8_etat.span.badge OFF
.col-xs-6.col-md-4
#D8_On.button.btn.btn-success.btn-lg(type='button') ON
.col-xs-6.col-md-4
#D8_Off.button.btn.btn-danger.btn-lg(type='button') OFF
div#tab_configuration.tab-pane.fade
h2 Configuration
.btn-group
button#labelTheme.btn.btn-default Theme
button.btn.btn-default.dropdown-toggle(data-toggle='dropdown')
span.caret
ul.dropdown-menu
li
a.change-style-menu-item(href='#' rel='bootstrap') Boostrap
li
a.change-style-menu-item(href='#' rel='cerulean') Cerulean
li
a.change-style-menu-item(href='#' rel='cosmo') Cosmo
li
a.change-style-menu-item(href='#' rel='cyborg') Cyborg
li
a.change-style-menu-item(href='#' rel='darkly') Darkly
li
a.change-style-menu-item(href='#' rel='flatly') Flatly
li
a.change-style-menu-item(href='#' rel='journal') Journal
li
a.change-style-menu-item(href='#' rel='lumen') Lumen
li
a.change-style-menu-item(href='#' rel='paper') Paper
li
a.change-style-menu-item(href='#' rel='readable') Readable
li
a.change-style-menu-item(href='#' rel='sandstone') Sandstone
li
a.change-style-menu-item(href='#' rel='simplex') Simplex
li
a.change-style-menu-item(href='#' rel='slate') Slate
li
a.change-style-menu-item(href='#' rel='spacelab') Spacelab
li
a.change-style-menu-item(href='#' rel='superhero') Superhero
li
a.change-style-menu-item(href='#' rel='united') United
li
a.change-style-menu-item(href='#' rel='yeti') Yeti
.row(style="position:absolute; bottom:0; width:100%")
.col-xs-2.col-md-2
img(src="img/logo.png" width="30" height="30")
.col-xs-5.col-md-5
p
a(href='http://www.projetsdiy.fr') Version francaise : www.projetsdiy.fr
.col-xs-5.col-md-5
p
a(href='http://www.diyprojects.io') English version : www.diyprojects.io
//script(src='js/script.js')
script().
var Timer_UdpateMesures;
$('a[data-toggle=\"tab\"]').on('shown.bs.tab', function (e) {
//On supprime tous les timers lorsqu'on change d'onglet
clearTimeout(Timer_UdpateMesures);
var target = $(e.target).attr("href")
console.log('activated ' + target );
// IE10, Firefox, Chrome, etc.
if (history.pushState)
window.history.pushState(null, null, target);
else
window.location.hash = target;
if (target=='#tab_mesures') {
$('#table_mesures').bootstrapTable('refresh',{silent:true, url:'/tabmesures.json'});
}
});
// Créé un timer qui actualise les données régulièrement - Create a timer than update data every n secondes
$('#tab_mesures').on('load-success.bs.table',function (e,data){
console.log("tab_mesures loaded");
if ($('.nav-tabs .active > a').attr('href')=='#tab_mesures') {
Timer_UdpateMesures=setTimeout(function(){
$('#table_mesures').bootstrapTable('refresh',{silent: true, showLoading: false, url: '/tabmesures.json'});
updateMesures();
},5000);
}
});
function updateMesures(){
$.getJSON('/mesures.json', function(data){
//console.log("Mesures envoyees : " + JSON.stringify(data) + "|" + data.t + "|" + data.h + "|" + data.pa) ;
$('#temperature').html(data.t);
$('#humidite').html(data.h);
$('#pa').html(data.pa);
}).fail(function(err){
console.log("err getJSON mesures.json "+JSON.stringify(err));
});
};
function labelFormatter(value, row){
//console.log("labelFormatter");
//console.log(value);
console.log(row);
var label = "";
if ( value === "Température" ) {
label = value + "<span class='glyphicon " + row.glyph + " pull-left'></span>";
} else if ( value === "Humidité" ) {
label = value + "<span class='glyphicon " + row.glyph + " pull-left'></span>";
} else if ( value === "Pression Atmosphérique" ) {
label = value + "<span class='glyphicon " + row.glyph + " pull-left'></span>";
} else {
label = value;
}
return label;
}
function valueFormatter(value, row){
//console.log("valueFormatter");
var label = "";
if ( row.valeur > row.precedente ) {
label = value + row.unite + "<span class='glyphicon glyphicon-chevron-up pull-right'></span>";
} else {
label = value + row.unite + "<span class='glyphicon glyphicon-chevron-down pull-right'></span>";
}
return label;
}
function vpFormatter(value, row){
//console.log("valueFormatter");
var label = "";
if ( row.valeur > row.precedente ) {
label = value + row.unite
} else {
label = value + row.unite
}
return label;
}
// Commandes sur le GPIO - GPIO change
$('#D5_On').click(function(){ setBouton('D5','1'); });
$('#D5_Off').click(function(){ setBouton('D5','0'); });
$('#D6_On').click(function(){ setBouton('D6','1'); });
$('#D6_Off').click(function(){ setBouton('D6','0'); });
$('#D7_On').click(function(){ setBouton('D7','1'); });
$('#D7_Off').click(function(){ setBouton('D7','0'); });
$('#D8_On').click(function(){ setBouton('D8','1'); });
$('#D8_Off').click(function(){ setBouton('D8','0'); });
function setBouton(id, etat){
$.post("gpio?id=" + id + "&etat=" + etat).done(function(data){
//console.log("Retour setBouton " + JSON.stringify(data));
var id_gpio = "#" + id + "_etat";
//console.log(id_gpio);
if ( data.success === "1" ) {
if ( data.etat === "1" ) {
$(id_gpio).html("ON");
} else {
$(id_gpio).html("OFF");
}
} else {
$(id_gpio).html('!');
}
}).fail(function(err){
console.log("err setButton " + JSON.stringify(err));
});
}
// Changement du thème - Change current theme
// Adapté de - Adapted from : https://wdtz.org/bootswatch-theme-selector.html
var supports_storage = supports_html5_storage();
if (supports_storage) {
var theme = localStorage.theme;
console.log("Recharge le theme " + theme);
if (theme) {
set_theme(get_themeUrl(theme));
}
}
// Un nouveau thème est sélectionné - New theme selected
jQuery(function($){
$('body').on('click', '.change-style-menu-item', function() {
var theme_name = $(this).attr('rel');
console.log("Changement de theme " + theme_name);
var theme_url = get_themeUrl(theme_name);
console.log("URL theme : " + theme_url);
set_theme(theme_url);
});
});
// Recupere l'adresse du theme - Get theme URL
function get_themeUrl(theme_name){
$('#labelTheme').html("Thème : " + theme_name);
var url_theme = "";
if ( theme_name === "bootstrap" ) {
url_theme = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css";
} else {
url_theme = "https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/" + theme_name + "/bootstrap.min.css";
}
if (supports_storage) {
// Enregistre le theme sélectionné en local - save into the local database the selected theme
localStorage.theme = theme_name;
}
return url_theme;
}
// Applique le thème - Apply theme
function set_theme(theme_url) {
$('link[title="main"]').attr('href', theme_url);
}
// Stockage local disponible ? - local storage available ?
function supports_html5_storage(){
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
}
Code HTML généré
Le fichier HTML généré qu’il vous suffit de téléverser sur la zone SPIFFS de l’ESP8266
<!DOCTYPE html>
<html charset="UTF-8">
<head>
<meta name="viewport">
<!--script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js')-->
<script src="js/jquery.min.js"></script>
<!--script(src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js')-->
<script src="js/bootstrap.min.js"></script>
<!--script(src="http://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.11.0/bootstrap-table.min.js")-->
<script src="js/bootstrap-table.min.js"></script>
<!--link(rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.11.0/bootstrap-table.min.css")-->
<link rel="stylesheet" href="css/bootstrap-table.min.css">
<link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/superhero/bootstrap.min.css" rel="stylesheet" title="main">
<title>Demo ESP8266 SPIFFS + Boostrap - www.projetsdiy.fr</title>
</head>
<body>
<div class="container-fluid">
<h1>Demo Webserver ESP8266 + Bootstrap</h1>
<ul class="nav nav-tabs" id="tab">
<li class="active"><a href="#tab_mesures" data-toggle="tab">Mesures</a></li>
<li><a href="#tab_graphs" data-toggle="tab">Graphiques</a></li>
<li><a href="#tab_gpio" data-toggle="tab">GPIO </a></li>
<li><a href="#tab_configuration" data-toggle="tab">Configuration</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane fade in active" id="tab_mesures">
<h2>Mini station météo (DHT22 + BMP180)</h2>
<ul class="nav nav-pills">
<li class="active"><a href="#">
<div class="span badge pull-right" id="temperature">-</div> Température</a></li>
<li><a href="#">
<div class="span badge pull-right" id="humidite">-</div> Humidité</a></li>
<li><a href="#">
<div class="span badge pull-right" id="pa">-</div> Pression atmosphérique</a></li>
</ul><br>
<table id="table_mesures" data-toggle="table" data-show-colunns="true">
<thead>
<tr>
<th data-field="mesure" data-align="left" data-sortable="true" data-formatter="labelFormatter">Mesure</th>
<th data-field="valeur" data-align="left" data-sortable="true" data-formatter="valueFormatter">Valeur</th>
<th data-field="precedente" data-align="left" data-sortable="true" data-formatter="vpFormatter">Valeur Précédente</th>
</tr>
</thead>
</table>
</div>
<div class="tab-pane fade" id="tab_graphs">
<h2>Graphs</h2>
</div>
<div class="tab-pane fade" id="tab_gpio">
<h2>GPIO</h2>
<div class="row">
<div class="col-xs-6 col-md-4">
<h4 class="text-left">D5
<div class="span badge" id="D5_etat">OFF</div>
</h4>
</div>
<div class="col-xs-6 col-md-4">
<div class="button btn btn-success btn-lg" id="D5_On" type="button">ON</div>
</div>
<div class="col-xs-6 col-md-4">
<div class="button btn btn-danger btn-lg" id="D5_Off" type="button">OFF</div>
</div>
<div class="col-xs-6 col-md-4">
<h4 class="text-left">D6
<div class="span badge" id="D6_etat">OFF</div>
</h4>
</div>
<div class="col-xs-6 col-md-4">
<div class="button btn btn-success btn-lg" id="D6_On" type="button">ON</div>
</div>
<div class="col-xs-6 col-md-4">
<div class="button btn btn-danger btn-lg" id="D6_Off" type="button">OFF</div>
</div>
<div class="col-xs-6 col-md-4">
<h4 class="text-left">D7
<div class="span badge" id="D7_etat">OFF</div>
</h4>
</div>
<div class="col-xs-6 col-md-4">
<div class="button btn btn-success btn-lg" id="D7_On" type="button">ON</div>
</div>
<div class="col-xs-6 col-md-4">
<div class="button btn btn-danger btn-lg" id="D7_Off" type="button">OFF</div>
</div>
<div class="col-xs-6 col-md-4">
<h4 class="text-left">D8
<div class="span badge" id="D8_etat">OFF</div>
</h4>
</div>
<div class="col-xs-6 col-md-4">
<div class="button btn btn-success btn-lg" id="D8_On" type="button">ON</div>
</div>
<div class="col-xs-6 col-md-4">
<div class="button btn btn-danger btn-lg" id="D8_Off" type="button">OFF</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="tab_configuration">
<h2>Configuration </h2>
<div class="btn-group">
<button class="btn btn-default" id="labelTheme">Theme</button>
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a class="change-style-menu-item" href="#" rel="bootstrap">Boostrap</a></li>
<li><a class="change-style-menu-item" href="#" rel="cerulean">Cerulean</a></li>
<li><a class="change-style-menu-item" href="#" rel="cosmo">Cosmo</a></li>
<li><a class="change-style-menu-item" href="#" rel="cyborg">Cyborg</a></li>
<li><a class="change-style-menu-item" href="#" rel="darkly">Darkly</a></li>
<li><a class="change-style-menu-item" href="#" rel="flatly">Flatly</a></li>
<li><a class="change-style-menu-item" href="#" rel="journal">Journal</a></li>
<li><a class="change-style-menu-item" href="#" rel="lumen">Lumen</a></li>
<li><a class="change-style-menu-item" href="#" rel="paper">Paper</a></li>
<li><a class="change-style-menu-item" href="#" rel="readable">Readable</a></li>
<li><a class="change-style-menu-item" href="#" rel="sandstone">Sandstone</a></li>
<li><a class="change-style-menu-item" href="#" rel="simplex">Simplex</a></li>
<li><a class="change-style-menu-item" href="#" rel="slate">Slate</a></li>
<li><a class="change-style-menu-item" href="#" rel="spacelab">Spacelab</a></li>
<li><a class="change-style-menu-item" href="#" rel="superhero">Superhero</a></li>
<li><a class="change-style-menu-item" href="#" rel="united">United</a></li>
<li><a class="change-style-menu-item" href="#" rel="yeti">Yeti </a></li>
</ul>
</div>
</div>
</div>
<div class="row" style="position:absolute; bottom:0; width:100%">
<div class="col-xs-2 col-md-2"><img src="img/logo.png" width="30" height="30"></div>
<div class="col-xs-5 col-md-5">
<p><a href="http://www.projetsdiy.fr">Version francaise : www.projetsdiy.fr</a></p>
</div>
<div class="col-xs-5 col-md-5">
<p><a href="http://www.diyprojects.io">English version : www.diyprojects.io</a></p>
</div>
</div>
</div>
<!--script(src='js/script.js')-->
<script>
var Timer_UdpateMesures;
$('a[data-toggle=\"tab\"]').on('shown.bs.tab', function (e) {
//On supprime tous les timers lorsqu'on change d'onglet
clearTimeout(Timer_UdpateMesures);
var target = $(e.target).attr("href")
console.log('activated ' + target );
// IE10, Firefox, Chrome, etc.
if (history.pushState)
window.history.pushState(null, null, target);
else
window.location.hash = target;
if (target=='#tab_mesures') {
$('#table_mesures').bootstrapTable('refresh',{silent:true, url:'/tabmesures.json'});
}
});
// Créé un timer qui actualise les données régulièrement - Create a timer than update data every n secondes
$('#tab_mesures').on('load-success.bs.table',function (e,data){
console.log("tab_mesures loaded");
if ($('.nav-tabs .active > a').attr('href')=='#tab_mesures') {
Timer_UdpateMesures=setTimeout(function(){
$('#table_mesures').bootstrapTable('refresh',{silent: true, showLoading: false, url: '/tabmesures.json'});
updateMesures();
},5000);
}
});
function updateMesures(){
$.getJSON('/mesures.json', function(data){
//console.log("Mesures envoyees : " + JSON.stringify(data) + "|" + data.t + "|" + data.h + "|" + data.pa) ;
$('#temperature').html(data.t);
$('#humidite').html(data.h);
$('#pa').html(data.pa);
}).fail(function(err){
console.log("err getJSON mesures.json "+JSON.stringify(err));
});
};
function labelFormatter(value, row){
//console.log("labelFormatter");
//console.log(value);
console.log(row);
var label = "";
if ( value === "Température" ) {
label = value + "<span class='glyphicon " + row.glyph + " pull-left'></span>";
} else if ( value === "Humidité" ) {
label = value + "<span class='glyphicon " + row.glyph + " pull-left'></span>";
} else if ( value === "Pression Atmosphérique" ) {
label = value + "<span class='glyphicon " + row.glyph + " pull-left'></span>";
} else {
label = value;
}
return label;
}
function valueFormatter(value, row){
//console.log("valueFormatter");
var label = "";
if ( row.valeur > row.precedente ) {
label = value + row.unite + "<span class='glyphicon glyphicon-chevron-up pull-right'></span>";
} else {
label = value + row.unite + "<span class='glyphicon glyphicon-chevron-down pull-right'></span>";
}
return label;
}
function vpFormatter(value, row){
//console.log("valueFormatter");
var label = "";
if ( row.valeur > row.precedente ) {
label = value + row.unite
} else {
label = value + row.unite
}
return label;
}
// Commandes sur le GPIO - GPIO change
$('#D5_On').click(function(){ setBouton('D5','1'); });
$('#D5_Off').click(function(){ setBouton('D5','0'); });
$('#D6_On').click(function(){ setBouton('D6','1'); });
$('#D6_Off').click(function(){ setBouton('D6','0'); });
$('#D7_On').click(function(){ setBouton('D7','1'); });
$('#D7_Off').click(function(){ setBouton('D7','0'); });
$('#D8_On').click(function(){ setBouton('D8','1'); });
$('#D8_Off').click(function(){ setBouton('D8','0'); });
function setBouton(id, etat){
$.post("gpio?id=" + id + "&etat=" + etat).done(function(data){
//console.log("Retour setBouton " + JSON.stringify(data));
var id_gpio = "#" + id + "_etat";
//console.log(id_gpio);
if ( data.success === "1" ) {
if ( data.etat === "1" ) {
$(id_gpio).html("ON");
} else {
$(id_gpio).html("OFF");
}
} else {
$(id_gpio).html('!');
}
}).fail(function(err){
console.log("err setButton " + JSON.stringify(err));
});
}
// Changement du thème - Change current theme
// Adapté de - Adapted from : https://wdtz.org/bootswatch-theme-selector.html
var supports_storage = supports_html5_storage();
if (supports_storage) {
var theme = localStorage.theme;
console.log("Recharge le theme " + theme);
if (theme) {
set_theme(get_themeUrl(theme));
}
}
// Un nouveau thème est sélectionné - New theme selected
jQuery(function($){
$('body').on('click', '.change-style-menu-item', function() {
var theme_name = $(this).attr('rel');
console.log("Changement de theme " + theme_name);
var theme_url = get_themeUrl(theme_name);
console.log("URL theme : " + theme_url);
set_theme(theme_url);
});
});
// Recupere l'adresse du theme - Get theme URL
function get_themeUrl(theme_name){
$('#labelTheme').html("Thème : " + theme_name);
var url_theme = "";
if ( theme_name === "bootstrap" ) {
url_theme = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css";
} else {
url_theme = "https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/" + theme_name + "/bootstrap.min.css";
}
if (supports_storage) {
// Enregistre le theme sélectionné en local - save into the local database the selected theme
localStorage.theme = theme_name;
}
return url_theme;
}
// Applique le thème - Apply theme
function set_theme(theme_url) {
$('link[title="main"]').attr('href', theme_url);
}
// Stockage local disponible ? - local storage available ?
function supports_html5_storage(){
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
}
</script>
</body>
</html>
Code Arduino compatible ESP8266
Le code Arduino qu’il vous suffit de téléverser après avoir modifié les paramètres de connexion au réseau Wi-Fi.
#include <ESP8266WebServer.h>
#include <DHT.h>
#include <Adafruit_BMP085.h>
#include <FS.h>
#define ssid "xxxx" // WiFi SSID
#define password "xxxx" // 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 pa = 0;
int sizeHist = 100; // Nombre de points dans l'historique - History size
// Création des objets / create Objects
DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP085 bmp;
ESP8266WebServer server ( 80 );
void updateGpio(){
String gpio = server.arg("id");
String etat = server.arg("etat");
String success = "1";
int pin = D5;
if ( gpio == "D5" ) {
pin = D5;
} else if ( gpio == "D7" ) {
pin = D7;
} else if ( gpio == "D8" ) {
pin = D8;
} else {
pin = D5;
}
Serial.println(pin);
if ( etat == "1" ) {
digitalWrite(pin, HIGH);
} else if ( etat == "0" ) {
digitalWrite(pin, LOW);
} else {
success = "1";
Serial.println("Err Led Value");
}
String json = "{\"gpio\":\"" + String(gpio) + "\",";
json += "\"etat\":\"" + String(etat) + "\",";
json += "\"success\":\"" + String(success) + "\"}";
server.send(200, "application/json", json);
Serial.println("GPIO mis a jour");
}
void sendMesures() {
String json = "{\"t\":\"" + String(t) + "\",";
json += "\"h\":\"" + String(h) + "\",";
json += "\"pa\":\"" + String(pa) + "\"}";
server.send(200, "application/json", json);
Serial.println("Mesures envoyees");
}
void sendTabMesures() {
double temp = 0; // Récupère la plus ancienne mesure (temperature) - get oldest record (temperature)
String json = "[";
json += "{\"mesure\":\"Température\",\"valeur\":\"" + String(t) + "\",\"unite\":\"°C\",\"glyph\":\"glyphicon-indent-left\",\"precedente\":\"" + String(temp) + "\"},";
temp = 0; // Récupère la plus ancienne mesure (humidite) - get oldest record (humidity)
json += "{\"mesure\":\"Humidité\",\"valeur\":\"" + String(h) + "\",\"unite\":\"%\",\"glyph\":\"glyphicon-tint\",\"precedente\":\"" + String(temp) + "\"},";
temp = 0; // Récupère la plus ancienne mesure (pression atmospherique) - get oldest record (Atmospheric Pressure)
json += "{\"mesure\":\"Pression Atmosphérique\",\"valeur\":\"" + String(pa) + "\",\"unite\":\"mbar\",\"glyph\":\"glyphicon-dashboard\",\"precedente\":\"" + String(temp) + "\"}";
json += "]";
server.send(200, "application/json", json);
Serial.println("Tableau mesures envoyees");
}
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() );
if (!SPIFFS.begin())
{
// Serious problem
Serial.println("SPIFFS Mount failed");
} else {
Serial.println("SPIFFS Mount succesfull");
}
server.on("/tabmesures.json", sendTabMesures);
server.on("/mesures.json", sendMesures);
server.on("/gpio", updateGpio);
/*HTTP_POST, []() {
updateGpio();
});
*/
server.serveStatic("/js", SPIFFS, "/js");
server.serveStatic("/css", SPIFFS, "/css");
server.serveStatic("/img", SPIFFS, "/img");
server.serveStatic("/", SPIFFS, "/index.html");
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();
pa = bmp.readPressure() / 100.0F;
delay(100);
}
Interface WEB obtenue
Depuis un navigateur internet, saisissez l’adresse IP de l’ESP8266.
Vous avez maintenant des mesures qui s’affichent
ainsi qu’une interaction complète avec le GPIO. Le libellé (On / Off) est mis à jour une fois que l’ESP8266 a exécuté la commande.
Dans le prochain tutoriel, nous verrons comment enregistrer les mesures pour créer un historique et créer des graphiques et des gauges.
Accéder rapidement aux autres parties du projet
Voici les liens pour accéder aux autres parties du projet
Vous êtes ici
- Projet de station météo avec interface HTML sur ESP8266 (DHT22 + BMP180)
- Projet station météo ESP8266 (Partie 3). Récupérer l’heure avec NTPClient et stockage SPIFFS
- ESP8266. Comment utiliser la librairie WiFiManager pour gérer les connexions WiFi
- Piloter le GPIO de l’ESP8266 (Serveur Web) depuis Jeedom en TCP/IP sans fil – Partie 2
- Piloter le GPIO de l’ESP8266 (2) depuis Domoticz en TCP/IP sans fil. Serveur Web et requête HTTP
Bonjour tu indiques
“Par contre si vous ne souhaitez pas que l’ESP8266 puisse accéder à internet et y accéder en mode AP (point d’accès), il est nécessaire de tout embarquer dans la zone SPIFFS.”
Mais pourquoi l’ESP aurait besoin d’avoir accès à internet ?
Par contre je suis d’accord avec toi pour la conclusion du mode AP et de la zone SPIFF si le client n’a pas d’autres interfaces réseaux
Et merci pour ce tuto !
Bonjour François. C’est surtout dans le cas ou l’on développe une interface graphique qui nécessite du CSS (Bootstrap par exemple). C’est un gain de temps et surtout fonctionne sur le réseau local même s’il n’y a pas de connexion internet (ça peut arriver)
Bonjour,
D’abord je voulais vous féliciter car je trouve que vos tutos sont excellent!!!
J’ai fais un condensé de votre code afin de commander uniquement une gpio avec une interface html ultra simple (2 boutons on/off.). Le but est de l’améliorer au fur et à mesure de mes compétences.
Maintenant que j’ai posé le décore voici mon problème. Lorsque que je clique sur l’un de mes boutons on ou off le code fonctionne parfaitement le lien entre le code html et arduino s’exécute sans problème, ma led s’allume et s’éteint c’est parfait !! Néanmoins lorsque je fais inspecter sur ma page, une erreur jquery liée à l’envoi de la requête POST intervient est retourne ceci => POST http://192.168.1.13/gpio?id=D5&etat=0 net::ERR_EMPTY_RESPONSE cette erreur n’intervient uniquement quand le code html est stocké dans la mémoire SPIFFS sinon lorsque le html est dans le code arduino je n’ai aucun problème.
Avez-vous une solution à me proposer ?
Merci d’avance.
Bon j’ai trouvé ma réponse tout seul à force de relecture. 😉
Bonjour Buhan et merci beaucoup. C’est super encourageant :D. Désolé pour le retard, j’ai pris quelques jours de congé. Peux tu nous expliquer ce qui posait problème ?
Bonjour,
C’est très simple j’avais juste oublié un “server.send ” dans la fonction de mise à jour des GPIO. Du coup il est normal d’avoir une erreur ->”ERR_EMPTY_RESPONSE”.
Sinon Je m’intéresse aussi à la librairie Wifi Manager et me je me demandais si il est possible de traduire l’interface ? Merci d’avance.
Merci pour le retour Julien. Oui, il est possible de traduire l’interface de la librairie WiFi Manager. Le problème, c’est qu’à chaque mise à jour il faudra tout refaire. Les librairies sont installées dans le dossier (mes) Documents/Arduino/libraries/wifimanager. A mon avis il faut traduire les constantes HTTP_XXXX au début du fichier WiFiManager.h et les chaines dans le fichier WiFiManager.cpp. Je pense qu’il n’est pas nécessaire de traduire le fichier WiFimanager.template.html qui se trouve dans le dossier extras. Bon courage, dis nous ce que ça donne. Bon week end
En effet, excellent tuto.
J’utilise à fond le Spiffs pour la gestion de mes pages et logos.
J’utilise aussi l’Upgrade OTA pour effectuer la mise à jour de mon code.
Par contre quand je modifie aussi certains fichiers (html ou logos) sur mon serveur externe de mise à jour, je souhaiterais pouvoir les upgrader à leurs tours sur l’ESP.
Pensez vous qu’il soit possible d’uploader un fichier binaire (.png par ex.) en provenance d’un serveur externe, sur l’ESP pour le transférer dans le Spiffs ensuite ?
On devrait pourvoir faire quelque chose en FTP. Le transfert vers le SPIFFS étant très long, j’ai regardé pour transférer uniquement le fichier HTML modifié. Il y a un petit projet sur GitHub (https://github.com/nailbuster). Le tuto sera publié vendredi. Le seul problème actuellement, c’est que l’arborescence n’est pas gérée. Dites moi ce que vous en pensé.
Merci pour l’info. Je vais regarder cela dès que j’aurais un peu de temps.
Le lien retourne une erreur. Si l’on parle bien du fsUploadFile je le remet : https://github.com/nailbuster/myWebServer
Oui c’est ça
Quel travail !!!! Merci de nous faire partager ton savoir et de manière aussi pédagogique.
L’ESP8266 est vraiment puissant car il se suffit à lui même en utilisant ce type de tuto et il est vraiment pas cher.
On peut imaginer en acheter un pour chaque type de fonction et cela coutera seulement 4 euros….
– Par contre je me suis perdu en chemin par rapport au code html : ou est il passé ? est il d’ans un appel d’un fichier ?
– Que faut il televerser dans l’ESP8266 ? seulement le sketch arduino ? d’autres librairies mais comment ?
je te remercie.
Bonjour Gandolfi. Oui, le rapport possibilités/performances/prix est imbattable.
– Le code HTML est a dnc été ‘sorti’ du code Arduino. C’est un fichier HTML classique comme pour un site internet. On le place avec les autres fichiers (style CSS, code javascript, images…) dans un dossier data qu’il faut télécharger manuellement dans une zone mémoire spéciale du l’Arduino/ESP8266, appelée SPIFFS. C’est le 1er tuto de la série : http://www.projetsdiy.fr/esp8266-web-server-partie1-stockage-spiffs-interface-web/
– Idem, tout est expliqué dans le 1er tuto.
A la fin de la série il y aura un article de synthèse pour que tout le monde s’y retrouve
Effectivement le tuto et très intéressant et fourni, dommage qu’il n’y ai pas de liens de navigation entre les différentes parties..
Bonjour Nonozor. Absolument, je viens de les ajouter au début de chaque article.
Merci. j’étais passé à coté de cette info très importante.Effectivement le tuto indiqué est très technique.
– Cela ouvre beaucoup de possibilité car cela permet de créer une page web et d’ensuite pouvoir programmer à la chaine des objets indépendants d’un serveur ou autres protocoles (mqtt, node,red…)
– Tu penses que sur la nouvelle version du wemos ou une autre puce bon marché, on pourra créer un serveur dynamique php ou apparenté ?
bonne soirée
Oui c’est assez fantastique et en plus le serveur Web est disponible en quelques secondes (par rapport à un ordinateur ou un mini-pc).
Oui, ce sont bien des pages dynamiques et les données sont actualisées à intervalle régulier.
IMPRESSIONNANT !!!
BRAVO !!
tu pousse les possibilités de l’esp8266 dans ces derniers retranchements et j’en apprend plus a chacun de tes tutos !
merci !!!
Bonjour Visvic. Merci beaucoup. Oui, je suis aussi un fan de l’ESP8266. Il y a encore plein de choses à découvrir…