WiFiServer hangs after 24+ hours

I am running WiFiServer & WiFiClient on a Arduino WiFi Rev2. After a day or more, the web server becomes unresponsive, causing an ERR_EMPTY_RESPONSE on my Chrome browser. Tracing the Ethernet traffic, I can see that Arduino accepts the tcp connection, and acknowledges my "GET /" http request, but immediately after this closes the tcp connection with a FIN packet.

Anybody out there also having stability problems with WiFiServer ?

I initially thought that the USB connection would provide a good debug backdoor to my Arduino, but when I plug-in a USB cable to my hanging Arduino and connect it to the web-based Arduino Editor, the Arduino is reset when I start the Monitor. Also Monitor data is often garbled (suspect bad USB driver on pc, since garbling requires a pc reboot to be fixed).

Does anybody have an idea how I can establish a backdoor to my hanging Arduino, to tweak out some data from it ?

#include "DHT.h"
#include <WiFiNINA.h>

#define DHTPIN 2     // Digital pin connected to the DHT sensor
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
DHT dht(2, DHT22);

char ssid[] = "home_network";             //  your network SSID (name) between the " "
char pass[] = "password";      // your network password between the " "
int status = WL_IDLE_STATUS;      //connection status
WiFiServer server(80);            //server socket
WiFiClient client = server.available();

int ledPin = 2;
int statHttpRequests = 0;
int statSensorOk = 0;
int statSensorZero = 0;
int statSensorError = 0;
int statWifiReconnects = 0;
long unsigned statLoopCnt = 0;
long unsigned statLoopCntHigh = 0;
long unsigned watchdogCnt = 0;

void resetFunc() { asm volatile ("jmp 0"); }

void printStatus() {
  client.print("<br>http_request ");
  client.print(statHttpRequests);
  client.print("<br>sensor_ok ");
  client.println(statSensorOk);
  client.print("<br>sensor_zero ");
  client.println(statSensorZero);
  client.print("<br>sensor_err ");
  client.println(statSensorError);
  client.print("<br>wifi_rssi ");
  client.print(WiFi.RSSI());
  client.print("dBm<br>");
  client.print("wifi_reconnects ");
  client.print(statWifiReconnects);
  client.print("<br>statLoopCnt ");
  client.print(statLoopCnt);
}

void printHumTemp() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  
  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    statSensorError++;
    client.println("sensor1 ?? ??");
    // The 'F' macro moves the constant string from the 6KB ram to the 48KB flash 
    Serial.println(F("Warning: failed to read from DHT sensor"));
    return;
  } else if (h == 0.0 || t == 0.0) {
    statSensorZero++;
  } else {
    statSensorOk++;
  }

  // Rendering of html removes multiple spaces.
  client.print("sensor1 ");
  client.print(h, 1);
  client.print("%RH ");
  client.print(t, 1);
  client.println("degC<br>");
  
}

void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");

  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
}

void webWifiStatus () {
  // same as 'printWifiStatus' but as a web line
  client.print("SSID ");
  client.println(WiFi.SSID());

  long rssi = WiFi.RSSI();
  client.print("RSSI ");
  client.print(rssi);
  client.println(" dBm");
}

void enable_WiFi() {
  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < "1.0.0") {
    Serial.println("Please upgrade the firmware");
  }
}

void connect_WiFi() {
  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
    // wait 10 seconds for connection:
    delay(10000);
  }
}

void printWEB() {

  if (client) {                             // if you get a client,
    Serial.println("---------- Received from browser client: -----------------");
    statHttpRequests++;
    String currentLine = "";                
    String httpGetLine = "";
    while (client.connected()) {            
      if (client.available()) {             
        char c = client.read();             
        Serial.write(c);                    // echo http request on terminal
        if (c != '\n' && c != '\r')
          currentLine += c;

        if (c == '\n') {
          if (currentLine.startsWith("GET "))
            httpGetLine = currentLine;
            
          if (currentLine.length() == 0) {
            // got a blank line meaning http request header is done, and we can send a http response
            
            if (httpGetLine.startsWith("GET / ")) {

              // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
              // and a content-type so the client knows what's coming, then a blank line:
              client.println("HTTP/1.1 200 OK");
              client.println("Content-type:text/html");
              client.println();
  
              printHumTemp();
              printStatus();

              //client.println("</body></html>");
  
            }
            else {
              // GET on anything else than '/' is not supported. This was a bug with the original web server code, where the browser
              // would also request some icon file, causing the web server to resend the page
              client.println("HTTP/1.1 404 Not Found");
              client.println();
            }
            
            // The http request header was processed and a response was sent. Now exit the request-reading loop
            break;
            
          }
          
          currentLine = "";
        } // done processing http request header line
      } // done processing http character
    }
    // close tcp connection:
    client.stop();
    Serial.println("Info: browser client link closed");
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  while (!Serial);
  
  enable_WiFi();
  connect_WiFi();

  server.begin();
  printWifiStatus();

  dht.begin();
}

void loop() {
  // 1372 count/second
  statLoopCnt++;
  watchdogCnt++;
  if (statLoopCnt == 0) {
    statLoopCntHigh++;
  }
  
  // Reset if no web requests has been received for 2 hours. Observed that Arduino web server would stop working,
  // for unknown reason. ping showed that WiFi connection was working.
  if (watchdogCnt > 1372*60*60*2) {
    resetFunc();
  }
  
  client = server.available();
  if (client) {
    watchdogCnt = 0;
    printWEB();
  }
  
  status = WiFi.status();
  if (status != WL_CONNECTED) {
    Serial.print("Wifi connection lost, WiFi.status = ");
    Serial.println(WiFi.status());
    statWifiReconnects++;
    connect_WiFi();
    printWifiStatus();
  }
}

don't use String

Thanks for the tip. I rewrote my program avoiding the String type, see below. My problem however still persists: after 24+ hours the webserver becomes unresponsive. Ethernet traffic however looks different: now not even a tcp connection is established. Arduino responds with a [RST, ACK]. ICMP (ping) and ARP protocol seems to be working.

My only idea is start reporting heap, stack & free ram sizes, and see if there are memory leaks in the WiFiNINA or the DHT libraries.

Any idea how to get a backdoor into an Arduino ?

#include "DHT.h"
#include <WiFiNINA.h>

#define DHTPIN 2     // Digital pin connected to the DHT sensor
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
DHT dht(2, DHT22);

char ssid[] = "network";             //  your network SSID (name) between the " "
char pass[] = "password";      // your network password between the " "
int status = WL_IDLE_STATUS;      //connection status
WiFiServer server(80);            //server socket
WiFiClient client = server.available();

int ledPin = 2;
int statHttpRequests = 0;
int statSensorOk = 0;
int statSensorZero = 0;
int statSensorError = 0;
int statWifiReconnects = 0;
long unsigned statLoopCnt = 0;
long unsigned statLoopCntHigh = 0;
long unsigned watchdogCnt = 0;

// 
void resetFunc() { asm volatile ("jmp 0"); }

// Max length of strings. Must be at least long enough to hold "GET / HTTP".
#define MAX_LEN 120

void add_char(char dest_str[], char new_char) {
  int slen = strlen(dest_str);
  if (slen < MAX_LEN-1) {
    dest_str[slen] = new_char;
    dest_str[slen+1] = '\0';
  }
}

void printStatus() {
  client.print("<br>http_request ");
  client.print(statHttpRequests);
  client.print("<br>sensor_ok ");
  client.println(statSensorOk);
  client.print("<br>sensor_zero ");
  client.println(statSensorZero);
  client.print("<br>sensor_err ");
  client.println(statSensorError);
  client.print("<br>wifi_rssi ");
  client.print(WiFi.RSSI());
  client.print("dBm<br>");
  client.print("wifi_reconnects ");
  client.print(statWifiReconnects);
  client.print("<br>statLoopCnt ");
  client.print(statLoopCnt);
}

void printHumTemp() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  
  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    statSensorError++;
    client.println("sensor1 ?? ??");
    // The 'F' macro moves the constant string from the 6KB ram to the 48KB flash 
    Serial.println(F("Warning: failed to read from DHT sensor"));
    return;
  } else if (h == 0.0 || t == 0.0) {
    statSensorZero++;
  } else {
    statSensorOk++;
  }

  // Rendering of html removes multiple spaces.
  client.print("sensor1 ");
  client.print(h, 1);
  client.print("%RH ");
  client.print(t, 1);
  client.println("degC<br>");
  
}

void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");

  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
}

void webWifiStatus () {
  // same as 'printWifiStatus' but as a web line
  client.print("SSID ");
  client.println(WiFi.SSID());

  long rssi = WiFi.RSSI();
  client.print("RSSI ");
  client.print(rssi);
  client.println(" dBm");
}

void enable_WiFi() {
  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < "1.0.0") {
    Serial.println("Please upgrade the firmware");
  }
}

void connect_WiFi() {
  // attempt to connect to Wifi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }
}

// Longest http line seen:
//   Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
// but only needs to be long enough to handle "GET / http1.1"
char currentLine[MAX_LEN];
char httpGetLine[MAX_LEN];
int curlen;
void printWEB() {

  if (client) {                             // if you get a client,
    Serial.println("---------- Received from browser client: -----------------");
    statHttpRequests++;
    currentLine[0] = '\0';                
    httpGetLine[0] = '\0';
    while (client.connected()) {            
      if (client.available()) {             
        char c = client.read();             
        Serial.write(c);                    // echo http request on terminal
        if (c != '\n' && c != '\r')
          add_char(currentLine, c);
          
        if (c == '\n') {
          if (strncmp(currentLine, "GET ", 4) == 0)
            // httpGetLine = currentLine;
            strlcpy(httpGetLine, currentLine, sizeof(httpGetLine));
            
          if (strlen(currentLine) == 0) {
            // got a blank line meaning http request header is done, and we can send a http response
            
            if (strncmp(httpGetLine, "GET / HTTP", 10) == 0) {

              // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
              // and a content-type so the client knows what's coming, then a blank line:
              client.println("HTTP/1.1 200 OK");
              client.println("Content-type:text/html");
              client.println();
  
              // To view the raw html in Chrome: right-click and select 'View page source'. The 'html' and 'head' tags
              // are not required. Without these Chrome still renders page ok.
              //
              //client.println("<html><head></head>");
              //client.println("<body>");
              
              printHumTemp();
              printStatus();

              //client.println("</body></html>");
  
            }
            else {
              // GET on anything else than '/' is not supported. This was a bug with the original web server code, where the browser
              // would also request some icon file, causing the web server to resend the page
              client.println("HTTP/1.1 404 Not Found");
              client.println();
            }
            
            // The http request header was processed and a response was sent. Now exit the request-reading loop
            break;
            
          }
          
          currentLine[0] = '\0';
        } // done processing http request header line
      } // done processing http character
    }
    // close tcp connection:
    client.stop();
    Serial.println("Info: browser client link closed");
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  while (!Serial);
  
  enable_WiFi();
  connect_WiFi();

  server.begin();
  printWifiStatus();

  dht.begin();
}

void loop() {
  // 1372 count/second
  statLoopCnt++;
  watchdogCnt++;
  if (statLoopCnt == 0) {
    statLoopCntHigh++;
  }
  
  // Reset if no web requests has been received for 2 hours. Observed that Arduino web server would stop working,
  // for unknown reason. ping showed that WiFi connection was working.
  if (watchdogCnt > 1372*60*60*2) {
    resetFunc();
  }
  
  client = server.available();
  if (client) {
    watchdogCnt = 0;
    printWEB();
  }
  
  status = WiFi.status();
  if (status != WL_CONNECTED) {
    Serial.print("Wifi connection lost, WiFi.status = ");
    Serial.println(WiFi.status());
    statWifiReconnects++;
    connect_WiFi();
    printWifiStatus();
  }
}

I don't think it is a problem with WiFiNINA. I test it in my large project with 3 servers and it works pretty good.
I use fw an library build form latest source code. But there are no bugs fixed compared to released versions.

My WiFiNINA WiFiServer is exposed to the internet, and can therefore potentially see malformed / malicious packets. Is this also true in your usage of WiFiNINA ?

The last 3 WiFiServer crashes/unresponsiveness happens after 2-6 days of ok functioning. When analyzing traffic to the unresponsive Arduino, I can see ok TCP handshake establishing a connection. Then internet web browser sends "GET /" request to Arduino, and WiFiNINA responds by closing the TCP connection with a TCP FIN packet. This indicates that WiFiServer is not working, but TCP stack works fine.