JAndrassy / WiFiEspAT

Arduino networking library. Standard Arduino WiFi networking API over ESP8266 or ESP32 AT commands.
GNU Lesser General Public License v2.1
271 stars 44 forks source link

WiFiServer server.available() consistently returns no clients after a few minutes #74

Closed lowercasename closed 2 years ago

lowercasename commented 2 years ago

I built a simple server using WiFiEspAT, a Challenger RP2040 WiFi board and Earle Philhower's Pi Pico Arduino Core.

The server is reverse proxied behind a larger server and exposed to the Internet.

After a variable amount of time (minutes, not hours), the server seems to simply stop accepting clients. I've tried stress testing the server by refreshing multiple times to try and trigger the behaviour, but am not convinced stress is what makes it stop accepting clients. The loop() function continues to run after the server cuts out.

Here is my MWE, where this behavior occurs:

#include <WiFiEspAT.h>
#include <ChallengerWiFi.h>

WiFiServer server(80);

int previousMillis = 0;
const int interval = 30000;

void setup() {

  Serial.begin(115200);

  while (!Serial);

  Serial2.begin(115200);
  WiFi.init(Serial2, PIN_ESP8285_RST);

  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println(F("Communication with WiFi module failed!"));
    // don't continue
    while (true);
  }

  Serial.println(F("Waiting for connection to WiFi"));
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print('.');
  }
  Serial.println();

  server.begin();

  IPAddress ip = WiFi.localIP();
  Serial.println();
  Serial.println(F("Connected to WiFi network."));
  Serial.print(F("To access the server, enter \"http://"));
  Serial.print(ip);
  Serial.println(F("/\" in web browser."));
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    Serial.println("Pong.");
    previousMillis = currentMillis;
  }

  int lineNumber = 0;

  WiFiClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        String line = client.readStringUntil('\n');
        line.trim();
        if (lineNumber == 0){
          Serial.println(line);
        }
        lineNumber++;

        // if you've gotten to the end of the HTTP header (the line is blank),
        // the http request has ended, so you can send a reply
        if (line.length() == 0) {
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Connection: close"));
          client.println(F("Content-Type: text/plain"));
          client.print  (F("Content-Length: "));
          client.println(strlen("Hello, world!"));
          client.println();
          client.println(F("Hello, world!"));
          client.flush();
          break;
        }
      } // if
    } // while
    client.stop();
  } // if
}

Here's some output:

[...]
HEAD / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET /favicon.ico HTTP/1.1
Pong.
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
Pong.
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
Pong.
GET / HTTP/1.1
GET / HTTP/1.1
GET / HTTP/1.1
Pong.
Pong.
Pong.
Pong.
Pong.
[...]

No clients are accepted after that final GET, either from the internal IP or the external URL. The server does respond to ping.

Am I simply hitting up against a limitation in the WiFiEspAT library? If not, could you recommend some steps to debug or log what is going on? And could this be an issue specifically with the Challenger RP2040 WiFi board?

Edit: AT firmware reported:

AT firmware version 2.1.0.0-rc1
AT firmware is OK for the WiFiEspAT library.

Edit: Here's a debugging log of the final two requests before the server stopped accepting clients: https://ttm.sh/i-g.txt

JAndrassy commented 2 years ago

please use the 1.7.5 version of the AT firmware for esp8266. the link to it is in the README file

lowercasename commented 2 years ago

Thank you for the advice! I tried to load 1.7.5 but I'm currently unable to write the firmware due to a problem between esptool and the board which I'm trying to debug.

Do you know what it is in the v2 firmware which could be causing this problem? Perhaps there is a way I could work around it.

JAndrassy commented 2 years ago

try a sketch without String

atesin commented 2 years ago

@lowercasename : as jandrassy cleverly pointed, try replacing Strings objects with char arrays (C-style strings) ..... i had similar problems in the past, and was because String objects eventually EAT ALL THE RAM ...

i wanna share a little replacement table with you, the only caution is c strings are fixed-size (must reserve memory previously) and must terminated with nul (ascii 0)...

seriously.... c strings are actually arrays of bytes (ascii characters), c string functions detects the end of the char sequence when they find a 0 (the byte value not the ascii character '0'), if you dont terminate you could end reading or overwriting further memory blocks!!... dont overflow or bad things could happen, like server with insufficient ram returning earlier without clients xD

String.func()           cstring equivalente
-------------------------------------------
(easy)                  (hard)
(dynamic size)          (fixed size)
(fragments ram)         (dont fragments ram)
(heavy library)         (low level faint library (part of C))

String str              char str[4]
str = "asd"             str = {'a','s','d','\0'}

String str = "asd"      char str[] = "asd"         // auto dimension and terminates
String str              char str[max + 1]          // plus the final '\0'
str = "asd"             strcpy(str, "asd")
                        strcpy_P(str, PSTR("asd")) // recommended for constants
str.length()            strlen(str)
str + s2                strcat(str, s2)            // str must have enough space

str.charAt(n)           str[n]
str.setCharAt(n, c)     str[n] = c

str.indexOf(c)          strchr(str, c) - str     
str.indexOf(c, n)       strchr(str + n, c) - str 
str.lastIndexOf(c)      strrchr(str, c)

str.indexOf(s2)         strstr(str, s2) - str    
str.indexOf(s2, n)      strstr(str + n, s2) - str
s2 = str.substring(l)   strncpy(s2, str, l)      
s2 = str.substring(f, l)    strncpy(s2, str+f, n)

str.compareTo(s2)       strcmp(str, s2)
comparar hasta n        strncmp(str, s2, n)      
... many more...

... find more string commands here ... if you dont reply maybe you fixed your problem

atesin commented 2 years ago

to update .... one time i didn't have my ftdi, and i used my arduino uno compatible to flash the esp-01s

just load the examples/basic/bare-minimum builtin sketch and connect straight to esp this way (see note below).... you need a 3.3v voltage regulator for esp, DONT CONNECT 3V3 TO 5V OR WILL BURN (however rx+tx are 5v tolerant)

[pc-usb] --> [arduino uno] --> [esp] <-- [regulator (3v3 + gnd)]

arduino             esp
-----------------------
pin 0 (uart rx) --- rx
pin 1 (uart tx) --- tx
gnd (any)       --- gnd

than you can use your virtual com port from your pc as if were an ftdi ... basically you are replacing the ftdi ft232 usb virtual com chip by an ch341ser usb virtual com chip (or the attiny xxx ?? in case of a genuino) ... you still need the usb virtual com port chip drivers installed on your pc

note below:
with this configuration you actually will bypass the arduino soc cpu (atmega328p), you wont need it in this case, you wont use it, just need the usb serial interface chip... that is because you must load the bare minimum sketch, to make the arduino doing nothing and leave the builtin usb-serial chip unmanaged by the arduino because you need to manage it by your pc through usb to use it like a bridge