mercredi 13 février 2019

Un WEB server ARDUINO sur Ethernet (avec graphes HighCharts)

Un WEB server ARDUINO sur Ethernet

(avec graphes HighCharts)


Nous allons essayer dans cet article de monter un WEB server avec une carte ARDUINO et un shield Ethernet.
Ensuite, nous allons voir comment rendre ce serveur plus interactif et comment afficher des graphes HighCharts.

NOTE: HighCharts est une librairie d'affichage de graphes JAVASCRIPT.
Elle est gratuite seulement dans le cadre de développement d'applications non commerciales.

1. Le matériel

Le matériel utilisé pour cette expérimentation est le suivant :
  • une carte ARDUINO UNO ou NANO
  • un shield Ethernet
  • un câble Ethernet
  • un module SDCard
Le composant de base du shield Ethernet est le W5100, W5200 ou W5500. Il peut se présenter sous diverses formes :
Cette carte s'enfiche directement sur une carte UNO, ce qui ne l'empêche pas de pouvoir être utilisée avec une NANO ou une MINI. Aucun câblage n'est nécessaire. Elle possède de plus un lecteur µSD.
Celui-ci a l'avantage d'avoir un format réduit. C'est l'idéal sous l'on l'utilise avec une carte NANO ou MINI.
J'ai choisi cette carte pour mon projet.

2. Le câblage

L'ARDUINO communique avec la carte Ethernet à l'aide du bus SPI. Si vous réalisez le câblage avec des fils, celui-ci est réalisé comme ceci :


Ce schéma ne montre pas le lecteur de carte SDCard, inutile dans un premier temps.

Branchez un câble Ethernet entre le module Ethernet et un switch ou un port de votre box.

3. L'IDE ARDUINO

Il faut installer une librairie :
https://github.com/arduino-libraries/Ethernet.git

De préférence installez la dernière version.

4. Votre réseau

Commencez par récupérer l'adresse de votre sous-réseau :

Sous LINUX, tapez ifconfig dans un terminal :

$ ifconfig
eno1 Link encap:Ethernet  HWaddr 54:04:a6:13:da:1c 
          inet adr:192.168.1.121  Bcast:192.168.1.255  Masque:255.255.255.0
          adr inet6: fe80::b571:1928:a596:da5e/64 Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Packets reçus:206702 erreurs:0 :0 overruns:0 frame:0
          TX packets:167704 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:1000
          Octets reçus:207586902 (207.5 MB) Octets transmis:21594954 (21.5 MB)
          Interruption:18 Mémoire:fb500000-fb520000


Sous WINDOWS :
https://www.digitalcitizen.life/find-ip-address-windows

5. Le sketch

Le sketch à charger est celui-ci, l'exemple WebServer de la librairie :
https://github.com/arduino-libraries/Ethernet/blob/master/examples/WebServer/WebServer.ino

Adaptez l'adresse IP à votre réseau. Les trois premiers éléments de l'adresse réseau doivent être les mêmes que celle de votre PC ou de votre box :

IPAddress ip(192, 168, 1, 177);

Augmentez le baudrate de la ligne série et retirez ou commentez les lignes suivantes dans le setup :

  Serial.begin(115200);
//  while (!Serial) {
//    ; // wait for serial port to connect. Needed for native USB port only
//  }
  Serial.println("Ethernet WebServer Example");


Chargez le sketch.

6. Tests de base

L'utilitaire ping vous permet de voir si la liaison avec le module fonctionne :

$ ping 192.168.1.177
PING 192.168.1.177 (192.168.1.177) 56(84) bytes of data.
64 bytes from 192.168.1.177: icmp_seq=1 ttl=128 time=0.118 ms
64 bytes from 192.168.1.177: icmp_seq=2 ttl=128 time=0.072 ms
64 bytes from 192.168.1.177: icmp_seq=3 ttl=128 time=0.096 ms
64 bytes from 192.168.1.177: icmp_seq=4 ttl=128 time=0.103 ms
^C
--- 192.168.1.177 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3000ms
rtt min/avg/max/mdev = 0.072/0.097/0.118/0.018 ms


Ping ne vous permet pas de savoir si le WebServer fonctionne. Il vous dit tout simplement : le module Ethernet est visible le réseau.

Ensuite il ne vous reste plus qu'à vérifier si le WebServer répond. Entrez l'adresse de votre WebServer dans votre navigateur :

Nous voici donc avec un WebServer en état de marche. Il n'affiche pas grand chose d'intéressant, simplement l'état de pins analogiques 0 à 5.

7. Cahier des charges

Le serveur que nous allons développer doit accepter 6 URLs
  • affichage d'une page d'accueil avec un message et des boutons
  • affichage des températures sur la journée
  • affichage de l'humidité sur la journée
  • affichage de graphiques température et humidité 
  • affichage de pages à l'aide de templates stockées en mémoire FLASH
  • affichage de pages à l'aide de templates stockées sur carte SD
Le serveur doit accepter des arguments associés à certaines URLs :
  • affichage de la températures à une heure précise
  • affichage de l'humidité à une heure précise
Ces arguments seront passés dans la requête par le navigateur en utilisant la méthode classique :

http://ADDRESSE_IP/url?argument1=valeur1&argument2=valeur2

Le serveur doit être modulaire et aider à simplifier l'écriture de la partie applicative.
Toutes les parties génériques doivent être réutilisable et facilement livrables sous forme de librairie.

7.1. Méthode GET ou POST

La comparaison porte principalement sur la récupération des données d'un formulaire.
Lorsque l'on valide un formulaire la requête envoyée par le navigateur est formée d'une nouvelle URL suivie du caractères '?' puis d'une suite d'arguments nom=valeur séparés par des caractères '&'.

Imaginons un formulaire permettant de saisir une entrée de répertoire. L'URL pourrait ressembler à ceci :

http://form_contacts?name=dupont&age=25&tel=0102034455&email=dupont@orange.fr

Deux méthode d'envoi existent : GET ou POST.

En méthode GET les arguments suivent l'URL et on les voit dans la barre d'adresse du navigateur lorsque l'on valide le formulaire.
Les arguments font partie de l'URL, qui elle-même fait partie de l'entête HTTP.

En méthode POST les arguments ne sont pas visibles dans la barre d'adresse du navigateur lorsque l'on valide le formulaire.
Les arguments sont envoyés après l'entête HTTP. Un double caractère '\n' signale la fin de l'entête et donc le début des arguments.

Dans cet exemple les requêtes GET et POST sont traitées.
Un autre article expose la mise en œuvre des requêtes POST :
https://riton-duino.blogspot.com/2020/03/arduino-ethernet-esp32-comparons.html

7.2. Caractère échapés

Des caractères échappés peuvent apparaître dans une URL:

  • %20 : espace
  • %40 : @
  • etc
Ceux-ci doivent être décodés par nos soins. La classe EthernetServer ne prévoit rien à ce sujet.
Le code présenté ici dispose d'une méthode pour réaliser cette conversion.

8. Le code

Afin de nous simplifier la vie par la suite, nous allons d'une part découper ce code en différents fichiers et lui donner une structure un peu plus sympathique et évolutive.

Les classes EthernetServer et EthernetClient existent déjà. Par contre ces classes ne fournissent aucun support de gestion des requêtes HTTP.

La classe que nous allons ajouter sera nommée naturellement http_request. Son rôle sera de recevoir les requêtes HTTP et de les exécuter. Elle regroupe toutes les données et les méthodes nécessaires pour traiter une requête.Elle porte donc bien son nom. Ceci est important car en matière de développement orienté objet, le nom d'une classe doit être un nom et non pas un verbe ou un adjectif.

Nous n'allons pas adopter une structure à une seule fonction de traitement des requêtes, composée d'une suite de comparaisons :
  • si URL == URL1
    • alors envoyer la réponse RESP1
  • sinon si URL == URL2
    • alors envoyer la réponse RESP2
  • etc.
Nous allons plutôt découper en fonctions, une pour chaque URL.

Les fonctions de traitement associées à chaque URL ne doivent pas appartenir à la classe de base, censée être une brique générique, c'est à dire réutilisable. Par contre cette classe de base doit les exécuter et donc les connaître.

Dans ce but nous allons introduire la notion de fonctions "handlers". Ces handlers sont situés dans l'application. Chaque handler est associé à une URL précise et permet l'envoi d'une réponse adaptée à cette URL.
L'application enregistre un certain nombre de couples URL / handler auprès de la classe http_request qui les stocke dans une liste, une sorte de dictionnaire.

Lorsqu'une requête est reçue par la classe http_request, celle-ci analyse la requête, en extrait la racine de l'URL ainsi que les arguments et exécute le handler trouvé dans la liste. Si le handler n'existe pas, elle renvoie "Erreur 404".

Le code source est découpé en 4 parties :
  • un fichier http.h : définition de la classe http_request
  • un fichier http.cpp : implémentation de la classe http_request, le moteur :
    • réception des requêtes
    • analyse (parsing) de l'URL et des arguments
    • exécution des handlers fournie par l'application 
    • handler "404 Not found"
  • un fichier pages.cpp : une template
  • un fichier w5100.ino : l'application et ses handlers
Pour récupérer le projet voir plus bas :  10. Téléchargements.

http.h :

Dans ce fichier il y a quelques valeurs modifiables :

// USE_TEMPLATE & USE_TEMPLATE_FILE only work on MEGA
// define to 0 for UNO or NANO boards
#define USE_TEMPLATE          1
#define USE_TEMPLATE_FILE     1


// La longeur maximale d'une requête
#define REQUEST_MAX     150
// La longeur maximale d'une URL
#define URL_MAX               80
// La longeur maximale de la chaîne d'arguments
#define ARGSTR_MAX            100
// Le nombre maximal d'arguments suivant une URL
#define ARG_MAX         5

// Le nombre maximal d'arguments suivant une URL
#define ARG_MAX         5

// Le nombre maximal d'arguments suivant une URL
#define ARG_MAX         5

// Le nombre maximal d'URLs (handlers) différentes
#define HANDLER_MAX           10
// Le nombre maximal de tags dans une template
 #define NAMESPACE_MAX         10 
// La taille maximale d'un tag
#define NAMESPACE_NAME_MAX    20
 // La taille maximale d'une donnée
 #define NAMESPACE_DATA_MAX    20

Attention : ces constantes conditionnent grandement l'espace occupé en mémoire RAM ou FLASH.

Si l'on veut par exemple activer USE_TEMPLATE il vaut mieux disposer d'une carte MEGA, avec un module SD si l'on veut en plus bénéficier des fichiers templates sur SD.

Si l'on désire activer les traces de la classe http_request, dé-commenter cette ligne :

//#define DEBUG  // uncomment to debug

http.cpp :

La méthode http_request::read est la pièce maîtresse de cette classe. Elle reçoit la requête du navigateur et la traite à l'aide des fonction handlers fournies préalablement par l'application.

On voit un handler http_request::handleNotFound qui est appelé lorsque l'url est invalide.

La méthode http_request::parse utilise strtok pour découper la requête. Cette fonction modifie la requête en y insérant des '\0' à la place des séparateurs. Les adresses des données sont sauvegardées dans des pointeurs.
Cette technique permet de ne pas consommer de mémoire supplémentaire pour stocker les données de la requête.

pages.cpp :

Ce fichier contient une page HTML convertie en chaîne C, une template.

w5100.ino :

Dans la fonction setup() l'application fournit 6 handlers à l'instance http_request à l'aide de la méthode http_request::setHandler :
  • handleRoot
  • handleTemp
  • handleHum
  • handleGraphics
  • handleTemplate 
  • handleTemplateFile
Ces handler permettent de découper facilement l'application en fonctions.

La fonction loop() est réduite à sa plus simple expression.

Le serveur propose 6 pages :
  • une page d'accueil avec un titre et 7 boutons "Température", "Humidité", "Graphics", "Template", "Template File", "Température à 10:00", "Humiditéà 10:00".
  • une page températures
  • une page humidité
  • une page graphique
  • une page template 
  • une page template-file
Le lien entre les boutons de la page d'accueil et les fonctions "handlers" est fait comme ceci :

"<input type=\"button\" onclick=\"location.href='/temp';\""
"value=\"Go to temperature page\" style=\"margin:5px; color:darkblue; border-color:blue;\"/>"


Si l'utilisateur clique sur le bouton "Température", le navigateur envoie une requête "/temp".
La fonction handler enregistrée pour cette URL est handleTemp. Le serveur appellera donc cette fonction s'il reçoit "/temp".

Un bouton peut bien sûr générer une URL avec des arguments :

"<input type=\"button\" onclick=\"location.href='/temp?hour=10:00';\""
"value=\"Temperature at 10:00\" style=\"margin:5px; color:darkblue; border-color:blue;\"/>"


Les URLs /temp et /hum accepte un argument : hour

Voici le résultat affiché en fonction de l'URL :

http://192.168.1.177/temp

temperature at 07:00 : 20.10°C
temperature at 08:00 : 20.00°C
temperature at 09:00 : 19.80°C
temperature at 10:00 : 20.10°C
temperature at 11:00 : 20.05°C
temperature at 12:00 : 20.20°C
temperature at 13:00 : 20.30°C
temperature at 14:00 : 20.10°C
temperature at 15:00 : 19.80°C
temperature at 16:00 : 20.00°C
temperature at 17:00 : 20.20°C
temperature at 18:00 : 20.10°C


http://192.168.1.177/temp?hour=10:00

temperature at 10:00 : 20.10°C

http://192.168.1.177/hum

Humidity at 07:00 : 35%
Humidity at 08:00 : 35%
Humidity at 09:00 : 34%
Humidity at 10:00 : 36%
Humidity at 11:00 : 35%
Humidity at 12:00 : 36%
Humidity at 13:00 : 35%
Humidity at 14:00 : 34%
Humidity at 15:00 : 33%
Humidity at 16:00 : 35%
Humidity at 17:00 : 36%
Humidity at 18:00 : 36%


http://192.168.1.177/hum?hour=10:00

Humidity at 10:00 : 36%

http://192.168.1.177/graphics :
http://192.168.1.177/template :
http://192.168.1.177/template-file :



9. Afficher des graphes

Nous allons aborder la partie traitant de l'affichage de graphiques sur une page HTML.Des objets JAVASCRIPT HighCharts sont utilisés. Différentes options peuvent être appliquées à ces objets.

Le code de l'application génère le code correspondant à chaque objet HighCharts. Voici à quoi ressemble le code source HTML vu depuis le navigateur :

