Go Down

Topic: [solved] esp8266 very slow to serve files from SPIFFS (Read 11405 times) previous topic - next topic

DaveEvans

Jul 25, 2016, 08:15 am Last Edit: Jul 26, 2016, 07:24 pm by DaveEvans
I'm attempting to connect an existing Arduino project to "the world" with an ESP8266.

The following test code is a baby step toward that goal.  It serves a small web page with a 300 kB BMP image (which is a nightly screenshot of my project's TFT).  The page and image are in the ESP's SPIFFS memory.

With IE 11, it takes a barely tolerable 26 seconds to load the image.  On Firefox 47, the image takes an excruciatingly long 17 minutes(!) to load.  Also, on IE, the image loads "all at once," whereas with FF, it loads line by line, bottom up.

For "regular" internet usage, FF seems to be fine.  I tried "refreshing" FF, but it didn't help.

I'd appreciate suggestions on how to speed up the image load on Firefox (and IE 11), if possible.

Thank you,
Dave

PS: I don't know if this is related to the issue, but Firefox generates a half dozen or so "GET /favicon.ico HTTP/1.1" requests after the image loads (whereas IE does not ask for favicon.ico at all).

Code: [Select]
/*--------------------------------------------------------------

esp8266 web server that serves up a basic web
page containing an image and some text.

Requires SPIFFS to contain
   index.htm, page2.htm and pic.jpg <--initial test
       and
   indexBMP.htm and 2016b.BMP <-- subsequently added this

This is a combination of code from:

  http://blog.startingelectronics.com/arduino-web-server-tutorial/ by W.A. Smith
  
  http://forum.arduino.cc/index.php?topic=342991.0 by zoomkat

  modified by DaveEvans for esp8266 and its SPIFFS July 2016

--------------------------------------------------------------*/

#include "FS.h" // for SPIFFS
#include <ESP8266WiFi.h>
//#include <ESP8266WebServer.h> //my attempt to do basic auth didn't work

//ESP8266WebServer server(80);
WiFiServer server(80);

File webFile;

// size of buffer used to capture HTTP requests
#define REQ_BUF_SZ   20

char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer

const char* ssid = "abc";
const char* password = "xyz";

//const char* www_username = "abc";  // for basic auth attempt
//const char* www_password = "xyz";

unsigned long prevTime;

/////////////////////////

void setup()
{

  Serial.begin(115200);
  Serial.println("");
  Serial.println("serveBMPspiffs");

  bool result = SPIFFS.begin();
  Serial.print("SPIFFS opened: ");
  Serial.println(result);

  Serial.println();
  Serial.print("Cnnctng to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("at IP:");

  server.begin();
  Serial.println(WiFi.localIP());

}

/////////////////////////

void loop()
{
  WiFiClient client = server.available();  // try to get client
  //WiFiClient client = server.client();  // try to get client (auth attempt)
  
  if (client) {
    Serial.println("new clnt");
    boolean currentLineIsBlank = true;

    while (client.connected()) {
      if (client.available()) {   // client data available to read
        char c = client.read(); // read 1 byte (character) from client

        // buffer first part of HTTP request in HTTP_req array (string)
        // leave last element in array as 0 to null terminate string (REQ_BUF_SZ - 1)
        if (req_index < (REQ_BUF_SZ - 1)) {
          HTTP_req[req_index] = c;          // save HTTP request character
          req_index++;
        }

        Serial.print(c);

        // last line of client request is blank and ends with \n
        // respond to client only after last line received
        if (c == '\n' && currentLineIsBlank) {
          // open requested web page file
          if (StrContains(HTTP_req, "GET / ")
              || StrContains(HTTP_req, "GET /index.htm")) {
              
            //if (!server.authenticate(www_username, www_password))
            //  return server.requestAuthentication();
            
            client.println("HTTP/1.1 200 OK");
            client.println("Content-Type: text/html");
            client.println("Connnection: close");
            client.println();

            webFile = SPIFFS.open("/indexBMP.htm", "r");
            Serial.println("serving indexBMP");
          }
          else if (StrContains(HTTP_req, "GET /page2.htm")) {
            client.println("HTTP/1.1 200 OK");
            client.println("Content-Type: text/html");
            client.println("Connnection: close");
            client.println();

            webFile = SPIFFS.open("/page2.htm", "r");
            Serial.println("serving page2");
          }
          else if (StrContains(HTTP_req, "GET /2016b.BMP")) {

            webFile = SPIFFS.open("/2016b.BMP", "r");
            Serial.println("serving BMP");

            if (webFile) {
              client.println("HTTP/1.1 200 OK");
              client.println();
            }
          }

          // The code in this "if" statement is Zoomkat's.
          // I had to add (const unit8_t) to the client.writes
          // to get it to work on the esp8266
          if (webFile) {

            byte clientBuf[64];
            int clientCount = 0; //why an int?  why not byte?

            prevTime=millis();
            
            while (webFile.available())
            {
              clientBuf[clientCount] = webFile.read();
              clientCount++;

              if (clientCount > 63)
              {
                // Serial.println("Packet");                
                client.write((const uint8_t *)clientBuf, 64); // DAE added cast to const...
                clientCount = 0;
              }
            }
            //final <64 byte cleanup packet
            if (clientCount > 0) client.write((const uint8_t *)clientBuf, clientCount); // DAE added cast to const...
            
            // close the file:
            webFile.close();
          } else {
            Serial.println("file !fnd");
          }

          Serial.print("time to serve: ");
          Serial.println((millis()-prevTime)/1000);
          
          delay(1);

          // reset buffer index and all buffer elements to 0
          req_index = 0;
          StrClear(HTTP_req, REQ_BUF_SZ);
          break;
        }

        // every line of text received from the client ends with \r\n
        if (c == '\n') {
          // last character on line of received text
          // starting new line with next character read
          currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // a text character was received from client
          currentLineIsBlank = false;
        }
        
      } // end if (client.available())
    } // end while (client.connected())
    delay(1);      // give the web browser time to receive the data
    client.stop(); // close the connection
    Serial.println("clnt stppd");  
  } // end if (client)
}

/////////////////////////

// sets every element of str to 0 (clears array)
void StrClear(char *str, char length)
{
  for (int i = 0; i < length; i++) {
    str[i] = 0;
  }
}

/////////////////////////

// searches for the string sfind in the string str
// returns 1 if string found
// returns 0 if string not found
char StrContains(char *str, char *sfind)
{
  char found = 0;
  char index = 0;
  char len;

  len = strlen(str);

  if (strlen(sfind) > len) {
    return 0;
  }
  while (index < len) {
    if (str[index] == sfind[found]) {
      found++;
      if (strlen(sfind) == found) {
        return 1;
      }
    }
    else {
      found = 0;
    }
    index++;
  }

  return 0;
}

blimpyway

Here are normal headers a HTTP server sends before sending an image file:
Code: [Select]

  HTTP/1.1 200 OK
  Content-Type: image/jpeg
  Content-Length: 60916
  Connection: keep-alive
  Keep-Alive: timeout=15
  Date: Mon, 25 Jul 2016 22:47:00 GMT
  Server: Apache
  Last-Modified: Sat, 21 Jan 2012 14:09:05 GMT
  ETag: "40dd6d13-edf4-4b70a571d6523"
  Accept-Ranges: bytes


Your ESP8266 server should add a proper "Content-Length" (and "Content-Type" header), otherwise, if you don't close the client connection, the browsers cannot tell the received file ended and keep expecting more data.

Or/and close the client socket after you-re done sending the image.

DaveEvans

Solved!  Now it takes less than a second to either IE or FF!




DaveEvans

#3
Jul 26, 2016, 08:35 pm Last Edit: Jul 26, 2016, 08:43 pm by DaveEvans
PS: Blimpy - thanks for the advice.  I've added the suggested headers.


Also, edited to title to be better descriptive....

Go Up