ARDUINO + Ethernet ou ESP32 : comparons
Certaines personnes s'imaginent à tort qu'une carte Ethernet est facile à utiliser et que la librairie Ethernet offre les mêmes facilités que la librairie ESP32 ou ESP8266.
Il n'en est rien. Récupérer par exemple les arguments d'une requête en méthode POST n'est pas du tout à la portée de l'amateur.
Mieux vaut être prévenu avant de s'engager dans une voie sans issue.
1. Les deux approches
Cet article a pour but de comparer deux approches dans l'écriture d'un serveur HTTP.La première solution utilise une carte ARDUINO NANO + carte Ethernet :
La seconde un module WIFI ESP32 :
2. 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.
3. La version ARDUINO + Ethernet
Le sketch est disponible ici :ethernet.ino
Le sketch permet d'envoyer le formulaire avec la méthode GET ou POST :
#define FORM POST
ou
#define FORM GET
Avec la classe EthernetServer il faut lire et décortiquer la requête soi-même pour récupérer les informations :
- méthode : GET ou POST ou autre
- URL
- protocole
- adresse IP du client
- arguments
- etc.
Des caractères échappés peuvent apparaître dans une URL:
- %20 : espace
- %40 : @
- etc
Parlons de la méthode POST.
Classiquement il faudrait lire l'entête complète pour pouvoir récupérer les arguments, ce qui réclamerait au minimum 500 à 600 octets de mémoire RAM.
Je me suis débrouillé pour que la requête soit lue partiellement, y compris en méthode POST, en gérant les arguments à part, ceci afin de consommer le moins de mémoire RAM possible :
Les variables globales utilisent 846 octets (41%) de mémoire dynamique, ce qui laisse 1202 octets pour les variables locales. Le maximum est de 2048 octets.
Y a t-il des limitations ?
Oui, étant donné le peu de mémoire disponible il faut limiter la taille de certains buffers :
#define REQUEST_MAX 150 // longueur maximale de la requête HTTP
#define URL_MAX 100 // longueur maximale d'une URL
#define ARGSTR_MAX 80 // longueur maximale des arguments
#define ARG_MAX 5 // nombre maximal d'arguments
Ces valeurs limitent fortement les possibilités de notre application.
On pourra adopter une carte ARDUINO MEGA pour plus de souplesse.
Je n'ai pas utilisé la classe String pour des raisons évidentes de fragmentation :
https://riton-duino.blogspot.com/2020/02/arduino-la-fragmentation-memoire.html
4. La version ESP32
Le sketch est disponible ici :esp32.ino
Je suis parti de l'exemple HelloServer.
Avec la classe WebServer ESP32 tout ce qui a été fait précédemment dans la version ARDUINO + Ethernet est déjà fait.On pourrait faire la même chose, avec la même facilité, avec un ESP8266 et la classe ESP8266WebServer.
Je n'ai pas utilisé de stockage des pages HTML en SPIFFS, afin de pouvoir comparer les deux solutions sur un pied d'égalité.
5. Mode d'emploi
Après le chargement les deux sketches affichent sur le terminal série l'adresse IP sur laquelle il faut se connecter.Voici les différentes URL que le serveur accepte :
http://xxx.xxx.xxx.xxx/parser : affiche les arguments de l'URL
Exemple : http://xxx.xxx.xxx.xxx/parser?arg1=1234&arg2=4567
Cette requête affichera les informations suivantes :
- l'entête HTTP
- adresse et port local
- adresse et port distant
- arguments : arg1=1234 et arg2=4567
Après avoir renseigné les champs (l'adresse mail doit comporter un caractère @) et cliqué sur Send la page suivante affiche les informations du formulaire.
On peut imaginer toutes sortes de traitements :
- stockage en SD
- ajout à une base de données
- etc.
http://xxx.xxx.xxx.xxx/sensor-form : affiche un formulaire enrichi :
Ce formulaire comporte un ensemble de cases à cocher, de boutons radios et de champs numériques bornés.
Après avoir renseigné les champs (les champs numériques comportent des valeurs minimales / maximales) et cliqué sur Send la page suivante affiche les informations du formulaire.
Un petit exemple pour apprendre à se servir de différents champs "input".
A remarquer : la technique de formatage d'une page WEB avec sprintf().
Il existe beaucoup de documentation sur le WEB :
https://developer.mozilla.org/fr/docs/Web/HTML/Element/Input
6. Comparaison des deux solutions
La version ARDUINO + Ethernet :- 1 journée de travail
- 386 lignes de code
- complexité importante
- 30 minutes de travail
- 122 lignes de code (hors formulaire enrichi)
- complexité moyenne
Je ne pense pas que qui que ce soit aurait la moindre préférence pour la solution Ethernet.
A moins d'être absolument obligé d'utiliser Ethernet, l'ESP32 ou ESP8266 reste la solution idéale.
6.1. Le DHCP
La librairie Ethernet ne permet pas la modification du HostName par contre celui-ci est composé du mot WIZnet + les 3 derniers caractères de l'adresse Mac. Il y a donc moyen d'avoir un HostName différent par carte.Côté ESP32 une méthode WiFi.setHostname(name) existe. Malheureusement elle ne fonctionne pas.
Une inspection des paquets DHCP avec tcpdump révèle que le HostName n'est pas présent dans la requête.
Il y a une astuce ou plutôt un contournement à connaître. J'ai trouvé l'information ici : Hostname not sent via DHCP request #2537
char hostName[12];
uint8_t mac[6];
WiFi.mode(WIFI_STA);
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
WiFi.macAddress(mac);
sprintf(hostName, "Esp32%x%x%x", mac[3], mac[4], mac[5]);
Serial.printf("setting hostname %s: %d\n", hostName, WiFi.setHostname(hostName));
Serial.print("Connecting to "); Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.printf("%s (%s): connected to %s", WiFi.getHostname(), WiFi.macAddress().c_str(), ssid);
Serial.print("IP address: "); Serial.println(WiFi.localIP());
Le nom attribué est libre. J'ai choisi ESP32 + les 3 derniers caractères de l'adresse Mac, mais c'est juste mon choix.
Accessoirement il est à noter qu'une LiveBox n'affichera ces changements qu'après redémarrage.
Avec tcpdump (sous Linux) il est facile d'inspecter les paquets DHCP :
$ sudo tcpdump -i eno1 -vvv -s 1500 '((port 67 or port 68) and (udp[38:4] = 0x3c71bf47a5b0))'
tcpdump: listening on eno1, link-type EN10MB (Ethernet), capture size 1500 bytes
13:03:41.012470 IP (tos 0x0, ttl 255, id 0, offset 0, flags [none], proto UDP (17), length 336)
0.0.0.0.bootpc > 255.255.255.255.bootps: [udp sum ok] BOOTP/DHCP, Request from 3c:71:bf:47:a5:b0 (oui Unknown), length 308, xid 0xf7aa125b, Flags [none] (0x0000)
Client-Ethernet-Address 3c:71:bf:47:a5:b0 (oui Unknown)
Vendor-rfc1048 Extensions
Magic Cookie 0x63825363
DHCP-Message Option 53, length 1: Discover
MSZ Option 57, length 2: 1500
Hostname Option 12, length 11: "Esp3247a5b0"
Parameter-Request Option 55, length 12:
Subnet-Mask, Default-Gateway, BR, Domain-Name-Server
Domain-Name, Netbios-Name-Server, Netbios-Node, Netbios-Scope
Router-Discovery, Static-Route, Classless-Static-Route, Vendor-Option
END Option 255, length 0
PAD Option 0, length 0, occurs 33
eno1 est le nom de l'interface Ethernet de mon PC. On voit bien passer le HostName dans la requête.
7. Liens utiles
Librairie Ethernet :https://github.com/arduino-libraries/Ethernet
Un autre article sur le sujet ARDUINO + Ethernet:
https://riton-duino.blogspot.com/2019/02/un-web-server-sur-ethernet.html
Cet article a été mis à jour dernièrement. Le serveur accepte maintenant les requêtes POST.
Cordialement
Henri
8. Mises à jour
16/03/2020 : ajout d'un formulaire enrichi (ESP32)06/04/2020 : ajout DHCP et HostName
Aucun commentaire:
Enregistrer un commentaire