ESP32 --> Speicher-Probleme mit großen CharArrays?

Hi zusammen,

ich hab ein Problemchen mit einem Sketch, der auf einem ESP8266 gelaufen ist aber auf dem ESP32 nun Probleme bereitet. In dem Fall muss es aber zwingend ein ESP32 sein (d.h. die einfachere Lösung, zurück auf den ESP8266 zu gehen, scheidet aus).

Da ich aus der Vergangenheit gelernt habe, möchte ich gleich drauf hinweisen, dass ich auch mal im offiziellen ESP32 Forum von Espressif gepostet habe.

https://www.esp32.com/viewtopic.php?f=19&t=24952

Da ich dieses Forum aber sehr schätze, würde ich gerne auch parallel hier um Rat fragen.
Ich werde im Anschluss an diesen Post auch im anderen Forum noch drauf hinweisen, dass ich hier im Forum frage. Ich hoffe das ist ok.

Ich habe eine stark vereinfachte Variante des Sketches zusammengenagelt (siehe unten) und im Header jeweils die Bibliotheken des ESP8266 bzw. des ESP32 gepackt (damit man den Sketch schnell mit beiden testen kann).
Im Char Array hab ich, um diesen Beitrag nicht zu sprengen, nur eine Zeile als Blindtext reingepackt. Im angehängten ino-File ist da dann ein Blindtext mit ca. 10000 Zeichen drin.

Wenn man den Sketch für den ESP8266 kompiliert, wird, wenn man sich mit dem AP verbindet, alles angezeigt. Beim ESP32 wird auch alles fehlerfrei kompiliert aber nach dem Verbinden nur eine leere Seite angezeigt.

Erst wenn man die Anzahl der Zeichen des Arrays deutlich reduziert, wird was angezeigt.
Hat von euch jemand eine Erklärung dafür? Es kann doch nicht sein, dass das Nachfolgermodel weniger Speicher hat (falls es ein RAM Problem ist) als der ESP8266, oder?

Mit diesen zwei ESP32 Boards hab ich getestet:

