• 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 carte Arduino sur le réseau local

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

Voici un chapitre qui va vous faire encore passer un niveau ! Le pilotage de l'Arduino depuis votre réseau.

L'objectif est de créer des interfaces, basées sur les pages html, qui vont vous permettre de commander les pins de l'Arduino à distance, donc depuis toute machine (tablette, ordinateur, smartphone...) dotée d'un navigateur web et d'une connexion sur votre réseau.

Vous allez dans un premier temps apprendre le principe des liens hypertextes et comment les utiliser avec l'Arduino en mode serveur.

Vous allez ensuite pouvoir créer une interface plus complète, toujours en html, pour piloter des LEDs ou tout autre matériel relié à votre Arduino.

Nous partirons des connaissances déjà acquises dans les chapitres précédents afin de les compléter et de les adapter à nos projets.

Ne tardons pas plus...

Les liens hypertexte

Nous allons utiliser une des fonctions du HTML pour piloter notre Arduino. Il s'agit du lien hypertexte.

Lorsque vous naviguez sur un site, certains mots sont soulignés. En cliquant dessus, vous vous déplacez dans la page ou simplement vous changez de page. Ces liens sont en fait des parties actives de la page qui vont, après être cliqués, envoyer au serveur des informations qu'il va traiter pour vous renvoyer une nouvelle page.

J'entends par nouvelle page, soit la même page mise à jour, soit une autre page, soit directement un lien vers un autre serveur.

Pour ce faire, il existe un balise spéciale en HTML : la balise <a>  </a>.

Elle comporte certains paramètres à ajouter dans la balise d'ouverture.

Nous allons, dans un premier temps, non pas travailler avec l'Arduino, mais avec un éditeur de texte, et des fichiers enregistrés sur votre ordinateur.

Je vous propose donc de créer un dossier nommé "testHTML" (si vous souhaitez le nommer autrement, cela n'a pas d'importance)

Dans ce dossier, vous allez créer un fichier texte nommé page.txt avec le code suivant :

<!DOCTYPE HTML>
<html>
    <head>
        <title>Essai</title>
    </head>
    <body>
        <h1>Essai</h1>
        Voici une page simple<br><br>
        <a href=page.html target=_self>Encore</a>
    </body>
</html>

Vous pouvez observer plusieurs choses :

  • Nous avons deux paramètres dans la balise d'ouverture <a> : l'adresse de la page sur laquelle aller et la cible (target) visée.

  • Entre la balise nous mettons un mot, cela peut-être une phrase ou un objet.

  • Nous fermons la balise.

Renommer votre fichier avec l'extension .html, et ouvrez-le avec un navigateur.

Vous voyez que le mot "encore" est souligné. C'est le principe de la balise <a>, elle crée des liens hypertextes.

Si vous cliquez dessus, rien ne se passe en apparence. En fait votre page s'ouvre à nouveau ! Ce lien permet un rafraîchissement de la page.

Nous allons maintenant modifier ce fichier.

Ouvrez-le avec votre éditeur et modifiez comme suit :

<!DOCTYPE HTML>
<html>
    <head>
        <title>Essai</title>
    </head>
    <body>
        <h1>Essai</h1>
        Voici une page simple<br><br>
        <a href=http://www.google.fr target=_blank>Rechercher</a>
        <br><br>
        <a href=page.html target=_self>Encore</a>
    </body>
</html>

En testant cette page, vous voyez que le lien ouvre un nouvel onglet (ou une nouvelle page) sur la page de recherche de Google.

Nous pouvons donc déduire que :

  • href : indique le lien vers la page ;

  • target : indique qu'on utilise le même onglet pour afficher le lien (_self) ou un nouvel onglet (_blank).

Là encore, vous pouvez aller plus loin dans le HTML pour l'utilisation des balises <a> </a>. Ce n'est pas le but de ce cours.

Il faut savoir qu'il est tout à fait possible d'envoyer des informations au serveur grâce à un lien hypertexte.

Il suffit d'utiliser une méthode d'envoi à travers l'adresse de la page.

Méthode GET

La méthode que vous allez voir ici va nous permettre d'envoyer facilement des informations à l'Arduino. Cette méthode porte le nom de GET. Elle est construite automatiquement lorsqu'on utilise des formulaires. Nous allons la construire à la main pour l'utiliser ensuite.

La méthode GET ajoute des informations à l'URL en utilisant trois symboles :

  • Le ? qui est à placer juste après l'adresse de la page, il indique que des données vont suivre ;

  • Le = qui sépare le nom d'une donnée de sa valeur associée ;

  • le &  qui permet de séparer plusieurs groupes donnée/valeur.

Voici le format d'un lien avec des informations mises en GET :

page.html?age=20&taille=180

Cette ligne envoie à l'adresse "page.html" en précisant deux informations au serveur : la donnée age qui vaut 20 et la donnée taille qui vaut 180.

Modifiez donc votre programme comme suit :

<!DOCTYPE HTML>
<html>
    <head>
        <title>Essai</title>
    </head>
    <body>
        <h1>Essai</h1>
        Voici une page simple<br><br>
        <a href=page.html?age=20&taille=180 target=_self>Envoi</a>
        <br><br>
        <a href=page.html target=_self>Encore</a>
    </body>
</html>

En cliquant sur "Envoi", vous voyez que dans la barre d'adresse, vos informations sont ajoutées à l'URL (contrairement à ce qui se passe si vous appuyez sur "Encore" )

Alors tout ceci est bien beau, mais comment allons nous récupérer ces informations avec l'Arduino ?

Et bien je ne vous cache pas que c'est là que ça se complique...

Observons déjà ce que reçoit l'Arduino si on lui fait créer une page de ce genre et si on clique sur un des liens...

Voici un programme qui contient deux liens, un pour rafraîchir, l'autre pour envoyer des informations avec la méthode GET :

#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()) { // tant qu'il a des infos à transmettre
        char c = client.read(); // on lit le caractère
        Serial.write(c);// on l'écrit sur le moniteur série
        delay(1); //délai de lecture
      }
      //réponse au client
      entete(client);
      client.println("<a href=?valeur=100>Envoi</a><br>");
      client.println("<a href=?>Refresh</a><br>");
      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>");
}

Si vous cliquez sur envoi, vous voyez sur le moniteur série un affichage qui ressemble à ça :

GET /?valeur=100 HTTP/1.1
Host: 192.168.1.123
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
Accept-Language: fr-fr
Referer: http://192.168.1.123/
Accept-Encoding: gzip, deflate

Ce qui nous intéresse, c'est la première ligne :

GET /?valeur=100 HTTP/1.1

Nous observons que la valeur est bien reçue dans l'adresse après un '\'.

Le 'v' de valeur se situe juste après le caractère '?' du début de la réception. Et la valeur qui nous intéresse est terminée par un espace. Nous allons donc pouvoir créer un décodeur !

L'idée est la suivante :

  • On parcourt la réception des données jusqu'à obtenir un ? ;

  • On crée une chaîne pour le nom avec tous les caractères qui suivent jusqu'à rencontrer un = ;

  • On crée une chaîne de caractère avec tous les caractères qui suivent jusqu'à rencontrer un &  ou un espace ;

  • Si c'est un &  on recommence ; 

  • Si c'est un espace, on a fini, le reste n'est pas utile.

Vous pouvez essayer vous-mêmes de créer ce programme (vous avez assez d'informations avec les chapitres précédents) afin qu'il affiche sur la page le nom de la valeur, suivi de " : ", suivi de la valeur elle-même...

Voici le programme qui permet de réaliser notre objectif :

#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"); //on le dit...
    if (client.connected()) { // si le client est en connecté
      String affichage = GET(client); //appel de la fonction de décodage
      //réponse au client
      entete(client);
      client.println(affichage);
      client.println("<a href=?valeur=100&autreValeur=10&anniv=oui target=_self>Envoi</a><br>");
      client.println("<a href=? target=_self>Refresh</a><br>");
      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\n");
    }
  }
}
//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>");
}
//fonctin décodage GET
String GET(EthernetClient cl) {
  int lecture = 0; // variable pour les étapes de décodage
  String resultat = "<br>"; //initialisation de la chaine de réponse
  String donnee = ""; //chaine pour stocker la lecture des données
  while (cl.available()) { // tant qu'il a des infos à transmettre
    char c = cl.read(); // on lit le caractère
    if (lecture == 0 && c == '?') { //début de lecture des données donc d'un nom
      lecture = 1;
      donnee = "";
    }
    else if (lecture == 1 && c == '=') { //début de lecture d'une valeur
      lecture = 2;
      resultat += donnee + " : "; //on construit la chaîne de réponse
      donnee = "";
    }
    else if (lecture == 2 && c == '&') { //nouveau nom
      lecture = 1;
      resultat += donnee + "<br>"; //on construit la chaîne de réponse
      donnee = "";
    }
    else if ((lecture == 2 || lecture == 1) && c == ' ') { //fin de lecture
      lecture = 3;
      resultat += donnee + "<br><br>"; // on finit la chaîne réponse.
    }
    else if (lecture == 1 || lecture == 2) {//récupération des données de nom ou de valeur
      donnee += c;
    }
    delay(1); //delai de lecture
  }
  return resultat; // retour le la chaîne de réponse
}

Évidemment, comme nous analysons les données sans savoir combien elles sont, le programme est plus compliqué.

Vous pouvez vous amuser à changer le contenu de la balise de lien qui contient les données en mettant les vôtres.

Vous commencez je pense, à percevoir les possibilités de ce genre de décodage.

En effet, si à la place d'afficher les données reçues, nous les utilisions pour agir sur les pins de l'Arduino, ce serait un moyen de le piloter par le réseau. Voyons justement le TP suivant...

Allumez et éteignez des LED avec Arduino via le réseau interne

Je vous propose donc un exercice, j'allais dire simple, mais pas tant que ça.

(Ne soufflez pas ainsi Lukas, ça me décoiffe...)

Vous allez connecter 5 LED sur votre Arduino, respectivement aux pins 2, 3, 5, 6 et 7. Nous n'utiliserons pas le pin 4 (réservé à la carte SD).

L'objectif est de créer une page web qui proposera 5 liens. Chacun permettra de piloter une LED pour qu'elle soit allumée, éteinte ou clignotante (fréquence 0,5 Hz).

Donnez un titre à votre page web et à la fenêtre, par exemple : "Web-Commande de LED".

Elle doit pouvoir afficher le mode actuel de la LED (ON, OFF, CLIGNOTE)  et à côté un lien qui permet de changer ce mode. Le lien sera le mot "Changer".

Pour vous aider un peu :

  • Le lien n'enverra pas un état de toutes les LED mais l'information d'un changement d'état d'une LED (une donnée suffit finalement : le numéro de LED à changer). Attention de penser que ce qui est lu est un caractère. Par exemple, '1' n'est pas 1 en valeur numérique, mais 49. Il faut donc penser à le convertir en numérique.

  • Il faudra penser à gérer le temps et à permettre au programme de faire d'autres tâches pendant qu'il attend de recevoir des données (comme le clignotement des LED). 

  • Pensez aux résistances pour les montages...

Voici un exemple d'affichage :

Exemple de rendu de page web
Exemple de rendu de page web

Si vous y arrivez, c'est que vous avez saisi le principe global ! N'hésitez pas à chercher, tester, essayer... jusqu'à réussir. Ça peut prendre un certain temps.

TP : Correction

Voici le programme que je vous propose. Bien sûr, si vous avez réussi à faire fonctionner votre page web avec un programme différent, c’est tout aussi valide.

#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
unsigned long tpsDep; //temps départ pour la gestion des LED
int pinLed[5] = {2, 3, 5, 6, 7}; //tableau des pins de LED
boolean etatLed[5] = {0, 0, 0, 0, 0}; //tableau des états des LED
int modeLed[5] = {0, 0, 0, 0, 0}; // tableau des modes des LED
EthernetServer serveur(80); // déclare l'objet serveur au port d'écoute 80

void setup() {
  for (int l = 0; l < 5; l++) {
    pinMode(pinLed[l], OUTPUT);
  }
  Serial.begin (9600); //initialisation de communication série
  Ethernet.begin (mac, ip); //initialisation de la communication Ethernet
  Serial.print("*\n-> Le 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() {
  gestionClient(); // fonction qui gère toute la communication avec le client
  gestionLed(); // fonction qui gère l'allumage des LED
}

//----------------------Fonctions----------------------
//fonction qui gère la communication avec le client
void gestionClient() {
  EthernetClient client = serveur.available(); //on écoute le port
  if (client) { //si client existe
    Serial.println("Client en ligne"); //on le dit...
    if (client.connected()) { // si le client est connecté
      GET(client); //appel de la fonction de décodage
      //réponse au client
      entete(client); // fonction pour l'entête de la page HTML
      corps(client); // fonction pour le corps
      piedPage(client); // fonction pour le pied de page
      Serial.println("Fin de communication avec le client\n");
      client.stop(); //on déconnecte le client
    }
  }
}
//fonction de fabrication 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>Web-Commande de LED</title></head>");
  cl.println("<body><h1>Web-Commande de LED</h1><hr><br>");
}
//fonction de fabrication du corps de page
void corps(EthernetClient cl) {
  cl.println("<br>"); // saut de ligne
  //boucle pour construire chaque ligne en fonction des LED
  for (int l = 0; l < 5; l++) {
    cl.print("<br>LED ");
    cl.print(l);
    cl.print(" ");
    Serial.println(l);
    switch (modeLed[l]) {
      case 0:
        cl.print("OFF ");
        break;
      case 1:
        cl.print("ON ");
        break;
      case 2:
        cl.print("CLI ");
        break;
    }
    cl.print(" <a href=?"); //création du lien inutile de répéter l'adresse du site
    cl.print(l);
    cl.println(" target=_self >Change</a><br>");
  }
}
//fonction de fabrication du pied de page
void piedPage(EthernetClient cl) {
  //balises de pied de page
  cl.println("<br><hr>");
  cl.println("</body>");
  cl.println("</html>");
}
//fonctin décodage GET
void GET(EthernetClient cl) {
  boolean lu = 0; //variable pour indiquer l'état de lecture
  while (cl.available()) { // tant qu'il a des infos à transmettre
    char c = cl.read(); // on lit le caractère
    delay(1); //delai de lecture
    if (c == '?' && lu == 0) { //si "?" repéré
      c = cl.read(); //on lit le caractère suivant qui contient la donnée
      int led = int(c) - 48; //on la transforme en nombre
      modeLed[led] = (modeLed[led] + 1) % 3; //on change l'état de la LED
      delay(10);
      lu = 1; // on dit qu'on a lu l'info
    }

  }
}
//fonction d'allumage des LED par rapport au tableau de mode.
void gestionLed() {
  unsigned long tpsAct = millis(); // récupération du temps actuel
  if (tpsAct - tpsDep > 250) { //si délai dépassé
    for (int l = 0; l < 5; l++) { // chaque LED
      if (modeLed[l] == 0) { // si mode éteint
        etatLed[l] = 0; // on éteind
      }
      else if (modeLed[l] == 1) {// si mode allumé
        etatLed[l] = 1; //on allume
      }
      else if (modeLed[l] == 2) { //si mode clignotant
        etatLed[l] = !etatLed[l]; //on inverse l'état
      }
      digitalWrite(pinLed[l], etatLed[l]); //on met le pin à jour
      tpsDep = tpsAct; //on réinitialise le temps
    }
  }
}

J'ai choisi de créer des fonctions qui construisent l'en-tête, le corps et le pied de page HTML. C'est plus lisible et plus simple pour se concentrer sur la construction de la page.

Pour l'envoi d'informations dans l'URL, je ne m'embête même pas à créer un texte du genre :

 ?variable=valeur.

Je construis juste le lien en mettant le numéro de la LED juste après le ?.

La fonction d'analyse du GET met à jour les tableaux qui seront ensuite exploités par la fonction d'allumage des LED.

Je vous laisse imaginer toutes les possibilités qui s'offrent à vous juste avec cette méthode d'envoi de données. 

En résumé

Vous avez vu dans ce chapitre comment faire de votre Arduino un serveur sur votre réseau interne. 

Vous savez maintenant lui envoyer des données simples grâce à l'utilisation de la méthode GET utilisée avec les liens hypertexte, ou trier des données plus complexes.

Vous pouvez donc commander votre Arduino avec tout matériel connecté à votre réseau et qui dispose d'un navigateur web.

Il nous faut maintenant ouvrir notre Arduino sur le monde, c'est ce que nous allons voir dans le chapitre suivant. ;)

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