[ Arduino 802 ] Arduino et Ethernet : client

Commençons doucement à découvrir ce shield en utilisant notre Arduino comme client.
Dans ce chapitre, nous allons découvrir comment faire en sorte que notre Arduino aille interroger un site internet pour obtenir une information de sa part.

Ici nous allons interroger un site internet, mais cela aurait très bien pu être un autre service, sur un autre port (vous allez comprendre)

Afin de bien comprendre, je vous propose tout d’abord quelques explications sur le protocole HTTP et comment se passe un échange de données (requête + réponse).
Ensuite, nous verrons comment mettre en œuvre le shield et faire en sorte qu’il puisse accéder au monde extérieur.
Enfin, nous mettrons tout en œuvre pour faire une simple requête sur un moteur de recherche.
Pour terminer, je vous propose un petit exercice qui permettra de voir un cas d’utilisation : le « monitoring » d’un serveur.

Client et requêtes HTTP

Faisons un petit retour étendu sur la notion de client et surtout découvrons plus en détail en quoi consiste exactement une requête http.

Un client, ça fait quoi ?

Le rôle du client est finalement assez simple. Son but sera d’aller chercher des informations pour les afficher à un utilisateur ou effectuer une action particulière. D’une manière générale, le client sera celui qui traite l’information que le serveur envoie.

Prenons l’exemple du navigateur web. C’est un client. Lorsque vous l’utilisez, vous allez générer des requêtes vers des serveurs et ces derniers vont ensuite renvoyer un tas d’informations (la page web). Le navigateur va alors les traiter pour vous les afficher sous une forme agréable et prévue par le développeur web.

Finalement, c’est simple d’être client, ça consiste juste à demander des choses et attendre qu’elles arrivent 🙂 !

Les termes « client » et « serveur » (et même « requête ») sont d’ailleurs très bien choisis car ils illustrent très bien les mêmes rôles que leur équivalent « dans la vraie vie ».

Une requête HTTP, c’est quoi

Des requêtes HTTP il en existe de plusieurs types. Les plus classiques sont sûrement les GET et les POST, mais on retrouve aussi les PUT, DELETE, HEAD… Pour faire simple, nous allons uniquement nous intéresser à la première car c’est celle qui est utilisée la plupart du temps !

Émission

Dans la spécification du protocole HTTP, on apprend que GET nous permet de demander une ressource en lecture seule. On aura simplement besoin de spécifier une page cible et ensuite le serveur http de cette page nous renverra la ressource ou un code d’erreur en cas de problème. On peut passer des arguments/options à la fin de l’adresse que l’on interroge pour demander des choses plus particulières au serveur.

Par exemple, si vous êtes sur la page d’accueil de Google et que vous faites une recherche sur « Arduino », votre navigateur fera la requête suivante : GET /search?q=arduino HTTP/1.0. Il interroge donc la page principale « / » (racine) et envoie l’argument « search?q=arduino ». Le reste définit le protocole utilisé.

Réception

Une fois la requête faite, le serveur interrogé va vous renvoyer une réponse. Cette réponse peut être découpée en deux choses : l’en-tête (header) et le contenu. On pourrait comparer cela à un envoi de colis. L’en-tête possèderait les informations sur le colis (destinataires etc) et le contenu est ce qui est à l’intérieur du colis.

Typiquement, dans un réponse minimaliste on lira les informations suivantes :

  • Le code de réponse de la requête (200 si tout s’est bien passé)
  • Le type MIME du contenu renvoyé (text/html dans le cas d’une page web)
  • Une ligne blanche
  • Le contenu

Les deux premières lignes font partie du header, puis après viendra le contenu.

Si on veut chercher des informations, en général on le fera dans le contenu.

Utilisation du shield comme client

Maintenant que l’on en sait un peu plus, on va pouvoir faire des requêtes et aller interroger le monde…

Préparation minimale

Pour commencer, il va falloir configurer notre module Ethernet pour qu’il puisse travailler correctement. Pour cela, il va falloir lui donner une adresse MAC et une adresse IP (qui peut être automatique, nous y reviendrons). On va utiliser 4 variables différentes :

  • Un tableau de byte (ou char) pour les 6 octets de l’adresse MAC ;
  • Un objet IPAddress avec l’adresse IP que l’on assigne au module ;
  • Un objet EthernetClient qui nous servira à faire la communication ;
  • Une chaîne de caractères représentant le nom du serveur à interroger.

L’adresse MAC doit normalement être écrite sur un autocollant au dos de votre shield (sinon inventez en une !)

De manière programmatoire, on aura les variables ci-dessous.

Démarrer le shield

Maintenant que nous avons nos variables, nous allons pouvoir démarrer notre shield dans le setup(). Pour cela, il suffira d’appeler une fonction bien nommée : begin(), et oui, comme pour la voie série ! Cette fonction prendra deux paramètres, l’adresse MAC et l’adresse IP à utiliser. C’est aussi simple que cela !

Simple non ?

le DHCP

Si vous bricolez à domicile, il est fort probable que vous soyez en train d’utiliser un routeur ou votre box internet. Bien souvent ces derniers ont par défaut la fonction DHCP activée. Cette technologie permet de donner une adresse IP automatiquement à tout les équipements qui se connectent au réseau. Ainsi, plus besoin de le faire manuellement !

Du coup, on peut faire évoluer notre script d’initialisation pour prendre en compte cela.

Envoyer une requête

Une fois que le shield est prêt, nous allons pouvoir commencer à l’utiliser ! Accrochez-vous, les choses amusantes commencent !

Requête simple

Pour débuter, il va falloir générer une requête pour interroger un serveur dans l’espoir d’obtenir une réponse. Je vous propose de commencer par une chose simple, récupérer une page web très sommaire, celle de http://perdu.com !

C’est là que notre variable « client » de type EthernetClient entre enfin en jeu. C’est cette dernière qui va s’occuper des interactions avec la page. Nous allons utiliser sa méthode connect() pour aller nous connecter à un site puis ensuite nous ferons appel à sa méthode println() pour construire notre requête et l’envoyer.

Étudions un peu ces quelques lignes.

Tout d’abord, on va essayer de connecter notre client au serveur de « perdu.com » sur son port 80 qui est le port par défaut du protocole http :

Ensuite, une fois que la connexion semble correcte, on va construire notre requête pas à pas.
Tout d’abord, on explique vouloir faire un GET sur la racine (« / ») du site et le tout sous le protocole HTTP version 1.1.

Ensuite, on redéclare le site qui devra faire (héberger, « host » en anglais) la réponse. En l’occurrence on reste sur perdu.com.

Puis, on signale au serveur que la connexion sera fermée lorsque les données sont transférées.

Enfin, pour prévenir que l’on vient de finir d’écrire notre en-tête (header), on envoie une ligne blanche.

J’ai ensuite rajouté un traitement des erreurs pour savoir ce qui se passe en cas de problème.

Requête avec paramètre

Et si l’on voulait passer des informations complémentaires à la page ? Eh bien c’est simple, il suffira juste de modifier la requête GET en lui rajoutant les informations !
Par exemple, admettons que sur perdu.com il y ait une page de recherche « recherche.php » qui prenne un paramètre « m » comme mot-clé de recherche. On aurait alors :

Ce serait équivalent alors à aller demander la page « perdu.com/recherche.php?m=monmot », soit la page « recherche.php » avec comme argument de recherche « m » le mot « monmot ».

Lire une réponse

Lorsque la requête est envoyée (après le saut de ligne), le serveur va nous répondre en nous renvoyant la ressource demandée. En l’occurrence se sera la page d’accueil de perdu.com. Eh bien vous savez quoi ? On fera exactement comme avec une voie série. On commencera par regarder si des données sont arrivées, et si c’est le cas, on les lira une à une pour en faire ensuite ce que l’on veut (recomposer des lignes, chercher des choses dedans…)
Dans l’immédiat, contentons nous de tout afficher sur la voie série !
Première étape, vérifier que nous avons bien reçu quelque chose. Pour cela, comme avec Serial, on va utiliser la méthode available() qui nous retourne le nombre de caractères disponibles à la lecture.

Si des choses sont disponibles, on les lit une par une (comment ? Avec « read() » bien sûr ! 😀 ) et les envoie en même temps à la voie série :

Enfin, quand tout a été lu, on va vérifier l’état de notre connexion et fermer notre client si la connexion est terminée.

Globalement, voici le code pour lire une réponse :

Avez-vous remarqué, plutôt que de lire tout les caractères avec un while, on n’en lira qu’un seul à la fois par tour dans loop(). C’est un choix de design, avec un while cela marcherait aussi !

Code complet

En résumé, voici le code complet de la lecture de la page « perdu.com »

Et voici le résultat que vous devez obtenir dans votre terminal série. Remarquez la présence du header de la réponse que l’on reçoit.

Faire des requêtes en continu

Faire une requête en début de programme c’est bien, mais ce serait mieux de pouvoir la faire quand on veut. Faire évoluer notre programme ne va pourtant pas être si simple…

Pour commencer, on va ajouter quelques nouvelles variables. La première servira à se souvenir de quand date (via millis()) la dernière requête faite. Ce sera donc un long(). La seconde sera une constante servant à indiquer le temps minimal devant s’écouler entre deux requêtes. Enfin, la dernière variable sera un booléen servant de flag pour indiquer l’état de la connexion (ouverte ou fermée) entre deux tours de boucle loop().

Ensuite, on va créer une fonction sobrement nommée requete() qui se chargera de construire et envoyer la requête. Aucune nouveaute la dedans, c’est le code que vous connaissez deja.

La seule différence ici est que l’on ferme le client si la connexion a un problème et aussi on enregistre le moment auquel a été faite la requête.

Bien. Maintenant il faut revoir notre loop.

Le début ne change pas, on récupère/affiche les caractères si ils sont disponibles.

Ensuite, on avait l’étape de fermeture du client si la connexion est fermée. Dans notre cas actuel, on ne doit pas l’arrêter n’importe quand car sinon une connexion pourrait de nouveau avoir lieu sans que celle là soit fini. On va donc la fermer QUE si elle était déjà fermée à la toute fin du tour précédent. Voici comme cela peut-être représenté :

Maintenant, il ne nous reste plus qu’à re-déclencher une requete si nos dix secondes se sont écoulé et que la précédente connexion à fini de travailler :

C’est tout clair ? Ci-dessous le code complet pour vous aider à y voir un peu mieux dans l’ensemble 😉 .

Le code complet

L’intérêt d’un client

En lisant tout ça vous vous dites peut-être qu’utiliser le shield Ethernet en mode client est un peu inutile. C’est vrai, après tout on ne peux même pas afficher les pages reçues !

Cependant, avec un peu d’imagination on pourrait facilement voir des utilisations plus que pratiques !

Télécharger une configuration

La première idée par exemple pourrait être de mettre à disposition des fichiers de paramètres à l’Arduino. Imaginons par exemple que je fais une application qui dépend des saisons. Mon application pourrait utiliser des moteurs et des capteurs pour faire certaines actions mais ces dernières pourraient être différente en fonction du moment de l’année. Plutôt que de stocké 4 ensemble de paramètres dans l’Arduino, cette dernière pourrait vérifier régulièrement en ligne (en interrogeant un petit script que nous aurions fait) pour savoir si quelque chose doit changer. Et voila, configuration à distance !

Enregistrer des données en ligne

J’ai souvent lu sur des forums que des gens aimeraient pouvoir sauvegarder des choses dans une base de données. Bien entendu l’Arduino seule ne peux pas le faire, gérer une BDD est bien trop compliquée pour elle. En revanche, elle pourrait facilement interroger des pages en insérant des paramètres dans sa requête. La page qui reçoit alors la requête lirait ce paramètre et s’occuperait de sauvegarder les infos.
Par exemple, on pourrait imaginer la requête http://mapageweb.com/enregistrer.php?&analog1=123&analog2=28&millis=123456. Cette requête possède deux paramètres, analog1 et analog2 qui pourrait être les valeurs des entrées analogiques et un dernier « millis » qui pourrait être la valeur de millis() d’Arduino. Notre page « enregistrer.php » ferait alors la sauvegarde dans sa base de données !
Pour construire une telle requête, le code suivant devrait faire l’affaire :

Sûrement plein d’autres choses !

Il existe surement un paquet d’autres idées auquel je n’ai pas pensé !

Exercice, lire l’uptime de Eskimon.fr

Pour finir ce chapitre je vous propose un petit exercice, allez lire l’uptime (temps écoulé depuis la mise en route du système) de mon blog, eskimon.fr.

Consigne

Pour cela, je vous ai concocté une petite page juste pour vous qui renvoie uniquement cette information, sans tout le bazar http qui va avec une page classique. Vous obtiendrez ainsi simplement le header de la requête et la valeur brute de l’uptime.

La page à interroger pour votre requête est : http://eskimon.fr/public/arduino.php

Essayer de modifier votre code pour afficher cette information 😉

Vous êtes maintenant en mesure de vous balader sur internet pour aller y chercher des informations. Bienvenu dans l’IoT, l’Internet of Things !

Une expérience (bonne ou mauvaise) à partager ? Des questions à poser ? Des conseils à donner ? Ne soyez pas timide !

17 commentaires

  1. Salut Eskimon, encore merci de nous éclairer par tes lumières!!! En effet, ce shield peut tout à fait être utilisé dans un système de gestion comparable à ceux vendus par IBM…. En attendant le tuto du serveur si… Je te remercie encore une fois de nous mettre à dispo toutes ces ressources. Bye

    • Bonne nouvelle, le tuto serveur avance bien 😉 Encore un tout petit peu de patience et ca devrait arriver bientôt (avec une approche du code un peu différente/simplifiée de ce que l’on voit d’habitude sur ce shield je pense)

  2. Bonjour,
    Dans ton exercice tu proposes de lire le temps ecoulé.
    je compte m’en inspirer pour avoir la date et l’heure. Connais tu un site sur lequel je pourrai recuperer la date et l’heure?
    ou pourrais tu la rajouter dans ton site d’exemple.
    le but est de ne pas mettre de module RTC sur mon arduino et d’avoir quand meme la date et l’heure.
    Merci d’avance.

  3. Bonjour,
    j’essaie de configurer la liaison ethernet et quelles que soient les configuration, rien ne fonctionne. J’ai simplifier le code au maximum (peut-être trop …) si tu peux m’aider, merci d’avance.
    Essai 1 : Sur routeur (Freebox) avec câble droit puis croisé (mais ça ne change rien)

    En DHCP avec le code ci-dessous : seul message obtenu sur la console : « Essai adressage DHCP »

    #include
    #include

    // Adress MAC de la carte Ethernet
    byte mac[] = {
    0x90, 0xA2, 0xDA, 0x10, 0x4F, 0x17 };

    void setup()
    {
    // Initialise la console série
    Serial.begin(9600);
    // Commence la communication Ethernet:
    Serial.println(« Essai adressage DHCP »);
    Ethernet.begin(mac);
    Serial.println(« Fin adressage DHCP »);
    // Récupération de l’adresse IP:
    Serial.println(Ethernet.localIP());
    }

    void loop()
    {

    }

    En IP fixe avec le code ci-dessous : messages obtenus sur la console :
    Essai adressage Fixe
    Fin adressage Fixe
    0.192.192.192

    #include
    #include

    // Adress MAC de la carte Ethernet
    byte mac[] = {
    0x90, 0xA2, 0xDA, 0x10, 0x4F, 0x17 };
    // Adress Fixe de la carte Ethernet : 193.168.1.33 en DHCP pour un PC sur la meme prise de la freebox
    byte ip[]{192, 168, 0, 33};

    void setup()
    {
    // Initialise la console série
    Serial.begin(9600);
    // Commence la communication Ethernet:
    Serial.println(« Essai adressage Fixe »);
    Ethernet.begin(mac,ip);
    Serial.println(« Fin adressage Fixe »);
    // Récupération de l’adresse IP:
    Serial.println(Ethernet.localIP());
    }

    void loop()
    {

    }

    Et sur la RJ45 du PC (avec câble droit ou croisé), c’est la même chose !
    Pourtant la prise de la box fonctionne correctement en DHCP avec un PC, donc la liaison physique est OK.
    Je craque !!!

      • Bonjour,

        il te manque EthernetClient client; je ne suis pas sur qu’il soit obligatoire mais ça vaut le coût d’essayer.
        Est ce que tu as fait le test avec l’exemple DhcpAdressPrinter?
        J’ai eu le meme pb quoi avec ma freebox mon code ne marchait pas puis j’ai mis un switch entre ma carte et ma freebox avec le même code et la ça marche.

        Bon courage.

  4. Bonjours a tous,
    Il faut que je fasse la même chose mais en wifi …..
    Est t-il possible de m’aidé a trouver le code et quel balise mettre a la place de EthernetClient ?
    Merci a tous 😉

  5. Bonjour,
    j’ai des variables stocke sur une page HTML simple. est il possible de recuperer les variables avec l’arduino pour la traiter ensuite ? ou l’arduino doit automatiquement etre en mode serveur ?

    merci

  6. Bonjour a tous ,

    Salut Eskimon, merci de nous éclairer par tes noble explications.
    j’ai une question sur le module wifi RN171 je réalise un programme pour configurer le module en mode ADHOC et en même temps en mode serveur du coup j’ai réussi a générer ma page web en utilisant le module, ma question c’est comment je p faire pour envoyer une chaîne de caractères (a partir de ma page web )que je souhaite stocké sur l’EEPROM de ma carte arduino.?

  7. Bonjour à tous, impossible de téléverser le code avec le shield ethernet connecté à l’arduino: est-ce normal? J’ai donc téléversé le programme et ensuite connecté le shield et rien ne s’affiche dans le moniteur série… Pourriez-vous m’aider?

Laisser un commentaire