Création serveur

A) Schéma de la structure d’un serveur Arduino

B) Initialisation en continu de la page HTML avec des valeurs en provenance de capteurs :

C) Enchaînement de pages 

D) Appel d’une page HTML en passant des paramètres 

E) Affichage des images dans une page HTML

F) Protection d’un site par password

G) Récupération du statut des capteurs et affichage dans un canevas

H) Enregistrement d’un fichier des accès 

 Les informations ci-dessous sont en grande partie inspirées de l’excellent tutorial de Starting Electronics

A) Schéma de la structure d’un  Serveur Arduino :

ServeurArduinoAjax

Résultat : Capture d'écran - 26032014 - 20-46-10

Les fichiers exemples sont situés ici Le programme « Squelette » qui va présenter au client une page HTML  « mapage.htm » stockée sur la carte SD du Shield Arduino Ethernet, le serveur Arduino retournera si l’utilisateur clique sur un bouton l’état d’un switch placé sur la broche 3 du shield (cf. page sur les contacts ) :

 //************************************************************  
 //*** Lecture des caracteres en provenance d un client web **  
 //************************************************************  
 #include <SPI.h>   
 #include <Ethernet.h>   
 #include <SD.h>  
 //--- Declarations ---  
 byte mac[] = { 0x00, 0xAD, 0xBE, 0xEF, 0xFE, 0x50 }; //-- Addresse MAC à adapter  
 IPAddress ip(192,168,0,50);              //-- Adresse IP à adapter  
 EthernetServer serveurHTTP(80);            //-- objet serveur utilisant le port 80 = port HTTP  
 String msg="";                    //-- pour réception chaine requete  
 File webFile;                     //-- pour afficher la page HTML     
 //--- Setup ---  
 void setup() {   
   Serial.begin(9600);   
   //-- Initialisation de la carte SD  
   Serial.println("Initializing SD card...");  
   if (!SD.begin(4)) {  
     Serial.println("ERREUR - La carte SD est indisponible!");  
     return;}  
   Serial.println("Carte SD OK");  
   if (!SD.exists("mapage.htm")) {  
     Serial.println("ERREUR - Fichier mapage.html absent !");  
     return;}  
   Serial.println("Fichier mapage.html OK");  
      //---- initialise le serveur ----  
      Ethernet.begin(mac, ip);   
      delay(1000);   
      Serial.print("Shield Ethernet OK : L'adresse IP du shield Ethernet est : " );  
      Serial.println(Ethernet.localIP());  
      serveurHTTP.begin();  
      Serial.println("Serveur Ethernet OK : Ecoute sur port 80 (http)");  
 }   
 //--- Traitement ---  
 void loop(){   
      EthernetClient client = serveurHTTP.available(); //-- Objet Client Web  
      if (client) { //-- Si un navigateur interroge l'URL de la carte Arduino  
        Serial.println ("... Reception demande client...");       
        ////////////////// Réception requete //////////////////////////  
        boolean currentLineIsBlank = true;  
        msg="";   
        //-- Chargement des caractères
        while (client.connected()) {   
           if (client.available()) { //-- Si octets disponibles en lecture  
             char c = client.read();   
             msg+=c;   
           if (c == '\n' && currentLineIsBlank) {     //--Dernière ligne du client reçue est vide       
               Serial.println ("Reception requete terminee");  
               //-- Analyse de la requete reçue   
               Serial.println("------------ Analyse ------------");   
               requeteOk(client);  
               if (msg.indexOf("ajax_switch") > -1) {  
                 Serial.println ("Requete GET AJAX valide !");  
                 //-- envoi de la réponse HTTP ---  
                 envoiReponse(client);  
                 Serial.println("La reponse Ajax envoyee au client distant...");}        
               else { //-- si pas la chaine souhaitée on envoie page entiere  
                 Serial.println("Envoi mapage.html");  
                 webFile = SD.open("mapage.htm");  
                 if (webFile) {while(webFile.available()) {  
                     client.write(webFile.read());}} //-- Envoi de la page web au client               
                 webFile.close();}  
             //-- Affichage de la requete HTTP sur le port série  
             Serial.print(msg);  
             msg = "";        
             break;}  
         //-- Chaque ligne de texte reçue se termine par \r\n  
         if (c == '\n') { //-- Fin de ligne reçue  
           currentLineIsBlank = true;}   
         else if (c != '\r') { //-- Un caractère en provenance du client est reçu  
           currentLineIsBlank = false;}}}  
      //-- fermeture de la connexion avec le client après envoi réponse  
      delay(1);   
      client.stop();  
      Serial.println("------------ Fermeture de la connexion avec le client ------------");   
      Serial.println ("");  
      }   
 }   
 //------------------------------------------------------  
 // Texte HTML à envoyer pour signifier que tout est ok            
 //-----------------------------------------------------  
 void requeteOk(EthernetClient client){  
   client.println("HTTP/1.1 200 OK");  
   client.println("Content-Type: text/html");  
   client.println("Connection: keep-alive");  
   client.println();}  
 //------------------------------------------------------  
 // Chaine à envoyer au client en réponse à une requête       
 //----------------------------------------------------- 
 void envoiReponse(EthernetClient client){      
 //-- Envoi au client en fonction de l'état d'une broche
    if (digitalRead(3)) {client.println("Etat switch 3 : ON");}
    else {client.println("Etat switch 3 : OFF");}} 
}  

La page HTML qui affichera suite à l’appui sur un bouton le message que la carte Arduino aura prévu d’envoyer (ici « Etat du switch 3 »); ATTENTION, pour tester le fonctionnement d’une page AJAX il est nécessaire de la placer dans le répertoire www d’un serveur Apache :

 <!DOCTYPE html>  
 <html>  
 <head>  
  <title>AJAX ARDUINO</title>  
  <script>  
  function GetSwitchState() {  
  nocache = "&nocache="+ Math.random() * 1000000;  
  //-- Défibition d'un objet XHR  
  var request = new XMLHttpRequest();  
  //-- Fonction de callback pour gérer la réponse du serveur  
  request.onreadystatechange = function() {  
   if (this.readyState == 4) {  
    if (this.status == 200) {  
     if (this.responseText != null) {  
       document.getElementById("switch_txt").innerHTML = this.responseText;}}}}  
  //-- Définition d'un GET à envoyer au serveur Arduino                 
  request.open("GET", "ajax_switch" + nocache, true); //-- nocache pour compatibilité IE  
  //-- Envoi du GET au serveur Arduino  
  request.send(null);  
  }  
  </script>  
 </head>  
 <body>  
  <h1>Arduino AJAX Test</h1>  
  <p id="switch_txt">Etat du switch 3 : inconnu...</p>  
  <button type="button" onclick="GetSwitchState()">SWITCH 3</button>  
 </body>  
 </html>  

 

B) Initialisation en continu de la page HTML avec des valeurs en provenance de capteurs :

ArduinoAnalog

Pour réaliser cela la fonction d’affichage des valeurs devra être rafraichie à l’aide de l’instruction setTimeout() et à l’aide de la balise <body onload= »founction()« > comme indiqué dans la page suivante (fichiers ici) :

 <!DOCTYPE html>  
 <html>  
 <head>  
  <title>Arduino Analog Page</title>  
  <script>  
  function GetSwitchAnalogData() {  
   nocache = "&nocache=" + Math.random() * 1000000;  
   var request = new XMLHttpRequest();  
   request.onreadystatechange = function() {  
   if (this.readyState == 4) {  
    if (this.status == 200) {  
    if (this.responseText != null) {  
      document.getElementById("sw_an_data").innerHTML = this.responseText;}}}}  
   request.open("GET", "ajax_switch" + nocache, true);  
   request.send(null);  
      //-- Rafraichisement de la page toute les secondes (1000 ms)  
   setTimeout('GetSwitchAnalogData()', 1000);}  
  </script>  
 </head>  
 <body onload="GetSwitchAnalogData()">  
  <h1>Arduino AJAX Input</h1>  
  <div id="sw_an_data"></div>  
 </body>  
 </html>  

Seule la fonction de retour de données est changée dans le sketch Arduino  (ici test d’une broche Analogique 2 et d’un switch situé en broche digitale 3 ) :

  void envoiReponse(EthernetClient client){   
   int analog_val;    
      //-- Lecture état broche 3  
   if (digitalRead(3)) {  
     client.println("<p>Switch 3 : ON</p>");}  
   //-- Lecture de la broche analogique A2  
   analog_val = analogRead(2);  
   client.print("<p>Analog A2: ");  
   client.print(analog_val);  
   client.println("</p>");}  

 

C) Enchaînement de pages :

Nous aborderons ici l’appel de lien permettant la navigation de page en page. Ainsi le serveur Arduino pourra être constitué d’une page « index » contenant des liens vers n pages.

Pour déclarer un lien vers une page (ici page.htm)  il faut utiliser la balise HTML <a> la syntaxe étant :

 <a href="page.htm">libellé_lien</a>  

