Le navigateur aux commandes de l'Arduino - Partie 2 : mettre en place les mécanismes pour envoyer au navigateur des fichiers (HTML, CSS...) écrits sur une carte microSD,
Un tutoriel d'Hervé Troadec
Le 2020-03-26 14:41:10, par naute, Rédacteur
Bonjour .
J'ai le plaisir de vous annoncer la mise en ligne du tutoriel :
dont l'objet est de mettre en place les mécanismes permettant d'envoyer au navigateur des fichiers (HTML, CSS...) écrits sur une carte microSD.
Ce tutoriel fait suite au tutoriel Le navigateur aux commandes de l’Arduino. La troisième et dernière partie, consacrée à la gestion des requêtes avec AJAX est en cours de finalisation.
J'espère que ce tutoriel pourra vous être utile et je vous en souhaite une bonne lecture.
Amicalement,
naute
Retrouvez les meilleurs cours et tutoriels pour apprendre Arduino
J'ai le plaisir de vous annoncer la mise en ligne du tutoriel :
Le navigateur aux commandes de l’Arduino - 2
dont l'objet est de mettre en place les mécanismes permettant d'envoyer au navigateur des fichiers (HTML, CSS...) écrits sur une carte microSD.
Ce tutoriel fait suite au tutoriel Le navigateur aux commandes de l’Arduino. La troisième et dernière partie, consacrée à la gestion des requêtes avec AJAX est en cours de finalisation.
J'espère que ce tutoriel pourra vous être utile et je vous en souhaite une bonne lecture.
Amicalement,
naute
-
Jay MExpert confirméMerci pour cela. Quelques commentaires constructifs (éventuellement
) sur des points de détails (ou pas) Comme vous pouvez le constater, j’ai légèrement remanié ce code pour le rendre plus lisible, notamment en ajoutant les indentations. Je peux à présent le faire sans soucis, car ce code ne sera plus intégré au sketch Arduino, et donc, la place qu’il occupe ne sera plus un problème.
Dans le code d'exemple de réceptionCode : 1
2
3
4
5
6
7
8
9
10
11while (client.available()) { char carLu = client.read(); if (carLu != 10) { reception += carLu; } else { break; }
Cette réception n'est pas correcte en général. Elle fonctionne car vous avez un peu de chance sur le timing.
Il se pourrait que "client.available()" soit vide mais qu'on n'ait pas encore reçu toute la requête si la connexion ethernet est très lente ou le client pas rapide et donc que vous n'ayez pas encore reçu tout le "GET / HTTP/1.1" dans votre chaîne.
Normalement il faut lire jusqu'à une ligne vide (fin de l'en-tête) ou un timeout éventuellement et pas dépendre de la rapidité de vidage du buffer client par rapport à la rapidité de remplissage.
La documentation Arduino proposeCode : 1
2
3
4
5
6
7
8
9
10
11// an http request ends with a blank line boolean currentLineIsBlank = true; while (client.connected()) { if (client.available()) { char c = client.read(); Serial.write(c); // if you've gotten to the end of the line (received a newline // character) and the line is blank, the http request has ended, // so you can send a reply if (c == '\n' && currentLineIsBlank) { ...
Cela dit et en référence à votre premier tuto où vous ditesPourquoi ce test, alors que la ligne reception += carlu; suffit pour récupérer la chaîne reçue ? En fait, comme nous le verrons ultérieurement, seule la première ligne envoyée par le navigateur du client nous intéresse. Chaque ligne se termine par le caractère de code ASCII 10, appelé caractère de fin de ligne (LF pour Line Feed en anglais). Ce test, qui nous permet de détecter la fin de la première ligne, va donc également nous permettre d’empêcher la récupération des caractères inutiles contenus dans le tampon. Cela permet, d’une part, de gagner du temps, et, d’autre part, de ne stocker dans la variable reception que les renseignements utiles et donc d’éviter d’encombrer pour rien la mémoire vive dont la capacité n’est pas phénoménale
Quand vous dites:Malheureusement, la bibliothèque SdFat ne gère pas la réinscription automatique du lecteur sur le bus SPI lors de la réinsertion de la carte microSDThere is no reliable way to detect insertion of an SD without a detect switch. Many sockets have pull-ups or other problems so SdFat does not have a function to detect SD insertion
Quand vous faites votre fonction accusé de réceptionCode : 1
2
3
4
5
6
7
8void arHtml(EthernetClient nomClient, char type) { nomClient.println(F("HTTP/1.1 200 OK")); nomClient.print(F("Content-Type: ")); nomClient.println(type); nomClient.println(F("Connection: close")); nomClient.println(); }
Code : void arHtml(EthernetClient& nomClient, char type)
de manière générale, la duplication mémoire dans un buffer n'est pas nécessaire quand vous faitesCode : 1
2
3
4
5void envoiFichier(EthernetClient nomClient, String fichierEnCours) { char tableau[fichierEnCours.length()+1]; fichierEnCours.toCharArray(tableau, fichierEnCours.length()+1); if (SD.exists(tableau)) {....
On ferait doncCode : 1
2
3void envoiFichier(EthernetClient& nomClient, String& fichierEnCours) { if (SD.exists(fichierEnCours.c_str())) {....
Code : 1
2
3void envoiFichier(EthernetClient& nomClient, const char* fichierEnCours) { if (SD.exists(fichierEnCours)) {....
En espérant que ce soit utile
bonne journée de confinement à toutes et tous!le 30/03/2020 à 12:03 -
Jay MExpert confirméPour être plus précis c'est uniquement le type (const) char* qui est accepté (cf le code)
je pense que Baptou88 a dû tester après avoir changé l'en-tête de la fonction enCode : void envoiFichier(EthernetClient& nomClient, const char* fichierEnCours)
le 08/11/2020 à 17:58 -
nauteRédacteurJ'aurais effectivement pu écrire :
Ça aurait été plus rigoureux.
C'est tout à fait possible. En tout cas, une chose est sûre, exists() n'accepte pas les String (c'est pourtant pas vilain). le 08/11/2020 à 19:45 -
Jay MExpert confirméSalut
C’est plus sur la faute de frappeLe type char est accepté par la fonction exists()
La personne qui a écrit SDFat n’a pas voulu encourager sans doute cet usage, et même si vous avez une String nomFichier dans votre code, il suffit d’appeler avec nomFichier.c_str() et ça passe la cSting sous jacente.le 08/11/2020 à 20:03 -
nauteRédacteurEn effet, ceci parce-que la bibliothèque SD, qui est un sous-ensemble de la bibliothèque SdFat, surcharge la méthode exists() comme suit :
Code c : 1
2
3
4
5
6// Methods to determine if the requested file path exists. boolean exists(const char *filepath); boolean exists(const String &filepath) { return exists(filepath.c_str()); }
ce qui correspond exactement à la proposition de Jay M :Envoyé par Jay M
le 09/11/2020 à 19:25 -
Bonsoir,
Le lien était effectivement erroné. C'est corrigé. le 04/04/2022 à 19:45 -
ThierryCalvetNouveau Candidat au ClubBonjour,
Pourriez-vous mettre en ligne la version pdf?
Le lien ne fonctionne pas.
D'avance merci,
Thierry Calvetle 30/03/2020 à 10:52 -
nauteRédacteurBonjour
.
@ThierryCalvet
Les téléchargements sont à présent actifs.
@Jay M
C'est exact, mais c'est également valable pour la plupart des fichiers HTML, écrits par des professionnels, qui circulent sur le Web, et apparemment ça ne pose de problème à personne. En ce qui concerne le tuto, le fichier HTML est tout petit et le gain de temps sera vraisemblablement négligeable, d'autant plus que ce fichier est destiné à n'être chargé qu'une fois par session, comme on le verra dans la troisième partie.
Question de point de vue. Pour moi, c'est plutôt le '\n' qui est magique. Le caractère ASCII "10" et sa signification sont, quant à eux, connus de tous les langages de programmation et donc d'un usage universel. Quant aux commentaires, si à l'évidence je suis convaincu de leur utilité dans le code, je trouve plus clair et plus logique, s'agissant d'un tutoriel, de les mettre en dehors.
Ce n'est pas faux, et d'ailleurs, avant je vidais le buffer avec un truc comme ça :
Code arduino : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15String reception; boolean flag = true; while (client.available()) { char carLu = client.read(); if (flag) { if (carLu != 10) { reception += carLu; } else flag = false; } }
.
Je n'ai bien évidemment jamais voulu dire ça !
Tout à fait exact : je vais envisager une mise à jour.
Dans la troisième partie, je donne une version qui se passe de la classe String et qui utilise donc les tableaux de caractères. On gagne à peu près 2 Kio de mémoire flash mais on y perd un peu en simplicité. De plus, la mémoire dynamique est impactée, car pour ne pas compliquer encore plus le code avec des réallocations de mémoire, je déclare un tableau de taille fixe, et donc pour être tranquille, supérieur à mes besoins réels. Cela dit, en production, on peut ajuster précisément cette taille.
Bien sûr.
Merci pour vos commentaires,
amicalement,
nautele 30/03/2020 à 15:59 -
Jay MExpert confirméOui - pour la taille du fichier HTML l'idée était plus de faire la remarque que c'est à prendre en compte. Quand on travaille sur un ordinateur qui a de la mémoire en Giga-octets et une liaison ethernet Gigabit ou 10 Gigabits, on peut se le permettre. Certains serveur web vont même faire le ménage à l'émission pour compresser ce qui est envoyé... mais là on est sur un petit arduino
pour le '\n' OK, chacun son point de vue le votre est tout à fait valide.
Pour leCode : while (client.available()) {
Oui la classe String apporte quelques simplifications mais si on est habitué au fonctions standard, on retrouve assez facilement ses petits et comme vous le dites on gagne pas mal de place Flash.
Merci encore pour le travail !le 30/03/2020 à 16:30 -
Baptou88Membre du ClubBonsoir à tous,
je suis actuellement en train d'implémenter ce tuto pour mon serveur web existant ( Heltec ESP32 Wifi ), mais je ne parvient pas à compiler cette fonction:
Code : 1
2
3
4void envoiFichier(EthernetClient nomClient, String fichierEnCours) { char tableau[fichierEnCours.length()+1]; fichierEnCours.toCharArray(tableau, fichierEnCours.length()+1);
Comment remédier à ce problème ?
Cordialement Baptistele 06/11/2020 à 22:25