<!DOCTYPE HTML>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<html><head>
<link href="data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJCQk/yQkJP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/0ZGRv8XFxf/////AP///wD///8A////AP///////////////+7u7v9OTk7/sbGx/11dXf9KSkr///////////+lpaX/4eHh/////wD///8A////AAAAAP8AAAD///////////+8vLz///////X19f/t7e3/gICA/5eXl////////////wAAAP8AAAD/////AKysrP8AAAD/AAAA/wAAAP+enp7/////////////////////////////////5OTk/wAAAP8AAAD/AAAA/6ysrP////8A////AP///wCurq6PlJSUzP///wD///8A////AP///wD///8A////AJSUlMyQkJDY////AP///wD///8A////AP///wD///8AAAAA/wAAAP////8A////AP///wD///8A////AP///wAAAAD/AAAA/////wD///8A////AP///wD///8AAAAA/wAAAP8AAAD/////AP///wD///8A////AP///wD///8AAAAA/wAAAP8AAAD/////AP///wD///8ACwsL/wAAAP8AAAD/lJSU2AAAAP8AAAD/cnJy/1hYWP8AAAD/AAAA/2FhYdgHBwf/AAAA/wAAAP////8A////AP///wD///8A////AP///wAAAAD/s7Oz/wAAAP8AAAD/x8fH/wAAAP////8A////AP///wD///8A////AP///wD///8A////AP///wAAAAD/AAAA/////wAAAAD/AAAA/////wAAAAD/AAAA/////wD///8A////AP///wD///8A////AP///wD///8AAAAA/wAAAP+qqqqlAAAA/wAAAP9lZWXjAAAA/wAAAP////8A////AP///wD///8A////AP///wD///8A////ALa2tv////8A9vb2UQAAAP8AAAD/////Uf///wDAwMD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBcXFz/AAAA/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AADAAwAAwAMAAIABAAAAAAAA5+cAAOfnAADH4wAAgAEAAPgfAADyTwAA8A8AAPZvAAD+fwAA//8AAA==" rel="icon" type="image/x-icon" />
<title>
Graphics
</title>
</head>
<body>
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script type="text/javascript">
jQuery(document).ready(function() {
  var optionsTemp = {
    chart: {
      renderTo: 'containerTemp',
      type: 'line'
    },
    title: {
      text: 'Temperature'
    },
    xAxis: {
      categories: ['07:00', '08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00']
    },
    yAxis: {
      title: {
        text: 'Temperature (°C)'
      }
    },
    series: [{
      name: 'Temperature',
      data: [20.10, 20.00, 19.80, 20.10, 20.05, 20.20, 20.30, 20.10, 19.80, 20.00, 20.20, 20.10]
    }]
  };
  var optionsHum = {
    chart: {
      renderTo: 'containerHum',
      type: 'line'
    },
    title: {
      text: 'Humidity'
    },
    xAxis: {
      categories: ['07:00', '08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00']
    },
    yAxis: {
      title: {
        text: 'Humidity (%)'
      }
    },
    series: [{
      name: 'Humidity',
      data: [35, 35, 34, 36, 35, 36, 35, 34, 33, 35, 36, 36]
    }]
  };
  var chartTemp = new Highcharts.Chart(optionsTemp);
  var chartHum = new Highcharts.Chart(optionsHum);
});</script>
<div id="containerTemp" style="width:100%; height:400px;"></div>
<div id="containerHum" style="width:100%; height:400px;"></div>
</body></html>

Trois parties principales se distinguent :
Le code d'importation des scripts JQuery et HighCharts :

<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>

Le code lié à la déclaration des deux objets chartTemp et chartHum

jQuery(document).ready(function() {
  var optionsTemp = {
    chart: {
      renderTo: 'containerTemp',
      type: 'line'
    },
...
  var optionsHum = {
    chart: {
      renderTo: 'containerHum',
      type: 'line'
    },
...
  var chartTemp = new Highcharts.Chart(optionsTemp);
  var chartHum = new Highcharts.Chart(optionsHum);
 
Le code HTML permettant de créer les deux containers containerTemp et containerHum :
<div id="containerTemp" style="width:100%; height:400px;"></div>
<div id="containerHum" style="width:100%; height:400px;"></div>

9.1. Générer le JAVASCRIPT en direct

Il est possible de renvoyer du code JAVASCRIPT directement depuis le code de l'application :

  client->print(F("<script src = \"https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js\"></script>\n"
                  "<script src=\"https://code.highcharts.com/highcharts.js\"></script>\n"));


Cette méthode est assez lourde et convient pour des cas simples.Dans l'exemple, la fonction handleGraphics emploie cette méthode.

9.2. Générer le JAVASCRIPT  à l'aide d'une template

Il est plus judicieux de passer par une template dans laquelle on place des tags représentant les données (voir pages.cpp) :

const char PROGMEM tempHumTemplate[] = "<script src = \"https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js\"></script>\n"
"<script src=\"https://code.highcharts.com/highcharts.js\"></script>\n"
"<script type=\"text/javascript\">\n"
// ...

"      categories: [$(TEMP_CATEGORIES)]\n"// ...
"<div id=\"containerHum\" style=\"width:100%; height:400px;\"></div>\n";

Dans cet exemple partiel, on voit un tag $(TEMP_CATEGORIES) qui sera repéré par le code du serveur et remplacé par les données réelles dans la page HTML à renvoyer.

Le ligne :

"      categories: [$(TEMP_CATEGORIES)]\n"

Sera remplacée par :

"      categories: ['07:00', '08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00']\n"

Cette méthode est beaucoup plus légère.
Dans l'exemple, la fonction handleTemplate emploie cette méthode.

Le fichier pages.cpp est un fichier HTML sous forme de chaîne de caractères C.

const char PROGMEM tempHumTemplate[] = "Fichier HTML";

Dans le code HTML et JAVASCRIPT il suffit d'échaper les " par des \", et d'encadrer chaque lignes par des " et "\n.

C'est une chaîne de caractère en FLASH (PROGMEN) afin d'éviter de consommer de la mémoire RAM.

Il serait intéressant d'utiliser un support mémoire (SD ?) pour y stocker les templates, et d'écrire un petit bout de code permettant de lire la template et d'y insérer les données nécessaires (comme on le ferait en PYTHON avec Cheetah).

9.3. Générer le JAVASCRIPT  à l'aide d'une template sur SD

Il ne reste plus qu'à ajouter un module SDCard :
C'est un modèle 3.3V.
Sur la photo on ne voit pas les pins DO DI : la sérigraphie SPI se trouve derrière.
L'alimentation se fait en 3.3V.

Il faut installer une librairie :
https://github.com/greiman/SdFat.git

De préférence installez la dernière version.

Il fonctionne sans problème avec une NANO et l'exemple QuickStart.

Mais vous l'aurez sans doute deviné : le module SDCard et la carte W5100 ne cohabitent pas.

Les deux branchés ensemble sur le bus SPI :
Le W5100 fonctionne sans problème.
Le module SDCARD ne fonctionne pas.

J'ai fait pas mal de recherches sur des forums.
La réponse est souvent : mettre les CS du W5100 à 1.
Et beaucoup de gens ont résolu leur problème comme ça.
J'ai même tiré le CS à 1 avec un fil : nada

Le symptôme le plus visible : à l'oscilloscope la pin 12 MISO est tirée à ZÉRO par le W5100, quelque soit la position de son CS.
Quand j'essaie d'initialiser la SD La pin MISO ne dépasse pas 800 mV.

Il faut que je débranche le MISO du W5100 pour que la SD soit vue.

Même chose avec une autre carte Ethernet.
Pendant que j'écrivais ces lignes j'ai trouvé :  ;D

Il faut absolument initialiser la carte Ethernet si l'on désire utiliser la SdCard seule, et initialiser la SD ensuite.

Et cela suffit. La pin MISO remonte. Je pense qu'avec un Ethernet shield avec SD intégrée, on doit avoir le même problème.

Un dernier problème : La carte SD ne s'initialise pas toujours.
Après raccourcissement des fils du SPI, toujours le même problème.
Solution : un condensateur 10µF entre VCC et GND du module SD.

Un deuxième entre VCC et GND de la carte Ethernet ne fera pas de mal.

Après ajout du code de lecture de la template à partir de la SD, et compilation, mauvaise nouvelle : le code est trop gros pour la NANO : 101% de mémoire FLASH occupée.

Nous devons changer de carte pour une ARDUINO MEGA.
Le câblage de vient celui-ci :


MISO : D50
MOSI : D51
SCK : D52
CS de la carte Ethernet W5100 : D10
CS du module SD : D4
VCC du module SD : 3.3V

Le code est à jour et inclut le fichier "template.txt" à placer sur la SD. Voir plus bas :  10. Téléchargements.

Certaines µSD donnent de mauvais résultats.
La Kingston SDHC 4Gb que j'utilisais au début produisait des défauts aléatoires : fichier non trouvé, présence de caractères étranges lors de lectures.
Avec une Sandisk Extreme SDHC le résultat est bien plus fiable.

10. Téléchargement

Pour télécharger le projet : https://bitbucket.org/henri_bachetti/arduino-ethernet-web-server.git

Cette page vous donne toutes les informations nécessaires :
https://riton-duino.blogspot.com/p/migration-sous-bitbucket.html

11. Conclusion

Que conclure de tout ceci ?

L'écriture de la partie génération JAVASCRIPT en C++ est fastidieuse. La technique des templates permet d'alléger, surtout si l'on utilise une SD.

Une carte SD permet également de stocker des pages statiques.

La moindre erreur dans le code JAVASCRIPT conduit généralement à l'affichage d'une page vide. Mais la console du navigateur peut vous aider à trouver les problèmes.
Dans le navigateur, l'affichage du code source HTML de la page permet aussi de voir certaines erreurs (en rouge).

Ce code tourne sur un ARDUINO NANO (sans la gestion des templates sur SD) mais les limites sont vite atteintes :

Le croquis utilise 28774 octets (93%) de l'espace de stockage de programmes. Le maximum est de 30720 octets.
Les variables globales utilisent 1329 octets (64%) de mémoire dynamique, ce qui laisse 719 octets pour les variables locales. Le maximum est de 2048 octets.


Sur la MEGA (logiciel complet avec la gestion des templates sur SD), c'est beaucoup plus confortable :

Le croquis utilise 36860 octets (14%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 2029 octets (24%) de mémoire dynamique, ce qui laisse 6163 octets pour les variables locales. Le maximum est de 8192 octets.


Les modules ESP8266 ou ESP32 peuvent également être une solution très intéressante.

En effet, qu'est-ce qui pourrait nous empêcher d'adapter ce code de gestion de templates sur SD à ces plateformes ?
Dans un prochain article sans doute ...

Cette expérimentation visait surtout à faire un petit exercice sur un petit ARDUINO pour vérifier qu'il était possible d'obtenir une solution capable d'afficher des graphiques.
Ce n'est pas la panacée en terme de confort d'écriture sauf si l'on ajoute la Carte SD, mais c'est plutôt réactif au bout du compte.

A partir du moment où les besoins n'incluent pas une base de données, l'utilisation d'un ARDUINO ou d'un ESP8266 ou ESP32 est amplement suffisante :
  • commande de relais et actionneurs
  • commande de chauffage
  • petit serveur domotique
    • interface homme / machine
    • administration de centrale de sécurité
    • suivi température et humidité
    • suivi consommation ou production EDF
    • etc.
La RASPBERRY PI ouvre d'autres possibilités :
J'ai déjà développé un serveur basé sur CherryPy fonctionnant avec des templates Cheetah.

12. Liens utiles

https://github.com/arduino-libraries/Ethernet.git

Un article comparant ARDUINO + Ethernet et ESP32 :
https://riton-duino.blogspot.com/2020/03/arduino-ethernet-esp32-comparons.html


Cordialement
Henri

13. Mises à jour

14/02/2019 : ajout 9.2. Générer le JAVASCRIPT  à l'aide d'une template
15/02/2019 : ajout 9.3. Générer le JAVASCRIPT  à l'aide d'une template sur SD
15/02/2019 : correction dans http_request::sendDataFromTemplateFile()
13/02/2020 : correction dans http_request::parse()
                     12. Liens utiles

14/03/2020 : ajout des requêtes POST

2 commentaires:

  1. Bonjour et merci pour ce post ultra détaillé.
    je souhaiterais faire le meme travail mais sur un ESP32.
    l'ESP32 log les infos sur une carte SD et fait office de web server.
    Il me reste la partie graphe pour laquelle je rame beaucoup.
    Je n'ai pas votre niveau de connaissance mais je sais faire du copier coller, pas sur que cela suffise !

    Est-ce possible de se contacter afin d'avoir un coup de pouce?
    merci et au plaisir

    RépondreSupprimer
    Réponses
    1. Le plus simple est de poser vos questions sur le forum ARDUINO :
      https://forum.arduino.cc/t/un-web-server-arduino-sur-ethernet-graphes-highcharts/573925

      Supprimer