Tuesday, May 3, 2016

How to prevent Ajax overflow/server crash? (Arduino/ESP8266 Environment)

Leave a Comment

I have been recently learning how to program in Arduino to host an HTML/CSS/Javascript webpage on an Adafruit HUZZAH ESP8266 breakout, so please forgive me if my methods here are completely off.

I'm using Ajax to update a few pressure gauge values on a page and it works for a little while but eventually I just get ERR_CONNECTION_TIMED_OUT.

I get this all the time, especially when I'm just starting the server up. If I reset the wifi card enough times eventually it seems to load just fine but it's problematic and really unstable.

I've also noticed that it seems that the ESP8266 can only handle one user on it at a time, and once I try to connect from another computer/phone it crashes the server and I have to hit the reset button to get it working on that new host.

Can someone help me understand if I'm just using Ajax in a really inefficient way or why this is happening?

(Old Code removed to fit new code)

EDIT: So I believe I've improved my code in two ways:

  • I've modified the hundreds of client.println() statements and instead stored the html/css/js content into four separate char arrays that are then printed out in only four client.println() statements.

  • I've also reduced the number of ajax calls I had from five (not counting the two corresponding to the LED light that are still necessary) down to one by sending all of the variables at once within a comma delimited string that I then filter the corresponding variable content out of individually.

However, I am still experiencing frequent, yet random ERR_CONNECTION_TIMED_OUT errors, and I say random because I recently was able to successfully run the server with consecutive successful ajax calls for almost two hours before error messages began cropping up, and then immediately after restarting the server more error messages came up within only 30 seconds.

At this point I can't figure out if I am still using ajax inefficiently or if this is simply due to limitations of the ESP8266?

#include <ESP8266WiFi.h> #include <WiFiClient.h>  #include <Wire.h>  WiFiServer server(80); WiFiClient client; String HTTP_req; String req; double test = 42; String LEDstatus = "off";   (Some variable initializations removed for space)  char webpagePartOne[2500];  char webpagePartTwo[2500];   char webpagePartThree[2500];   char webpagePartFour[2500];   void switchLEDon() {  //Serial.println("TEST LED ON");  int ledStatusLength = 1;  subsys = 0x13;   //Account for the end of message messageLength = ledStatusLength + 2;  messageContent = 1;  Serial.write(som); Serial.write(messageLength); Serial.write(subsys); Serial.write(messageContent); Serial.write(eom); //Serial.println(""); //Serial.println("TURN LED ON|"); LEDstatus = "on"; } //end switchLEDon  void switchLEDoff() {      (almost the same as switchLEDon(), removed to lower char count here) } //end switchLEDoff  void setStatus(WiFiClient cl) {  currFluid += 22;  if(currFluid > 100) {     currFluid -= 100; }  cl.print(currNumRefresh); cl.print(","); cl.print(currPressureC); cl.print(","); cl.print(currPressureD); cl.print(","); cl.print(currPressureE); cl.print(","); cl.print(currFluid); }  void setup() {   Serial.begin(115200);    pinMode(LED_PIN, OUTPUT); WiFi.begin(SSID, pass);  while (WiFi.status() != WL_CONNECTED)  { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected");  // Start the server server.begin(); Serial.println("Server started");  Serial.println(WiFi.localIP()); }  void loop() {   //Communication Protocol if(Serial.available() > 0) {     //Serial.print("SERIAL.AVAILABLE: ");     //Serial.println(Serial.available());      //delay(1000);      if(initialCounter == 0)     {         rx_byte = Serial.read();     /*         //Print Start of Message         Serial.print("0x");         if(rx_byte<0x10)         {             Serial.print("0");         }         Serial.println(rx_byte, HEX);     */         //Serial.println(rx_byte, BIN);         //Serial.println(rx_byte);          initialCounter++;     }       if((!messageBegun) && (rx_byte == som))     {         messageBegun = true;          //Serial.println("MESSAGE BEGUN TRUE");     } //end if (messageInProgress && rx_byte)      if((messageBegun) && (!messageInProgress))     {         serialCurrent = Serial.available();          if(serialCurrent > 0)         {                   receivedMessageLength = (uint8_t)Serial.read();         /*             Serial.print("MESSAGE LENGTH: ");             Serial.println(receivedMessageLength);         */             messageBegun = false;              messageInProgress = true;              //Serial.println("MESSAGE IN PROGRESS TRUE");          } //end if (serialCurrent)     } //end if (messageBegun && messageInProgress)      if(messageInProgress)     {         serialCurrent = Serial.available();           if(serialCurrent >= receivedMessageLength)         {              Serial.readBytes(rxBuff, receivedMessageLength);              if((byte)rxBuff[receivedMessageLength-1] != eom)             {                 //Serial.println("ERROR");                 //Serial.write(Serial.read());             } //end if (rxBuff != eom)              else             {                 messageInProgress = false;                   for(int i=0; i<receivedMessageLength; i++)                 {                     if(rxBuff[i] == eom)                     {                     /*                         //Print End of Message                         Serial.print("0x");                         if(rx_byte<0x10)                         {                             Serial.print("0");                         }                         Serial.println(rxBuff[i], HEX);                     */                         initialCounter = 0;                          receivedMessageLength = 0;                      } //end if                      else if(i == 0)                     {                         receivedSubsys = rxBuff[i];                      /*                         //Print Subsystem                         Serial.print("0x");                         if(rx_byte<0x10)                         {                             Serial.print("0");                         }                         Serial.println(rxBuff[i], HEX);                     */                     } //end if                     else                     {                         if(receivedSubsys == 0x14)                         {                             currNumRefresh = rxBuff[i];                         } //end if                          else if(receivedSubsys == 0x15)                         {                             currPressureC = rxBuff[i];                         } //end else if                          else if(receivedSubsys == 0x16)                         {                             currPressureD = rxBuff[i];                         } //end else if                          else if(receivedSubsys == 0x17)                         {                             currPressureE = rxBuff[i];                         } //end else if                      } //end else                 } //end for              } //end else          } //end if (serialCurrent)      } //end if (messageInProgress) } //end if (Serial.available)  WiFiClient client = server.available(); if (client) {      boolean currentLineIsBlank = true;     String currentLine = "";     /*     if(digitalRead(LED_PIN))     {         LEDstatus = "on";     }     else if(!digitalRead(LED_PIN))     {         LEDstatus = "off";     }     */     while (client.connected()) {         if (client.available()) {              char c = client.read();             HTTP_req += c;              if (c == '\n' && currentLineIsBlank)              {                 client.println("HTTP/1.1 200 OK");                 client.println("Content-Type: text/html");                 client.println("Connection: keep-alive");                 client.println();                  //LED Functions                 if (HTTP_req.indexOf("ajax_LED_switch_on") > -1) {                     switchLEDon();                 }                 else if(HTTP_req.indexOf("ajax_LED_switch_off") > -1) {                     switchLEDoff();                 }                 else if(HTTP_req.indexOf("ajax_set_status") > -1) {                     setStatus(client);                 }                  else {                  //Part One                      strcpy(webpagePartOne,"<!DOCTYPE html>\n");                      strcat(webpagePartOne,"<html>\n");                         strcat(webpagePartOne,"<head>\n");                         strcat(webpagePartOne,"<title>Adafruit HUZZAH ESP8266</title>\n");                         strcat(webpagePartOne,"<style type='text/css'>\n");                              (css here removed for space)                          //Part Two                              (Some more css here removed for space)                          strcat(webpagePartTwo,"</style>\n");                         strcat(webpagePartTwo,"<script>\n");                              strcat(webpagePartTwo,"var currPressureTest = 0;\n");                             strcat(webpagePartTwo,"var currFluidTest = 0;\n");                              //Set Status function                             strcat(webpagePartTwo,"function setStatus(){\n");                                  strcat(webpagePartTwo,"nocache = \"&nocache=\"+ Math.random() * 1000000;\n");                                 strcat(webpagePartTwo,"var request = new XMLHttpRequest();\n");                                 strcat(webpagePartTwo,"request.onreadystatechange = function() {\n");                                     strcat(webpagePartTwo,"if (this.readyState == 4) {\n");                                         strcat(webpagePartTwo,"if (this.status == 200) {\n");                                             strcat(webpagePartTwo,"if (this.responseText != null) {\n");                                                  strcat(webpagePartTwo,"var totalStatus = this.responseText;\n");                                                  strcat(webpagePartTwo,"var splitStatus = totalStatus.split(',');\n");                                                  //strcat(webpagePartThree,"alert('Pressure C: ' + splitStatus[0] + ', Pressure D: ' + splitStatus[1] + ', Pressure E: ' + splitStatus[2]);\n");                                                  //Num Refresh                                                 strcat(webpagePartTwo,"document.getElementById(\"demo\").innerHTML = splitStatus[0];\n");                                                  //PRESSURE C                                                 strcat(webpagePartTwo,"var pressureValue = document.querySelector('.gauge-c');\n");                                                 strcat(webpagePartTwo,"pressureValue.style.transform = 'rotate('+ splitStatus[1] +'deg)';\n");                                                  strcat(webpagePartTwo,"var pressureText = (((splitStatus[1])/180)*100).toFixed(0);\n");                                                 strcat(webpagePartTwo,"document.getElementById(\"pressurePercentC\").innerHTML = pressureText + '%';\n");                                                  strcat(webpagePartTwo,"if(pressureText > 75){\n");                                                     strcat(webpagePartTwo,"pressureValue.style.background = 'red';\n");                                                    strcat(webpagePartTwo,"}\n");                                                  strcat(webpagePartTwo,"else if(pressureText > 25 && pressureText < 75){\n");                                                     strcat(webpagePartTwo,"pressureValue.style.background = 'yellow';\n");                                                  strcat(webpagePartTwo,"}\n");                                                  strcat(webpagePartTwo,"else if(pressureText < 25){\n");                                                     strcat(webpagePartTwo,"pressureValue.style.background = 'green';\n");                                                 strcat(webpagePartTwo,"}\n");                                                                              //PRESSURE D                                                 strcat(webpagePartTwo,"var pressureValue = document.querySelector('.gauge-d');\n");                                                 strcat(webpagePartTwo,"pressureValue.style.transform = 'rotate('+ splitStatus[2] +'deg)';\n");                                                  strcat(webpagePartTwo,"var pressureText = (((splitStatus[2])/180)*100).toFixed(0);\n");                                                 strcat(webpagePartTwo,"document.getElementById(\"pressurePercentD\").innerHTML = pressureText + '%';\n");                                                  strcat(webpagePartTwo,"if(pressureText > 75){\n");                                                     strcat(webpagePartTwo,"pressureValue.style.background = 'red';\n");                                                    strcat(webpagePartTwo,"}\n");                                                  strcat(webpagePartTwo,"else if(pressureText > 25 && pressureText < 75){\n");                                                     strcat(webpagePartTwo,"pressureValue.style.background = 'yellow';\n");                                                  strcat(webpagePartTwo,"}\n");                                                  strcat(webpagePartTwo,"else if(pressureText < 25){\n");                                                     strcat(webpagePartTwo,"pressureValue.style.background = 'green';\n");                                                 strcat(webpagePartTwo,"}\n");                                                   //PRESSURE E                                                 strcat(webpagePartTwo,"var pressureValue = document.querySelector('.gauge-e');\n");                                                 strcat(webpagePartTwo,"pressureValue.style.transform = 'rotate('+ splitStatus[3] +'deg)';\n");                                                  strcat(webpagePartTwo,"var pressureText = (((splitStatus[3])/180)*100).toFixed(0);\n");                                                 strcat(webpagePartTwo,"document.getElementById(\"pressurePercentE\").innerHTML = pressureText + '%';\n");                                              //Part Three                                                  strcpy(webpagePartThree,"if(pressureText > 75){\n");                                                     strcat(webpagePartThree,"pressureValue.style.background = 'red';\n");                                                    strcat(webpagePartThree,"}\n");                                                  strcat(webpagePartThree,"else if(pressureText > 25 && pressureText < 75){\n");                                                     strcat(webpagePartThree,"pressureValue.style.background = 'yellow';\n");                                                  strcat(webpagePartThree,"}\n");                                                  strcat(webpagePartThree,"else if(pressureText < 25){\n");                                                     strcat(webpagePartThree,"pressureValue.style.background = 'green';\n");                                                 strcat(webpagePartThree,"}\n");                                                   //FLUID LEVEL                                                 strcat(webpagePartThree,"var fluidValue = document.querySelector('.fluidMeter');\n");                                                 strcat(webpagePartThree,"fluidValue.value = splitStatus[4];\n");                                                  strcat(webpagePartThree,"var fluidText = splitStatus[4];\n");                                                 strcat(webpagePartThree,"document.getElementById(\"fluidPercent\").innerHTML = fluidText + '%';\n");                                  strcat(webpagePartThree,"}}}}\n");                                 strcat(webpagePartThree,"request.open(\"GET\", \"ajax_set_status\" + nocache, true);\n");                                 strcat(webpagePartThree,"request.send(null);\n");                                  strcat(webpagePartThree,"setTimeout('setStatus()', 5000);\n");                              strcat(webpagePartThree,"}\n");                              strcat(webpagePartThree,"function LEDswitch(){\n");                                 strcat(webpagePartThree,"var LEDswitchCheck = document.getElementById('myonoffswitch').checked;\n");                                  strcat(webpagePartThree,"if(LEDswitchCheck){\n");                                     strcat(webpagePartThree,"nocache = \"&nocache=\"+ Math.random() * 1000000;\n");                                     strcat(webpagePartThree,"var request = new XMLHttpRequest();\n");                                     strcat(webpagePartThree,"request.onreadystatechange = function() {\n");                                         strcat(webpagePartThree,"if (this.readyState == 4) {\n");                                             strcat(webpagePartThree,"if (this.status == 200) {\n");                                                 strcat(webpagePartThree,"if (this.responseText != null) {\n");                                                     //strcat(webpagePartThree,"document.getElementById(\"LEDbtn\").innerHTML = this.responseText;\n");                                     strcat(webpagePartThree,"}}}}\n");                                     strcat(webpagePartThree,"request.open(\"GET\", \"ajax_LED_switch_on\" + nocache, true);\n");                                     strcat(webpagePartThree,"request.send(null);\n");                                 strcat(webpagePartThree,"}\n");                                 strcat(webpagePartThree,"else if(!LEDswitchCheck) {\n");                                     strcat(webpagePartThree,"nocache = \"&nocache=\"+ Math.random() * 1000000;\n");                                     strcat(webpagePartThree,"var request = new XMLHttpRequest();\n");                                     strcat(webpagePartThree,"request.onreadystatechange = function() {\n");                                         strcat(webpagePartThree,"if (this.readyState == 4) {\n");                                             strcat(webpagePartThree,"if (this.status == 200) {\n");                                                 strcat(webpagePartThree,"if (this.responseText != null) {\n");                                                     //strcat(webpagePartThree,"document.getElementById(\"LEDbtn\").innerHTML = this.responseText;\n");                                     strcat(webpagePartThree,"}}}}\n");                                     strcat(webpagePartThree,"request.open(\"GET\", \"ajax_LED_switch_off\" + nocache, true);\n");                                     strcat(webpagePartThree,"request.send(null);\n");                                 strcat(webpagePartThree,"}\n");                             strcat(webpagePartThree,"}\n");                         strcat(webpagePartThree,"</script>\n");                      strcat(webpagePartThree,"</head>\n");                     strcat(webpagePartThree,"<body style='background-color:#558C89;' onload=\"setStatus();\">\n");                          strcat(webpagePartThree,"<div style='background-color:#74AFAD;'>\n");                             strcat(webpagePartThree,"<h1 style='text-decoration: underline;'>Adafruit HUZZAH ESP8266</h1>\n");                         strcat(webpagePartThree,"</div>\n");                         strcat(webpagePartThree,"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\">\n</div>\n<div style=\"clear:both;\"></div><p>\n");                       strcat(webpagePartThree,"<div style='background-color:#74AFAD;'>\n");                             strcat(webpagePartThree,"<h2 style='color: red;'>LED Controls</h2>\n");                              strcat(webpagePartThree,"<div id='LEDbtn' class='onoffswitch'>\n");                                  /*                                 if (LEDstatus == "on")                                  {                                     strcat(webpagePartThree,"<input type='checkbox' name='onoffswitch' class='onoffswitch-checkbox' id='myonoffswitch' checked='checked' onclick='LEDswitch()'>\n");                                  } //end if                                 */                                 /*                                 else if(LEDstatus == "off")                                 {*/                                     strcat(webpagePartThree,"<input type='checkbox' name='onoffswitch' class='onoffswitch-checkbox' id='myonoffswitch' onclick='LEDswitch()'>\n");                                 //} //end else                                  strcat(webpagePartThree,"<label class='onoffswitch-label' for='myonoffswitch'>\n");                                     strcat(webpagePartThree,"<span class='onoffswitch-inner'></span>\n");                                     strcat(webpagePartThree,"<span class='onoffswitch-switch'></span>\n");                                 strcat(webpagePartThree,"</label>\n");                             strcat(webpagePartThree,"</div>\n");                         strcat(webpagePartThree,"</div>\n");                          //Part Four                          strcpy(webpagePartFour,"<div style='background-color:#74AFAD;'>\n");                             strcat(webpagePartFour,"<h2 style='color: green;'>Num Refresh Test</h2>\n");                             strcat(webpagePartFour,"<div id=\"demo\"><h2>Let AJAX change this text</h2></div>\n");                         strcat(webpagePartFour,"</div>\n");                     strcat(webpagePartFour,"</div>\n");                     //strcat(webpagePartFour,"<div id='gaugeCounter'></div>\n");                     strcat(webpagePartFour,"<div class='pressureRow'>\n");                         strcat(webpagePartFour,"<div class='pressureContainer'>\n");                             strcat(webpagePartFour,"<div class='gauge-a'></div>\n");                             strcat(webpagePartFour,"<div class='gauge-b'></div>\n");                             strcat(webpagePartFour,"<div class='gauge-c'></div>\n");                             strcat(webpagePartFour,"<div class='gauge-data'><h1 id='pressurePercentC'>0%</h1></div>\n");                         strcat(webpagePartFour,"</div>\n");                         strcat(webpagePartFour,"<div class='pressureContainer'>\n");                             strcat(webpagePartFour,"<div class='gauge-a'></div>\n");                             strcat(webpagePartFour,"<div class='gauge-b'></div>\n");                             strcat(webpagePartFour,"<div class='gauge-d'></div>\n");                             strcat(webpagePartFour,"<div class='gauge-data'><h1 id='pressurePercentD'>0%</h1></div>\n");                         strcat(webpagePartFour,"</div>\n");                         strcat(webpagePartFour,"<div class='pressureContainer'>\n");                             strcat(webpagePartFour,"<div class='gauge-a'></div>\n");                             strcat(webpagePartFour,"<div class='gauge-b'></div>\n");                             strcat(webpagePartFour,"<div class='gauge-e'></div>\n");                             strcat(webpagePartFour,"<div class='gauge-data'><h1 id='pressurePercentE'>0%</h1></div>\n");                         strcat(webpagePartFour,"</div>\n");                     strcat(webpagePartFour,"</div>\n");                      strcat(webpagePartFour,"<div class='fluidContainer'>\n");                         strcat(webpagePartFour,"<meter class='fluidMeter' max='100' value='50' low='25' high='75' optimum='100'></meter>\n");                          strcat(webpagePartFour,"<div class='fluid-data'><h1 id='fluidPercent'>0%</h1></div>\n");                     strcat(webpagePartFour,"</div>\n");                     strcat(webpagePartFour,"</body>\n");                     strcat(webpagePartFour,"</html>");                      client.print(webpagePartOne);                     client.print(webpagePartTwo);                     client.print(webpagePartThree);                     client.print(webpagePartFour);                 }              req = HTTP_req;                  //    Serial.print(HTTP_req);                 HTTP_req = "";                 break;             } //end if            }     }     delay(100);     client.stop(); } } 

4 Answers

Answers 1

I've also noticed that it seems that the ESP8266 can only handle one user on it at a time

What i suggest to do it to try and not to use the Keep-Alive header which might cause problems.

client.println("Connection: keep-alive"); 

When you set the Keep-alive its keeping the connection open all the time and this might cause the error.

If your server for example has a session timeout this should keep the session alive all the time.

The keep alive also has a timeout and maximum requests

Since you get this error every once in a while i assume that its a matter of your timeout + max requests that the server allow to accept on the keep alive connection.

Remove and test it without it.

Answers 2

try to use a packet sniffer Wireshark and screenshot the result

Answers 3

From: https://mcuoneclipse.com/2014/11/30/tutorial-web-server-with-the-esp8266-wifi-module/

Try closing the connection after you sent your repsonse:

Closing Connection: CIPCLOSE

So things are working:-). The trick is that I have to close the connection after I have sent the data. There is a CIPCLOSE command I can use:

AT+CIPCLOSE= which I can use to close a channel. So I close the connection with

AT+CIPCLOSE=0

Answers 4

So after a lot of testing I came to the conclusion that the ESP8266 cannot handle constant ajax updates and ended up using websockets to accomplish my task (which, thus far, appears to be FAR more stable than the ajax ever was).

Here's the example I based my result off of: https://gist.github.com/bbx10/667e3d4f5f2c0831d00b

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment