Le ESP8266 est un microcontrôleur doté d'un module de communication WIFI. Il est fabriqué par le constructeur chinois ESPRESSIF. Il est commercialisé sous formes de différents modules ou cartes plus au moins sophistiqués.
Les caractéristiques principales du processeur ESP8266 sont :
J'ai utilisé la carte Wemos D1 mini (D1 R1) pour réaliser ce tuto
La carte NodeMCU 1.0 (ESP-12E) est quasiment identique
Les deux cartes se caractérisent par :
Beaucoup de gens parlent du module ESP-01
Je vais tout de même lui consacrer un petit paragraphe pour les gens qui veulent l'utiliser.
On peut utiliser l'environnement de programmation ARDUINO-IDE mais il faut le configurer pour supporter les modules ESP8266,
Cela dépend des modules. Mais quelque soit le module il faut savoir qu'il supporte deux mode de fonctionnement:
Dans la suite de ce tutoriel, je vais utiliser le module Wemos D1mini. Ce module possède un port USB, il suffit de le brancher au PC avec un câble de chargeur téléphonique. Le basculement entre mode PROG et RUN est réalisé automatiquement par l'IDE-ARDUINO. Quand on téléverse un programme dans le module, l'IDE place le module en mode PROG et à la fin du téléversement, le module est replacé en mode RUN. Il arrive des fois que le programme ne démarre pas à la fin du téléversement, il suffit de cliquer sur le bouton RESET du module pour le placer en mode RUN.
Si vous utiliser un module qui n'as pas de connecteur USB comme l'ESP-01, voir le paragraphe réservé à ce module pour savoir comment le brancher. Pour basculer d'un mode à l'autre il faut utiliser un branchement avec un ou deux boutons poussoir.
Pour faire le premier essai, rien de mieux que de faire clignoter la LED présente sur le module. la LED est connectée sur la pate 2 du ESP8266 qui est elle même connectée à la broche D4 du module D1mini. Le mieux est d'utiliser la constante prédéfinie LED_BUILTIN qui s'adapte au module utilisé
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
delay(500);
}
Quand on utilise ce genre de module, c'est pour faire de la commande et de l'acquisition de données. Nous avons vu avec l'exemple de la LED que l'on peut commander une E/S numérique exactement comme sur un Arduino. Rappelons que le module Wemos D1 possède 9 E/S numérique (D0 ...D8) et une entrée analogique A0.
Dans ce paragraphe, on va réaliser une acquisition de données à partir d'un capteur doté d'une interface I2C. Pour des raisons de disponibilité, j'ai choisi le LM75. Le bus I2C est un standard et ce qui suit peut être adapté à n'importe quel autre capteur avec bus I2C. Pour avoir un peu plus d'information sur ce sujet, je vous conseille de jeter un coup d'œil ICI
Le bus I2C adresse les circuits connectés sur le bus à l'aide d'une adresse 7 bits. Le LM75 possède 3 entrée A2 A1 A0 qui permette à l'utilisateur de fixer l'adresse à laquelle le circuit va répondre, les 4 autres bits sont fixés en interne à 1001.
Pour ma part, j'ai soudé les 3 entrés A2 A1 A0 à la masse ce qui me donne l'adresse 1001000 = 0x48
/* Programme esp_lm75.ino A. Oumnad
lecture de température sur un LM75A (température sur 11 bits)
les 3 bits d'adresse I2C du circuit sont fixé à 000 => adresse = 1001 000 = 0x48
Le pointeur est fixé une seule fois dans le main()
la fonction loop() lit le registre de température toutes les secondes sans préciser le pointeur à chaque fois
*/
#include <Wire.h>
int8_t msb, lsb;
float T ;
#define DEV_ADR 0x48
void setup()
{
Serial.begin(9600); // initialiser l'interface série pour l'affichage
Wire.begin(); // initialiser l'interface I2C
Wire.beginTransmission(DEV_ADR);
Wire.write(0); // pointeur sur registre de température
Wire.endTransmission();
}
void loop()
{
Wire.requestFrom(DEV_ADR, 2); // demander 2 octets à partir de la position courante du pointeur
msb = Wire.read();
lsb = Wire.read();
T = msb + (lsb >> 5)*0.125;
Serial.println("Température = " + String(T,3));
delay(1000);
}
// Programme connect_IP_auto.ino A. Oumnad
//===========================================================================
#include <ESP8266WiFi.h>
// Modifier selon votre point d'accès WIFI
const char* mySSID = "votre SSID";
const char* mypassword = "votre mot de passe";
void setup() {
Serial.begin(115200); // pour le moniteur série
Serial.println("\r\n\r\n=====================================================");
// Connexion au point d'accès
Serial.print(" Connexion à ");
Serial.println(mySSID);
WiFi.disconnect();
WiFi.mode(WIFI_STA);
WiFi.begin(mySSID, mypassword);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print(" Connecté à ");
Serial.println(mySSID);
// Afficher l'adresse IP affecté par le point d'accès
Serial.print(" L'adresse IP du ESP8266 est: ");
Serial.println(WiFi.localIP());
Serial.println("==========================================================");
}
void loop() {
}
On va se connecter à un point d'accès avec l'adresse IP fixe 192.168.0.50
Pour choisir l'adresse IP, il faut connaître l'adresse passerelle par défaut de votre point d'accès et la plage d'affectation automatique. Pour le savoir, assurez vous que votre PC est connecté à ce point d'accès et taper ipconfig dans la fenêtre de commande Windows
Dans mon cas, le point d'accès a affecté l'adresse 192.168.0.104 à mon PC. On comprend que le DHCP commence l'affectation à partir de 192.168.0.100. J'ai décidé d'utiliser l'adresse fixe 192.168.0.50 pour l'ESP8266. Comme ça je suis sur que le DHCP de mon point d'accès ne l'affecte à personne.
// Programme connect_IP_fixe.ino A. Oumnad
//===========================================================================
#include <ESP8266WiFi.h>
// Modifier selon votre point d'accès WIFI
const char* mySSID = "votre SSID";
const char* mypassword = "votre mot de passe";
//Paramètres IP de la connexion
IPAddress IP(192, 168, 0, 50); //adresse fixe
IPAddress gateway(192, 168, 0, 1); //passerelle par défaut
IPAddress subnet(255, 255, 255, 0); //masque de sous réseau
IPAddress dns(8, 8, 8, 8); //DNS
void setup(void){
//=========================== Initialiser le Moniteur Série ========================
Serial.begin(115200);
Serial.println("\r\n\r\n============================================================\r\n");
//Connexion à un point d'accès
Serial.print(" Connexion à ");
Serial.println(mySSID);
WiFi.disconnect();
WiFi.config(IP,gateway,subnet,dns);
WiFi.begin(mySSID, mypassword);
WiFi.mode(WIFI_STA); //mode station
// attendre l'établissement de la connexion
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("\r\nConnecté à ");
Serial.println(mySSID);
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP());
}
void loop() {
}
Nous allons définir plusieurs points d'accès. Le module se connecte à celui qui a le signal le plus fort
// Programme connect_AP_multi.ino A. Oumnad
//=====================================================================
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti MywifiMulti; // instance de la classe ESP8266WiFiMulti
void setup() {
Serial.begin(115200);
while(!Serial);
Serial.println("\r\n===================================================");
MywifiMulti.addAP("SSID 1", "mot de passe 1");
MywifiMulti.addAP("SSID 2", "mot de passe 2");
MywifiMulti.addAP("SSID 3", "mot de passe 3");
// ......
Serial.println("Connecting ...");
while (MywifiMulti.run() != WL_CONNECTED) {
delay(1000);
Serial.print('.');
}
Serial.print("\r\nConnected to ");
Serial.println(WiFi.SSID());
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop() { }
Nous y voila, dans ce paragraphe, nous allons utiliser le ESP8266 comme un Objet Connecté. Il sera configuré comme serveur et nous allons communiquer avec lui à partir d'une page web client sur un PC ou un smartphone.
Dans cette première version, nous allons utiliser la librairie ESP8266WiFi. Son utilisation est un peut ardue, mais ça permet de comprendre comment se font les échanges de requêtes HTTP entre le serveur et le navigateur Client
Pour commencer nous allons faire simple. La page web client sera constituée de deux boutons ALLUMER et ETEINDRE qui nous permettront d'allumer ou d'éteindre la LED du module ESP8266.
Le code html de la page web sera intégré dans le programme du ESP8266 (serveur). J'ai utilisé une page Web très basique. Si vous êtes à l'aise avec HTML/CSS vous pouvez la modifier à votre guise.
Quand quelqu'un (un client) se connecte au serveur à l'aide d'un navigateur, le serveur lui enverra la page, ce qui donne quelque chose comme ça:
// Programme espLED_server.ino A. Oumnad
//===========================================================================
#include <ESP8266WiFi.h>
static const String pageweb = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width">
</head>
<body>
<div style="text-align: center;">
<br><br>
<a href="LEDON"><button>ALLUMER LED</button></a>
<br><br><br>
<a href="LEDOFF"><button>ETEINDRE LED</button></a>
</div>
</body>
</html>
)=====";
// Modifier selon votre point d'accès WIFI
const char* SSID = "votre SSID";
const char* password = "votre mot de passe";
WiFiServer MonServeur(80); // créer le serveur web sur le port 80
WiFiClient MonClient; // créer le client
#define LED_PIN LED_BUILTIN
void setup() {
Serial.begin(9600); // pour le moniteur série
delay(10);
Serial.println();
Serial.println();
Serial.println("=====================================================");
Serial.println(" Test LED ON/OFF sur ESP8266");
// Sur le WemosD1, la LED est branché en pullup, il faut la commander avec une logique inversée
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH); // éteindre la LED au départ
// Connexion au point d'accès
WiFi.disconnect();
WiFi.mode(WIFI_STA);
Serial.print(" Connexion à ");
Serial.println(SSID);
WiFi.begin(SSID, password);
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print(" Connecté à ");
Serial.println(SSID);
// Afficher l'adresse IP affecté par le point d'accès
Serial.print(" L'adresse IP du ESP8266 est: ");
Serial.println(WiFi.localIP());
Serial.println("==========================================================");
// Démarrer le serveur web
MonServeur.begin();
Serial.println(" Serveur Web démarré");
}
void loop() {
// Si aucune page Web client ne se connecte, on ne fait rien
MonClient = MonServeur.available();
if (!MonClient) {
return;
}
delay(100);
// Lire la première ligne de la requête
if(MonClient.available() == 0)return;
String request = MonClient.readStringUntil('\r');
// un client s'est connecté
Serial.println(" Client connecté");
Serial.print(" Requête---->"); // on affiche la requête reçue du client
Serial.print(request);
Serial.println("<----");
// Traiter la requête
if(request.indexOf("/ ") != -1){ // requête "/" on affiche la page Web sur le client
envoyer_pageweb();
}
else if (request.indexOf("/LEDON") != -1){ // requête "/LEDON"
digitalWrite(LED_PIN, LOW); // allumer la LED (active LOW)
Serial.println(" LED allumée"); // notification sur le moniteur série
envoyer_reponsevide(); // ce n'est pas la peine de rafraîchir la page
}
else if (request.indexOf("/LEDOFF") != -1){ // requête "/LEDOFF"
digitalWrite(LED_PIN, HIGH); // éteindre la LED (active LOW)
Serial.println(" LED Eteinte"); // notification sur le moniteur série
envoyer_reponsevide(); // ce n'est pas la peine de rafraîchir la page
}
else {
Serial.println(" Requête non prise en charge");
envoyer_reponsevide();
}
while(MonClient.available())MonClient.read(); //vider le buffer de réception
delay(5);
Serial.println(" Client déconnecté");
Serial.println("==========================================================");
}
void envoyer_pageweb(){
MonClient.println("HTTP/1.1 200 OK");
MonClient.println("Content-Type: text/html");
MonClient.println("Content-Length: " + String(pageweb.length()));
MonClient.println("Connection: close");
MonClient.println(""); //ligne vide
MonClient.println(pageweb);
Serial.println(" Page Web envoyée");
}
void envoyer_reponsevide(){
MonClient.println("HTTP/1.1 204 No Content");
MonClient.println("Connection: close");
MonClient.println(""); //ligne vide
}
static const String pageweb = R"=====(
....
....
)=====";
const char* SSID = "votre point d'accès";
const char* password = "votre mot de passe";
WiFiServer MonServeur(80); // créer le serveur web sur le port 80
WiFiClient MonClient; // créer le client
Quand on téléverse le programme dans le ESP8266. C'est la fonction setup() qui s'exécute en premier, on obtient la trace suivante sur le moniteur série.
Donc le ESP8266 s'est connecté au point d'accès et a démarré le serveur Web et nous a communiqué son adresse IP.
A partir de maintenant c'est la fonction loop() qui se met à l'écoute et attend qu'un client utilisateur se connecte à partir de son navigateur. Si personne ne se connecte, on ne fait rien
client = MonServer.available();
if (!client) {
return;
}
Quand un client se connecte avec l'adresse IP fournie à partir d'un navigateur sur un PC ou un smartphone connecté sur le même point d'accès. Le navigateur envoie au serveur une requête HTTP de type GET :
GET / HTTP/1.1
Host: 192.168.1.101
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,fr;q=0.8
Comme on peut le constater, la requête est constituée de plusieurs lignes. Pour l'instant, il n'y a que la première ligne qui nous intéresse: GET / HTTP/1.1
C'est la requête "/ " qui demande au serveur de retourner la page web à afficher
Le programme du serveur détecte la connexion du client et lit la première ligne de la requête envoyée par celui-ci:
String request = MonClient.readStringUntil('\r');
Il vérifie que c'est la requête "/ " (/ suivi d'un espace), et envoie la page web à l'aide de la fonction envoyer_pageweb();
if(request.indexOf("/ ") != -1){ // requête "/" on affiche la page Web sur le client
envoyer_pageweb();
}
La fonction envoyer_pageweb() est la suivante:
void envoyer_pageweb(){
MonClient.println("HTTP/1.1 200 OK");
MonClient.println("Content-Type: text/html");
MonClient.println("Content-Length: " + String(pageweb.length()));
MonClient.println("Connection: close");
MonClient.println(""); //ligne vide
MonClient.println(pageweb);
Serial.println(" Page Web envoyée");
}
Elle respecte le format d'une réponse HTTP en envoyant:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 221
Connection: close
<!DOCTYPE html>
<html>
<head> <meta name="viewport" content="width=device-width"> </head>
<body>
<br><br>
<a href="LEDON"><button>ALLUMER</button></a>
<br><br><br>
<a href="LEDOFF"><button>ETEINDRE</button></a>
</body>
</html>
Quand le client reçoit la page web, il l'affiche sur le navigateur
Ensuit, le navigateur envoie la requête "/favicon.ico" pour demander au serveur le fichier qui contient l'icône qui normalement sera affiché sur l'onglet de la page dans le navigateur. Pour ce qui nous concerne, nous allons ignorer cette requête. Mais le navigateur attend une réponse pour chaque requête, on lui envoie la réponse HTTP 204 No Content pour lui indiquer qu'il n'y a pas de réponse à cette requête. C'est la fonction envoyer_reponsevide() qui s'en charge.
else {
Serial.println(" Requête non prise en charge");
envoyer_reponsevide();
}
void envoyer_reponsevide(){
MonClient.println("HTTP/1.1 204 No Content");
MonClient.println("Connection: close");
MonClient.println(""); //ligne vide
}
Maintenant si on clique sur le bouton ALLUMER, le navigateur envoie la requête GET "/LEDON"
Le programme du ESP8266 détecte la requête, allume la LED et informe le navigateur qu'il n'a pas de réponse à cette requête.
else if (request.indexOf("/LEDON") != -1){ // requête "/LEDON"
digitalWrite(LED_PIN, LOW); // allumer la LED (active LOW)
Serial.println(" LED allumée"); // notification sur le moniteur série
envoyer_reponsevide(); // ce n'est pas la peine de rafraîchir la page
}
Si on clique sur le bouton ETEINDRE, les choses se passent de la même façon, le navigateur envoie la requête GET "/LEDOFF"
Le programme du ESP8266 détecte la requête, éteint la LED et informe le navigateur qu'il n'a pas de réponse à cette requête.
else if (request.indexOf("/LEDOFF") != -1){ // requête "/LEDOFF"
digitalWrite(LED_PIN, HIGH); // éteindre la LED (active LOW)
Serial.println(" LED Eteinte"); // notification sur le moniteur série
envoyer_reponsevide(); // ce n'est pas la peine de rafraîchir la page
}
Dans ce paragraphe, nous allons faire exactement le même travail mais en utilisant la librairie ESP8266WebServer. Cette librairie fait appel à la librairie ESP8266WiFi pour créer des classes qui gèrent les échanges HTTP entre le client et le serveur. Par exemple, on n'a pas besoin de lire les requêtes du navigateur et faire les tests, on utilise la classe .on qui facilite grandement le travail. Exemple:
myserver.on("/REQXX", fonctionxx); dit au serveur: si tu reçois la requête "/REQXX", exécutes la fonction "fonctionxx".
On peut trouver un peux plus d'information dans cette page
Voici le code entier :
// Programme esp_LED_webserver.ino A. Oumnad
//==========================================================
#include <ESP8266WebServer.h>
//=========================== Définir la page Web ===================================
String Page_Client = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width">
</head>
<body>
<div style="text-align: center;">
<br><br>
<a href="LEDON"><button>ALLUMER</button></a>
<br><br><br>
<a href="LEDOFF"><button>ETEINDRE</button></a>
</div>
</body>
</html>
)=====";
//=========================== Définir les variables globales ========================
#define LED LED_BUILTIN
const char* ssid = "votre SSID";
const char* password = "votre mot de passe";
ESP8266WebServer monWebServer(80); //Server on port 80
//==============================================================
// SETUP
//==============================================================
void setup(void){
//=========================== Initialiser le Moniteur Série ========================
Serial.begin(9600);
while (!Serial);
Serial.println();
Serial.println();
Serial.println("============================================================");
Serial.println();
//=========================== Configurer et éteindre la LED ========================
pinMode(LED,OUTPUT);
digitalWrite(LED,HIGH); // La LED de esp8266 est branchée à l'envers, (logique inversée)
//====================== Connexion à un point d'accès ========================
Serial.print(" Connexion à ");
Serial.println(ssid);
WiFi.begin(ssid, password); //Connect to your WiFi router
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connecté à ");
Serial.println(ssid);
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP()); //IP address assigned to your ESP
//=========================== Dire au serveur ce qu'il a à faire ===============
monWebServer.on("/", aff_page_web); // requête / => appel fonction aff_page_web
monWebServer.on("/LEDON", allumer_led); // requête /LEDON => appel fonction allumer_led
monWebServer.on("/LEDOFF", eteindre_led);// requête /LEDOFF => appel fonction eteindre_led
//=========================== Démarrer le serveur ============================
monWebServer.begin();
Serial.println("Serveur HTTP démarré");
Serial.println("==========================================================");
}
//==============================================================
// LOOP
//==============================================================
void loop(void){
monWebServer.handleClient(); // gérer les requêtes
}
//====================== Définir les fonctions qui réaliseront les tâches ========================
void aff_page_web() {
monWebServer.send(200, "text/html", Page_Client); //Envoyer la page Web
Serial.println("-----> Page Web envoyée");
}
void allumer_led() {
Serial.println("-----> LED allumée");
digitalWrite(LED,LOW); //active LOW
monWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
void eteindre_led() {
Serial.println("-----> LED éteinte");
digitalWrite(LED,HIGH); //active LOW
monWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
A partir de maintenant, nous allons toujours utiliser la librairie ESP8266WebServer. Elle est beaucoup plus efficace et facilite beaucoup le travail. Mais rien ne nous empêche de faire appel à des classes de la librairie de base ESP8266WiFi
Dans l'exemple que nous avons traité, le client utilise une page Web qui ne fait qu'envoyer des commandes vers le serveur. Nous allons essayer maintenant d'ajouter la possibilité de recevoir des données venant du serveur.
Ceci va nous poser quelques petit soucis. En effet, sans précaution particulière, quand le serveur envoie quelque chose (des données) au navigateur, celui-ci efface la page web et la remplace par ce qu'il vient de recevoir.
Pour illustrer tout ça, on va rajouter un bouton CAPTEUR à notre page Web. En cliquant sur ce bouton, le navigateur envoie la requête /SENSOR au serveur pour lui demander d'envoyer la valeur de la température. On ajoute target="_blank" à la balise <a> du bouton pour dire au navigateur d'afficher la réponse dans un nouvel onglet
L'exemple n'utilise pas un vrai capteur mais envoie une donnée fictive qu'il modifie à chaque utilisation. Vous pouvez brancher un vrai capteur et remplacer la donnée fictive par la vrai mesure
// Programme espCOLECT_00
//===================================================
#include <ESP8266WebServer.h>
//=========================== Définir la page Web ===================================
String Page_Client = R"=====(
<!DOCTYPE html>
<html>
<head> <meta name="viewport" content="width=device-width"> </head>
<body>
<div style="text-align: center;">
<br><br>
<a href="LEDON"><button>ALLUMER</button></a>
<br><br><br>
<a href="LEDOFF"><button>ETEINDRE</button></a>
<br><br><br>
<a target="_blank" href="SENSOR"><button>CAPTEUR</button></a>
</div>
</body>
</html>
)=====";
//=========================== Définir les variables globales ========================
#define LED LED_BUILTIN
const char* ssid = "VotreSSID";
const char* password = "VotrePassword";
ESP8266WebServer MonWebServer(80); //Server on port 80
//==============================================================
// SETUP
//==============================================================
void setup(void){
//=========================== Initialiser le Moniteur Série ========================
Serial.begin(9600);
while(!Serial);
Serial.println("\r\n============================================================\r\n");
//=========================== Configurer et éteindre la LED ========================
pinMode(LED,OUTPUT);
digitalWrite(LED,HIGH); // La LED de esp8266 est branchée à l'envers, (logique inversée)
//====================== Connexion à un point d'accès ========================
Serial.print(" Connexion à ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connecté à ");
Serial.println(ssid);
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP());
//=========================== Dire au serveur ce qu'il a à faire ===============
MonWebServer.on("/", aff_page_web); // requête / => appel fonction aff_page_web
MonWebServer.on("/LEDON", allumer_led); // requête /LEDON => appel fonction allumer_led
MonWebServer.on("/LEDOFF", eteindre_led);// requête /LEDOFF => appel fonction eteindre_led
MonWebServer.on("/SENSOR", envoyerMesures);// requête /SENSOR => appel fonction envoyerMesures
//=========================== Démarrer le serveur ============================
MonWebServer.begin();
Serial.println("Serveur HTTP démarré");
Serial.println("==========================================================");
}
//==============================================================
// LOOP
//==============================================================
void loop(void){
MonWebServer.handleClient(); // gérer les requêtes
}
//====================== Définir les fonctions qui réaliseront les tâches ========================
void aff_page_web() {
Serial.println("-----> Afficher page WEB");
MonWebServer.send(200, "text/html", Page_Client); //Envoyer la page Web
}
//====================================================================
void allumer_led() {
Serial.println("-----> Allumer la LED");
digitalWrite(LED,LOW); //active LOW
MonWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
//====================================================================
void eteindre_led() {
Serial.println("-----> Eteindre la LED");
digitalWrite(LED,HIGH); //active LOW
MonWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
//====================================================================
float T = 125.00;
void envoyerMesures() {
MonWebServer.send(200, "text/html", String(T,3));
Serial.println("-----> Donnée envoyée");
T = T + 0.125;
}
Voici le résultat, au début la page web s'affiche. Quand on clique sur le bouton CAPTEUR, le serveur nous envoie la valeur de la température, le navigateur ouvre un nouvel onglet et y affiche la valeur reçue
Ce n'est pas vraiment terrible, mais on va essayer d'améliorer tout ça
On va rajouter à notre page Web un ou deux champs de type SPAN (ou autre) pour afficher les données mesurées. Pour l'instant, on n'utilise pas un vrai capteur, on envoie des données fictives, une correspondant à la température, l'autre à l'humidité
Au niveau du serveur, quand on reçoit la requête associée au bouton CAPTEUR, on n'envoie pas seulement les données, mais on en voie toute la page web en prenant soin d'actualiser les champs SPAN par les nouvelles mesures. Avec une page Web réduite comme la notre, cela ne pose pas de problème de rafraîchir toute la page Web à chaque fois qu'une donnée change. Avec une page Web plus volumineuse, cela peur rendre les choses moins fluides
Voici le code qui illustre cette solution,
// Programme espCOLECT_raff_all A. OUMNAD
//======================================================
#include <ESP8266WebServer.h>
//=========================== Définir la page Web ===================================
String Page_Client = R"=====(
<!DOCTYPE html>
<html>
<head> <meta name="viewport" content="width=device-width"> </head>
<body>
<div style="text-align: center;">
<br><br>
<a href="LEDON"><button>ALLUMER</button></a>
<br><br><br>
<a href="LEDOFF"><button>ETEINDRE</button></a>
<br><br><br>
<a href="SENSOR"><button>CAPTEUR</button></a>
<span style="background-color: yellow; margin-left: 2ch; ">DATA1</span>
<span style="background-color: #00FFFF; margin-left: 2ch; ">DATA2</span>
</div>
</body>
</html>
)=====";
//=========================== Définir les variables globales ========================
#define LED LED_BUILTIN
const char* ssid = "VotreSSID";
const char* password = "VotrePassword";
ESP8266WebServer MonWebServer(80); //Server on port 80
//==============================================================
// SETUP
//==============================================================
void setup(void) {
//=========================== Initialiser le Moniteur Série ========================
Serial.begin(9600);
while (!Serial);
Serial.println("\r\n============================================================\r\n");
//=========================== Configurer et éteindre la LED ========================
pinMode(LED, OUTPUT);
digitalWrite(LED, HIGH); // La LED de esp8266 est branchée à l'envers, (logique inversée)
//====================== Connexion à un point d'accès ========================
Serial.print(" Connexion à ");
Serial.println(ssid);
WiFi.begin(ssid, password); //Connect to your WiFi router
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connecté à ");
Serial.println(ssid);
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP()); //IP address assigned to your ESP
//=========================== Dire au serveur ce qu'il a à faire ===============
MonWebServer.on("/", aff_page_web); // requête / => appel fonction aff_page_web
MonWebServer.on("/LEDON", allumer_led); // requête /LEDON => appel fonction allumer_led
MonWebServer.on("/LEDOFF", eteindre_led);// requête /LEDOFF => appel fonction eteindre_led
MonWebServer.on("/SENSOR", envoyerMesures);// requête /SENSOR => appel fonction envoyerMesures
//=========================== Démarrer le serveur ============================
MonWebServer.begin();
Serial.println("Serveur démarré");
Serial.println("==========================================================");
}
//==============================================================
// LOOP
//==============================================================
void loop(void) {
MonWebServer.handleClient(); // gérer les requêtes
}
//====================== Définir les fonctions qui réaliseront les tâches ========================
void aff_page_web() {
Serial.println("-----> Afficher page WEB");
MonWebServer.send(200, "text/html", Page_Client); //Envoyer la page Web
}
void allumer_led() {
Serial.println("-----> Allumer la LED");
digitalWrite(LED, LOW); //active LOW
MonWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
void eteindre_led() {
Serial.println("-----> Eteindre la LED");
digitalWrite(LED, HIGH); //active LOW
MonWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
float T = 25.0, H = 50;
void envoyerMesures() {
Serial.println("-----> Envoyer une donnée");
T = T + 0.125;
H = H + 1;
String Page_Tempo = Page_Client;
Page_Tempo.replace("DATA1", String(T, 3) );
Page_Tempo.replace("DATA2", String(H) );
MonWebServer.send(200, "text/html", Page_Tempo);
}
Nous allons à présent explorer une solution qui consiste à utiliser JavaScript et des requêtes AJAX (Asynchronous JavaScript and XML) pour éviter un rafraîchissement complet de la page Web.
Les échanges se font comme suit:
Il faut apporter des modification au code html de la page Web, le reste ne change pratiquement pas
<button onclick="demanderMesures()">CAPTEUR</button>
<span id="data_ID1" style="background-color: yellow; margin-left: 2ch; ">TEMPÉRATURE</span>
<span id="data_ID2" style="background-color: #00FFFF; margin-left: 2ch; ">HUMIDITÉ</span>
var ReqAjax = null;
if (window.XMLHttpRequest) { ReqAjax =new XMLHttpRequest(); }
else { ReqAjax =new ActiveXObject("Microsoft.XMLHTTP"); }
function demanderMesures(){
ReqAjax.open("GET","SENSOR",true); //***envoyer une requête avec le texte SENSOR ***
ReqAjax.send();
ReqAjax.onreadystatechange = function(){
if(ReqAjax.readyState == 4 && ReqAjax.status==200) { //***Traiter la réponse***
var reponse = this.responseText;
var rep = reponse.split(","); // séparateur = ','
document.getElementById('data_ID1').innerHTML = rep[0];
document.getElementById('data_ID2').innerHTML = rep[1];
}
}
}
Voici le code complet de cette solution. Cet exemple constitue une solution intéressante qui peut être adaptée pour faire des réalisations plus réalistes
// Programme espCOLECT_raff_par.ino A. OUMNAD
//================================================================================
#include <ESP8266WebServer.h>
//=========================== Définir la page Web ===================================
String Page_Client = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type">
<meta name="viewport" content="width=device-width">
<script>
var ReqAjax = null;
if (window.XMLHttpRequest) { ReqAjax =new XMLHttpRequest(); }
else { ReqAjax =new ActiveXObject("Microsoft.XMLHTTP"); }
function demanderMesures(){
ReqAjax.open("GET","SENSOR",true); //***envoyer une requête avec le texte SENSOR ***
ReqAjax.send();
ReqAjax.onreadystatechange = function(){
if(ReqAjax.readyState == 4 && ReqAjax.status==200) { //***Traiter la réponse***
var reponse = this.responseText;
var rep = reponse.split(","); // séparateur = ','
document.getElementById('data_ID1').innerHTML = rep[0];
document.getElementById('data_ID2').innerHTML = rep[1];
}
}
}
</script>
</head>
<body>
<div style="text-align: center;">
<br><br>
<a href="/LEDON"><button>ALLUMER</button></a>
<br><br><br>
<a href="/LEDOFF"><button>ETEINDRE</button></a>
<br><br><br>
<button onclick="demanderMesures()">CAPTEUR</button>
<span id="data_ID1"; style="background-color: yellow; margin-left: 2ch; ">TEMPÉRATURE</span>
<span id="data_ID2"; style="background-color: #00FFFF; margin-left: 2ch; ">HUMIDITÉ</span>
</div>
</body>
</html>
)=====";
//=========================== Définir les variables globales ========================
#define LED LED_BUILTIN
const char* ssid = "VotreSSID";
const char* password = "VotrePassword";
ESP8266WebServer MonWebServer(80); //Server on port 80
//==============================================================
// SETUP
//==============================================================
void setup(void){
//=========================== Initialiser le Moniteur Série ========================
Serial.begin(9600);
while (!Serial);
Serial.println();
Serial.println();
Serial.println("============================================================");
Serial.println(" Test LED ON/OFF sur ESP8266 avec Librairie ESP8266WebServer");
Serial.println();
//=========================== Configurer et éteindre la LED ========================
pinMode(LED,OUTPUT);
digitalWrite(LED,HIGH); // La LED de esp8266 est branchée à l'envers, (logique inversée)
//====================== Connexion à un point d'accès ========================
Serial.print(" Connexion à ");
Serial.println(ssid);
WiFi.begin(ssid, password); //Connect to your WiFi router
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connecté à ");
Serial.println(ssid);
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP()); //IP address assigned to your ESP
//=========================== Dire au serveur ce qu'il a à faire ===============
MonWebServer.on("/", aff_page_web); // requête / => appel fonction aff_page_web
MonWebServer.on("/LEDON", allumer_led); // requête /LEDON => appel fonction allumer_led
MonWebServer.on("/LEDOFF", eteindre_led);// requête /LEDOFF => appel fonction eteindre_led
MonWebServer.on("/SENSOR", envoyerMesures);// requête /SENSOR => appel fonction envoyerMesures
//=========================== Démarrer le serveur ============================
MonWebServer.begin(); //Start server
Serial.println("Serveur HTTP démarré");
Serial.println("==========================================================");
}
//==============================================================
// LOOP
//==============================================================
void loop(void){
MonWebServer.handleClient(); // gérer les requêtes
}
//====================== Définir les fonctions qui réaliseront les tâches ========================
void aff_page_web() {
Serial.println("-----> Afficher page WEB");
MonWebServer.send(200, "text/html", Page_Client); //Envoyer la page Web
}
//==============================================================
void allumer_led() {
Serial.println("-----> Allumer la LED");
digitalWrite(LED,LOW); //active LOW
MonWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
//==============================================================
void eteindre_led() {
Serial.println("-----> Eteindre la LED");
digitalWrite(LED,HIGH); //active LOW
MonWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
//==============================================================
float T = 40.0, H = 50;
void envoyerMesures() {
Serial.println("-----> Envoyer une donnée");
T = T+0.125; // remplacer par une vraie mesure
H = H+1; // remplacer par une vraie mesure
MonWebServer.send(200, "text/html", String(T,3)+","+String(H));
}
Dans l'exemple précédent, on clique sur le bouton CAPTEUR pour demander une mesure. Dans beaucoup d'applications, on préfère un rafraîchissement automatique des mesures. Pour y arriver on va remplacer le bouton par un timer JavaScript qui appelle la fonction demanderMesures() à intervalles réguliers.
// Programme espCOLECT_raff_periodic.ino A. OUMNAD
//==================================================================================
#include <ESP8266WebServer.h>
//=========================== Définir la page Web ===================================
String Page_Client = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<meta name="viewport" content="width=device-width">
<script>
var ReqAjax = null;
if (window.XMLHttpRequest) { ReqAjax =new XMLHttpRequest(); }
else { ReqAjax =new ActiveXObject("Microsoft.XMLHTTP"); }
// demarrer le timer qui invoque la fonction demanderMesures() toutes les 2 secondes
var myVar = setInterval(demanderMesures, 2000);
function demanderMesures(){
ReqAjax.open("GET","SENSOR",true); //***envoyer une requête avec le texte SENSOR ***
ReqAjax.send();
ReqAjax.onreadystatechange = function(){
if(ReqAjax.readyState == 4 && ReqAjax.status==200) { //***Traiter la réponse***
var reponse = this.responseText;
var rep = reponse.split(","); // séparateur = ','
document.getElementById('data_ID1').innerHTML = rep[0];
document.getElementById('data_ID2').innerHTML = rep[1];
}
}
}
</script>
</head>
<body>
<div style="text-align: center;">
<br><br>
<a href="LEDON"><button>ALLUMER</button></a>
<br><br><br>
<a href="LEDOFF"><button>ETEINDRE</button></a>
<br><br><br>
<span id="data_ID1"; style="background-color: yellow; margin-left: 2ch; ">TEMPÉRATURE</span>
<span id="data_ID2"; style="background-color: #00FFFF; margin-left: 2ch; ">HUMIDITÉ</span>
</div>
</body>
</html>
)=====";
//=========================== Définir les variables globales ========================
#define LED LED_BUILTIN
const char* ssid = "VotreSSID";
const char* password = "VotrePassword";
ESP8266WebServer MonWebServer(80); //Server on port 80
//==============================================================
// SETUP
//==============================================================
void setup(void){
//=========================== Initialiser le Moniteur Série ========================
Serial.begin(9600);
delay(10);
Serial.println();
Serial.println();
Serial.println("============================================================");
Serial.println(" Test LED ON/OFF sur ESP8266 avec Librairie ESP8266WebServer");
Serial.println();
//=========================== Configurer et éteindre la LED ========================
pinMode(LED,OUTPUT);
digitalWrite(LED,HIGH); // La LED de esp8266 est branchée à l'envers, (logique inversée)
//====================== Connexion à un point d'accès ========================
Serial.print(" Connexion à ");
Serial.println(ssid);
WiFi.begin(ssid, password); //Connect to your WiFi router
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connecté à ");
Serial.println(ssid);
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP()); //IP address assigned to your ESP
//=========================== Dire au serveur ce qu'il a à faire ===============
MonWebServer.on("/", aff_page_web); // requête / => appel fonction aff_page_web
MonWebServer.on("/LEDON", allumer_led); // requête /LEDON => appel fonction allumer_led
MonWebServer.on("/LEDOFF", eteindre_led);// requête /LEDOFF => appel fonction eteindre_led
MonWebServer.on("/SENSOR", envoyerMesures);// requête /SENSOR => appel fonction envoyerMesures
//=========================== Démarrer le serveur ============================
MonWebServer.begin(); //Start server
Serial.println("Serveur HTTP démarré");
Serial.println("==========================================================");
}
//==============================================================
// LOOP
//==============================================================
void loop(void){
MonWebServer.handleClient(); // gérer les requêtes
}
//====================== Définir les fonctions qui réaliseront les tâches ========================
void aff_page_web() {
Serial.println("-----> Afficher page WEB");
MonWebServer.send(200, "text/html", Page_Client); //Envoyer la page Web
}
//==============================================================
void allumer_led() {
Serial.println("-----> Allumer la LED");
digitalWrite(LED,LOW); //active LOW
MonWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
//==============================================================
void eteindre_led() {
Serial.println("-----> Eteindre la LED");
digitalWrite(LED,HIGH); //active LOW
MonWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
//==============================================================
float T = 40.0, H = 50;
void envoyerMesures() {
Serial.println("-----> Envoyer une donnée");
MonWebServer.send(200, "text/html", String(T,3)+","+String(H));
T = T+0.125;
H = H+1;
}
L'ESP8266 dispose d'une zone mémoire flash dans laquelle on peut créer un système de fichier pour stocker des fichiers comme dans un disque normal. Voir cette page pour plus de détails
Il existe deux systèmes permettant de créer un système de fichier dans cette zone, SPIFFS (Serial Peripheral Interface Flash File System) et LittleFS (Son petit frère). SPIFFS commence à devenir obsolète, les travaux de développement se concentrent sur LittleFS. Ce dernier gère mieux les dossier et il est plus rapide. Il est très facile de migrer les programmes d'un système vers l'autre car ils s'installent et s'utilisent exactement de la même façon. Les deux systèmes ne sont pas compatibles. Si vous créez des fichier dans un File System SPIFFS, vous ne pouvez pas y accéder à l'aide de LIttleFs et vise-versa. Il faut choisir un système et s'y tenir.
Avant de créer un FileSystem, il faut sélectionner la taille selon votre module. Avec le Wemos D1 mini, on a 4Mo de mémoire flash que l'on peut répartir entre mémoire programme, file système, OTA et autres fonctionnalités. La zone OTA (Over The Air) est une zone intermédiaire qui permet de téléverser le programme en utilisant le WiFi. Voir paragraphe dédié
Arduino-IDE permet de choisir la répartition de la mémoire. Dans la suite nous allons répartir notre mémoire approximativement comme suit: Mémoire programme: 2Mo, File System: 2Mo, OTA: 1Mo
Dans l'exemple ci.dessous:
//Programme LittleFS_W_R_DIR
//=============================================
#include <LittleFS.h>
void setup() {
Serial.begin(9600);
while(!Serial);
Serial.println("\r\n============================================================");
// Créer le FileSystem
bool success = LittleFS.begin();
if (!success) {
Serial.println("Error mounting the file system");
return;
}
// ouvert fichier pour écriture (w)
// s'il existe déjà, son contenu est effacé
String NOM_fichier = "/test0.csv" ; // test0.csv dans la racine
File ID_fichier = LittleFS.open(NOM_fichier, "w");
if (!ID_fichier) {
Serial.println( "Erreur ouverture fichier " + NOM_fichier + " en mode écriture" );
return;
}
Serial.println("Ouverture du fichier " + NOM_fichier + " en mode écriture réussie");
// Écriture quelques valeurs dans le fichier
float T = 25.0;
for (int j = 0; j < 10; j++){
for (int i=0; i < 10;i++){
ID_fichier.print(T);
ID_fichier.print(';'); // on utilise ; comme séparateur
T =T+0.11;
}
ID_fichier.println();
}
ID_fichier.close(); //fermeture du fichier
Serial.println("Écriture de données dans le fichier "+NOM_fichier+" réussie");
// Ouverture du fichier pour lecture
ID_fichier = LittleFS.open(NOM_fichier, "r");
if (!ID_fichier) {
Serial.println("Erreur ouverture fichier "+NOM_fichier+" en mode lecture");
return;
}
Serial.println("Ouverture du fichier "+NOM_fichier+" en mode lecture réussie");
Serial.println("===========================================================");
Serial.println("Voici le contenu du fichier: ");
while (ID_fichier.available()) {
Serial.write(ID_fichier.read());
}
Serial.println();
ID_fichier.close(); // fermeture du fichier
// lister les fichier contenu dans le LittleFS
Serial.println("\r\n================DIR===============");
Dir mydir = LittleFS.openDir("/");
while (mydir.next()) {
String fichier = mydir.fileName();
size_t taille = mydir.fileSize();
Serial.println(fichier + " " + taille);
}
Serial.println();
}
void loop() {}
Ici pas de page web client. Le téléversement et l'exécution du code dans le D1 mini permet de nettoyer le File System. Nous verrons plus tard des exemples avec plus de possibilités
// Programme LittleFS_del_all.ino A. Oumnad
//================================================================
#include <LittleFS.h>
void setup() {
Serial.begin(115200);
while(!Serial);
Serial.println("\r\n================================================");
// Monter le File System
bool success = LittleFS.begin();
if (!success) {
Serial.println("Erreur création système de fichier");
return;
}
Dir dossier = LittleFS.openDir("/");
while (dossier.next()) {
String NOM_fichier = dossier.fileName();
if(LittleFS.remove(NOM_fichier))Serial.println(NOM_fichier+" supprimé avec succès");
else Serial.println("Échec suppression de "+NOM_fichier);
}
}
void loop() {}
Coté client, on a une page web avec un seul bouton "Télécharger" qui envoie la requête /DOWNLOAD vers le serveur D1 mini. Quand le serveur reçoit cette requête, il stream le contenu du fichier test0.csv vers le client. On verra plus tard un exemple où l'on peut choisir le fichier à télécharger dans une liste.
// Programme LittleFS_download.ino A. Oumnad
//=====================================================
#include <ESP8266WebServer.h>
#include <LittleFS.h>
//=============== Définition de la Page Web du client ====================================================
const String Page_main = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<title>LittleFS Download</title>
</head>
<body>
<div style="text-align: center;">
<a href='/DOWNLOAD'><button>Télécharger</button></a>
</div>
</body>
</html>
)=====";
const char* SSID = "votre SSID";
const char* password = "votre mot de passe";
// ================Créer un serveur web sur le port 80=====================================
ESP8266WebServer MonWebServer(80);
File ID_fichier;
//==================================setup()=======================================================
void setup() {
Serial.begin(9600);
while(!Serial);
Serial.println("\r\n===================================================");
// ======================Se connecter à un point d'accès WIFI============
WiFi.disconnect();
WiFi.mode(WIFI_STA);
Serial.print("Connection à ");
Serial.println(SSID);
WiFi.begin(SSID, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\r\n Adresse IP");
Serial.print(WiFi.localIP());
// =====================définir les action à faire selon la requête HTTP envoyée par le client
MonWebServer.on("/", handleRoot);
MonWebServer.on("/DOWNLOAD", handledownload);
// ======================= démarrer le serveur========================================
MonWebServer.begin(); //Start server
Serial.println("\r\nLe serveur HTTP est démarré");
// ==================initialiser le FS
if (!LittleFS.begin()) {
Serial.println("\r\nErreur Creation File System");
while(1);
}
Serial.println("\r\n================DIR===============");
Dir racine = LittleFS.openDir("/");
while (racine.next()) {
String fileName = racine.fileName();
size_t fileSize = racine.fileSize();
Serial.println(fileName + " " + fileSize);
}
Serial.println();
}
//==============================================loop()=========================================
void loop() {
MonWebServer.handleClient(); // voir si le client a envoyé quelque chose
}
void handleRoot() {
MonWebServer.send(200, "text/html", Page_main); //Send web page
Serial.println("\r\nPage web envoyée");
}
void handledownload() {
String NOM_fichier = "test0.csv";
ID_fichier = LittleFS.open(NOM_fichier , "r"); //si on omet le / ==> racine
if (ID_fichier) {
MonWebServer.sendHeader("Content-Type", "text/text");
MonWebServer.sendHeader("Content-Disposition", "attachment; filename = " + NOM_fichier );
MonWebServer.sendHeader("Connection", "close");
MonWebServer.streamFile(ID_fichier, "application/octet-stream");
ID_fichier.close();
Serial.println("\r\nFichier " + NOM_fichier + " envoyé");
} else{
Serial.println("\r\nFichier " + NOM_fichier + " introuvable");
MonWebServer.send(404, "text/html", "\r\nFichier " + NOM_fichier + " introuvable");
}
}
Voici enfin un mini explorateur de fichiers qui permet de réaliser les opérations essentielles dans la zone File System du D1 mini
// Programme LittleFS_explorer_webserver.ino A. Oumnad
//===============================================================
#include <ESP8266WebServer.h>
#include <LittleFS.h>
const String Page_Top = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<meta name="viewport" content="width=device-width">
<title>LittleFS Explorer</title>
</head>
<body>
<h1>ESP8266 LittleFS Explorer</h1>
<p>
=============================================<br>
LittleFS Total Space = TOTALSPACE bytes<br>
LittleFS Used Space = USEDSPACE bytes<br>
=============================================<br><br>
</p>
)=====";
const String Page_Botom = R"=====(
<!-- le tag <form> sera rajouté par le code pour englober la liste des fichiers -->
<br>
<input name="tache" value="DELETE" type="submit">
<input name="tache" value="DOWNLOAD" type="submit">
</form>
<br>=============================================<br><br>
<form method="post" enctype="multipart/form-data" action="URI_UPLOAD">
<input name="file2download" type="file">
<input name="REQ_UPLOAD" value="UPLOAD" type="submit">
</form>
<br>=============================================<br><br>
<a href="REQ_DELALL"><button>DELETE ALL</button></a>
<a href="REQ_FORMAT"><button>FORMAT</button></a>
</body></html>
)=====";
const String Page_Delall = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type">
<meta name="viewport" content="width=device-width">
</head>
<body>
<br><br><h2> Vous allez supprimer tous les fichiers !</h2>
<a href='DEL_ALL_OK'><button>CONFIRMER</button></a>
<a href='/'><button>ANNULER</button></a><br>
</body>
</html>
)=====";
const String Page_Format = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type">
<meta name="viewport" content="width=device-width">
</head>
<body>
<br><br><h2> Vous allez supprimer tous les fichiers du File System</h2>
<a href='FORMAT_OK'><button>CONFIRMER</button></a>
<a href='/'><button>ANNULER</button></a><br>
</body>
</html>
)=====";
const char* monssid = "VotreSSID";
const char* monpassword = "VotrePassword";
ESP8266WebServer MonWebServer(80); // créer le serveur web sur le port 80
String nomFichier, webpage;
File UploadFileID;
//==========================================================================
void setup() {
Serial.begin(9600); // pour le moniteur série
while(!Serial);
Serial.println("\r\n=====================================================");
// Connexion au point d'accès
WiFi.disconnect();
WiFi.mode(WIFI_STA);
Serial.print("Connexion à ");
Serial.println(monssid);
WiFi.begin(monssid, monpassword);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connecté à ");
Serial.println(monssid);
// Afficher l'adresse IP affecté par le point d'accès
Serial.print("L'adresse IP du ESP8266 est: ");
Serial.println(WiFi.localIP());
//==========================================================================
MonWebServer.on("/", handleroot);
MonWebServer.on("/REQ_DELALL", handledelall);
MonWebServer.on("/DEL_ALL_OK", handledelallok);
MonWebServer.on("/RADIO", handleradio);
MonWebServer.onFileUpload(handlefileupload);
MonWebServer.on("/URI_UPLOAD", handleupload);
MonWebServer.on("/REQ_FORMAT", handleformat);
MonWebServer.on("/FORMAT_OK", handleformatok);
//==========================================================================
// Démarrer le serveur web
MonWebServer.begin();
Serial.println("Serveur Web démarré");
//Monter le File system
if (LittleFS.begin()) {
Serial.println("Montage File System LittleFS OK");
}else{
Serial.println("Echec Montage File system LittleFS");
while(1);
}
Serial.println("==========================================================");
}
//==========================================================================
void loop(void){
MonWebServer.handleClient(); // gérer les requêtes des clients
}
//==========================================================================
void page_refresh(void){
FSInfo fs_info;
LittleFS.info(fs_info);
webpage = Page_Top;
webpage.replace("TOTALSPACE",String(fs_info.totalBytes));
webpage.replace("USEDSPACE",String(fs_info.usedBytes));
// form contient la liste des fichiers + les deux boutons situés dans Page_Botom
webpage += "<form action='RADIO'>";
Dir racine = LittleFS.openDir("/");
while (racine.next()) {
nomFichier = racine.fileName();
size_t fileSize = racine.fileSize();
webpage += "<input type='radio' name='filelist' value="+nomFichier+"><label>" +nomFichier+ " " +fileSize+ "</label><br>";
}
webpage += Page_Botom;
MonWebServer.send(200, "text/html", webpage);
}
//==========================================================================
void handleroot(void){
Serial.print("==========================");
Serial.print(MonWebServer.uri());
Serial.println("==========================");
page_refresh();
Serial.println("Page Web envoyée");
}
//==========================================================================
void handledelall(void){
Serial.print("==========================");
Serial.print(MonWebServer.uri());
Serial.println("==========================");
MonWebServer.send(200, "text/html", Page_Delall);
}
//==========================================================================
void handledelallok(void){
Serial.print("==========================");
Serial.print(MonWebServer.uri());
Serial.println("==========================");
Dir dossier = LittleFS.openDir("/");
while (dossier.next()) { // List the file system contents
String NOM_fichier = dossier.fileName();
if(LittleFS.remove(NOM_fichier))Serial.println(NOM_fichier+" supprimé avec succès");
else Serial.println("Echec suppression ");
}
page_refresh();
}
//==========================================================================
void handleradio(void){
Serial.print("==========================");
Serial.print(MonWebServer.uri());
Serial.println("==========================");
nomFichier = MonWebServer.arg("filelist");
Serial.print("--->");
Serial.print(nomFichier);
Serial.println("<---");
if(nomFichier==""){
Serial.println("Il faut sélectionner un fichier");
MonWebServer.send(204, "text/html", "No Content");
return;
}
if(MonWebServer.arg("tache") == "DOWNLOAD"){
File ID_fichier = LittleFS.open("/"+nomFichier , "r");
if (ID_fichier) {
//int N = ID_fichier.available();
MonWebServer.sendHeader("Content-Type", "application/octet-stream");
MonWebServer.sendHeader("Content-Disposition", "attachment; filename="+nomFichier );
MonWebServer.sendHeader("Connection", "close");
MonWebServer.streamFile(ID_fichier, "application/octet-stream");
ID_fichier.close();
} else{
Serial.println();
Serial.println("Erreur ouverture fichier /" + nomFichier);
}
}
else if(MonWebServer.arg("tache") == "DELETE"){
if(LittleFS.remove("/"+nomFichier))Serial.println(nomFichier+" supprimé avec succès");
else Serial.println("Echec suppression ");
page_refresh();
}
}
//==========================================================================
void handlefileupload() {
Serial.print("==========================");
Serial.print("gestion POST type=file");
Serial.println("==========================");
if(MonWebServer.uri() != "/URI_UPLOAD") return;
HTTPUpload& upload2fs = MonWebServer.upload();
if(upload2fs.status == UPLOAD_FILE_START) {
nomFichier = upload2fs.filename;
if(nomFichier==""){
Serial.println("Il faut sélectionner un fichier");
MonWebServer.send(204, "text/html", "No Content");
return;
}
UploadFileID = LittleFS.open("/" + nomFichier, "w");
Serial.print("Ouverture fichier "); Serial.println(nomFichier);
} else if (upload2fs.status == UPLOAD_FILE_WRITE) {
if(UploadFileID){
Serial.print("Ecriture "); Serial.print(upload2fs.currentSize); Serial.println(" Octets");
UploadFileID.write(upload2fs.buf, upload2fs.currentSize);
}
} else if (upload2fs.status == UPLOAD_FILE_END) {
if (UploadFileID){
UploadFileID.close();
Serial.print("Fermeture fichier "); Serial.println(nomFichier);
}
}
}
//==========================================================================
void handleupload(){
page_refresh();
}
//==========================================================================
void handleformat(){
MonWebServer.send(200, "text/html", Page_Format);
Serial.println("Page formatage envoyée");
}
//==========================================================================
void handleformatok(){
const String Page_Echec_Format = "<br><br><h2>Echec Formatage</h2>"
"<a href='/'><button>OK</button></a><br>";
Serial.println("Formatage En cours");
bool formatted = LittleFS.format();
if(formatted){
page_refresh();
Serial.println("Formatage terminé avec succès");
}else{
MonWebServer.send(200, "text/html", Page_Echec_Format);
Serial.println("Echec Formatage");
}
}
l s'agit d'un plugin à ajouter à l'IDE Arduino et qui permet d'uploder les fichiers qui se trouve dans le dossier data du projet vers le File System LittleFS
J'ai eu un peu de mal à installer une version qui marche. Après quelques recherches, J'ai fini par comprendre que cet outil utilise un script python. Or le package python installé par le gestionnaire de cartes ESP8266 est passé dernièrement de Python2 à Python3 qui ne sont pas vraiment compatibles.
J'ai fini par trouver une version qui marche avec Arduino-IDE 1.x ICI
Une fois le fichier Zip téléchargé, extraire son contenu et copier le dossier ESP8266LittleFS dans le dossier tools de votre dossier de croquis (le mien s'appelle ARDUINO_IDE). Remarquez que le dossier ESP8266LittleFS contient lui même un dossier tools qui contient le fichier esp8266fs.jar. On doit obtenir une arborescence qui ressemble à ça:
Vous pouvez aussi le placer dans le dossier d'installation de l'IDE-Arduino
Ne pas oublier de Redémarrer L'IDE-Arduino après cette opération
Dans tous les exemples que nous avons vus, le code HTML de la page web est inclus dans le fichier .ino qui gère le serveur. Nous allons voir dans cet exemple que le code html peut être placé dans le File System du module. Quand un client se connecte au serveur, celui-ci lui envoie la page web à partir du FileSystem. La procédure est la suivante:
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type">
<meta name="viewport" content="width=device-width">
<script>
var ReqAjax = null;
if (window.XMLHttpRequest) { ReqAjax =new XMLHttpRequest(); }
else { ReqAjax =new ActiveXObject("Microsoft.XMLHTTP"); }
function demanderMesures(){
ReqAjax.open("GET","SENSOR",true); //***envoyer une requête avec le texte SENSOR ***
ReqAjax.send();
ReqAjax.onreadystatechange = function(){
if(ReqAjax.readyState == 4 && ReqAjax.status==200) { //***Traiter la réponse***
var reponse = this.responseText;
var rep = reponse.split(","); // séparateur = ','
document.getElementById('data_ID1').innerHTML = rep[0];
document.getElementById('data_ID2').innerHTML = rep[1];
}
}
}
</script>
</head>
<body>
<br><br>
<a href="/LEDON"><button>ALLUMER</button></a>
<br><br><br>
<a href="/LEDOFF"><button>ETEINDRE</button></a>
<br><br><br>
<button onclick="demanderMesures()">LIRE CAPTEUR</button>
<span id="data_ID1"; style="background-color: yellow; margin-left: 2ch; ">TEMPÉRATURE</span>
<span id="data_ID2"; style="background-color: yellow; margin-left: 2ch; ">HUMIDITÉ</span>
</body>
</html>
//Programme html_in_FS.ino A. Oumnad
//=========================================================
#include <ESP8266WebServer.h>
#include <LittleFS.h>
//=========================== Définir les variables globales ========================
#define LED 2
float T = 40.0, H = 50;
const char* ssid = "votre SSID";
const char* password = "votre mot de passe";
ESP8266WebServer myWebServer(80); //Server on port 80
//=======================SETUP=======================================
void setup(void){
//=========================== Initialiser le Moniteur Série ========================
Serial.begin(115200);
delay(10);
Serial.println();
Serial.println();
Serial.println("============================================================");
Serial.println();
//=========================== Configurer et éteindre la LED ========================
pinMode(LED,OUTPUT);
digitalWrite(LED,HIGH); // La LED de esp8266 est branchée à l'envers, (logique inversée)
//====================== Connexion à un point d'accès ========================
Serial.print(" Connexion à ");
Serial.println(ssid);
WiFi.begin(ssid, password); //Connect to your WiFi router
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connecté à ");
Serial.println(ssid);
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP()); //IP address assigned to your ESP
//=========================== Dire au serveur ce qu'il a à faire ===============
myWebServer.on("/", aff_page_web); // requête / => appel fonction aff_page_web
myWebServer.on("/LEDON", allumer_led); // requête /LEDON => appel fonction allumer_led
myWebServer.on("/LEDOFF", eteindre_led);// requête /LEDOFF => appel fonction eteindre_led
myWebServer.on("/SENSOR", envoyerMesures);// requête /SENSOR => appel fonction envoyerMesures
//=========================== Monter le File system ============================
if(LittleFS.begin()){
Serial.println("Montage File system LittleFS OK");
}else{
Serial.println("Erreur montage File system LittleFS");
while(1);
}
//=========================== Démarrer le serveur ============================
myWebServer.begin();
Serial.println("Serveur HTTP démarré");
Serial.println("==========================================================");
}
//=========================LOOP=====================================
void loop(void){
myWebServer.handleClient(); // gérer les requêtes
}
void aff_page_web() {
File file_ID = LittleFS.open("PageWeb.html", "r");
if(file_ID){
myWebServer.streamFile(file_ID, "text/html");
file_ID.close();
Serial.println("-----> PageWeb.html envoyé");
}else{
Serial.println("Problème d'ouverture du fichier PageWeb.html");
Serial.println("Assurez vous de l'avoir téléversé à l'aide \r\n de l'outils LittleFS data upload");
String rep = ""
"Problème d'ouverture du fichier PageWeb.html \r\n"
"Assurez vous de l'avoir téléversé à l'aide \r\n"
"de l'outils LittleFS Data Upload \r\n";
myWebServer.send(404, "text/plain; charset=utf-8", rep);
}
}
void allumer_led() {
Serial.println("-----> Allumer la LED");
digitalWrite(LED,LOW); //active LOW
myWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
void eteindre_led() {
Serial.println("-----> Eteindre la LED");
digitalWrite(LED,HIGH); //active LOW
myWebServer.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
void envoyerMesures() {
Serial.println("-----> Envoyer une donnée");
myWebServer.send(200, "text/html", String(T,3)+","+String(H));
T = T+0.125;
H = H+1;
}
Dans les exemples précédents, le serveur envoyait des mesures fictives vers le client. Dans l'exemple qui suit, on va prendre de vraies mesures sur un capteur de température (LM75). On enregistre les données sur le File-System dans un fichier au format .csv, ensuite on télécharge le fichier pour le traiter sur Excel.
Pour dater les mesures, il faut utiliser un serveur NTP (Network Time Protocol). On peut y accéder à l'aide de la librairie NTPclient. La version installable par le gestionnaire de librairie ne contient pas toutes les fonctions comme getDate() et getFormattedDate()... Je vous conseille de télécharger cette version: et de l'installer comme ça:
Voici à quoi ressemble la page web
Voici le code entier:
// Programme dataloggerLM75.ino A. Oumnad
//====================================================================
#include <ESP8266WebServer.h>
#include <LittleFS.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <Wire.h>
//=========================== Définir la page Web ===================================
String Page_Client = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type">
<meta name="viewport" content="width=device-width">
<script>
var ReqAjax = null;
if (window.XMLHttpRequest) { ReqAjax =new XMLHttpRequest(); }
else { ReqAjax =new ActiveXObject("Microsoft.XMLHTTP"); }
function recordings(){
var req_txt = document.getElementById('R_bouton').value;
ReqAjax.open("GET",req_txt,true);
ReqAjax.send(); // on envoie le label du bouton comme requête
ReqAjax.onreadystatechange = function(){
// on remplace le label du bouton par la réponse du serveur
if(ReqAjax.readyState == 4 && ReqAjax.status==200) { //***Traiter la réponse***
document.getElementById('R_bouton').value = this.responseText;
}
}
}
</script>
</head>
<body>
<br><br>
<a href="LEDON"><button>ALLUMER</button></a><br><br>
<a href="LEDOFF"><button>ETEINDRE</button></a><br><br>
<input id="R_bouton" value="START_RECORDING" onclick="recordings()" type="button"><br><br>
<a href='DOWNLOAD'><button>TELECHARGER</button></a>
</body>
</html>
)=====";
//=========================== Définir les variables globales ========================
#define LED LED_BUILTIN
#define LM75_I2C_ADR 0x48
bool rec_status = false;
const char* ssid = "votre SSID";
const char* password = "votre mot de passe";
String DataFileName = "data00.csv" ;
ESP8266WebServer MonWebServeur(80); //Server on port 80
File DataFileID;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 3600);
//==============================================================
// SETUP
//==============================================================
void setup(void){
//=========================== Initialiser le Moniteur Série ========================
Serial.begin(115200);
while(!Serial);
Serial.println("\r\n\r\n============================================================\r\n");
//=========================== Configurer et éteindre la LED ========================
pinMode(LED,OUTPUT);
digitalWrite(LED,HIGH); // La LED de esp8266 est branchée à l'envers, (logique inversée)
bool success = LittleFS.begin();
if (success) Serial.println("File system LittleFS monté avec succès");
else{
Serial.println("Error montage File System LittleFS");
return;
}
//====================== Connexion à un point d'accès ========================
Serial.print(" Connexion à ");
Serial.println(ssid);
WiFi.begin(ssid, password); //Connect to your WiFi router
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connecté à ");
Serial.println(ssid);
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP()); //IP address assigned to your ESP
timeClient.begin();
//=========================== Dire au serveur ce qu'il a à faire ===============
MonWebServeur.on("/", aff_page_web); // requête / => appel fonction aff_page_web
MonWebServeur.on("/LEDON", allumer_led); // requête /LEDON => appel fonction allumer_led
MonWebServeur.on("/LEDOFF", eteindre_led);// requête /LEDOFF => appel fonction eteindre_led
MonWebServeur.on("/START_RECORDING", demarrer_R);
MonWebServeur.on("/STOP_RECORDING", arreter_R);
MonWebServeur.on("/DOWNLOAD", handledownload);
//=========================== Démarrer le serveur ============================
MonWebServeur.begin(); //Start server
Serial.println("Serveur Web démarré");
Serial.println("==========================================================");
Wire.begin();
Wire.beginTransmission(LM75_I2C_ADR);
Wire.write(0); // pointeur sur registre de température
Wire.endTransmission();
}
//==============================================================
// LOOP
//==============================================================
unsigned long ancien=0, nouveau=0;
float T = 0;
int8_t msb;
byte lsb;
void loop(void){
MonWebServeur.handleClient(); // gérer les requêtes
if(!rec_status)return;
nouveau = millis();
if ((unsigned long)(nouveau - ancien) >= 5000) { // une mesure toutes les 5 secondes à peu près
timeClient.update();
Wire.requestFrom(LM75_I2C_ADR, 2); // demander 2 octets à partir de la position courante du pointeur
msb = Wire.read();
lsb = Wire.read();
T = msb + (lsb >> 5) * 0.125; //Température
DataFileID.print(timeClient.getFormattedDate()+";");
DataFileID.print(timeClient.getFormattedTime()+";");
DataFileID.println(T,1);
Serial.print(timeClient.getFormattedDate()+" ");
Serial.print(timeClient.getFormattedTime()+" ");
Serial.println(T,2);
ancien = nouveau;
}
}
//====================== Définir les fonctions qui réaliseront les tâches ========================
void aff_page_web() {
MonWebServeur.send(200, "text/html", Page_Client); //Envoyer la page Web
Serial.println("-----> Page WEB envoyée");
}
void allumer_led() {
Serial.println("-----> Allumer la LED");
digitalWrite(LED,LOW); //active LOW
MonWebServeur.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
void eteindre_led() {
Serial.println("-----> Eteindre la LED");
digitalWrite(LED,HIGH); //active LOW
MonWebServeur.send(204, "text/html", "No Content"); // pas de réponse HTTP
}
void demarrer_R() {
DataFileID = LittleFS.open(DataFileName, "w");
if (DataFileID)Serial.println("Ouverture fichier "+DataFileName+" Pour écriture Réussie");
else{
Serial.println("Erreur ouverture fichier "+DataFileName+" En écriture");
return;
}
rec_status = true;
MonWebServeur.send(200, "text/html", "STOP_RECORDING"); // réponse AJAX pour modifier le bouton
Serial.println("-----> Enregistrement En cours");
}
void arreter_R() {
delay(10);
DataFileID.close();
Serial.println("-----> enregistrement Arrêté");
rec_status = false;
MonWebServeur.send(200, "text/html", "START_RECORDING"); // réponse AJAX pour modifier le bouton
}
void handledownload() {
Serial.println("-----> Téléchargement");
if( rec_status){
delay(10);
DataFileID.close();
}
DataFileID = LittleFS.open(DataFileName , "r");
if (DataFileID) {
Serial.println("Ouverture fichier "+DataFileName+" Pour Lecture Réussie");
MonWebServeur.sendHeader("Content-Type", "text/text");
MonWebServeur.sendHeader("Content-Disposition", "attachment; filename="+DataFileName );
MonWebServeur.sendHeader("Connection", "close");
MonWebServeur.streamFile(DataFileID, "application/octet-stream");
DataFileID.close();
if( rec_status)DataFileID = LittleFS.open(DataFileName , "a"); //Si enregistrement en cours, on continue
} else{
Serial.println("Echec Ouverture fichier "+DataFileName+" en Lecture");
MonWebServeur.send(404, "text/html", "Echec Ouverture fichier "+DataFileName+" en Lecture");
}
}
Une fois téléchargé, le fichier de mesures au format (.csv) est compatible avec Microsoft Excel
// Programme datalogger_LM75_final A. Oumnad
//================================================================
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <LittleFS.h>
#include <WiFiUdp.h>
#include <Wire.h>
//=========================== Définir la page Web ===================================
String Page_Client = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta content="text/html; charset=utf-8" http-equiv="content-type">
<title>Datalogger</title>
<script>
var ReqAjax = null;
if (window.XMLHttpRequest) { ReqAjax =new XMLHttpRequest(); }
else { ReqAjax =new ActiveXObject("Microsoft.XMLHTTP"); }
// demarrer le timer qui cadence l'actualisation des champs de la page
var myVar = setInterval(actualiser, 1000);
function actualiser(){
ReqAjax.open("GET","ACTUALISER",true); //***envoyer une requête avec le texte SENSOR ***
ReqAjax.send();
ReqAjax.onreadystatechange = function(){
if(ReqAjax.readyState == 4 && ReqAjax.status==200) { //***Traiter la réponse***
var reponse = this.responseText;
var rep = reponse.split(" ");
document.getElementById('TEMP_ID').innerHTML = rep[0] + " °C";
document.getElementById('ECH_ID').innerHTML = rep[1];
if(rep[2]=="1"){
document.getElementById('RSTATUS').innerHTML = "ON";
document.getElementById('START_R').style.visibility = "hidden";
document.getElementById('RESUME_R').style.visibility = "hidden";
document.getElementById('STOP_R').style.visibility = "visible";
}else{
document.getElementById('RSTATUS').innerHTML = "OFF";
document.getElementById('START_R').style.visibility = "visible";
document.getElementById('RESUME_R').style.visibility = "visible";
document.getElementById('STOP_R').style.visibility = "hidden";
}
}
}
}
</script>
</head>
<body> <br>
<br>
<div style="text-align: center;"> <br>
Température<br>
<span id="TEMP_ID" style="display: inline-block; border-style: solid;width: 100px;">0 °C</span><br><br>
<a href="START_REC"><button id="START_R">START RECORDING</button></a><br><br>
<a href="RESUME_REC"><button id="RESUME_R">RESUME RECORDING</button></a><br><br>
<a href="STOP_REC"><button id="STOP_R">STOP RECORDING</button></a><br><br>
Recording Status<br>
<span id="RSTATUS" style="display: inline-block; border-style: solid; width: 109px; margin-left: -7px;">X</span><br><br>
Samples Recorded<br>
<span id="ECH_ID" style="display: inline-block; border-style: solid; width: 109px; margin-left: -7px;">0</span><br><br>
<a href="DOWNLOAD"><button>DOWNLOAD</button></a></div>
</body>
</html>
)=====";
//=========================== Définir les variables globales ========================
#define LED LED_BUILTIN
#define LM75_I2C_ADR 0x48
bool record_status = false;
const char* ssid = "votre SSID";
const char* password = "votre mot de passe";
String DataFileName = "data00.csv" ;
File DataFileID;
float T = 0;
int N_ECH=0;
int8_t msb;
byte lsb;
// pour le serveur NTP
WiFiUDP ntpUDP;
NTPClient Date_heure(ntpUDP, "pool.ntp.org", 3600);
//Server Web sur port 80
ESP8266WebServer MonServeurWeb(80);
//Adresse IP fixe
IPAddress IP(192, 168, 0, 150); //adresse fixe
IPAddress gateway(192, 168, 0, 1); //adresse du point d'accès
IPAddress subnet(255, 255, 255, 0); //masque de sous réseau
IPAddress dns(8, 8, 8, 8); //DNS
//====================== Définir les fonctions qui réaliseront les tâches ========================
void aff_page_web() {
Serial.println("-----> Afficher page WEB");
MonServeurWeb.send(200, "text/html", Page_Client); //Envoyer la page Web
}
void actualiser() {
//Serial.println("-----> Actualiser");
MonServeurWeb.send(200, "text/html", String(T,3)+" "+String(N_ECH)+" "+String(record_status));
}
void demarrer_R() {
DataFileID = LittleFS.open(DataFileName, "w"); // ancien contenu effacé
if (!DataFileID) {
Serial.println("Erreur ouverture fichier "+DataFileName);
MonServeurWeb.send(204, "text/html", "No content");
return;
}
digitalWrite(LED,LOW);
Serial.println("-----> Enregistrement En cours");
record_status = true;
N_ECH = 0;
MonServeurWeb.send(204, "text/html","No content");
}
void continue_R() {
DataFileID = LittleFS.open(DataFileName, "a"); // append
if (!DataFileID) {
Serial.println("Erreur ouverture fichier "+DataFileName);
MonServeurWeb.send(204, "text/html", "No content");
return;
}
digitalWrite(LED,LOW);
Serial.println("-----> Enregistrement En cours");
record_status = true;
MonServeurWeb.send(204, "text/html","No content");
}
void arreter_R() {
delay(10);
DataFileID.close();
digitalWrite(LED,HIGH);
Serial.println("-----> enregistrement Arrêté");
record_status = false;
MonServeurWeb.send(204, "text/html", "No content");
}
void handledownload() {
if(record_status){
delay(10);
DataFileID.close();
Serial.println("-----> enregistrement Arrêté");
record_status = false;
}
DataFileID = LittleFS.open(DataFileName , "r");
if (DataFileID) {
Serial.println("-----> Téléchargement en cours");
MonServeurWeb.sendHeader("Content-Type", "text/text");
MonServeurWeb.sendHeader("Content-Disposition", "attachment; filename="+DataFileName );
MonServeurWeb.sendHeader("Connection", "close");
MonServeurWeb.streamFile(DataFileID, "application/octet-stream");
DataFileID.close();
} else{
Serial.println();
Serial.println("Fichier introuvable");
MonServeurWeb.send(204, "text/html", "Fichier introuvable");
}
}
//==============================================================
// SETUP
//==============================================================
void setup(void){
//=========================== Initialiser le Moniteur Série ========================
Serial.begin(115200);
while(!Serial);
Serial.println("\r\n\r\n============================================================\r\n");
//=========================== Configurer et éteindre la LED ========================
pinMode(LED,OUTPUT);
digitalWrite(LED,HIGH); // La LED de esp8266 est branchée à l'envers, (logique inversée)
// file System
bool success = LittleFS.begin();
if (!success) {
Serial.println("Error mounting the file system");
return;
}
//====================== Connexion à un point d'accès ========================
Serial.print(" Connexion à ");
Serial.println(ssid);
WiFi.disconnect(); //Prevent connecting to wifi based on previous configuration
WiFi.config(IP,gateway,subnet,dns);
WiFi.begin(ssid, password);
WiFi.mode(WIFI_STA); //WiFi mode station, connect to wifi router only
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connecté à ");
Serial.println(ssid);
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP()); //IP address assigned to your ESP
//=========================== Dire au serveur ce qu'il a à faire ===============
MonServeurWeb.on("/", aff_page_web); // requête / => appel fonction aff_page_web
MonServeurWeb.on("/ACTUALISER", actualiser);
MonServeurWeb.on("/START_REC", demarrer_R);
MonServeurWeb.on("/RESUME_REC", continue_R);
MonServeurWeb.on("/STOP_REC", arreter_R);
MonServeurWeb.on("/DOWNLOAD", handledownload);
//=========================== Démarrer le serveur ============================
MonServeurWeb.begin(); //Start server
Serial.println("Serveur HTTP démarré");
Serial.println("==========================================================");
// initialiser I2c et le LM75
Wire.begin();
Wire.beginTransmission(LM75_I2C_ADR);
Wire.write(0); // pointeur sur registre de température
Wire.endTransmission();
// démarrer le client NTP
Date_heure.begin();
}
//==============================================================
// LOOP
//==============================================================
unsigned long ancien=0, nouveau=0;
void loop(void){
MonServeurWeb.handleClient(); // gérer les requêtes des clients
nouveau = millis();
if ((unsigned long)(nouveau - ancien) >= 5000) { // une mesure toutes les 5 secondes à peu près
Date_heure.update();
Wire.requestFrom(LM75_I2C_ADR, 2); // demander 2 octets à partir de la position courante du pointeur
msb = Wire.read();
lsb = Wire.read();
T = msb + (lsb >> 5) * 0.125; //Température
if(record_status){
DataFileID.print(Date_heure.getFormattedDate()+";");
DataFileID.print(Date_heure.getFormattedTime()+";");
DataFileID.println(T,3);
Serial.print(Date_heure.getFormattedDate()+" ");
Serial.print(Date_heure.getFormattedTime()+" ");
Serial.println(T,3);
N_ECH++;
}
ancien = nouveau;
}
}
Pendant la phase de développement d'un projet, il est tout à fait pratique de téléverser les programmes dans l'ESP8266 via une liaison série sur le câble USB. Quand le projet est terminé et le module est placé dans son boîtier final, il peut s'avérer nécessaire après utilisation d'apporter des mises à jour au programme. Dans ce cas, le téléversement via une connexion Wireless est bien plus pratique. On parle de mise à jour OTA (Over The Air). Pour y parvenir, il faut ajouter au programme une partie dont la tâche est de rester à l'écoute de l'IDE Arduino pour monitorer le téléversement. La nouvelle version du programme et d'abord copiée dans la zone mémoire OTA avant d'écraser l'ancienne version dans la mémoire programme. Pour cette raison, la mémoire programme doit avoir au moins la taille de la mémoire OTA. La figure ci-dessous illustre un exemple de répartition de la mémoire flash du module
Je n'ai pas d'exemple à vous proposer pour l'instant. Ça viendra peut être un jour
Bien que je n'aime pas trop ce module. Je lui consacre un petit paragraphe parce que je sais que beaucoup de gens l'ont déjà acheté.
Le module ESP8266 fonctionne dans deux modes différents:
l faut utiliser un adaptateur USB-Serial. Si vous avez essayé, vous savez déjà que ça ne marche pas bien. Même le petit adaptateur avec le connecteur jaune spécialement fait pour ça ne marche pas. Le problème vient du fait que ESP-01 n'a rien qui lui permet de passer d'un mode à l'autre. Pour le programmer, il faut le brancher à un adaptateur USB-Serial en ajoutant deux boutons poussoir comme indiqué sur la figure ci dessous:
Les broches RST, EN et IO0 disposent de résistances de pull-up internes. Il n'est pas nécessaire de rajouter des résistances externes avec les boutons.
Un autre problème vient s'ajouter: La majorité des modules USB-Serial du commerce délivrent un signal TX de 5V alors que le module ESP-01 attend un signal 3.3V sur sa pate RX. il faut essayer de trouver une USB-Serial qui délivre un TX de 3.3V ou alors prévoir un diviseur de tension pour abaisser la tension (moi je l'ai branché directement à mes risques et périls).
Et comme si ça ne suffisait pas, le connecteur du ESP-01 n'a pas le bon écartement et ne peut être placé sur les carte d'essai breadboard. Il faut acheter (ou bricoler) un adaptateur.
Comme vous pouvez le constater, ce module n'est pas vraiment un cadeau. On ne pas pas dire que le gars qui l'a conçu soit très inspiré. J'en avais plusieurs, il ont tous fini à la poubelle.
Si vous n'avez pas un adaptateur USB-Serial, pas de panique, il y'en a un sur l'Arduino. Mais il faut faire attention car dans ce cas le module ESP-01 et branché en parallèle avec le processeur du Arduino. Par conséquent:
Ne pas faire ça si vous voulez utiliser votre module à l'aide des commandes AT car vous allez écraser le firmware AT
Pour tester si votre module contient un firmware AT: