Platform
ESP8266
IDE / Tooling
Arduino (IDE/CLI)
What happened?
I'm using SSE feature. It seems, there is a leek of 312 bytes for each client connected. I've setup a minimum application (see MRE). It prints the HEAP every minute and also prints clients connecting and disconnecting:
12:06:08.501 -> Connected to WiFi: 192.168.88.27
12:07:04.537 -> 45992
12:08:04.544 -> 45992
12:08:41.651 -> Connect client: 42510 -> 1
12:09:04.577 -> 45464
12:10:04.571 -> 45464
12:10:05.887 -> Disconnect client: 0 -> 0
12:11:04.556 -> 45680
12:12:04.565 -> 45680
12:12:17.605 -> Connect client: 43500 -> 1
12:13:04.565 -> 45152
12:13:06.468 -> Disconnect client: 0 -> 0
12:14:04.582 -> 45368
12:15:04.576 -> 45368
As you can see the difference of heap before a client is connected and after a client disconnected is always 312 bytes never returned to heap. With this leak after 144 clients connected the system will crash. I guess even earlier because actions taken by clients also need heap temporarily. It is totally OK to spent a fraction of the heap for clients but this leak seems to be linear.
Is there something I'm doing wrong or is this a bug?
Versions used:
- ESP Async TCP 2.0.0
- ESP Async Webserver 3.7.7
Stack Trace
I'v a menu "Debug level" in ArduinoIDE but setting to any value like "CORE" or "HTTP_CLIENT+HTTP_SERVER" did not change the output in serial monitor which above.
Minimal Reproductible Example (MRE)
I created a tiny React web-application using the SSE: data.zip.
It is uploaded using the ArduinoIDE plugin as shown here.
This is the ESP8266 application the web-client connects to:
#define WIFI_SSID "YOUR-SSID"
#define WIFI_PASSWORD "YOUR-PWD"
#define WWW_USERNAME "username"
#define WWW_PASSWORD "password"
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
AsyncWebServer httpRestServer(80);
WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
AsyncEventSource statusEvents("/api/status-events");
uint8_t numberOfStatusEventsClients = 0;
void webappInitStatus(AsyncEventSourceClient *client) {
if(client->lastId()){
Serial.printf_P(PSTR("Client reconnected! Last message ID that it got is: %u\n"), client->lastId());
}
if (client->connected()) {
numberOfStatusEventsClients += 1;
Serial.printf_P(PSTR("Connect client: %u -> %u\n"), client->client()->remotePort(), numberOfStatusEventsClients);
client->client()->onDisconnect(disconnectStatusClient, NULL);
updateStatusClients();
}
}
void updateStatusClients() {
statusEvents.send(F("{}"), F("INIT"), millis());
}
void disconnectStatusClient(void *arg, AsyncClient *client) {
numberOfStatusEventsClients -= 1;
Serial.printf_P(PSTR("Disconnect client: %u -> %u\n"), client->remotePort(), numberOfStatusEventsClients);
}
void handleNotFound(AsyncWebServerRequest *request) {
Serial.println("Not found");
String message = F("File Not Found\n\n");
message += F("URI: ");
message += request->url();
message += F("\nMethod: ");
message += request->methodToString();
message += "\n";
request->send(404, F("text/plain"), message);
}
void onWifiConnect(const WiFiEventStationModeGotIP& event) {
Serial.print(F("Connected to WiFi: "));
Serial.println(WiFi.localIP().toString());
httpRestServer.onNotFound(handleNotFound);
httpRestServer
.serveStatic("/", LittleFS, "/www/")
.setDefaultFile("index.html")
.setCacheControl("no-cache, no-store, max-age=0")
.setAuthentication(WWW_USERNAME, WWW_PASSWORD);
statusEvents.onConnect(webappInitStatus);
httpRestServer.addHandler(&statusEvents);
httpRestServer.begin();
}
void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
httpRestServer.end();
}
void setup() {
Serial.begin(115200);
Serial.println("\n\n\n\nStarted\n\n\n\n");
if(!LittleFS.begin()) {
Serial.println(F("An error has occurred on mounting LittleFS"));
return;
}
wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);
WiFi.persistent(false);
WiFi.setAutoReconnect(false); // reconnect is done manually every minute
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
unsigned long previousTime = 0;
void loop() {
unsigned long currentMillis = millis();
if (currentMillis < previousTime) { // handle overflow
previousTime = currentMillis;
return;
}
if (currentMillis - previousTime < 60000) {
return;
}
previousTime = currentMillis;
Serial.println(ESP.getFreeHeap(), DEC);
updateStatusClients(); // ping SSE clients
}
I confirm that:
Platform
ESP8266
IDE / Tooling
Arduino (IDE/CLI)
What happened?
I'm using SSE feature. It seems, there is a leek of 312 bytes for each client connected. I've setup a minimum application (see MRE). It prints the HEAP every minute and also prints clients connecting and disconnecting:
As you can see the difference of heap before a client is connected and after a client disconnected is always 312 bytes never returned to heap. With this leak after 144 clients connected the system will crash. I guess even earlier because actions taken by clients also need heap temporarily. It is totally OK to spent a fraction of the heap for clients but this leak seems to be linear.
Is there something I'm doing wrong or is this a bug?
Versions used:
Stack Trace
I'v a menu "Debug level" in ArduinoIDE but setting to any value like "CORE" or "HTTP_CLIENT+HTTP_SERVER" did not change the output in serial monitor which above.
Minimal Reproductible Example (MRE)
I created a tiny React web-application using the SSE: data.zip.
It is uploaded using the ArduinoIDE plugin as shown here.
This is the ESP8266 application the web-client connects to:
I confirm that: