ESP32 Webserver multiple definition errors

Hallo,

ich möchte die von Fips angebotenen Webseiten Sketche auf einen XIAO ESP32S3 umziehen. Auf einem Arduino Nano ESP32 mit Arduino Core Package kompiliert und funktioniert alles. Ich verwende die Arduino IDE 2.3.4.
Mit dem aktuellen Espressif Core Package 3.1.1, egal ob für Arduino Nano ESP32 oder XIAO ESP32S3 verwendet, kompiliert es nicht.
Nehme ich den alten Espressif Core Package 2.0.17, der praktisch gleich dem Arduino ist, kompiliert es.
Für den XIAO ESP32S3 benötige ich sowieso den Espressif Core, ich möchte den aktuellen verwenden.
Ich erhalte immer einen "multiple definition of `RequestHandler ..." Error.
Ich konnte den Fehler auf 2 .ino Tabs runterbrechen.
Besteht aus dem Webserver Bsp. HttpBasicAuth und dem LittleFS Tab von Fips.

Linker Errors
C:/Users/z/AppData/Local/Arduino15/packages/esp32/tools/esp-x32/2405/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld.exe: C:\Users\z\AppData\Local\arduino\sketches\57786FAB64F608E24496B673D0F07C1B\libraries\WebServer\WebServer.cpp.o: in function `RequestHandler::addMiddleware(Middleware*)':
C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WebServer\src\detail/RequestHandlersImpl.h:13: multiple definition of `RequestHandler::addMiddleware(Middleware*)'; C:\Users\z\AppData\Local\arduino\sketches\57786FAB64F608E24496B673D0F07C1B\sketch\HttpBasicAuth_001.ino.cpp.o:C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WebServer\src/detail/RequestHandlersImpl.h:13: first defined here
C:/Users/z/AppData/Local/Arduino15/packages/esp32/tools/esp-x32/2405/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld.exe: C:\Users\z\AppData\Local\arduino\sketches\57786FAB64F608E24496B673D0F07C1B\libraries\WebServer\WebServer.cpp.o: in function `RequestHandler::removeMiddleware(Middleware*)':
C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WebServer\src\detail/RequestHandlersImpl.h:29: multiple definition of `RequestHandler::removeMiddleware(Middleware*)'; C:\Users\z\AppData\Local\arduino\sketches\57786FAB64F608E24496B673D0F07C1B\sketch\HttpBasicAuth_001.ino.cpp.o:C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WebServer\src/detail/RequestHandlersImpl.h:29: first defined here
C:/Users/z/AppData/Local/Arduino15/packages/esp32/tools/esp-x32/2405/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld.exe: C:\Users\z\AppData\Local\arduino\sketches\57786FAB64F608E24496B673D0F07C1B\libraries\WebServer\WebServer.cpp.o: in function `RequestHandler::process(WebServer&, http_method, String)':
C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WebServer\src\detail/RequestHandlersImpl.h:36: multiple definition of `RequestHandler::process(WebServer&, http_method, String)'; C:\Users\z\AppData\Local\arduino\sketches\57786FAB64F608E24496B673D0F07C1B\sketch\HttpBasicAuth_001.ino.cpp.o:C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WebServer\src/detail/RequestHandlersImpl.h:36: first defined here
C:/Users/z/AppData/Local/Arduino15/packages/esp32/tools/esp-x32/2405/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld.exe: C:\Users\z\AppData\Local\arduino\sketches\57786FAB64F608E24496B673D0F07C1B\libraries\WebServer\WebServer.cpp.o: in function `RequestHandler::addMiddleware(std::function<bool (WebServer&, std::function<bool ()>)>)':
C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WebServer\src\detail/RequestHandlersImpl.h:21: multiple definition of `RequestHandler::addMiddleware(std::function<bool (WebServer&, std::function<bool ()>)>)'; C:\Users\z\AppData\Local\arduino\sketches\57786FAB64F608E24496B673D0F07C1B\sketch\HttpBasicAuth_001.ino.cpp.o:C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WebServer\src/detail/RequestHandlersImpl.h:21: first defined here
collect2.exe: error: ld returned 1 exit status

Bibliothek WiFi in Version 3.1.1 im Ordner: C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WiFi  wird verwendet
Bibliothek Networking in Version 3.1.1 im Ordner: C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\Network  wird verwendet
Bibliothek ESPmDNS in Version 3.1.1 im Ordner: C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\ESPmDNS  wird verwendet
Bibliothek ArduinoOTA in Version 3.1.1 im Ordner: C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\ArduinoOTA  wird verwendet
Bibliothek Update in Version 3.1.1 im Ordner: C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\Update  wird verwendet
Bibliothek WebServer in Version 3.1.1 im Ordner: C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\WebServer  wird verwendet
Bibliothek FS in Version 3.1.1 im Ordner: C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\FS  wird verwendet
Bibliothek LittleFS in Version 3.1.1 im Ordner: C:\Users\z\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\LittleFS  wird verwendet
Bibliothek Streaming in Version 6.2.0 im Ordner: G:\Arduino IDE v2.3.4\portable\sketchbook\libraries\Streaming  wird verwendet
exit status 1

Die Webserver Lib unterscheidet sich zwischen Arduino Core 2.0.18 und Espressif Core 3.1.1.
Soweit ist mir das schon klar das es Probleme geben könnte.
Ich verstehe nur nicht warum der Compiler multiple Definitionen anmeckert.

Webserver.h
in class Webserver

  WebServer &addMiddleware(Middleware *middleware);
  WebServer &addMiddleware(Middleware::Function fn);
  WebServer &removeMiddleware(Middleware *middleware);

RequestHandlersImpl.h

RequestHandler &RequestHandler::addMiddleware(Middleware *middleware) {
  if (!_chain) {
    _chain = new MiddlewareChain();
  }
  _chain->addMiddleware(middleware);
  return *this;
}

RequestHandler &RequestHandler::addMiddleware(Middleware::Function fn) {
  if (!_chain) {
    _chain = new MiddlewareChain();
  }
  _chain->addMiddleware(fn);
  return *this;
}

RequestHandler &RequestHandler::removeMiddleware(Middleware *middleware) {
  if (_chain) {
    _chain->removeMiddleware(middleware);
  }
  return *this;
}

bool RequestHandler::process(WebServer &server, HTTPMethod requestMethod, String requestUri) {
  if (_chain) {
    return _chain->runChain(server, [this, &server, &requestMethod, &requestUri]() {
      return handle(server, requestMethod, requestUri);
    });
  } else {
    return handle(server, requestMethod, requestUri);
  }
}

Ich hätte gern gewusst was daran dem Compiler stört?
Für mich sind das unterschiedliche Signaturen.
Mein Sketchpaket. HttpBasicAuth_001.zip (8,2 KB)

Hey,
ich denke der Fehler tritt auf, weil RequestHandler-Methoden in einer Header-Datei (RequestHandlersImpl.h) definiert sind, die von mehreren .cpp- oder .ino-Dateien eingebunden wird.
LG

ICh probier mal kurz etwas rum und schau ob ich es gefixed bekomme...

also ich habe in der LittleFS.ino folgende Funktion eingefügt:

String getContentType(const String &path) {
  if (path.endsWith(".html")) return "text/html";
  if (path.endsWith(".css")) return "text/css";
  if (path.endsWith(".js")) return "application/javascript";
  if (path.endsWith(".json")) return "application/json";
  if (path.endsWith(".png")) return "image/png";
  if (path.endsWith(".jpg") || path.endsWith(".jpeg")) return "image/jpeg";
  if (path.endsWith(".gif")) return "image/gif";
  if (path.endsWith(".svg")) return "image/svg+xml";
  if (path.endsWith(".ico")) return "image/x-icon";
  if (path.endsWith(".txt")) return "text/plain";
  return "application/octet-stream";  // Standard, falls unbekannt
}

und diese Zeile:(Z.126)

return LittleFS.exists(path) ? ({File f = LittleFS.open(path, "r"); server.streamFile(f, StaticRequestHandler::getContentType(path)); f.close(); true;}) : false;

durch diese zeile ersetzt:

return LittleFS.exists(path) ? ({File f = LittleFS.open(path, "r"); server.streamFile(f, getContentType(path)); f.close(); true;}) : false;

außerdem hab ich

#include <detail/RequestHandlersImpl.h>

durch

#include <WebServer.h>

ersetzt.

LG

1 Like

Ich verstehe es auch nicht.
Nehme ich das "#include <detail/RequestHandlersImpl.h>" raus habe ich keinen Zugriff auf die getContentType() Funktion.

Mit Core 3.1.1 kam die Änderung.

Ich habe es nun mit einbinden der "#include <detail/mimetable.h>"
und einer eigenen Funktion zur Ermittlung des Mime Type gelöst.

Esp32 Filesystemmanger Update: 2025-01-29

1 Like

Die detail/RequestHandlersImpl.h mit getContentType hat schon früher beim ESP8266 bei einigen Core-Wechseln für Probleme gesorgt, fällt mir jetzt ein.

Gruß Tommy

Genau das Problem habe ich mit dem Code oben probiert zu lösen.
LG

Google sagt dazu

In neueren ESP32 Core 3.1.1 Versionen ist RequestHandlersImpl.h keine öffentliche API mehr.

Deswegen muss man es wohl nun anders regeln.
LG

Das kannst du für dich gerne so lösen.

Ich bin aber bestrebt das im Core schon vorhandene zu nutzen.
<detail/mimetable.h>

1 Like

War mir nicht bewusst das es das gibt ist natürlich auch eine gute Lösung

Hallo,

bitte nicht streiten. Mit so einer schnellen Lösung hatte ich nicht gerechnet. Vielen dank an alle. Zudem ich schon länger rumdoktere und deswegen noch die ältere fips Version verwende, hat sich zufällig überschnitten.

Die Lösung von hypergamer funktioniert bei mir.
Dann habe ich die aktuelle Version von LittleFS.ino von fips verwendet, kompiliert zwar, funktioniert leider nicht. Ich kann die Webseite von ESP nicht aufrufen.

Wegen deiner Antwort bzw. Vermutung.

Das ist eigentlich Sinn der Sache, dass man es mehrfach inkludieren und verwenden kann. Das include guard greift hier ein. Deswegen verstehe ich nicht warum der Compiler meckert. Verstanden hätte ich das Problem schon gern. Ich meine die Fehlermeldung hatte ich für mich auch schon gehabt. Nur dann hatte ich wirklich an mehreren Stellen die Definition stehen oder irgendeine Reihenfolge war falsch oder so ähnlich.

Bei mir funktioniert es. Eben nochmal getestet.
Ide Version 1.8.19 (Portable)
Esp Core Version: 3.1.1

C++ kompiliert jede Quelldatei für sich und erstellt für jede eine separate Übersetzungs-Einheit. Wenn du eine Methode in einem Header definierst, der in mehreren Dateien eingebunden wird, kann es passieren, dass diese Methode mehrmals kompiliert wird. So entsteht der Fehler "multiple definition", weil der Linker auf mehrere Definitionen derselben Funktion stößt.

Hallo,

fips:
Ich weiß nicht woran es lag. Der XIAO war mit der neuen LittleFS.ino Version nicht mehr im Netzwerk sichtbar. Ich hatte mehrfach beide Varianten hin und her geflasht, da, nicht da, da, nicht da usw. Jetzt habe ich im Router nochmals den XIAO bekannt gemacht, jetzt gehts. Die IP usw. blieb ja gleich. Keine Ahnung was hier los war. Danke.

Musstest du noch mehr deiner .ino Tabs an die neue Core Version anpassen?

hypergame:
Darüber muss ich erstmal nachdenken ...

Hallo,

um nochmal bei dem Bsp. zu bleiben.
Gehe ich auf die erste Startseite, die IP, dann werde ich nach dem Login gefragt. Gebe ich die /fs.html mit an, gelange ich ohne Login auf den FileManager. Woran liegt das?

Ständig alle Tabs zu prüfen schaff ich gar nicht.

Beide Esp32 Zeitschaltuhren habe ich nun noch aktualisiert.

Und die Funktion zum ermitteln des Mime Type nochmal gestrafft.

String getContentType(const String &path) {
  for (const auto &e : mime::mimeTable) {
    if (path.endsWith(e.endsWith)) return String(e.mimeType);
  }
  return "application/octet-stream";
}

So wie du es eingebunden hast wirkt die HTTP-Authentifizierung nur auf die "/".

  server.on("/", []() {
    if (!server.authenticate(www_username, www_password)) {
      return server.requestAuthentication();
    }
    server.send(200, "text/plain", "Login OK");
  });

Schau es dir in der LittleFS.ino der Zeitschaltuhr an. Da funktioniert es auf alle Dateien.

Hallo,

habe deine Funktion vor setup gesetzt

bool authenticated() {
  if (server.authenticate(www_username, www_password)) return true;
  server.requestAuthentication(DIGEST_AUTH, "Anmeldung erforderlich", "Authentifizierung fehlgeschlagen!");
  return false;
}

und im setup nur noch

ArduinoOTA.begin();

if (authenticated()) {
  server.send(200, "text/plain", "Login OK");
}  

server.begin();

Damit komme ich weiterhin ohne Login auf fs.html aber nicht mehr auf die Startseite.

Hallo,

ich habe das Hautprogramm original belassen und in der LittleFS.ino die Funktionen

bool handleFile(String &path) 
String existFolder( String const& foldername)
bool authenticated()

übernommen. Scheint zu funktionieren.
Ist das so 100% korrekt oder lauern noch Fallen?

In meiner Lesart schaut die Authentifizierung jetzt nur auf "/" und "/fs.html". Nichts anderes. Greift nicht pauschal auf alle Seiten.