Arduino & ESP8266 Webserver

Update:
This is a very old guide and things have moved on a lot since this was written. One of the main advances is the ESP8266 core for the Arduino IDE. This means the ESP8266 can now be programmed like an Arduino and this is how I use them now, no more messing around with AT commands. For a general overview and examples of using the ESP8266 with the Arduino core see ESP8266 and the Arduino IDE.

 
Here is my first attempt at a web server using the ESP8266. It includes a request count and also a text input field.

ESP8266 webpage

Enter your name and hit submit

ESP8266 webpage 2

The code is not very elegant and it can be made far more efficient. At present there are no checks to see if the command issued to the ESP8266 is successful or not. Ideally, each command should be checked to see if a “busy” reply has been received and if so, the command resent.

If a cipsend command is incorrect or not successful, the ESP8266 waits for the command to be completed and any subsequent commands will get a busy reply. The only way I have found to get out of this is a hard reset. In a future version I will connect the ESP8266 reset to the Arduino so I can control is from software.

The module is not as quick as I expected and I often get busy replies. To counter this I have used 2 second delays on each command but sometimes this is still not enough.

Another issue, I sometimes get extra messages from the ESP8266 after the page has been sent. I know absolutely nothing about http and don’t know where these come from. I haven’t had time to look in to this and for now I have added extra getReply()s at the end of the code to absorb any extra messages.

Commands Used

Remember that the ESP8266 expects all AT commands to end with the carriage return and newline characters (\r\n).

AT+CWMODE=1
The Esp8266 has three modes:

  • Station Mode (ST). The ESP8266 acts as a wifi device & can connect to a network / Access Point.
  • Access Point Mode (AP). The ESP8266 becomes the network (AP) and other devices can connect to it.
  • ST & AP Mode. The ESP8288 is both an AP and a station at the same time. In this mode the ESP8266 will have 2 ip addresses.

In the example below I am using Station Mode which is entered with the command AT+CWMODE=1

AT+CWJAP=”NetworkSSID”,”password”
AT+CWJAP is used to join a network. NetworkSSID is the name of the network and password is the password.

AT+CIFSR
AT+CIFSR returns the ip address of the ESP8266. In the example below this is assigned by the router when it joins the network.

AT+CIPMUX=1
AT+CIPMUX=1 tells the ESP8266 to allow multiple connections. This is required when using as a server.

AT+CIPSERVER
AT+CIPSERVER=1,80 starts a server on port 80. Port 80 is the normal port for web pages.

AT+CIPSEND
CIPSEND is used to send data to the ESP8266. AT+CIPSEND=0,25 tells the module to expect 25 characters using channel id 0.
After sending this command you then need to send the html data, for example espSerial.print(““);

AT+CIPCLOSE
AT+CIPCLOSE=0 closes the connection.

Code

You can download the sketch at the bottom of the page.

You will need to change the network settings to suit your own network
Change the following line:

espSerial.print("AT+CWJAP=\"myNetworkSSID\",\"password\"\r\n");

 

// Basic Arduino & ESP8266 webserver
//
// uses AltSoftSerial download from https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
// this can be replaced with the normal software serial
 
 
#include <AltSoftSerial.h>
// Arduino pin 08 for RX
// Arduino Pin 09 for TX
 
AltSoftSerial espSerial;
 
 
const bool printReply = true;
const char line[] = "-----\n\r";
int loopCount=0;
 
char html[50];
char command[20];
char reply[500]; // you wouldn't normally do this
 
char ipAddress [20];
char name[30];
int lenHtml = 0;
char temp[5];
 
 
void setup()
{
      Serial.begin(9600);
      Serial.println("Start\r\n\r\n");
 
      espSerial.begin(9600); // your ESP8266 module's baud rate might be different
 
 
      // reset the ESP8266
      Serial.println("reset the module"); 
      espSerial.print("AT+RST\r\n");
      getReply( 2000 );
 
 
      // configure as a station
      Serial.println("Change to station mode"); 
      espSerial.print("AT+CWMODE=1\r\n");
      getReply( 1500 );
 
 
      // connect to the network. Uses DHCP. ip will be assigned by the router.
      Serial.println("Connect to a network ");
 
     // Enter the SSID and password for your own network
      espSerial.print("AT+CWJAP=\"myNetworkSSID\",\"password\"\r\n");
      getReply( 6000 );
 
 
      // get ip address
      Serial.println("Get the ip address assigned ny the router"); 
      espSerial.print("AT+CIFSR\r\n");
      getReply( 1000 );
 
 
      // parse ip address.
      int len = strlen( reply ); 
      bool done=false;
      bool error = false;
      int pos = 0;
      while (!done)
      {
           if ( reply[pos] == 10) { done = true;} 
           pos++;
           if (pos > len) { done = true;  error = true;}
      }
 
      if (!error)
      {
            int buffpos = 0;
            done = false;
            while (!done)
            {
               if ( reply[pos] == 13 ) { done = true; }
               else { ipAddress[buffpos] = reply[pos];    buffpos++; pos++;   }
            }
            ipAddress[buffpos] = 0;
      }
      else { strcpy(ipAddress,"ERROR"); }
 
 
 
      // configure for multiple connections
      Serial.println("Set for multiple connections"); 
      espSerial.print("AT+CIPMUX=1\r\n");
      getReply( 1500 );
 
 
      // start server on port 80
      Serial.println("Start the server"); 
      espSerial.print("AT+CIPSERVER=1,80\r\n");
      getReply( 1500 );
 
      Serial.println("");
 
 
      Serial.println("Waiting for page request");
      Serial.print("Connect to "); Serial.println(ipAddress);
      Serial.println("");
}
 
 
void loop()
{
      if(espSerial.available()) // check if the ESP8266 is sending data
      {
          // this is the +IPD reply - it is quite long. 
          // normally you would not need to copy the whole message in to a variable you can copy up to "HOST" only
          // or you can just search the data character by character as you read the serial port.
          getReply( 2000 );      
 
 
          bool foundIPD = false;
          for (int i=0; i<strlen(reply); i++)
          {
               if (  (reply[i]=='I') && (reply[i+1]=='P') && (reply[i+2]=='D')   ) { foundIPD = true;    }
          }
 
 
          if ( foundIPD  )  
          {
 
              loopCount++;
              // Serial.print( "Have a request.  Loop = ");  Serial.println(loopCount); Serial.println(""); 
 
 
              // check to see if we have a name - look for name=
              bool haveName = false;
              int nameStartPos = 0;
              for (int i=0; i<strlen(reply); i++)
              {
                   if (!haveName) // just get the first occurrence of name
                   {
                         if (  (reply[i]=='n') && (reply[i+1]=='a') && (reply[i+2]=='m') && (reply[i+3]=='e')  && (reply[i+4]=='=') ) 
                         { 
                             haveName = true;
                             nameStartPos = i+5;
                         }
                   }     
              }
 
              // get the name - copy everything from nameStartPos to the first space character
              if (haveName)
              {
                    int tempPos = 0;
                    bool finishedCopying = false;
                    for (int i=nameStartPos; i<strlen(reply); i++)
                    {
                         if ( (reply[i]==' ') && !finishedCopying )  { finishedCopying = true;   } 
                         if ( !finishedCopying )                     { name[tempPos] = reply[i];   tempPos++; }
                    }              
                    name[tempPos] = 0;
              }
 
              if (haveName) { Serial.print( "name = ");  Serial.println(name); Serial.println(""); }
              else          { Serial.println( "no name entered");   Serial.println("");           }
 
 
 
              // start sending the HTML
 
 
              strcpy(html,"<html><head></head><body>");
              strcpy(command,"AT+CIPSEND=0,25\r\n");
              espSerial.print(command);
              getReply( 2000 );          
              espSerial.print(html);
              getReply( 2000 );                      
 
              strcpy(html,"<h1>ESP8266 Webserver</h1>");
              strcpy(command,"AT+CIPSEND=0,26\r\n");
              espSerial.print(command);
              getReply( 2000 );         
              espSerial.print(html);
              getReply( 2000 );          
 
              strcpy(html,"<p>Served by Arduino and ESP8266</p>");
              strcpy(command,"AT+CIPSEND=0,36\r\n");
              espSerial.print(command);
              getReply( 2000 );          
              espSerial.print(html);
              getReply( 2000 );           
 
              strcpy(html,"<p>Request number ");
              itoa( loopCount, temp, 10);
              strcat(html,temp);
              strcat(html,"</p>");
 
              // need the length of html
              int lenHtml = strlen( html );
 
              strcpy(command,"AT+CIPSEND=0,");
              itoa( lenHtml, temp, 10);
              strcat(command, temp);
              strcat(command, "\r\n");
              espSerial.print(command);
              getReply( 2000 );          
              espSerial.print(html);
              getReply( 2000 );                       
 
 
 
             if (haveName)
             {
                  // write name
                  strcpy(html,"<p>Your name is "); strcat(html, name ); strcat(html,"</p>");
 
                  // need the length of html for the cipsend command
                  lenHtml = strlen( html );
                  strcpy(command,"AT+CIPSEND=0,"); itoa( lenHtml, temp, 10); strcat(command, temp); strcat(command, "\r\n");
                  espSerial.print(command);
                  getReply( 2000 );          
                  espSerial.print(html);
                  getReply( 2000 );                           
             }
 
 
              strcpy(html,"<form action=\""); strcat(html, ipAddress); strcat(html, "\" method=\"GET\">"); strcat(command, "\r\n");
 
              lenHtml = strlen( html );
              itoa( lenHtml, temp, 10);
              strcpy(command,"AT+CIPSEND=0,"); 
              itoa( lenHtml, temp, 10); 
              strcat(command, temp);  
              strcat(command, "\r\n");
 
              espSerial.print(command);
              getReply( 2000 );          
              espSerial.print(html);
              getReply( 2000 );          
 
              strcpy(html,"Name:<br><input type=\"text\" name=\"name\">");
              strcpy(command,"AT+CIPSEND=0,40\r\n");
              espSerial.print(command);
              getReply( 2000 );         
              espSerial.print(html);
              getReply( 2000 );         
 
              strcpy(html,"<input type=\"submit\" value=\"Submit\"></form>");
              strcpy(command,"AT+CIPSEND=0,43\r\n");
              espSerial.print(command);
              getReply( 2000 );       
              espSerial.print(html);
              getReply( 2000 );       
 
              strcpy(html,"</body></html>");
              strcpy(command,"AT+CIPSEND=0,14\r\n");
              espSerial.print(command);
              getReply( 2000 ); 
              espSerial.print(html);
              getReply( 2000 ); 
 
              // close the connection
              espSerial.print( "AT+CIPCLOSE=0\r\n" );
              getReply( 1500 );            
 
 
              Serial.println("last getReply 1 ");
              getReply( 1500 );  
 
              Serial.println("last getReply 2 ");
              getReply( 1500 ); 
 
 
 
          } // if(espSerial.find("+IPD"))
      } //if(espSerial.available())
 
 
      delay (100);
 
      // drop to here and wait for next request.
 
}
 
 
void getReply(int wait)
{
    int tempPos = 0;
    long int time = millis();
    while( (time + wait) > millis())
    {
        while(espSerial.available())
        {
            char c = espSerial.read(); 
            if (tempPos < 500) { reply[tempPos] = c; tempPos++;   }
        }
        reply[tempPos] = 0;
    } 
 
    if (printReply) { Serial.println( reply );  Serial.println(line);     }
}

 

Serial Monitor Output

The output in the serial monitor looks like this:

Start
 
 
reset the module
AT+RST
 
 
OK
ÍÅÛÔÕtùnq!Åý€9Åü€ÿ
[System Ready, Vendor:www.ai-thinker.com]
 
-----
 
 
Change to station mode
AT+CWMODE=1
 
no change
 
-----
 
 
Connect to a network 
AT+CWJAP="myNetworkSSID","password"
 
 
OK
 
-----
 
 
Get the ip address assigned ny the router
AT+CIFSR
 
192.168.2.108
 
OK
 
-----
 
 
Set for multiple connections
AT+CIPMUX=1
 
 
OK
 
-----
 
 
Start the server
AT+CIPSERVER=1,80
 
 
OK
 
-----
 
 
 
Waiting for page request
Connect to 192.168.2.108
 
Link
 
+IPD,0,308:GET /192.168.2.108 HTTP/1.1
Host: 192.168.2.108
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
 
 
OK
 
-----
 
 
no name entered
 
AT+CIPSEND=0,25
 
> Link
 
-----
 
 
<html><head></head><body>
SEND OK
 
-----
 
 
AT+CIPSEND=0,26
 
> 
-----
 
 
<h1>ESP8266 Webserver</h1>
SEND OK
 
-----
 
 
AT+CIPSEND=0,36
 
> 
-----
 
 
<p>Served by Arduino and ESP8266</p>
SEND OK
 
-----
 
 
AT+CIPSEND=0,23
 
> 
-----
 
 
<p>Request number 1</p>
SEND OK
 
-----
 
 
AT+CIPSEND=0,42
 
> 
-----
 
 
<form action="192.168.2.108" method="GET">
SEND OK
 
-----
 
 
AT+CIPSEND=0,40
 
> 
-----
 
 
Name:<br><input type="text" name="name">
SEND OK
 
-----
 
 
AT+CIPSEND=0,43
 
> 
-----
 
 
<input type="submit" value="Submit"></form>
SEND OK
 
-----
 
 
AT+CIPSEND=0,14
 
> 
-----
 
 
</body></html>
SEND OK
 
-----
 
 
AT+CIPCLOSE=0
 
 
OK
Unlink
 
-----
 
 
last getReply 1 
 
-----
 
 
last getReply 2 
 
-----

 

Download

I had a typo in the original sketch; AltSoftSerial uses the Arduino pins 8 and 9 (not 9 and 10). Pin 8 is Rx and pin 9 is Tx. I have updated the sketch.


 
 

46 thoughts on “Arduino & ESP8266 Webserver”

    • The above sketch uses 2 serials; hardware serial to talk to a host PC and the serial monitor and AltSoftSerial to talk to the ESP8266 module. AltSoftSerial has the pins hardwired to D8 and D9 (there was a typo in the above sketch, Arduino D9 is TX and D8 is RX. Arduino TX goes to ESP8266 RX and Arduino RX goes to ESP8266 TX.

      Remember the ESP8266 is 3.3V and you should use a voltage divider on the Arduino TX line. Arduino RX can be connected directly to the ESP8266 TX pin. See https://www.martyncurrey.com/?p=1475 for a diagram.

      You can also use the regular SoftwareSerial which allows you to select which pins you want to use. See https://www.martyncurrey.com/?p=1475 for an example.

      Reply
  1. Hi…it’s just marvelous job!!
    but one problem is ….i have used this code to show the text to lcd.i’ve done it!!but it can’t print special characters…i:e: it prints ‘+’ sign instead of space…if i send name like “tom brian” in the webserver text box ..it prints “tom+brian”…could u please help…to get rid of it>>.

    Reply
  2. Hi,

    all you need to do is check for the “+” when copying the name and change it to a space.

    if (haveName)
    {
    int tempPos = 0;
    bool finishedCopying = false;
    for (int i=nameStartPos; i<strlen(reply); i++)
    {
    if ( (reply[i]==’ ‘) && !finishedCopying )
    { finishedCopying = true; }
    if ( !finishedCopying )
    {
    if (name[tempPos] == ‘+’) { name[tempPos] = ‘ ‘ }
    name[tempPos] = reply[i];
    tempPos++;
    }
    }
    name[tempPos] = 0;
    }

    *The above has not been tested.

    Reply
        • Thanks a lot …it’s working…but special characters shows like that.i:e: (for ‘ ! ‘ it shows ‘ %21′ ).
          i;ve tried it in this way
          if (reply[i] ==’%21′) { name[tempPos] =’!’;}
          else{name[tempPos] = reply[i];}
          ——-it’s not working.
          2ndly
          if (reply[i] ==’%’&&’2’&&’1′) { name[tempPos] =’!’;}
          else{name[tempPos] = reply[i];}
          3rdly
          if ((reply[i] ==’%’)&&(reply[i+1]=’2′)&&reply[i+2]=’1′) { name[tempPos] =’!’;}
          else{name[tempPos] = reply[i];}
          In the 2nd & 3rd attempts it shows ‘!21’ whether i want to show only ‘!’
          thanks again for great help

          Reply
          • This looks to be an issue with how the characters are encoded on the website although they display OK for me.

            You can also use the ascii value of the character. For example the plus sign is character 43 so you can use if (reply[i] == 43)

            Reply
  3. Hi,
    Thanks for your example.

    It runs ok for the setup() part for me. But I can’t connect to it. I write 192.168.1.32:80 on my browser on a machine connected to the same network and… nothing happens.
    Nothing on my browser (tested with chrome and firefox) and nothing on the serial…

    Do you have any clue to help me ?

    Have a nice day !
    Maxime.

    Reply
  4. Tom,

    what are you actually wanting to do? Character 33 is the exclamation mark. This would only be there if it is part of the name. Going back to the original example. The space is replaced with the plus sign so “Bessy Smith” becomes “Bessy+Smith”. A simple way to change it back is

    if ( !finishedCopying )
    {
    if (reply[i] == 43) { reply[i] = 32; }
    name[tempPos] = reply[i]; tempPos++;
    }

    Reply
      • ok, I understand know.

        Special characters are encoded as hexadecimal ascii values of the character. ! becomes %21, @ becomes %22, # becomes %23. etc

        You need to check for the % character and when you get one you know the next 2 characters are the hexadecimal value of the special character.

        Reply
  5. Hi,
    Great job, it work on my arduino uno. This is the most detailed tuto i found on the web.
    I have a question, I want to send a voice file to web server. Do you know how to build a code for this?

    Best regards
    Hunny

    Reply
  6. hi can said me please where i can download libarys? i cant found on anyplace
    im looking for esp8266WebServer.h esp8266WiFi.h esp8266mDNS.h

    thanks a lot

    Reply
  7. Hallo,
    I would like to know if there are new libraries of arduino for ESP8266 and how to find them, i saw your links but i would like to find them just like you did.

    Thanks in advanced.

    Reply
  8. Many thanks for this example but it takes a very long time to load the page. Why the long timeouts on every getReply and would it hurt to reduce them to the 200ms range?

    Reply
    • You can change the wait time by changing the value in getReply(). A better method would be to wait for the reply and use the timer as a time out.

      Be aware that this is a very old example that I wrote shortly after the ESP8266 modules were available and there are better examples available. Google for arduino + esp8266 server.

      Reply
  9. Thank you so much.It really helpful me.
    I really need your help. Please answer me here or lexuanbac291193@gmail.com
    when we set esp8266 01 as a access point we have a ip address.
    And when we set esp8266 01 as a station(client) we have another ip address.
    In this case( your code) where ip address we have to go to control ?( I mean the ip address you use to control). Thanks

    Reply
    • In the above example the ESP8266 is used in station mode. This means it acts like any other wifi enabled device and is allocated an ip address from the router.

      When used as an access point (AP) you can think of the ESP8266 as a router that other devices can connect to directly. In this mode the ESP8266 allocates ip addresses.

      Reply
  10. why the webpage can not display ,thi is my serial print

    Start

    reset the module
    AT+RST

    OK

    —–

    version
    Ltd.

    ready
    AT+GMR

    AT version:0.21.0.0
    SDK version:0.9.5

    OK

    —–

    Change to station mode
    AT+CWMODE=2

    OK

    —–

    Connect to a network
    Get the ip address assigned ny the router
    AT+CIFSR

    +CIFSR:APIP,”192.168.4.1″
    +CIFSR:APMAC,”1a:fe:34:0c:e6:a7″

    OK

    —–

    Set for multiple connections
    AT+CIPMUX=1

    OK

    —–

    Start the server
    AT+CIPSERVER=1,80

    OK

    —–

    Waiting for page request
    Connect to +CIFSR:APIP,”192.168.4.1″

    0,CONNECT

    +IPD,0,353:GET /?name=0 HTTP/1.1
    Host: 192.168.4.1
    Connection: k
    —–

    name = 0

    AT+CIPSEND=0,20

    OK
    >
    —–

    —–

    AT+CIPSEND=0,26
    busy s…

    SEND OK

    —–

    ESP8266 Webserver
    —–

    AT+CIPSEND=0,14

    OK
    >
    —–

    SEND OK

    —–

    Reply

Leave a Comment