Arduino Mini Server debug of GET /favicon.ico

The Problem

In my previous post,Mini Server on Arduino + ESP8266, I ended with an issue where my Arduino + ESP8266 mini server would lock periodically if I hit it too quickly with web page requests.  I noticed several “GET /favicon.ico” requests popping up and taking more and more IPD channels.  Here is an example from the UART log coming from the ESP8266..

+IPD,1,280:GET /favicon.ico HTTP/1.1
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
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
Link

+IPD,2,280:GET /favicon.ico HTTP/1.1
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

I waited a while and saw even more pop up even though I didn’t try to request a page from my PC or tablet or phone.  Couldn’t imagine someone else was trying to access it.  I figure I need to solve this first.

The Research

Wasn’t much research needed.  Quick googling on favicon.ico told me it has something to do with modern browsers looking for an icon to show in the web address bar or in the bookmarks.  I always wondered how that worked.

and now….

The Plan

Since I don’t have any icon to send back (even if I knew how), I am just going to hope that I can just send a AT+CIPCLOSE to that IPD channel and hope the browser goes away.  I think it keep trying over and over since I am not responding..

The Implementation

I am not going to go into detail on how I wrote the SW (since I am no expert on good SW style/structure).  The high level explanation is that I am just going to look for occurrence of the string “favicon.ico” and immediately just send the AT+CIPCLOSE back.

The Testing

Not 100% sure, but this seems to have helped the overall stability.  I can see the AT+CIPCLOSE immediately after each favicon.ico request.  I can seem to refresh the pages more often a little faster without crashing, but eventually something happens and the system crashes.  Will need to keep working.

The Code

Here is the code for now.  Still a work in progress…

/*
Start up a very mini web server
 ******* 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);
 String ClientId = String(ClientIdChar);
 
 echoFind("OK"); 
 //delay(100);
 SendHTML(ClientId);
 SendCommand("AT+CIPCLOSE=" + ClientId,"OK",true);
 }
 if (IncomingLine.indexOf("favicon.ico") != -1) {
 char ClientIdChar = IncomingLine.charAt(5);
 String ClientId = String(ClientIdChar);
 
 echoFind("OK"); 
 SendCommand("AT+CIPCLOSE=" + String(ClientIdChar),"OK",true);
 }
} 


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);
}

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



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){
 };
}


Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s