Serveur web avec Arduino et ESP8266

Parce que les applications du Raspberry Pi sont illimités...

Modérateur : Francois

Veloce
Messages : 79
Enregistré le : sam. 24 janv. 2015 20:12

Serveur web avec Arduino et ESP8266

Message par Veloce » lun. 9 mars 2015 21:30

Comment ça un Arduino ? Mais c'est un forum de Raspberry Pi ici Monsieur !

Oui, mais attendez je vais vous expliquer.
Au départ, je voulais fabriquer un thermostat de maison commandé par Internet.
J'ai un capteur de température DHT11, un écran LCD LTN211 avec un shield I2C,
un relais, bref tout ce qu'il faut. Ah non, il faut aussi un microcontrôleur.

Je me suis dit : Raspberry Pi, forcément, puisqu'il faut du Wifi.

Or, il se trouve qu'entre temps j'ai découvert les modules ESP8266, qui pour à peine plus de
3 euros permettent de connecter l'Arduino, ou n'importe quel bidule qui a une liaison série,
à Internet. L'avantage c'est qu'un Arduino consomme quand même beaucoup moins qu'un Raspberry,
et puis je me sens plus en confiance avec un tout petit programme en C qu'avec un Linux pour
une application qui doit fonctionner sans surveillance.
(Hum sur ce dernier point j'ai un peu changé d'avis, on en reparlera...)

Je n'étais pas pressé, avant de faire mon thermostat j'avais prévu de modifier ma machine à remonter le temps
pour permettre de changer l'heure d'origine et de destination temporelles par Internet. :twisted:

Mais d'une part je suis inscrit sur le MOOC (nan pas Mocq) de l'Institut Mines Telecom sur la Fabrication Numérique,
https://www.france-universite-numerique ... _2014/info
où l'un des exercice consiste à créer un serveur web qui allume et éteint une LED, avec un Arduino et un shield Ethernet;
D'autre part j'ai beaucoup discuté avec l'ami Jean-Marie sur ce forum, et du coup ça m'a motivé pour présenter un exemple.

Alors voilà, j'ai donc fait un serveur web avec un Arduino UNO, un ESP8266, une alim 3,3v maison, et un convertisseur de niveau maison.
Le plus dur finalement, c'est de gérer correctement les chaînes de caractères renvoyées par l'ESP: elles son plus faciles
à lire par un humain que par un programme finalement. Et un navigateur internet c'est fou comme c'est bavard.

Enfin ça marchouille, il ne faut juste pas cliquer trop souvent parce que ça sature l'ESP qui se reboote tout seul.
Du coup je commence à hésiter, parce qu'avant de faire planter un Apache sur le Raspberry, il faut se lever tôt. :?

Allez, assez bavardé, place au code :

Code : Tout sélectionner

/************************************
* ESP8266 Web server for Arduino
* switches LED on/off
* written by Laurent Sineux
*
* Arduino      ESP
* PIN 10       TXD
* PIN 11       RXD
* +3,3V        CH_PD (always selected)
*************************************/

#include <SoftwareSerial.h>
const boolean debug=true;  //logs all response from ESP to serial monitor

SoftwareSerial espSerial(10,11); // defines arduino pin 10 as RX and 11 as TX
HardwareSerial & dbgSerial = Serial; // serial monitor

#define BUFFER_SIZE 512
char buffer[BUFFER_SIZE]; //buffer to store incoming messages

#define pinLED 13
boolean ledValue = 0; //off by default

/******************************************
* FUNCTION : void setup(void)
* PURPOSE : run only once to initialize
*           the system after HW reset
*******************************************/
void setup()
{
    pinMode(2, INPUT);
    dbgSerial.begin(9600); // starts serial link to monitor
    espSerial.begin(9600); // starts serial link to ESP
    delay(5000);
    sendData("AT+RST\r\n",15000); // reset module
    sendData("AT+CWMODE=1\r\n",2000); // Configure as station
    sendData("AT+CWJAP=\"MON_SSID\",\"MON_PASSWORD\"\r\n",10000); // connecte à la livebox
    sendData("AT+CIPMUX=1\r\n",2000); // configure for multiple connections
    sendData("AT+CIPSERVER=1,80\r\n",2000); // turn on server on port 80
    pinMode(pinLED, OUTPUT); //define pin as output
}
  
