[Résolu] Affichage de données sur page Web (carte SD / esp8266)

Bonjour,

Quelques explications :

Je suis entrain de réaliser une station météo connectée avec à ma disposition :

  • une carte Arduino mega
  • un capteur de temperature DHT22
  • un écran LCD
  • un module pour carte SD (carte SD de 2Go)
  • une horloge externe
  • l'esp8266

L'Arduino mega s'occupe de l'acquisition de la température et de l'humidité ainsi que de l'affichage sur l'écran LCD.
Toutes les heures, les valeurs de température et d'humidité sont envoyées par communication série à l'esp8266 qui les sauvegarde dans la carte SD.
Remarque : l'esp8266 est utilisé en mode soft access point dans le but de rendre la station météo totalement indépendante.

mon souci :

Je souhaiterais afficher toutes les valeurs sauvegardées sur une page html.
De cette facon il est possible d'accéder à l'historique des valeurs depuis son smartphone.

Je suis donc à la recherche de conseils, pistes, liens, exemples …

Cordialement

Dorian

Quel ESP8266 utilises-tu ? Si c'est un NodeMCU, il pourrait faire l'ensemble et tu n'aurais pas besoin du Mega.

L'ESP8266 peut faire office de serveur web et créer une page web pour afficher les données mesurées. On trouve des tutos en cherchant un peu. Ce site regorge de bons tutos.

Merci pour votre réactivité.

Oui il s'agit d'un NodeMCU. Il est vrai que j'aurais pu me passer de la Mega, je m'en suis aperçu après commande …

Etant débutant, de nombreux tutos m'ont permis de créer la page html et afficher les valeurs en temps réel. Désormais cela fonctionne bien.

Mon souci apparaît lorsque je veux afficher un nombre important de valeur provenant de la carte SD.

mon fichier txt situé sur la carte SD est de la forme :

09.07.2019 Tuesday 13:00 Temperature : 25.20 °C Humidity : 47.30 %
09.07.2019 Tuesday 14:00 Temperature : 25.10 °C Humidity : 46.40 %
09.07.2019 Tuesday 15:00 Temperature : 25.12 °C Humidity : 45.20 %
….
10.05.2020 Tuesday 16:00 Temperature : 25.02 °C Humidity : 47.20 %

Je sais qu'il est possible de lire la carte SD, stocker les valeurs dans un tableau de caractère et enfin les afficher sur la page web. Cependant, mon ficher txt étant en constante evolution, je crains de dépasser les limites de mémoire imposées par l esp8266 NodeMCU.
Je cherche donc le moyen de palier à ce problème.

Cordialement

Dorian

Si le fichier ne te sert qu'à faire l'affichage, c'est à dire si tu n'as pas besoin de le consulter toi-même, tu peux le compacter en n'y mettant que ce qui sert.
Au lieu de

09.07.2019  Tuesday  13:00  Temperature : 25.20 °C  Humidity : 47.30 %

tu peux stocker

9,7,19,13,25.2,47.3

Ca prend déjà moins de place. Il faut ensuite adapter ta fonction de lecture du fichier à son contenu.

Bonjour,

Limiter la longueur du fichier dès le début contribuera à faciliter les futures recherches.

En créant un fichier par mois, cela limiter le nombre d'enregistrements à moins de 800 par fichiers.

Il suffit ensuite de nommer chaques fichiers en utilisant un format de type mmaa.dat pour commencer à affiner la recherche.

Pour gagner de la place et limiter les informations à lire, il est préférable d'enregistrer uniquement les données sans l'habillage qui sera rajouté lors de l'envoi du code html. Un simple séparateur suffira ';' ou 'tab' ou autres.

L'utilisation d'un timestamp de type Unix pourrait être un plus pour chaque enregistrement si tu comptes modifier la fréquence d'échantillonnage par la suite.

Merci pour vos conseils !

En effet, L'habillage dans le fichier txt n'est pas nécessaire, il peut donc se mettre sous forme contractée.

La fréquence d'échantillonnage n'est pas amenée à changer mais merci pour l'idée du timestamp de type Unix, je ne connaissais pas.

Si je comprends bien, l'idée semble être de scinder mon fichier txt et de lire mes valeurs mois par mois (par appui sur un bouton par exemple). De cette manière la RAM de l'esp8266 est respectée.

Pour l'affichage sur la page web, je dois alors écrire une fonction qui lit les valeurs d'un mois sur la carte SD et les place dans un tableau de caractère dans le but d'être envoyé sur la page web de la forme suivante :

String getPage() {
  String page = "<html lang=fr-FR><head><style> html { height:100%;font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}</style>";
  page += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
  page += "<title>Wheather station</title>";
  page += "</head>";
  page += "<body><style>body {min-height: 90%;margin: 0;padding: 10px;relative;font-family: Arial; Color: black;}</style>";

  page += ici mon tableau de valeur ?

  page += "</body>";
  return page;

Je suis débutant sur Arduino et je ne maîtrise pas bien les pointeurs …
Auriez vous un bout de code permettant de realiser le remplissage du tableau de char ?

L'avantage d'utiliser plusieurs fichiers est qu'en cas de mémoire insuffisante dans le spiffs (flash de l'esp utilisé pour stocker les fichiers), il suffit de supprimer les fichiers les plus anciens pour y stocker les nouveaux.

De plus, en cas de problème sur l'un des fichiers, on limite le nombres de données perdues.

Le transfert des fichiers (téléchargement des mmaa.dat) depuis l'interface Web permettrait de faire une sauvegarde des données sur un pc.
Il faudra donc réfléchir sur la façon de stocker les échantillons (format) pour une utilisation éventuelle sur un tableur.

Tu n'es pas obliger d'afficher le fichier complet. A toi de voir. Un datepicket pourrait te servir.

Tu comptes afficher tes informations sous forme graphique ou en brute ?

Au départ je comptais simplement les afficher de manière brute. Un simple copier-coller sur excel aurait permis de tracer l'évolution de la température/humidité en fonction du temps. Mais

Le transfert des fichiers (téléchargement des mmaa.dat) depuis l'interface Web

m'intéresse !!

Un datepicket pourrait te servir.

C'est une idée alléchante mais je ne suis pas certain de pouvoir coder ca ...

Tu peux te simplifier la vie avec des codes Javascript déjà existants comme le jquery.
Regarde la source un peu plus bas dans la page.

Il suffit d'intégrer les fichiers nécessaires (Javascript, CSS) dans une version Lite que tu peux créer sur le site en fonction de tes besoins et de les mettre dans le spiffs.

Lorsque le navigateur en fera la demande suite aux liens dans ta page html, le serveur devra les envoyer directement en fichiers brutes.

Édit: il en existe d'autres. A toi de choisir aussi par rapport à la place occupée dans le spiffs.

Lorsque le navigateur en fera la demande suite aux liens dans ta page html, le serveur devra les envoyer directement en fichiers brutes.

Désolé, je ne suis pas certain de tout saisir. Cela signifie que lors du choix du mois grâce au datepicket l'esp8266 envoie le fichier txt (dans un format adapté) sur la page web ?

L'insertion d'un datepicket me semble être du "plus" pour mon application. J'aimerais pour l'instant afficher les valeurs sauvegardées sur un mois et, par l'utilisation de deux boutons : "mois suivant" "mois précédent", afficher l'ensemble de l'historique.

Je vais essayer de me renseigner davantage sur le transfert des fichiers depuis l'interface web même si pour l'instant un affichage simple me convient, exemple :

Station météo

Température : 25 °C
Humidité : 45 %

historique

Juillet

date / heure / T°C / Hum %

9,7,19,13,25.2,47.3
9,7,19,14,26.2,43.3
9,7,19,15,25.2,44.3
9,7,19,16,27.2,47.3

29,7,19,20,25.2,48.3

<mois précédent mois suivant >

Il faut pour cela que j'écrive les valeurs de ma carte SD dans un tableau de char. C'est la que j'ai le plus de problème ..

Pour ce qui est de la lecture de tes données, regarde ici.

Utilise le caractère de fin de ligne pour obtenir une ligne complète.
Il te suffira ensuite d'utiliser le caractère ',' ou 'tab' pour séparer les données de chaque champs.

Pour les fichiers brutes, je parlais des fichiers Javascript et CSS qui sont à ajouter dans ton projet.

Ce sont les sources de la fonction datepicker qui seront ensuite télécharger par le navigateur.
Ce sont les liens ajoutés dans ta page html qui indiqueront au navigateur ou les télécharger.
Et ton serveur devra les envoyer, suite à cette demande, en les prenant dans le spiffs.

Édit: la fonction strtok est utile dans ce cas de figure :wink:
Et un lien intéressant pour un exemple de serveur qui retourne des fichiers présents dans le spiffs.

Pour écrire des valeurs dans un tableau de char, ton meilleur ami est sprintf

L'utilisation de la fonction datepicker nécessite donc une connexion internet or j'utilise l'esp8266 comme Access point dans le but d'être totalement indépendant.
Je pense que je vais tout d'abord essayer l'affichage que je vous ai montré en exemple.

Merci beaucoup pour vos conseils !!

ton meilleur ami est sprintf

Il me semble en effet que cette fonction est bien pratique.

J'essaie tout ça demain et je vous tiens au courant.

Encore merci :slight_smile:

Dorian

Non, les fichiers nécessaires pour utiliser le datepicker sont stockés dans le spiffs et téléchargé depuis le serveur de l'esp vers le navigateur du téléphone /pc/tablette.

Mais tu as raison. Commence par faire simple et tu pourra ensuite ajouter les petits plus par la suite.

C'est toujours comme ça que l'on procéde :wink:

Je reviens un peu plus tard que prévu. J'ai pu faire quelques recherches sur le téléchargement de mes données à partir de la carte SD.
Je suis tombé sur un code qui correspondait exactement à ce que je souhaitais initialement.

Je vous mets ci-dessous le code adapté à mon cas.

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <SPI.h>
#include <SD.h>

const char* ssid = "SunBrake";
const char* password = "12345678";

ESP8266WebServer server(80);

void setup() {
  delay(500);
  Serial.begin(9600);
  Serial.println();
  WiFi.softAP(ssid, password);
  IPAddress apip = WiFi.softAPIP();
  Serial.print("Please visit: \n");
  Serial.println(apip);

  // see if the card is present and can be initialized:
  if (!SD.begin(4)) {
    Serial.println("\nCard failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("\nCard Initialized.");

  server.on("/", handleRoot);
  server.onNotFound(handleNotFound);
  server.on("/download", handleDownload);
  server.on("/display", handleDisplay);
  server.begin();
  Serial.println("HTTP server started");
}

void loop() {
  server.handleClient();
}

void handleRoot() {
  server.send(200, "text/html", home());
}
void handleNotFound() {
  server.send(404, "text/plain", "404: Not found");
}

void handleDisplay() {
  File dataFile = SD.open("data_history.txt");
  int fsizeDisk = dataFile.size();
  Serial.print("fsizeDisk: ");
  Serial.println(fsizeDisk);
  size_t fsizeSent = server.streamFile(dataFile, "text/plain");
  Serial.print("fsizeSent: ");
  Serial.println(fsizeSent);
  dataFile.close();
}

void handleDownload() {
  File dataFile = SD.open("data_history.txt");
  int fsizeDisk = dataFile.size();
  Serial.print("fsizeDisk: "); Serial.println(fsizeDisk);
  String WebString = "";
  WebString += "HTTP/1.1 200 OK\r\n";
  WebString += "Content-Type: text/plain\r\n";
  WebString += "Content-Disposition: attachment; filename=\"data_history.txt\"\r\n";
  WebString += "Content-Length: " + String(fsizeDisk) + "\r\n";
  WebString += "\r\n";
  server.sendContent(WebString);
  char buf[1024];
  int siz = dataFile.size();
  while (siz > 0) {
    size_t len = std::min((int)(sizeof(buf) - 1), siz);
    dataFile.read((uint8_t *)buf, len);
    server.client().write((const char*)buf, len);
    siz -= len;
  }
  Serial.print(siz);
  Serial.println(" Bytes left!");
  dataFile.close();
}

String home() {
  String page = "<html lang=fr-FR><head><style> html { height:100%;font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}</style>";
  page += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
  page += "<title>Wheather station</title>";
  page += "</head>";
  page += "<body><style>body {min-height: 90%;margin: 0;padding: 10px;relative;font-family: Arial; Color: black;}</style>";
  page += "<h1>Weather Station </h1><style> h1 {font-size:250%; Color:#324cbd; }</style>";
  page += "<h3>In real time </h3>";
  page += "<p>Temperature : ";
  page += "25 "; // random value
  page += " C</p>";
  page += "<p>Humidity : ";
  page += "45 "; // random value
  page += " %</p>";
  page += "<p><a href=\"download\"><button>Download history</button></a>";
  page += "<a href=\"display\"><button>View history</button></a></p>";
  page += "<style> p {font-size:200%; Color: #7e4060; font-weight:bold; }</style>";
  page += "<form action='/' method='POST'>";
  page += "<input class=\"button\" type=\"submit\" name=\"Refresh\" value=\"Refresh\">";
  page += "<style>button { background-color: #195B6A; border: 2px solid white;border-radius:10px; color: white; padding: 16px 40px;</style>";
  page += "<style>text-decoration: none; font-size: 20px; margin: 1px; cursor: pointer;}</style>";
  page += "</body>";
  page += "<footer>Braking Research Centre</footer></html><style>footer{position:absolute;bottom:0;left:0;right:0;display:flex;justify-content:center;}</style>";
  return page;
}

Maintenant que le code fonctionne j'aimerais habiller ma page d'accueil avec une photo provenant de ma carte SD.

C'est le lien de ta page html (../photo.jpeg) qui va créer une requête sur le serveur de ce fichier.

Tout comme le fait handleDownload() pour un fichier 'txt', tu dois pouvoir répondre à cette demande en étoffant les possibilité de téléchargement de fichiers de type différents (*.js, *.css, *.jpeg,....).

Un lien donné plus haut te montre comment faire.

Merci beaucoup, je vais essayer !
Je passe mon problème en résolu :wink: