• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 13/07/2017

Pilotez votre Arduino sur le réseau mondial

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Ce que vous allez apprendre dans ce chapitre ne va pas ajouter une grande différence avec le chapitre précédent,  car vous allez le connecter non plus à votre réseau local (intranet) mais au réseau mondial (Internet).

La plus grosse difficulté va résider dans le paramétrage de votre box (votre accès Internet) qui peut varier d'une box à l'autre, donc là encore nous aborderons les principes généraux. 

Nous en profiterons pour ajouter dans nos codes HTML des boutons et des tableaux, c'est plus joli. ;)

Alors, c'est parti pour paramétrer la box, mais avant, un petit point nécessaire sur l'IP publique...

Votre IP publique

Comme nous l'avons vu au chapitre précédent, l'adresse IP est une adresse composée de plusieurs chiffres, séparés par des points. C'est un code qui permet à chaque machine d'être reconnu sur le réseau.

Pour un réseau interne,  les adresses sont souvent de la forme 192.168.xxx.xxx, les 'x' représentant des nombres.

Dans le chapitre précédent, nous avions programmé notre serveur Arduino avec l'adresse 192.168.1.123.

Si vous testez cette adresse depuis un poste qui n'est pas sur votre réseau interne, vous n'obtiendrez rien. La page ne s'affichera pas ou vous aurez une erreur. En effet, les adresse internes ne sont pas ouvertes sur le monde. Elles sont gérées par votre box.

Il existe une autre série d'adresses IP que l'on appelle publiques, qui elles sont fournies par les entreprises habilitées à le faire. Ce sont les fournisseurs d'accès (SFR, Bouygues, France télécom, Free... ). Il faut donc un boîtier relié au réseau (souvent appelée box) et un abonnement (et oui car rien n'est gratuit dans notre monde !).

Votre box a une adresse IP publique.

Comment connaître mon adresse publique ?

Et bien il y a plusieurs solutions. Soit en vous connectant sur l'interface de gestion de votre box, soit en allant sur un site qui vous la donne.

Solution 1 : Trouvez votre IP publique sur un site

Pour connaître votre IP publique facilement, il suffit d'aller sur les sites suivants :

Solution 2 : Trouvez votre IP publique sur l’interface de votre box

En fonction de votre box (ou plutôt de sa marque), vous avez accès à une interface de gestion de votre box...

  • Soit grâce à un programme que vous avez installé lorsque vous avez connecté votre box ;

  • Soit, plus simplement, grâce à une interface web interne à votre réseau.

En effet, tout comme peut le faire l'Arduino, votre box peut accéder au réseau interne. Lorsque c’est le cas, elle vous accueille avec des pages web (comme un site Internet) qui vous permettent de la configurer.

Il suffit souvent (en tous cas pour la livebox et la neufbox) de taper l'adresse interne : 192.168.1.1.

Si la connexion est réussie, vous avez une information quelque part sur la page (tout dépend de la marque de la box) qui vous indique votre IP publique. Par exemple, pour la neufbox, c'est en haut à droite sur la page d'accueil.

Une fois votre adresse publique récupérée, il va nous falloir mettre les mains dans le cambouis, et c'est là que ça risque de se compliquer...

Paramétrez votre box pour l'Arduino

Le principe est le suivant : nous allons paramétrer la box pour que lorsqu'on se connecte sur l'adresse publique et sur un certain port (numéro interne), la box dirige automatiquement la connexion vers l'Arduino (qui sera en mode serveur).

Il faut donc déjà entrer dans les autorisations de paramétrage de la box.

Pour la neufbox, il faut soit entrer un loggin et un mot de passe, soit appuyer sur un bouton pendant 5 secondes et se connecter avec l'adresse locale de la box (192.168.1.1).

Pour les autres box, il vous faudra vous renseigner sur la méthode à suivre pour accéder à l'autorisation de modification.

Maintenant que vous êtes dans les paramètres, vous allez procéder en trois étapes :

  1. Associer l'adresse de votre Arduino (192.168.1.123) à un nom ;

  2. Associer cette adresse avec l'adresse MAC (matérielle) de votre carte Arduino qui est écrite sur l'étiquette du shield Arduino, et donc la rendre statique ;

  3. Créer un relai entre l'adresse publique de votre box (et un port précis) et votre Arduino.

Étape 1 : Associez l'adresse locale à un nom

Pour ce faire, vous allez chercher les menus qui concernent la gestion ipV4.

En cherchant, vous devriez voir un menu lié au DNS (Domain Name System). Il est proposé d'associer une adresse IP à un nom. Chez moi, j'ai associé l'adresse 192.168.1.123 au nom "arduino". Ce qui permet par la suite de mieux le repérer.

Associer une IP locale fixe à un nom
Associez une IP locale fixe à un nom (ici sur neufbox)

Si votre adresse locale n'apparaît pas dans les listes proposées automatiquement (carré violet après le champ de saisi pour moi), vous tapez directement l'adresse en question ainsi que le nom.

Étape 2 : Fixez l'adresse locale et l'associer l'adresse MAC

Il faut ensuite aller dans le menu DHCP (Dynamic Host Configuration Protocol) qui va permettre à la box de gérer précisément cette adresse en l'associant bien à la bonne machine (donc votre Arduino).

Vous allez devoir configurer une adresse interne statique, c'est-à-dire que cette adresse pointera toujours sur la même machine. On associe donc une IP locale à une adresse MAC.

Associer une IP fixe à une adresse MAC
Associez une IP fixe à une adresse MAC (ici sur neufbox)

Pour entrer l'adresse MAC, il faut saisir des nombres hexadécimaux de deux chiffres séparés par ':'.

Il est possible que pour d'autres box, la saisie se fasse différemment, mais le principe reste le même.

Étape 3 : Reliez l'adresse interne avec l'adresse publique de votre box et un numéro de port

Il faut maintenant aller dans le menu NAT (Network Address Translation) qui va vous permettre de relier une adresse publique et un numéro de port à votre adresse locale statique, donc à votre Arduino.

Il va falloir saisir plusieurs choses :

  • Le nom de l'adresse liée (j'ai indiqué Arduino) ;

  • Le protocole utilisé (j'ai indiqué TCP) ;

  • L'association à un port ou à une plage (j'ai choisi un port précis) ;

  • Le numéro de port externe (entre 1024 et 65535) ;

  • L'IP interne à associer (ici 192.168.1.123) ;

  • Le port de destination (j'ai indiqué 80).

Relier l'adresse externe et un port précis, à l'adresse fixe interne (ici sur neufBox)
Reliez l'adresse externe et un port précis, à l'adresse fixe interne (ici sur neufbox)

Si vous avez réussi à tout faire, et bien je ne dirai qu'une chose... Ayé, c'est fini !

Vous pouvez dès maintenant tester le résultat. Chargez un programme serveur dans l'Arduino, soit ceux du chapitre précédent, soit celui qui suit en modifiant avec l’adresse MAC de votre carte Arduino dans les variables mac[]) :

#include <SPI.h> //bibliothèqe pour SPI
#include <Ethernet.h> //bibliothèque pour Ethernet
byte mac[] = {0x90, 0xA2, 0xDA, 0x0F, 0xDF, 0xAB}; //adresse mac de votre carte
byte ip[] = {192, 168, 1, 123}; //adresse IP
EthernetServer serveur(80); // déclare l'objet serveur au port d'écoute 80

void setup() {
  Serial.begin (9600); //initialisation de communication série
  Ethernet.begin (mac, ip); //initialisation de la communication Ethernet
  Serial.print("\nLe serveur est sur l'adresse : ");
  Serial.println(Ethernet.localIP()); //on affiche l'adresse IP de la connexion
  serveur.begin(); // démarre l'écoute
}

void loop() {
  EthernetClient client = serveur.available(); //on écoute le port
  if (client) { //si client connecté
    Serial.println("Client en ligne\n"); //on le dit...
    if (client.connected()) { // si le client est en connecté
      //réponse au client
      entete(client);
      client.println("Le monde nous est ouvert !<br>");
      client.println("Vive l'Arduino !");
      client.println("<br><hr></body></html>"); //ligne horizontale et fermeture des balises
      client.stop(); //on déconnecte le client
      Serial.println("Fin de communication avec le client");
    }
  }
}
//fonction d'affichage de l'entête HTML
void entete(EthernetClient cl) {
  cl.println("<!DOCTYPE HTML>");
  cl.println("<html>");
  cl.println("<head><title>Esssai</title></head>");
  cl.println("<body><h1>Essai</h1><hr><br>");
}

Une fois le programme chargé, tapez directement directement dans la barre du navigateur web (d'un PC connecté ou d'un smartphone...)  votre adresse publique suivie de ':' puis du numéro de port choisi, par exemple : 91.72.120.210:3456.

Cela devrait vous afficher votre page, et ce, depuis n'importe quel ordinateur du monde !! 

La classe non ? Que dis-je, la OpenClass !!!

Nous allons maintenant faire un nouveau point sur le HTML, juste pour améliorer vos présentations.

Il ne s'agit pas de vous faire un cours lourd et complet sur le HTML, mais juste un survol des possibilités offertes. Sachez en gros, que tout ce qui est possible (ou presque) en HTML, peut être programmé dans l'Arduino.

Les tableaux HTML

Les tableaux HTML permettent de structurer des données sur une page web, pour afficher des tableaux mais aussi pour arranger la disposition d’autres éléments de la page (photos, textes, formulaires, boutons…).

Nous allons donc voir comment créer un tableau en HTML. Ce n'est pas très compliqué, il faut juste un peu de rigueur. Pour ça, vous allez avoir besoin des balises <table>, <tr> et <td>.

Vous verrez qu'il existe beaucoup d'options pour ces balises. Nous allons ici nous limter à leur utilisation basique.

  • Un tableau se déclare en html dans le corps de la page, entre les balises <table> et </table>. L'option "rules" indique si l'on veut des lignes de séparation à l'intérieur du tableau. L'option "border" indique la taille du contour en pixel.

  • On crée une nouvelle ligne du tableau grâce à une balise <tr> (table row) que l'on ferme avec </tr>.

  • on crée une case grâce à la commande <td> (table data) que l'on ferme avec </td>.

Il faut être vigilant avec les ouvertures et les fermetures de lignes et cases, car on peut vite arriver à n'importe quoi. ;)

Voici un exemple de page html :

<!DOCTYPE HTML>
<html>
    <head>
        <title>Tableau</title>
    </head>
    <body>
        <table rules=all border=1px>
            <tr>
                <td>cellule 0.0</td>
                <td>cellule 1.1</td>
            </tr>
            <tr>
                <td>cellule 1.0</td>
                <td>cellule 1.1</td>
            </tr>
        </table>
    </body>
</html>

Je vous propose d'essayer de créer sur l'Arduino un programme qui affiche la table de Pythagore sur une page web.

TP : la table de pythagore

Petit rappel, la table de Pythagore est un tableau de résultats de produits des nombres de 0 à 9 (pour notre exemple).

(Lukas, pour "produit", on parle de multiplication là, pas de lessive...)

La première ligne contient les nombres de 0 à 9, la première colonne de même, et chaque case contient le résultat de la multiplication de la ligne par la colonne. Voici un exemple de résultat :

Affichage de la table de Pythagore
Affichage de la table de Pythagore en page web dans un tableau

Si vous deviez créer ce tableau "à la main" sur un fichier texte (par exemple) vous obtiendriez un fichier assez long, presque illisible et avec bien des risques d'erreur ! 

Une solution plus efficace est de construire automatiquement un tableau HTML à l'aide de boucles en programmation Arduino. En utilisant Arduino comme constructeur de page, vous automatisez la création des balises . Voici un code sorti de son contexte pour vous aider à construire votre programme :

client.println("<table rules=all border=1px>"); //ouvre la balise tableau avec deux paramètres
for (int l=0;l<5,;l++){
    client.println("<tr>"); //ouvre une balise ligne
    for (int c=0;c<5;c++){
        client.print("<td>");//ouvre une balise cellule
        client.print("un truc");//écrit "un truc" dans la cellule
        client.println("</td>");//ferme la balise cellule
    }
    client.println("</tr>");//ferme la balise ligne
}
client.println("</table>");//ferme la balise tableau

Ce code (placé au bon endroit dans un programme Arduino serveur) va créer un tableau de 5 lignes sur 5 colonnes avec, dans chaque cellule, les mots "un truc".

Je vous propose donc de créer cette table sur une page web consultable depuis le monde entier. Quelques contraintes :

  • Le titre de la fenêtre doit être : "Table de Pythagore" ;

  • Un titre doit apparaître sur la page : "Table de Pythagore" en gros caractères ;

  • La première cellule en haut à gauche doit contenir un "X" pour indiquer la multiplication ;

  • La première ligne doit contenir les nombres de 0 à 9 ;

  • La première colonne doit contenir les nombres de 0 à 9 ;

  • Chaque cellule doit contenir le résultat du produit du nombre d'entête de la ligne par le nombre d'entête de la colonne.

L'ensemble donne le résultat de l'image précédente.

Si vous n'arrivez pas à paramétrer votre box pour pouvoir accéder à votre page par le Web mondial ou si vous n'avez pas de connexion, vous pouvez toujours tester le programme en interne comme dans le chapitre précédent, c'est-à-dire en tapant dans votre navigateur 192.168.1.123, qui est l'adresse que nous utilisons depuis le début.

Je vous laisse vous lancer dans ce programme. Elle est moins simple qu'il n'y paraît. Je vous conseille de créer des fonctions pour l'entête et le corps, ça facilite la lecture du programme et l'éventuel débuggage. Car je vous le rappelle, l'IDE de l'Arduino ne vous indiquera pas de faute sur le code HTML, vu que pour lui, ce ne sont que des codes affichés.

Je vous laisse réfléchir tranquillement maintenant !

TP : Correction 

Il est probable, si vous avez réussi seul(e) que votre programme ne corresponde pas exactement au mien. Ce n'est pas un problème ! L'important est que vous ayez réussi !

Voici donc mon programme commenté :

#include <SPI.h> //bibliothèqe pour SPI
#include <Ethernet.h> //bibliothèque pour Ethernet
byte mac[] = {0x90, 0xA2, 0xDA, 0x0F, 0xDF, 0xAB}; //adresse mac de votre carte
byte ip[] = {192, 168, 1, 123}; //adresse IP
EthernetServer serveur(80); // déclare l'objet serveur au port d'écoute 80

void setup() {
  Serial.begin (9600); //initialisation de communication série
  Ethernet.begin (mac, ip); //initialisation de la communication Ethernet
  Serial.print("\nLe serveur est sur l'adresse : ");
  Serial.println(Ethernet.localIP()); //on affiche l'adresse IP de la connexion
  serveur.begin(); // démarre l'écoute
}

void loop() {
  EthernetClient client = serveur.available(); //on écoute le port
  if (client) { //si client connecté
    Serial.println("Client en ligne\n"); //on le dit...
    if (client.connected()) { // si le client est en connecté
      //réponse au client
      entete(client); // appel de la fonction pour créer l'entête de la page
      corps(client); // appel de la fonction pour créer le corps de la page
      client.println("<br><hr></body></html>"); //ligne horizontale et fermeture des balises
      client.stop(); //on déconnecte le client
      Serial.println("Fin de communication avec le client");
    }
  }
}

//------------------------------- Fonctions ------------------
//fonction d'affichage de l'entête HTML
void entete(EthernetClient cl) {
  //infos pour le navigateur
  cl.println("HTTP/1.1 200 OK"); // type du HTML
  cl.println("Content-Type: text/html; charset=ascii"); //type de fichier et encodage des caractères
  cl.println("Connection: close");  // fermeture de la connexion quand toute la réponse sera envoyée
  cl.println();
  //balises d'entête
  cl.println("<!DOCTYPE HTML>");
  cl.println("<html>");
  cl.println("<head><title>Table de Pythagore</title></head>");
  cl.println("<body><h1>Table de Pythagore</h1><hr><br>");
}
//fonction d'affichage du corps de la page HTML
void corps(EthernetClient cl) {
  cl.println("<table rules=all border=1px>"); //ouverture de la balise tableau
  for (int l = -1; l < 10; l++) { // boucle pour les lignes
    cl.println("<tr>"); //ouverture d'une balise ligne
    for (int c = -1; c < 10; c++) { // boucle pour les colonnes
      cl.println("<td>"); // ouverture d'une balise cellule
      if (c == -1 && l == -1) {// si case en haut à gauche
        cl.print("X"); //on affiche un X dans la cellule
      }
      else if (c > -1 && l == -1) {// si ligne d'entête
        cl.print(c); // on affiche le nombre d'entête de colonne dans la cellule
      }
      else if (c == -1 && l > -1) { // si colonne d'entête
        cl.print(l); // on affiche le nombre d'entête de ligne dans la cellule
      }
      else { // sinon
        cl.print(l * c); // on affiche le produit ligne x colonne
      }
      cl.println("</td>"); // on ferme la balise de cellule
      delay(1); //on attend un peu
    }
    cl.println("</tr>");// on ferme la balise de ligne
  }
  cl.println("</table>"); // on ferme la balise de tableau
}

Vous remarquez que pour ce code (qui est un simple affichage) je ne lis pas les informations en provenance du client. C'est une page statique.

Nous avons vu comment agencer nos données dans des tableaux sur nos pages web, voyons maintenant comment les rendre interactives avec des formulaires.

TP : Modifier la table de Pythagore grâce à un formulaire HTML

Dans notre cas, on va pouvoir utiliser un formulaire pour préciser les paramètres de notre table de multiplication et générer cette table à partir des paramètres choisis (par exemple, vous pourrez saisir dans ce formulaire si vous voulez faire une table de multiplication des nombres de 0 à 100, ou de 10 à 15, etc.).

Nous avons vu, pour le moment, que nous pouvions envoyer des informations à l'Arduino depuis notre navigateur grâce à un lien hypertexte contenant les infos à envoyer. On utilisait pour cela la méthode GET du HTML.

Mais qu'en est-il si nous souhaitons envoyer des mots ? Et bien on peut utiliser un formulaire HTML.

Vous avez tous déjà rencontré une page avec un formulaire : il y a des champs pour la saisie, et un bouton pour envoyer les informations.

Ces formulaires sont en fait construits avec des balises HTML spéciales, qui permettent d'envoyer plusieurs informations, de types différents, grâce à une seule page. Les informations ne sont pas fixées par le lien hypertexte, mais dépendent de ce que vont saisir les internautes.

Là encore, nous n'allons pas décortiquer toutes les possibilités du formulaire HTML, mais juste l'utiliser pour améliorer notre table de Pythagore. 

L'idée est la suivante : on peut saisir les nombres concernés par la table, pour obtenir uniquement ceux qui nous intéressent.

Mais avant cela, voyons les balises liées aux formulaires :

  • On place le formulaire entre les balises <form> et </form>. Elles peuvent prendre des arguments. Nous utiliserons ici, "method=post" et action=''.

  • On peut mettre une étiquette aux champs de saisie grâce aux balises <label> et </label>.

  • On crée un champ de saisie avec la balise <input/>. Elle ne se referme pas et prends des arguments. Nous utiliserons "type=text", "name=nom" et  "id=nom".

  • On crée un bouton d'envoi avec la balise <input /> en utilisant deux arguments : "type=submit" et "value=envoyer".

Voici donc le code HTML qui affiche un formulaire comme on pourrait en avoir besoin dans notre cas :

<!DOCTYPE HTML>
<html>
    <head>
        <title>Essai de formulaire</title>
    </head>
    <body>
        Un exemple de formulaire<br>
        <form method='post' action=''>
            <label for='mini'>Nombre minimum : </label>
            <input type='text' name='mini' id='mini'/> <br>
            <label for='maxi'>Nombre maximum : </label>
            <input type='text' name='maxi' id='maxi'/> <br>
            <input type='submit' value='Envoi' />
        </form>
    </body>
</html>

Voici ce que donne ce code html sur mon navigateur (lancé en interne) :

Un exemple de formulaire (affichage sur Safari)
Un exemple de formulaire (affichage sur Safari)

Alors un petit peu d'explications :

Dans la balise <form> :

  • 'method' : correspond à la méthode choisie pour l'envoi des données. Si vous choisissez GET, vous aurez les données liées à votre URL (elles seront limitées à 255 caractères), avec POST, les données ne sont pas visibles dans l'URL, et on peut envoyer de grandes quantités de données.

  • 'action' : correspond à l'adresse à laquelle vous envoyez le formulaire. Sur un Arduino connecté sur le NET, il faut donner l'adresse complète : http://122.13.210.45:3012 (adresse fictive ici, à remplacer par l'adresse publique de votre box et le port attribué).

Dans la balise <label> :

  • 'for' : sert à associer le label à la balise <input /> correspondante. Il faut donc que le nom qui suit for=  corresponde au nom et à l'id situé dans la balise <input /> qui suit.

Dans la balise <input/> :

  • 'type' : indique le type d'entrée, soit 'text' pour du texte, soit 'submit' pour un bouton d'envoi.

  • 'name' et 'id' : vont servir à repérer le champ de saisie pour l'associer à la réponse lors de l'envoi de données.

Voyons maintenant comment se récupèrent les informations envoyées par la méthode POST.

Décodez des informations reçues par la méthode POST

Voici un programme qui construit ce formulaire avec l'Arduino. Nous allons d'abord faire le test sur le réseau local.

#include <SPI.h> //bibliothèqe pour SPI
#include <Ethernet.h> //bibliothèque pour Ethernet
byte mac[] = {0x90, 0xA2, 0xDA, 0x0F, 0xDF, 0xAB}; //adresse mac de votre carte
byte ip[] = {192, 168, 1, 123}; //adresse IP
EthernetServer serveur(80); // déclare l'objet serveur au port d'écoute 80

void setup() {
  Serial.begin (9600); //initialisation de communication série
  Ethernet.begin (mac, ip); //initialisation de la communication Ethernet
  Serial.print("\nLe serveur est sur l'adresse : ");
  Serial.println(Ethernet.localIP()); //on affiche l'adresse IP de la connexion
  serveur.begin(); // démarre l'écoute
}

void loop() {
  EthernetClient client = serveur.available(); //on écoute le port
  if (client) { //si client connecté
    Serial.println("Client en ligne\n"); //on le dit...
    if (client.connected()) { // si le client est en connecté
      while (client.available()) {
        char c = client.read();
        if (c != 13)
          Serial.write(c);
      }
      Serial.println();
      //réponse au client
      entete(client); // appel de la fonction pour créer l'entête de la page
      formulaire(client); // appel de la fonction pour créer le formulaire de la page
      client.println("<br><hr></body></html>"); //ligne horizontale et fermeture des balises
      client.stop(); //on déconnecte le client
      Serial.println("Fin de communication avec le client");
    }
  }
}

//------------------------------- Fonctions ------------------
//fonction d'affichage de l'entête HTML
void entete(EthernetClient cl) {
  //infos pour le navigateur
  cl.println("HTTP/1.1 200 OK"); // type du HTML
  cl.println("Content-Type: text/html; charset=ascii"); //type de fichier et encodage des caractères
  cl.println("Connection: close");  // fermeture de la connexion quand toute la réponse sera envoyée
  cl.println();
  //balises d'entête
  cl.println("<!DOCTYPE HTML>");
  cl.println("<html>");
  cl.println("<head><title>Table de Pythagore</title></head>");
  cl.println("<body><h1>Table de Pythagore</h1><hr><br>");
}
//fonction d'affichage du formulaire HTML
void formulaire(EthernetClient cl) {
  cl.println("<form method='post' action=''>");
  cl.println("<label for='mini'>Nombre minimum : </label>");
  cl.println("<input type='text' name='mini' id='mini' /> <br>");
  cl.println("<label for='maxi'>Nombre maximum : </label>");
  cl.println("<input type='text' name='maxi' id='maxi' /> <br>");
  cl.println("<input type='submit' value='Envoi' />");
  cl.println("</form>");
}

En lançant ce programme, on obtient sur le moniteur série un affichage comme suit (pas exactement pareil en fonction de votre machine) :

POST / HTTP/1.1
Host: 192.168.1.123
Content-Type: application/x-www-form-urlencoded
Origin: http://192.168.1.123
Content-Length: 13
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12
Referer: http://192.168.1.123/
Accept-Language: fr-fr
Accept-Encoding: gzip, deflate

mini=0&maxi=9

On s'aperçoit que les données sont à la fin (contrairement à la méthode GET) et n'ont rien qui permette de les repérer directement (pour effectuer par exemple une lecture jusqu'à obtenir un caractère spécial).

Nous allons donc ruser un peu...

(Lukas, cessez ce rire sardonique, ce n'est pas un film... et pour la définition de sardonique, voyez avec Cunégonde !)

Nous allons utiliser une balise input invisible qui va nous permettre de repérer le début des données. Il suffit pour cela de lui donner l'attribut type='hidden'  (qui veut dire caché) et value='' . Ce qui donne la ligne suivante à ajouter juste après la balise <form> :

cl.println("<input type='hidden' name='x' id='x' value='' />");

Du coup nous pourrons repérer le premier & qui nous indiquera le début des données.

Comme nous entrons dans un décodage délicat, il faut se simplifier la vie. Je vous propose de limiter les noms de nos variables à 4 caractères plus le =. Voici la procédure de décodage :

  • On lit les données jusqu'à voir un & ;

  • On lit les 5 caractères suivants (qui doivent être  mini= ) ;

  • On lit la suite jusqu'au prochain &  (ben oui, car on ne sait pas combien de caractères sont saisis) ;

  • On teste s'il s'agit bien d'un nombre et on  transforme la suite de caractère en nombre (on stocke les caractères dans une chaîne de type String) ;

  • On lit les 5 caractères suivants (qui doivent être maxi= ), puis on lit les caractères jusqu'à là fin des données ;

  • Une fois les deux valeurs récupérées, on vérifie certaines limites (nombre inférieur à 100, pour éviter que les valeurs dépassent la valeur maximale possible pour un nombre de type int par exemple) ;

  • Si tout est ok, on met à jour les variables et on affiche la table, sinon on envoie un message d'erreur. 

TP : correction de la table de Pythagore avec formulaire

Voici le programme que je vous propose :

#include <SPI.h> //bibliothèqe pour SPI
#include <Ethernet.h> //bibliothèque pour Ethernet
byte mac[] = {0x90, 0xA2, 0xDA, 0x0F, 0xDF, 0xAB}; //adresse mac de votre carte
byte ip[] = {192, 168, 1, 123}; //adresse IP
int mini = 0; //variable pour la valeur inférieure
int maxi = 9; //varialbe pour la valeur supérieure
EthernetServer serveur(80); // déclare l'objet serveur au port d'écoute 80

void setup() {
  Serial.begin (9600); //initialisation de communication série
  Ethernet.begin (mac, ip); //initialisation de la communication Ethernet
  Serial.print("\nLe serveur est sur l'adresse : ");
  Serial.println(Ethernet.localIP()); //on affiche l'adresse IP de la connexion
  serveur.begin(); // démarre l'écoute
}

void loop() {
  EthernetClient client = serveur.available(); //on écoute le port
  if (client) { //si client connecté
    Serial.println("Client en ligne\n"); //on le dit...
    if (client.connected()) { // si le client est en connecté
      int r = decodage(client); //on appelle la fonction qui décode et on récupère le retour
      //réponse au client
      entete(client); // appel de la fonction pour créer l'entête de la page
      if (!r) { //si erreur de décodage
        client.println("<h1>Erreur de saisie</h1>"); //on l'indique
        mini = 0; //on fixe le minimum
        maxi = 9; //et le maximum
      }
      formulaire(client);// appel de la fonction pour créer le corps de la page
      corps(client); //on construit le tableau
      client.println("<br><hr></body></html>"); //ligne horizontale et fermeture des balises
      client.stop(); //on déconnecte le client
      Serial.println("Fin de communication avec le client");
    }
  }

}
//------------------------------- Fonctions ------------------
//fonction d'affichage de l'entête HTML
void entete(EthernetClient cl) {
  //infos pour le navigateur
  cl.println("HTTP/1.1 200 OK"); // type du HTML
  cl.println("Content-Type: text/html; charset=ascii"); //type de fichier et encodage des caractères
  cl.println("Connection: close");  // fermeture de la connexion quand toute la réponse sera envoyée
  cl.println();
  //balises d'entête
  cl.println("<!DOCTYPE HTML>");
  cl.println("<html>");
  cl.println("<head><title>Table de Pythagore</title></head>");
  cl.println("<body><h1>Table de Pythagore</h1><hr><br>");
}
//fonction d'affichage du formulaire HTML
void formulaire(EthernetClient cl) {
  cl.println("<form method='post' action=''>");
  cl.println("<input type='hidden' name='x' id='x' value='' />");
  cl.println("<label for='mini'>Nombre minimum : </label>");
  cl.println("<input type='text' name='mini' id='mini' /> <br>");
  cl.println("<label for='maxi'>Nombre maximum : </label>");
  cl.println("<input type='text' name='maxi' id='maxi' /> <br>");
  cl.println("<input type='submit' value='Envoi' />");
  cl.println("</form>");
}
//fonction de décodage des infos client
int decodage(EthernetClient cl) {
  String valeur = ""; //chaîne pour la récupération des valeurs
  char c = lecture(cl); // on lit la première donnée
  while (c != '&') { //tant qu'on ne lit pas un &
    c = lecture(cl); //on continue de lire
    if (c == -1) return 0; //renvoie erreur si fin de fichier
  }
  //& trouvé
  for (int t = 0; t < 5; t++) { //on lit les 5 caractères suivants
    c = lecture(cl);
  }
  while (c != '&') { // tant qu'on n'est pas au bout de la saisie
    c = lecture(cl);
    if (c > 47 && c < 58) { //si il s'agit d'un nombre (voic tableau ascii)
      valeur += c; //on l'ajoute à la chaîne valeur
    }
    //else return 0; //sinon on retourne une erreur
  }
  if (valeur == "") { // si rien n'est saisi
    mini = 0; //on fixe le minimum à 0
  }
  else {
    mini = valeur.toInt(); // on initialise la valeur minimum avec la saisie
  }
  if (mini < 0 || mini > 99) { //test si nombre saisi est correct
    return 0;
  }
  valeur = ""; //on réinitialise la valeur
  for (int t = 0; t < 5; t++) { //on lit les 5 caractères suivants
    c = lecture(cl);
  }
  while (cl.available()) { //tant qu'il reste des caractères à lire
    c = lecture(cl); //on les lit
    if (c != 0) { //si le caractère existe
      if (c > 47 && c < 58) { // si c'est un nombre
        valeur += c; //on l'ajoute à la chaîne
      }
      else return 0; //sinon on retourne une erreur
    }
  }
  if (valeur == "") { // si rien n'est saisi
    maxi = 9; //on fixe le maximum à 9
  }
  else {
    maxi = valeur.toInt(); // on initialise la valeur minimum avec la saisie
  }
  if (maxi <= mini || maxi > 99) { //test si nombre saisi est correct
    return 0;
  }
  return 1; // on retourne que tout va bien
}

//fonction d'affichage du corps de la page HTML
void corps(EthernetClient cl) {
  cl.println("<table rules=all border=1px>"); //ouverture de la balise tableau
  for (int l = mini - 1; l < maxi + 1; l++) { // boucle pour les lignes
    cl.println("<tr>"); //ouverture d'une balise ligne
    for (int c = mini - 1; c < maxi + 1; c++) { // boucle pour les colonnes
      cl.println("<td>"); // ouverture d'une balise cellule
      if (c == mini - 1 && l == mini - 1) { // si case en haut à gauche
        cl.print("X"); //on affiche un X dans la cellule
      }
      else if (c > mini - 1 && l == mini - 1) { // si ligne d'entête
        cl.print(c); // on affiche le nombre d'entête de colonne dans la cellule
      }
      else if (c == mini - 1 && l > mini - 1) { // si colonne d'entête
        cl.print(l); // on affiche le nombre d'entête de ligne dans la cellule
      }
      else { // sinon
        cl.print(l * c); // on affiche le produit ligne x colonne
      }
      cl.println("</td>"); // on ferme la balise de cellule
      delay(1); //on attend un peu
    }
    cl.println("</tr>");// on ferme la balise de ligne
  }
  cl.println("</table>"); // on ferme la balise de tableau
}

//fonction de lecture des données client avec attente.
char lecture(EthernetClient cli) {
  char ch = cli.read();
  delay(1);
  /*
   * Si vous voulez afficher la lecture, décommenter ce qui suit
   * if (c!=13) // si pas retour chariot
   *  Serial.write(c); //on écrit le caractère
   * else //sinon
   *  Serial.write('|'); //on écrit un symbole pour le retour chariot
   */
  return ch; //on renvoie le caractère lu
}

Un programme qui commence à être conséquent.  ;)

Ceci était bien sûr un exercice ! Vous imaginez bien qu'en fonction de vos projets, vous pouvez adapter ce code, l'améliorer, le complexifier.

Pour aller plus loin

Il faut savoir que la bibliothèque Ethernet liée au shield Arduino, permet bien d'autres choses. Je vous conseille de chercher à comprendre les exemples de codes fournis et d'essayer par vous-mêmes de refaire ces programmes.

Il existe aussi des bibliothèques liées au HTML qui peuvent faciliter la vie comme HttpClient ou encore Webduino. Je ne m'en sert personnellement pas, car souvent j'apprécie de gérer moi-même le codage et le décodage, mais n’hésitez pas à explorer leurs fonctionnalités.

Dans tous les cas, il faut bien chercher à savoir le type d'information à échanger et les contraintes liées à ce type d'échange. 

Bien préparer les formats d'échanges (noms des variables, valeur, méthode...) permet souvent de gagner du temps et facilitent la programmation.

Les programmes que je vous ai fournis ne sont peut-être pas les plus performants ou les plus concis, mais ils forment une bonne base pour vous permettre de faire le tour des concepts de programmation essentiels au pilotage d’Arduino via un réseau

En résumé

Vous avez vu dans ce chapitre comment paramétrer (dans les grandes lignes) votre Arduino et son shield Ethernet, pour qu'il soit accessible depuis le Web mondial, en utilisant votre IP publique et un numéro de port dédié.

Vous avez aussi appris les bases HTML pour créer des tableaux et des formulaires, ainsi que pour décoder ces derniers.

Vous avez donc, avec les chapitres qui précèdent, de quoi transformer votre petite carte en serveur multifonctions ! On peut très bien imaginer un affichage de la température, de la lumière, du mouvement capté par la carte. Et aussi des messages qui s'affichent sur un LCD, postés par depuis le monde entier. Ou encore le pilotage de servo-moteurs ou de codes infrarouges grâce à votre portable connecté sur votre Arduino.

Je vous propose d’ailleurs d’utiliser Arduino comme serveur pour faire un peu de domotique en pilotant une lampe depuis votre réseau…

Exemple de certificat de réussite
Exemple de certificat de réussite