Quand un navigateur charge une page, les données reçues par le serveur sont « GET /page.htm » ou « GET / » quand c’est la page index.htm. Ainsi, il suffit d’insérer dans notre code d’analyse de la requête reçue les instructions suivantes :

 //-- si pas requête souhaitée on regarde si l'utilisateur a cliqué sur un lien  
 if (msg.indexOf("GET / ") || StrContains(HTTP_req, "GET /index.htm")){  
     webFile = SD.open("index.htm");} //-- Envoie de la page Index (par défaut)  
 else if (msg.indexOf("GET /page2.htm")) {  
     webFile = SD.open("page.htm");} //-- Envoie de la page du lien cliqué (ici page.htm) 

D) Appel d’une page HTML en passant des paramètres :

Ceci peut être utile si l’on souhaite transférer des paramètres d’une page web à une autre.

 <!DOCTYPE html>  
 <html>  
 <head>  
 <meta charset=\"utf-8\" />   
 <title>Test de transfert de paramètre </title>  
 <script language="JavaScript">  
  //-- Appel de la page : demo.html?LED1=ON&CAPTEUR1=1234  
  function processUser()   
  { var parameters = location.search.substring(1).split("&"); //-- location.search contient la liste des paramètres (à retirer ? et à éclater selon les '&'  
   var param1 = parameters[0].split("=");  
   var param2 = parameters[1].split("=");  
      alert("1er param : '"+param1[1]+"' 2nd : '"+param2[1]+"'");  
  }  
 </script>  
 <!-- Fin du code Javascript -->   
 </head>  
 <body>  
  <p>  
  <button onclick="processUser();">Cliquez pour visualiser les parametres...</button>  
  </p>  
 </body>  
 </html>  

 

E) Affichage des images dans une page HTML :

Copiez les images sur la carte SD et placer les images dans la page HTML à l’aide de la balise img : <img src= »pic.jpg » />

Le chargement des images dans le navigateur se fait dès que la page est reçue. A la fin de la réception de la page, le serveur reçoit des commandes GET pour chaque image qu’il doit placer dans la page (exemple GET /pic.jpg

Il convient donc dans le sketch de tester la présence de l’instruction GET /pic.jpg pour envoyer le fichier image au client.

 if (msg.indexOf("GET /pic.jpg") > -1) {   
   Serial.println ("Envoi image !");   
   webFile = SD.open("pic.jpg");   
   if (webFile) {while(webFile.available()) {   
     client.write(webFile.read());}} //-- Envoi de la page web au client          
   webFile.close();}   

F) Protection d’un site par password :

Pour cela, la page d’accueil « acceuil.htm » contiendra un formulaire de saisie de login et password du type :

 <!DOCTYPE html>  
 <html>  
 <head>  
  <title>Connexion Arduino privée</title>   
 </head>  
 <body>     
      <TABLE border=0>   
      <FORM action="cible.htm" method=get>   
           <TR><TD align='right'>Votre login</TD><TD><INPUT size=12 name='log'></TD></TR>   
           <TR><TD align='right'>Votre mot de passe</TD><TD><INPUT type=password size=12 name='pass'></TD></TR>   
           <TR><TD></TD><TD><INPUT type=submit value="Validez" ></TD></TR></TABLE>   
 </body>   
 </html>  

Cette page appellera, à la validation du formulaire, une autre page « cible.htm » avec comme paramètre le login et le password, soit l’instruction « cible.htm?log=login&pass=pass« . Ces paramètres seront décodés par notre programme arduino pour valider l’affichage d’une 3ième page  « protegee.htm » qui sera celle de notre site…

Dans l’exemple ci-dessous, le serveur affiche la page « acceuil.htm » qui demande un login et mot de pass. en fonction du login et de la validité du password affichage d’une page « pageadm.htm » ou « pageuser.htm ». Gestion de la présence d’une image (ici « logo.png »).

 //******************************************************************  
 //*** Affichage de pages suite à la saisie d'un LOGIN et PASSWORD **  
 //******************************************************************  
 #include <SPI.h>   
 #include <Ethernet.h>   
 #include <SD.h>  
 //--- Declarations ---  
 byte mac[] = { 0x00, 0xAD, 0xBE, 0xEF, 0xFE, 0x50 }; //-- Addresse MAC à adapter  
 IPAddress ip(192,168,0,50);              //-- Adresse IP à adapter  
 EthernetServer serveurHTTP(80);            //-- objet serveur utilisant le port 80 = port HTTP  
 String msg="";                    //-- pour réception chaine requete  
 File webFile;                     //-- pour afficher la page HTML     
 //--- Setup ---  
 void setup() {   
      Serial.begin(9600);   
   //-- Initialisation de la carte SD  
   Serial.println("Initializing SD card...");  
   if (!SD.begin(4)) {  
     Serial.println("ERREUR - La carte SD est indisponible!");  
     return;}  
   Serial.println("Carte SD OK");  
   if (!SD.exists("pagepass.htm")) {  
     Serial.println("ERREUR - Fichier pagepass.html absent !");  
     return;}  
   Serial.println("Fichier pagepass.html OK");  
      //---- initialise le serveur ----  
      Ethernet.begin(mac, ip);   
      delay(1000);   
      Serial.print("Shield Ethernet OK : L'adresse IP du shield Ethernet est : " );  
      Serial.println(Ethernet.localIP());  
      serveurHTTP.begin();  
      Serial.println("Serveur Ethernet OK : Ecoute sur port 80 (http)");  
 }   
 //--- Traitement ---  
 void loop(){   
      EthernetClient client = serveurHTTP.available(); //-- Objet Client Web  
      if (client) { //-- Si un navigateur interroge l'URL de la carte Arduino  
        Serial.println ("... Reception demande client...");       
        ////////////////// Réception requete //////////////////////////  
        boolean currentLineIsBlank = true;  
        msg="";   
        while (client.connected()) {   
           if (client.available()) { //-- Si octets disponibles en lecture  
             char c = client.read();   
             msg+=c;   
       if (c == '\n' && currentLineIsBlank) {     //--Dernière ligne du client reçue est vide       
               Serial.println ("Reception requete terminee");  
               //-- Analyse de la requete reçue   
               Serial.println("------------ Analyse ------------");   
               requeteOk(client);  
                  //-- La chaine ok de connexion est "cible.htm?log=login&pass=password" où login et password sont les valeurs saisies par l'utilisateur  
               if (msg.indexOf("log=admin&pass=andro1") > -1) { //-- Si dans la requête récupérée le mot clef admin est inclu                    
                   Serial.println ("Mot de pass admin ok !");  
                   //-- envoi de la réponse HTTP ---  
                   envoiReponse(client,"pageadm.htm");}   
               if (msg.indexOf("GET logo.png") > -1) { //-- Si dans la requête récupérée le mot clef admin est inclu                    
                   Serial.println ("Envoi image logo...");  
                   //-- envoi de la réponse HTTP ---  
                   envoiReponse(client,"logo.png");}                     
               if (msg.indexOf("log=user&pass=vero") > -1) { //-- Si dans la requête récupérée le mot clef admin est inclu                    
                   Serial.println ("Mot de pass admin ok !");  
                   //-- envoi de la réponse HTTP ---  
                   envoiReponse(client,"pageuser.htm");}                             
               else { //-- si pas la chaine souhaitée on envoie page entiere  
                   Serial.println("Envoi page acceuil");  
                   envoiReponse(client,"acceuil.htm");}  
                  //-- Affichage de la requete HTTP sur le port série  
         Serial.print(msg);  
         msg = "";        
         break;}  
         //-- Chaque ligne de texte reçue se termine par \r\n  
         if (c == '\n') { //-- Fin de ligne reçue  
           currentLineIsBlank = true;}   
         else if (c != '\r') { //-- Un caractère en provenance du client est reçu  
           currentLineIsBlank = false;}}}  
      //-- fermeture de la connexion avec le client après envoi réponse  
      delay(1);   
      client.stop();  
      Serial.println("------------ Fermeture de la connexion avec le client ------------");   
      Serial.println ("");  
      }   
 }   
 //------------------------------------------------------  
 // Texte HTML à envoyer pour signifier que tout est ok            
 //-----------------------------------------------------  
 void requeteOk(EthernetClient client){  
   client.println("HTTP/1.1 200 OK");  
   client.println("Content-Type: text/html");  
   client.println("Connection: keep-alive");  
   client.println();}  
 //------------------------------------------------------  
 // Page à envoyer au client en réponse à une requête       
 //-----------------------------------------------------       
  void envoiReponse(EthernetClient client, String nompage){  
   char filename[nompage.length()+1];  
   nompage.toCharArray(filename, sizeof(filename));   
   //-- Envoi au client en fonction de l'état d'une broche  
   webFile = SD.open(filename);  
  if (webFile) {while(webFile.available()) {  
          client.write(webFile.read());}} //-- Envoi de la page web au client               
   webFile.close();  
      }  

G) Récupération du statut des capteurs et affichage dans un canevas :