In dem Fall müsste der Chip zwingend ein ESP32 sein, also dein Chipwechsel soll nicht die Lösung sein ;-(

Hier der Sketch (wäre nett wenn jemand von euch den mal auf einem ESP 32 testen könnte...

//Header für ESP32 Version - funktioniert nicht
#include <WiFi.h>
#include <WebServer.h>
WebServer server(80);

// Header für ESP8266 Version - funktioniert einwandfrei
//#include <ESP8266WiFi.h>
//#include <ESP8266WebServer.h>
//ESP8266WebServer server(80);

static const char indexPage[] PROGMEM = R"=====(
   Hier bitte zum Testen mindestens 10000 Zichen reinkopieren oder mein angehängtes Ino-File verwenden.
)=====";

void setup() {
  Serial.begin(115200);
  
  WiFi.softAP("Testserver1234", "123456789");
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(myIP);
  
  server.on("/", display_root);
  server.begin(); // Server starten
}

void loop() {
  server.handleClient();
  delay(100);
}

void display_root() {
  server.send(200, "text/html", indexPage);
}

Und hier der Sketch als Datei:

ramtest.ino.zip (4.1 KB)

1000 Dank für eure Hilfe schon mal :wink:

Der Compiler kann nicht erkennen, dass der String im Progmem liegt.

const char * httpType PROGMEM = "text/html";
void display_root() {
  server.send_P(200,  httpType, indexPage);
}

????

korrigiert

Hi @combie ,

aber warum funktioniert der gleiche Sketch wenn ich ihn auf dem ESP8266 laufen lasse?
Und warum funktioniert er, wenn ich den Char Array von 10000 Zeichen auf z.B. 500 Zeichen verkleinere?

Wie kann man dem Compiler denn sagen, dass der String im Progmem liegt?

Grüßle (und danke für deine schnelle Antwort)

Habe ich das nicht gerade eben schon schon gezeigt?
Und wenn das nicht klappt, dann hast du einen anderen
#include <WebServer.h>
Als meinen #include <WebServer.h>

Ach sorry :see_no_evil: ,

ich hatte mit meinen Glubschaugen das "_p" hinter dem server.send übersehen :wink:
Werd ich direkt nach Feierabend mal probieren.

Gibts dann da Unterschiede zwischen dem ESP8266 und dem ESP32? Denn auf dem ESP8266 funktionierts ja auch iohne das "_p" (auch wenns völlig wurst ist, wenns funktioniert).

Ich hoffe mal nicht :wink: Ich hab halt gestern das ESP32 über die Boardverwaltung hinzugefügt und dann direkt die Datei eingebunden. Wenn du große Lust hast, kannst du ja mal deine WebSerber.h an den Beitrag ranhängen... dann tausch ich die aus.

Aber probier erstmal das "_p" --> Danke dir :hugs:

Ach ja, habe auch was vergessen!
Das "text/html" muss dann auch im Progmem liegen
Habe ich oben korrigiert

1 Like

sind ja zwei verschiedene libs, vieleicht hat die ESP lib einen Parameter overload und die ESP32 eben eine separate Funktion mit _P ... aber dass solltest du selber in den jeweiligen .h Dateien auf deinem PC finden.

1 Like

Die ESP8266 Implementierung halte ich irgendwie für total krank:

  void send(int code, const char *content_type, const char *content) {
    send_P(code, content_type, content);
  }

Bei konstanten Strings wird automatisch davon ausgegangen dass sie im Flash liegen.

Ah, ok. Da wäre ich nicht drauf gekommen.
Ich (als Anfänger) gehe davon aus, dass server.send auf dem ESP32 das gleiche macht, wie server.send auf dem ESP8266... aber man lernt ja nie aus.

Auf jeden Fall danke. Probier ich nachher gleich aus.

Ich kann mir nicht vorstellen, dass es daran liegt. Weder der ESP32 noch der ESP8266 kennen PROGMEM, die ignorieren das einfach:
Aus pgmspace.h ESP32:

#define PROGMEM
#define PGM_P         const char *
...

und bei ESP8266:

#ifndef PROGMEM
#define PROGMEM
#endif

#ifndef PGM_P
#define PGM_P const char *
#endif
...

Auch die entsprechenden read-Funktionen pgm_read... werden per macros auf 'normale' Speicherzugriffe umgesetzt.

Bei Sketchen, die speziell für ESP8266/ESP32 geschrieben sind, kann man getrost auf das ganze PROGMEM und sein Umfeld verzichten. Das wird da nur aus Kompatibilitätsgründen überhaupt berücksichtigt.

1 Like

Hi,

@combie hast du den Sketch mal bei dir auf einem ESP32 mit 10000 Zeichen oder mehr probiert? Wenn's bei dir funktioniert hat liegts ja doch vielleicht irgendwie an meinem Board...wobei dann zwei Boards defekt wären was ja auch komisch wäre :thinking:

@MicroBahner hast du noch eine andere Idee wie man das Problem lösen könnte?
Es muss doch möglich sein auf dem ESP32 Wenn's auf dem ESP 8266 ohne Probleme funktioniert. Sonst könnte man ja keine großen Webseiten mit dem ESP32 realisieren.

Wieso? Du kannst die statischen Teile doch problemlos ins SPIFFS legen und die Daten über Fetch-API und JS austauschen.

Gruß Tommy

Nein, das habe ich nicht.
Wusste nur, dass es die *_P Methoden gibt, und die für solche Zwecke vorgesehen sind.
Hole mir meine Webseiten immer von SD Karte. Sind halt auch viele Bilder usw. dabei.
Ist leichter zu händeln.

Hallo,
Na ja, das bin ich bis gestern auch. Da habe ich versucht mit einem ESP32 einen Wert als pwm wert auszugeben, analogWrite(Pin ,wert). Ihr glaubt nicht wie oft ich mir die Zeile angesehen habe um den vermuteten Syntax Fehler zu finden.
Das wird beim ESP 32 völlig anders gemacht.
Heinz

@Tommy56 Die Dateien im SPIFFS zu lagern ist natürlich auch eine gute Option.

Dazu aber noch eine Frage:
Bekomm ich die Dateien nur über das ESP32 Filesystem-Plugin ins SPIFFS (also über den Data-Folder) oder geht das auch irgendwie über den httpupdateserver.

Hierüber hab ichs probiert, aber da sind ja nur Dateien vom Typ .bin erlaubt.

grafik

Denn wenn man irgendwo ein Gerät hat und will mal eine Datei aus der Ferne updaten (ohne IDE) muss das dann ja relativ komfortabel gehen.

Nichtsdestotrotz lässt mir mein anderes "Problem" keine Ruhe:
Warum geht das mit dem char array denn auf dem ESP8266 und nicht auf dem ESP32?

Beste Grüße
Daniel

Wie schon mehrfach gesagt: Das alles gibt es fertig bei Fips. Nur dort nachlesen musst Du selbst. Er hat einen Tab, da kannst Du problemlos die Dateien im Browser hochladen/löschen/anzeigen.

Dein anderes Problem wird eher Keinen interessieren, da das keiner braucht.

Gruß Tommy

Hallo,
Na ja , wenn Du im eigene Netz bist kannst Du aus der IDE heraus Werzeuge / ESP Sketch data Upload auch mittels OTA die Datei tauschen. Von außerhalb sollte das auch gehen wenn Du die IP des ESP freigegeben hast. Allerdings bootet der ESP dann neu. Der Fileserver von Fips ist natürlich da besser geeignet.
Heinz

Yes!
@combie :+1:
Dein Lösungsvorschlag funktioniert, habs grad getestet.

Nichtsdestotrotz wäre natürlich zu überlegen, ob das Ablegen der Dateien (oder zumindest teilweise) im SPIFFS die besserer Lösung wäre.

Aber für den Moment bin ich nun glücklich.

Wobei es aber vollkommen ausreicht, das '_P' an das 'send' anzuhängen:

  server.send_P(200, "text/html", indexPage);

Alles was mit PROGMEM zusammenhängt, kann man weglassen, das interessiert den ESP32 nicht.
Offensichtlich verhält sich das send_P anders, als das send, aber mit PROGMEM hat es nichts zu tun. Scheint nur ein Problem der Datenmenge zu sein.

1 Like

Manchmal juckt es einen dann ja, mal kurz in die Libs "einzutauchen" :wink: wobei ich Folgendes festgestellt habe (Irrtümer vorbehalten ...):

  • Die Umsetzung von send und send_P in den Bibliotheken ESP8266WebServer.cpp und WebServer.cpp sind sogar inklusive eines kleinen Typos im Kommentar ("asume" statt "assume") komplett identisch. Daran sollte es also nicht liegen ...
  • Die Verwendung des Flashspeichers beim ESP32 wird in der hardwarespezifischen Umsetzung in pgmspace.h festgelegt, in der allerdings alle Zugriffe mit _P auf die Standardfunktionen umdefiniert werden (wie z.B. memcpy_P zu memcpy) und auch die Unterscheidung nach _far und _near-Zugriffen aufgehoben wird, wenn ich die defines recht interpretiere.
  • Die Verwendung des Flashspeichers beim ESP8266 sieht dagegen tatsächlich anders aus. Das include bei esp8266/pgmspace.h verlinkt zu NewLib -xtensa pgmspace.h. Dort werden Funktionen z.T. in Assembler abgebildet, was die Analyse aufwendiger macht und daher wohl eher etwas für Spezialisten (oder lernbegierige Enthusiasten) ist ...

Für mich zumindest ist noch interessant, dass beim ESP32 als const definierte Daten anscheinend standardmäßig im Flash gespeichert werden; Quellen dazu const data -> Flash sowie auch hier const data -> Flash.

Ich vermute, dass die Differenz zwischen der Umsetzung auf ESP8266 und ESP32 auf der unterschiedlichen Umsetzung der Zugriffe in pgmspace zu finden sein wird. Das herauszufinden überlasse ich aber gerne einem/einer Dritten :wink: