Mini Server on Arduino + ESP8266

The Plan

In my post LED’s Control through TTY, I demonstrated how I can control the Arduino IO’s through Wifi using the ESP8266 module (which I wrote about in my past post  ESP8266 ($4 Wifi Module) Bring Up.  I am going to attempt to control the LEDs though a web page served up by the Arduino through the ESP8266.  The first step is to just have my setup be able to send out out a very simple web page to start.

The Preparation

Disclaimer: I do not claim to have any expertise here so forgive me if things are not complete and not totally correct.

After some research, I found out the basics of a web server.  A web page request will by default try to connect to the server at port 80.  So I set up my server to have port 80 open.  It is very similar to what I did in my post Simple Communication through ESP8266 Wifi module that listened for a client on port 8888.  Just changed the AT+CIPSERVER=1,8888 to AT+CIPSERVER=1,80.

I did this and pointed my web browser to the IP address of the ESP8266 and the ESP8266 spit this out….

Link

+IPD,0,366:GET / HTTP/1.1
Host: 192.168.0.173
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

From my simple understanding:

  1. the “GET / ” (“GET” with a blank space immediately after the “/”) is a HTML request.
  2. the “+IPD,0,336:” is something the ESP8266 firmware is adding to the actual web request from the client.  The “0” would be the channel a specific client is using. There can be multiple clients so the ESP8266 is signifying which client is sending that request.  The “366” must be the number of characters was received from the host.

The Implementation

Here is my simple plan… (it always starts as a “simple” plan doesn’t it.)

  1. I set up my web server as I did in my post: Auto ESP8266 Server Init
  2. Parse incoming characters and look for any occurrence of “GET / “.
  3. Parse out the IPD channel and send back a very simple HTML code web page.
  4. Send a “AT+CIPCLOSE=<IPD CH>” to tell the client I am done responding to the request.  If I don’t, my browser still keeps loading even though the full HTML page is completed.

So I struggled with the parsing because I kept missing characters if things came in too quick after each other.  I totally redid the parsing vs. what I posted in Arduino Parsing for Occurrence of String from Serial Port.  That worked for simple data but seemed to slow for more data coming.  Too hard to explain my final hacked implantation so you can try to dig through it in the attached code below.

BTW – The HTML page I send back is very simple.  If you know some basic HTML here is what the code looks like:

<HTML>
  <HEAD>
    <TITLE>Pete's Mini8266 Server</TITLE>
  </HEAD>
  <BODY>
    <H1>Welcome to Pete's ESP8266 "hacking" project</H1>
    <BR>
    <BR> 
        Up time: 
</BODY></HTML>

Note: I add some Arduino code to add the time returned from “millis()” (time the Arduino has been running in msec) after the “Up time:” in the page.  Helps me make sure that I am really getting an updated page from the Arduino and not just some buffered or stale data.

Testing / Results

I fired it up and get the web page like this…

SimpleWebPageExample

.. with the serial log to and from the ESP8266 like this with comments I added later in (red)

Link

+IPD,0,366:GET / HTTP/1.1                  (Here is the HTML request)
Host: 192.168.0.173
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebK
Someone requested a HTML Page on Client Id:0   (Sending HTML here using AT+CIPSEND=0,116 command)
it/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36+CIPSEND=0,116 
>
 <HTML><HEAD><TITLE>Pete's Mini8266 Server</TITLE></HEAD><BODY><H1>Welcome to Pete's ESP8266 "hacking" project</H1> 
SEND OK

AT+CIPSEND=0,20                         (Sending more HTML here to break up to smaller blocks of data)
>
 <BR><BR> Up time: 
SEND OK

AT+CIPSEND=0,7 
>
 14053 
SEND OK

AT+CIPSEND=0,16 
>
 </BODY></HTML> 
SEND OK

AT+CIPCLOSE=0     (Closing the connection so browser knows I am done)

+IPD,1,280:GET /favicon.ico HTTP/1.1   (What is this????  I don't know yet... it starts another IPD channel too)
Host: 192.168.0.173
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebK
it/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8


OK

OK
Link

+IPD,0,280:GET /favicon.ico HTTP/1.1     (What is this again???? I don't know yet)
Host: 192.168.0.173
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8


OK

This works pretty well for the most part and I can refresh the page a few times and get the Arduino to send back the page with the update up time.  I can even get it to work from my PC, move over to my Android tablet and get the page over there, move over to my Android phone and get the page over there, etc…Problem is that it eventually locks up if I refresh the page too quickly.  I am wondering if it has to do with the “GET /favicon.ico” request.  Will debug and post about it next time.

My full Arduino sketch

Please don’t laugh at my coding.  I am not a SW guy and doing this on the fly to just get it to work.  Clean up later….maybe.

/*
Start up a very mini web server
 ******* This requires the Mega 2560 board *****
 ******* This requires the Mega 2560 board *****
 ******* This requires the Mega 2560 board *****
 ******* This requires the Mega 2560 board *****
 ******* This requires the Mega 2560 board *****
 ******* This requires the Mega 2560 board *****
 ******* This requires the Mega 2560 board *****
 */


#define TIMEOUT 5000 // mS
#define GREENLED 4
#define REDLED 5
#define BLUELED 3

boolean RED_State = false;
boolean GREEN_State = false;
boolean BLUE_State = false;

void setup() 
{
 pinMode(REDLED,OUTPUT); 
 pinMode(GREENLED,OUTPUT);
 pinMode(BLUELED,OUTPUT);

 Serial.begin(115200);
 Serial1.begin(115200);

 int CommandStep = 1;

 //This initializes the Wifi Module as a server 
 BlinkLED(REDLED,CommandStep,50); 
 SendCommand("AT+RST", "Ready", true);
 BlinkLED(GREENLED,CommandStep,50);
 CommandStep++;

 BlinkLED(REDLED,CommandStep,50); 
 SendCommand("AT+GMR", "OK", true);
 BlinkLED(GREENLED,CommandStep,50);
 CommandStep++;

 delay(3000);

 BlinkLED(REDLED,CommandStep,50); 
 SendCommand("AT+CIFSR", "OK", true);
 BlinkLED(GREENLED,CommandStep,50);
 CommandStep++;


 BlinkLED(REDLED,CommandStep,50); 
 SendCommand("AT+CIPMUX=1","OK",true);
 BlinkLED(GREENLED,CommandStep,50);
 CommandStep++;

 BlinkLED(REDLED,CommandStep,50); 
 SendCommand("AT+CIPSERVER=1,80","OK",true);
 BlinkLED(GREENLED,CommandStep,50);

 digitalWrite(GREENLED,HIGH);
 //----------------------------------------------------
}

void BlinkLED(int LEDPin, int NumberOfBlinks, int OnDuration)
{
 for (int x=1; x <= NumberOfBlinks ; x ++){
 digitalWrite(LEDPin,HIGH);
 delay(OnDuration);
 digitalWrite(LEDPin,LOW);
 delay(OnDuration); 
 }
}


void loop(){
 String IncomingString="";
 char SingleChar;
 boolean StringReady = false;


 //*** Handle each character that is coming in from the ESP8266 ****
 while(Serial1.available()) 
 {
 IncomingChar (Serial1.read ());
 } 
 
 while(Serial.available())
 {
 Serial1.write(Serial.read());
 }

}



void ProcessCommand (const char * data)
{
 Serial.println (data);

 String IncomingLine = String(data);

 if (IncomingLine.indexOf("GET / ") != -1) {
 char ClientIdChar = IncomingLine.charAt(5);
 echoFind("OK"); 
 delay(100);
 SendHTML(String(ClientIdChar));
 }

} 


void SendHTML(String ClientId){
 Serial.println("Someone requested a HTML Page on Client Id:" + ClientId );

 SendClient("<HTML><HEAD><TITLE>Pete's Mini8266 Server</TITLE></HEAD>" 
 "<BODY><H1>Welcome to Pete's ESP8266 \"hacking\" project</H1>",ClientId);
 SendClient("<BR><BR> Up time: ",ClientId);
 SendClient(String(millis()),ClientId);
 SendClient("</BODY></HTML>",ClientId);
 SendCommand("AT+CIPCLOSE="+ ClientId,"OK",true);
}

void SendClient(String ToClientString,String ClientID){
 SendCommand("AT+CIPSEND=" + ClientID + ","+ (ToClientString.length() +2) ,">",false);
 SendCommand(ToClientString,"OK",false);
}



void IncomingChar (const byte InChar)
{
 static char InLine [500]; //Hope we don't get more than that in one line
 static unsigned int Position = 0;

 switch (InChar)
 {
 case '\r': // Don't care about carriage return so throw away.
 break;
 
 case '\n': 
 InLine [Position] = 0; 
 ProcessCommand (InLine);
 Position = 0; 
 break;

 default:
 InLine [Position++] = InChar;
 } 

} 


//************** This section specific to simple AT command with no need to parse the response ************
// Warning: Will drop any characters coming in while waiting for the indicated response.
boolean SendCommand(String cmd, String ack, boolean halt_on_fail)
{
 Serial1.println(cmd); // Send command to module

 // Otherwise wait for ack.
 if (!echoFind(ack)) // timed out waiting for ack string
 if (halt_on_fail)
 errorHalt(cmd+" failed");// Critical failure halt.
 else
 return false; // Let the caller handle it.
 return true; // ack blank or ack found
}

// Read characters from WiFi module and echo to serial until keyword occurs or timeout.
boolean echoFind(String keyword)
{
 byte current_char = 0;
 byte keyword_length = keyword.length();

 // Fail if the target string has not been sent by deadline.
 long deadline = millis() + TIMEOUT;
 while(millis() < deadline)
 {
 if (Serial1.available())
 {
 char ch = Serial1.read();
 Serial.write(ch);
 if (ch == keyword[current_char])
 if (++current_char == keyword_length)
 {
 Serial.println();
 return true;
 }
 }
 }
 return false; // Timed out
}

// Print error message and loop stop.
void errorHalt(String msg)
{
 Serial.println(msg);
 Serial.println("HALT");
 digitalWrite(REDLED,HIGH);
 digitalWrite(GREENLED,HIGH);
 while(true){
 };
}

5 thoughts on “Mini Server on Arduino + ESP8266

    • domaincorner,

      Thanks for the comment! I am REALLY new to how a server works under the hood and just pushing through from pure google “learning” and trial and error.

      I did a little google’ing and I think I found out what you mean on both issues. Sounds like I haven’t really responded fully to a page request and I found some examples of how to specify a blank favicon. I will give that a try next.

      I did already tried sending back only the close connection to the favicon request in my last post. It might have made things a little better but eventually I crash. Maybe your 2 suggestions will make it more stable.

      Thanks,
      Peter

      Like

      Reply
      • domaincorner,

        I tried adding the following to the section of my html page…

        I likely did that wrong because now I keep getting a request like this…

        +IPD,0,269:GET / HTTP/1.1
        Host: 192.168.0.173
        Connection: keep-alive
        Accept: */*
        User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebK
        Someone requested a HTML Page on Client Id:0
        it/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
        Accept-Encoding: gzip, deflate, sdch
        Accept-Language: en-US,en;q=0.8

        … which I think is the same favicon.ico request but with an empty string after the GET / which makes my server code think it is an html page request and spit out the HTML page again. I think it is the favicon request again because I notice the “Accept” line shows “*/*” instead of text/html,app…..

        Am I not specifying the icon as empty correctly?

        Thanks,
        Peter

        Like

  1. Pingback: Arduino Mini Server debug of GET /favicon.ico | PetesTechProjects

  2. Pingback: Arduino + ESP8266 Mini Server with Command Que | PetesTechProjects

Leave a comment