Nous utiliserons une page qui affichera dans un canevas le plan d’une maison. Chaque entrée de la maison étant pourvu de capteurs, la page sera alimentée par un fichier XML mis à jour toute les secondes par une carte Arduino MEGA.

La structure du fichier XML est :

 <?xml version = "1.0" ?>  
 <inputs>  
   <portailcours>OFF</portailcours>  
   <portecours>OFF</portecours>  
   <porteentree>OFF</porteentree>  
      <porteparking>OFF</porteparking>  
      <portail>OFF</portail>  
      <portillon>OFF</portillon>  
      <bal>OFF</bal>  
 </inputs>  

Notre programme Arduino enverra, à la réception du mot clef ‘alarmecontacts’ les lignes du fichier XML…

La structure de la fonction de récupération des informations des capteurs utilise maintenant l’instruction responseXML :

 function GetArduinoInputs()  
 {nocache = "&nocache=" + Math.random() * 1000000;  
  var request = new XMLHttpRequest();  
  request.onreadystatechange = function()  
  {if (this.readyState == 4) {  
   if (this.status == 200) {  
     if (this.responseXML != null) {  
       //-- Extraction des valeurs à partir du fichier XML reçu  
       dpPortailcours = this.responseXML.getElementsByTagName('portailcours')[0].childNodes[0].nodeValue;  
       dpPortecours  = this.responseXML.getElementsByTagName('portecours')[0].childNodes[0].nodeValue;  
       dpPorteentree = this.responseXML.getElementsByTagName('porteentree')[0].childNodes[0].nodeValue;  
       dpPorteparking = this.responseXML.getElementsByTagName('porteparking')[0].childNodes[0].nodeValue;  
       dpPortail   = this.responseXML.getElementsByTagName('portail')[0].childNodes[0].nodeValue;       
       dpPortillon  = this.responseXML.getElementsByTagName('portillon')[0].childNodes[0].nodeValue;       
       dpBal     = this.responseXML.getElementsByTagName('bal')[0].childNodes[0].nodeValue;                                          
             }}}}  
  request.open("GET", "alarmecontacts" + nocache, true);  
  request.send(null);  
  setTimeout('GetArduinoInputs()', 1000);}  

 

H) Enregistrement d’un fichier des accès :

La création d’un fichier Log permet de mémoriser les accès aux pages et de tracer des actions effectuées. Dans le cas d’une détection de présence il pourrait aussi enregistrer l’état des capteurs. Par exemple il est possible de gérer un fichier « log.txt » pour les accès et « capteurs.txt » pour l’état des capteurs.  Un seul fichier ne pouvant être ouvert à la fois et l’écriture du fichier se faisant à la fermeture du fichier, il suffit de placer le code suivant pour chaque événement devant être mémorisé.

 myFile = SD.open("log.txt", FILE_WRITE);  
 //-- si on peut ouvrir le fichier  
 if (myFile) {  
   Serial.print("Enregistrement Log");  
   myFile.println(dDateAcces,sProfil,sUser);  
   //-- Ecriture et fermeture du fichier  
   myFile.close();  
  } else {  
   //-- erreur avecle fichier  
   Serial.println("error opening test.txt");  
  }  

Remarque pour récupérer la date et heure de connexion il est nécessaire d’ajouter un module tel que le DS1307 pour récupérer des paramètres temporels.

 

 

3 comments on “Création serveur
  1. Aubier-Laure Christophe dit :

    Bonjour,
    J’ai essayé de suivre votre tutoriel ainsi que celui « http://startingelectronics.org/tutorials/arduino/ethernet-shield-web-server-tutorial/SD-card-AJAX-web-server/ » mais à chaque fois le téléversement se déroule normalement et le moniteur série indique que le document html a bien été trouvé mais la page du navigateur safari et Firefox) reste désespérément vierge. Quand vous précisez : »ATTENTION, pour tester le fonctionnement d’une page AJAX il est nécessaire de la placer dans le répertoire www d’un serveur Apache : » le serveur doit être installé sur l’ordinateur qui est utilisé pour visualiser la page ou bien sur l’arduino ?
    Merci de votre aide.
    Cal

  2. Aubier-Laure Christophe dit :

    Bonsoir,
    Merci de votre réponse. Mais j’avais compris que je devais mettre la page sur la carte SD alors qu’est-ce que je met sur mon serveur apache ? (j’ai installé Mamp).
    Cordialement
    Cal

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.

Articles récents
Commentaires récents
fatima dans Bienvenue !
AdminDroid dans Bienvenue !
fatima dans Bienvenue !
Archives
Catégories
%d blogueurs aiment cette page :