/********************************************
* FUNCTION : void loop (void)
* PURPOSE : main loop
*********************************************/   
void loop()
{    
    int ch; // channel ID
    int len; //  message length
    char *pb; // pointer inside the buffer
    if(espSerial.available())
     {
       espSerial.readBytesUntil('\n', buffer, BUFFER_SIZE); //read until newline
    
        if(debug)
        {
          dbgSerial.print("www :");
          dbgSerial.println(buffer);
        }
     }
    // we expect a message like this "+IPD,ch,len:data"
    if(strncmp(buffer, "+IPD,", 5)==0) //check if message starts with +IPD 
    {
      sscanf(buffer+5, "%d,%d", &ch, &len); //extract channel and length
      if (len > 0) // if message not empty
      {
        // to find the data we must move the cursor after the colon
        pb=buffer;
        while(*pb!=':') pb++; // increase pointer until character is a colon
        pb++; // +1 to move after the colon, not at the colon

        if(debug)
        {
          dbgSerial.print("ch:");
          dbgSerial.println(ch,DEC);
          dbgSerial.print("len:");
          dbgSerial.println(len,DEC);
          dbgSerial.print("pb:");
          dbgSerial.println(pb);
        }


        if (strstr(pb, "GET /?LED=ON"))
        {
          dbgSerial.println("led ON");
          flushSerial(); // get rid of message end eg "HTML/1.1"
          ledValue=1;
          homepage(ch);
        }
        else if (strstr(pb, "GET /?LED=OFF"))
        {
          dbgSerial.println("led OFF");
          flushSerial();// get rid of message end eg "HTML/1.1"
          ledValue=0;
          homepage(ch);
        }
        else if (strstr(pb, "GET /"))
        {
          flushSerial();// get rid of message end eg "HTML/1.1"
          homepage(ch);
        }
        if(ledValue==1) digitalWrite(pinLED,HIGH);
        else digitalWrite(pinLED, LOW);
    }
    }
    clearBuffer();
} 

  
/****************************************************
* FUNCTION : boolean sendData(String command, int timeout)
* PURPOSE : sends command to ESP8266, 
*           waits for response until timeout,
*           compares response from ESP with expected response
* PARAMETERS :
*          String command : command to be sent to the ESP
*          int timeout : number of milliseconds to wait
* RESULT :
*          boolean: true if response contains "OK"
*/
boolean sendData(String command, int timeout)
{
    const int respLen = 128;
    char response[respLen];
    int i;
    // erase the whole response buffer
    for (i=0;i<respLen;i++)
    {
        response[i]='\0';
    }
    
    
    if(debug)
    {
      dbgSerial.print(" > ");
      dbgSerial.print(command);
    }
    
    espSerial.print(command); // send command to the esp8266
    i=0;
    long int time = millis();
    while( (time+timeout) > millis())
    {    
      while(espSerial.available() && (i < respLen-1)) // as long as we have something to read and the buffer is not full
      {
        // The esp has data so display its output to the serial window
        response[i] = espSerial.read(); // read the next character.
        i++; //increment number of characters
      } 
    }
    response[i]=0; // always end the string with a null character
      if(debug && i!=0)
        {
          dbgSerial.print(i);
          dbgSerial.print(" < ");
          dbgSerial.println(response);
        }
   
    

   if( strstr(response, "OK") || strstr(response,"no change" )) return true;
   else return false;
   
}

/*************************************************************
* FUNCTION : void flushSerial(void)
* PURPOSE : read any remaining characters in espSerial buffer
*************************************************************/
void flushSerial(void)
{
  if(debug) dbgSerial.print("flushing :");
  while ( espSerial.available()) // while there are characters available
  {
    if(debug) dbgSerial.write(espSerial.read());
  }
  if(debug) dbgSerial.println("");
}

/**************************************************************
* FUNCTION : void clearBuffer (void)
* PURPOSE : erases buffer to ensure no false readings
**************************************************************/
void clearBuffer (void)
{
   for (int i =0;i<BUFFER_SIZE;i++ )
   {
      buffer[i]=0;
   } 
}

/**************************************************************
* FUNCTION : void homepage(int id)
* PURPOSE : send a web page and close connection
***************************************************************/
void homepage(int id)
{
    
    String Content; // this is my HTML form

    Content =   "<HTML>";
    Content += "<HEAD><TITLE>Arduino + ESP8266 Webserver</TITLE></HEAD>";
    Content += "<BODY><H1>LED =";
    if(ledValue == 0) Content += "OFF";
    else Content += "ON";
    Content += "</H1><BR><FORM name=\"LED Form\" action=\"/\" method=get>";
    Content += "<input type=\"submit\" name=\"LED\" value=\"ON\"><BR>";
    Content += "<input type=\"submit\" name=\"LED\" value=\"OFF\">";
    Content += "</FORM></BODY></HTML>";
    

    String Header; // this is the header message before the page
     
    Header = "HTTP/1.1 200 OK\r\n";
    Header += "Content-Type: text/html; charset=UTF-8\r\n";
    Header += "Connection: close\r\n";
    //Header += "Refresh: 5\r\n";
    Header += "Content-Length: ";
    Header += (int)(Content.length());
    Header += "\r\n\r\n"; 

    espSerial.print("AT+CIPSEND=");
    espSerial.print(id);
    espSerial.print(",");
    espSerial.println(Header.length()+Content.length());
    delay(100);
    if (espSerial.find(">"))
    {
        espSerial.print(Header);
        espSerial.print(Content);
        delay(1000);
    } 
    espSerial.print("AT+CIPCLOSE=");
    espSerial.print(id); 
    espSerial.print("\r\n"); 

}
C'est moche hein ? Bon je sais il reste beaucoup de debug, et puis je ne traite pas les cas où l'ESP répond "ERROR"
ou "Busy" ou "Nan, trop marrant"... Mais c'est un début, qu'est-ce que vous en pensez ?

Veloce :roll:
Modifié en dernier par Veloce le mar. 31 mars 2015 12:04, modifié 1 fois.

Avatar du membre
dbrion0606
Raspinaute
Messages : 164
Enregistré le : ven. 30 janv. 2015 15:51

Re: Serveur web avec Arduino et ESP8266

Message par dbrion0606 » ven. 13 mars 2015 14:07

qu'est-ce que vous en pensez ?
Beaucoup de bien :
vos sources sont lisibles et commentés.
vous faites appel à des fonctions standard (strstr).

Une petite réserve cependant (qui peut cesser d'être valide en employant des arduino mega, moins limités en ressources et dont les clones ont un prix acceptable) :
vous testez la présence de :
"GET /?LED=ON", puis "GET /?LED=OFF" et enfin ""GET /" (ce qui implique que des bouts de décodage sont doublonnés, ce qui est gênant pour un processeur limité en RAM -les chaînes de caractère y sont stockées par défaut dans le segment de texte)
Un autre ordre de stockage consisterait à tester la présence de GET /, puis celle de ?LED=O (en incrémentant le pointeur sur un buffer de 5 -arithmetique des pointeurs-), et enfin celle de N ou FF : cela permettrait de gagner glorieusement ...12 octets en RAM..... au prix d'un code plus difficile à lire.. (c'est la seule piste d'"am&lioration" qui me soit venue à l'idée)

Veloce
Messages : 79
Enregistré le : sam. 24 janv. 2015 20:12

Re: Serveur web avec Arduino et ESP8266

Message par Veloce » ven. 13 mars 2015 21:29

Merci pour tes commentaires (on peut se tutoyer ?) très constructifs.
Tu as raison, dans mon programme je re-teste toute la chaîne alors que ta solution est plus efficace.

Je ne suis pas hyper satisfait de la manière dont je traite les réponses du module: pour l'instant si ça échoue je ne fais rien. Il faudrait que je relance la commande après un délai, puis que je fasse un reset si vraiment ça ne marche pas.

Pour économiser la RAM, j'ai vu qu'on pouvait stocker des constantes dans la mémoire flash, avec la fonction F().
Par exemple au lieu de mettre Serial.print("coucou") il faut mettre Serial.print(F("coucou").
Aux standards actuels, 32K de page web ce n'est pas énorme, mais ça permettrait de mettre un bout de Javascript, ou alors une toute petite image !

à suivre...

Veloce

Avatar du membre
Jean-Marie
Raspinaute
Messages : 240
Enregistré le : sam. 24 janv. 2015 18:01
Localisation : Arlon, Belgique
Contact :

Re: Serveur web avec Arduino et ESP8266

Message par Jean-Marie » sam. 14 mars 2015 10:18

Hello Veloce

Je vais peut-être sortir complètement de l'objectif que tu t'étais fixé au départ, à savoir pouvoir consulter la page web générée par l'ESP de n'importe où, mais voilà il me vient une idée qui est peut-être réalisable si tu consultes toujours cette page web à partir du même PC.
Serait-ce possible de te constituer une page web aussi sophistiquée que tu veux, avec des photos, etc ... et de la sauvegarder localement sur ton PC. Lorsque tu veux consulter les données du ESP, tu lances la page web locale et c'est celle-ci qui interroge l'ESP pour la mise à jour des fenêtres qui lui sont dédiées.
Vu mon niveau de connaissance des réseaux, excuse-moi si cette proposition ne tient pas debout. :oops:

Je viens de ressortir mon Arduino du tiroir pour faire l'essai de ton programme dès que j'ai réglé les câbles, les connections et les alimentations.
Certaines lignes de ton programme dépassent les rudiments de C qui me restent d'un MOOC que j'ai suivi l'an dernier.

Avatar du membre
Jean-Marie
Raspinaute
Messages : 240
Enregistré le : sam. 24 janv. 2015 18:01
Localisation : Arlon, Belgique
Contact :

Re: Serveur web avec Arduino et ESP8266

Message par Jean-Marie » sam. 14 mars 2015 16:36

J'ai d'abord modifié la ligne sendData("AT+CWJAP=\"MON_SSID\",\"MON_PASSWORD\"\r\n",10000); avec mes données, puis chargé ton programme sans problème dans l'Arduino Uno.

Voici ce que le moniteur a reçu:

Code : Tout sélectionner

> AT+RST
127 < r—-š‡‘¾x‡’Röx‡’Ò6|
C!À¶	§º–.C‹ôÖ

Vendor:www.ai-thinker.com

SDK Version:0.9.5(b1)
Compiled @:Dec 25 2014
 > AT+CWMODE=1
38 < , 21:50:58
ready
AT+CWMODE=1

OK

 > AT+CWJAP="Wifi Collette",""
24 < AV])ÒºeÚ‹YK"

OK

 > AT+CIPMUX=1
19 < AT+CIPMUX=1

OK

 > AT+CIPSERVER=1,80
22 < AV%AM’eUIõ1,80

OK
Ensuite, j'ai tapé l'adresse 192.168.0.100 dans Google Chrome.
Le moniteur a ajouté ceci:

Code : Tout sélectionner

www :0,CONNECT

www :

www :+IPD,0,362:GET / HTTP/1.1

ch:0
len:362
pb:GET / HTTP/1.1

flushing :Host: 192.168.0.100
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36
DNT: 1
Accept-Encoding: gzip, deflate, sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4


OK

www : H¨A½rŠI	Ò©¨]Yº-òÁ•¢ëWYH´sseÿ=¨µ¥ëkÖnʹÑb’BÕÊúADx%Q*
www :UËAT+CIPCLOSE=0busy s...

www :

www :SEND OK

J'ai cru qu'il y avait un problème car il a fallu plusieurs minutes sans que rien de visible ne se passe avant que Chrome ne m'affiche l'écran suivant:
500.jpg
500.jpg (13 Kio) Vu 13551 fois

Avatar du membre
Jean-Marie
Raspinaute
Messages : 240
Enregistré le : sam. 24 janv. 2015 18:01
Localisation : Arlon, Belgique
Contact :

Re: Serveur web avec Arduino et ESP8266

Message par Jean-Marie » sam. 14 mars 2015 18:21

Lorsqu'on clique sur "ON", la LED13 prend 8 à 10 secondes pour s'allumer mais il faut 3 minutes et 10 secondes pour que la page web soit rafraîchie.
Que font l'Arduino et l'ESP pendant ce temps ? MYSTÈRE.

Avatar du membre
dbrion0606
Raspinaute
Messages : 164
Enregistré le : ven. 30 janv. 2015 15:51

Re: Serveur web avec Arduino et ESP8266

Message par dbrion0606 » lun. 16 mars 2015 17:49

Tout d'abord, je vous remercie de m'apprendre qu'on peut utiliser des chaînes de caractères stockées en flash sous Arduino (les uno sont très limités en RAM, un peu moins en mémoire de programme...)
Ensuite, il y a quelques "choses" qui me préoccuppent:
a) si la liaison série est à 9600 bauds, l'arduino peut emettre un caractère toutes les millisecondes : transmettre un kilooctet demandera 1 seconde -ce qui est ressenti par un hêtre humain-
b) avez vous pensé, avant d'automatiser avec un arduino, à tester votre ESP8266 avec ... un émulateur de terminal (vous tapez les commandes et vous voyez ce que vous recevez): il y a gtkterm/minicom sous GNUlinux, et -entre autres- teraterm sous Windows (XP, 7 et au delà: hyperterminal est très désagréable et j'y ai renoncé...

Veloce
Messages : 79
Enregistré le : sam. 24 janv. 2015 20:12

Re: Serveur web avec Arduino et ESP8266

Message par Veloce » lun. 16 mars 2015 19:08

Hello, désolé j'étais malade comme un chien ce week end, mais me revoilà !
Jean-Marie a écrit :Lorsqu'on clique sur "ON", la LED13 prend 8 à 10 secondes pour s'allumer mais il faut 3 minutes et 10 secondes pour que la page web soit rafraîchie.
Que font l'Arduino et l'ESP pendant ce temps ? MYSTÈRE.
:shock: c'est super lent ! Chez moi ça prend une grosse seconde. J'ai peut-être viré quelques temporisations dans mon programme ?
Je regarde. Sinon tu utilises bien le moniteur série de l'IDE Arduino pour le debug ?

dbrion0606 a écrit :a) si la liaison série est à 9600 bauds, l'arduino peut emettre un caractère toutes les millisecondes : transmettre un kilooctet demandera 1 seconde -ce qui est ressenti par un hêtre humain-
C'est vrai, je pourrais aller plus vite mais il faudrait utiliser l'UART et non SoftwareSerial. Après, je suppose qu'on peut essayer de charger un maximum d'info à la première connexion, puis en utilisant du JavaScript on n'échange que des petits messages avec le navigateur, au lieu de recharger toute une page comme dans un site web normal. Mais pour mes besoins, deux boutons ça suffit.
dbrion0606 a écrit :b) avez vous pensé, avant d'automatiser avec un arduino, à tester votre ESP8266 avec ... un émulateur de terminal (vous tapez les commandes et vous voyez ce que vous recevez): il y a gtkterm/minicom sous GNUlinux, et -entre autres- teraterm sous Windows (XP, 7 et au delà: hyperterminal est très désagréable et j'y ai renoncé...
Moui, en fait j'ai commencé comme ça: au départ j'ai utilisé ma Uno comme un bête convertisseur USB-TTL: en court-circuitant la borne RESET et en utilisant ses bornes RX et TX à l'envers. Je sais, c'est mal ! :twisted:
Mais autant un humain est satisfait des messages rigolos que l'ESP renvoie à la console, autant pour les interpréter dans un programme c'est compliqué.
Enfin tu as peut-être raison, au stade où j'en suis il faut peut-être comparer ce qu'il envoie et ce que mon programme en reçoit.

à suivre...
Veloce

Veloce
Messages : 79
Enregistré le : sam. 24 janv. 2015 20:12

Re: Serveur web avec Arduino et ESP8266

Message par Veloce » lun. 16 mars 2015 20:27

Excuse-moi Jean-Marie !

j'avais oublié de te dire que j'avais modifié mon montage. En fait, l'ESP8266 communique très bien vers la UNO via les entrées-sorties de l'UART, mais pas très bien quand on utilise SoftwareSerial: les niveaux logiques ne sont pas tout à fait bien reconnus et du coup l'Arduino reçoit beaucoup de caractères fantômes.
J'ai donc fait un convertisseur à base de 2 transistors NPN et de 4 résistances.

Comme j'ai oublié de t'en parler, ton Arduino doit mouliner dans la fonction flush() en attendant que l'ESP ait fini d'envoyer n'importe quoi !
Promis dès que j'ai un peu de temps je vais faire un schéma du convertisseur.

Désolé

Veloce :oops:

Avatar du membre
dbrion0606
Raspinaute
Messages : 164
Enregistré le : ven. 30 janv. 2015 15:51

Re: Serveur web avec Arduino et ESP8266

Message par dbrion0606 » lun. 16 mars 2015 20:52

Il y a aussi un petit problème qui me chagrine:
pourquoi ne pas permuter les entrées sorties serie vers l'ESP (actuellement software) et la sortie de débug (actuellement hardware, donc moins consommatrice de ressources);
l'avantage résiderait dans le fait que les adaptateurs de niveaux seraient plus simples (une seule voie en sortie vers un terminal PC , sous réserve de nécessité de ces adaptateurs) . De plus, logiquement, un UART logiciel, pour garantir des durées constantes, doit inhiber les interruptions -au moins le temps qu'il transmette un caractère- , privant de marge de manoeuvre si vous voulez rajouter des fonctions compliquées à votre Arduino.... S'il sert uniquement à envoyer des messages de debug, c'est a priori moins gênant que si on a des flots de données importants à transmettre sur une voie série...

Répondre

Retourner vers « Et tout le reste »