Ich habe den ESP8266-01M in einen Projekt von mir am Start - bin jetzt
aber an einen kuriosen Problem angelangt wo ich nicht wirklich den
root-Cause ermitteln kann.
Das Ziel soll es sein zur-Runtime hin mittels Webserver Request die WiFi
Credentials ändern zu können - sollten dies u.U. falsch sein, oder das
WiFi nicht vorhanden sein wird automatisch ein Fallback AP gestartet.
Das Funktioniert soweit - leider so wie es der Teufel will nutze ich
eine malloc() Funktion um dynamische Speicher zu reservieren.
Die Funktion für das WiFi funktioniert mit z.B. _size=5 Wunderbar!
Bereits beim wechseln auf _size= 10 kommt es zu Probleme.
Das Problem besteht darin, dass die WiFi credentials erfolgreich
upgedated werden (ob richtig oder falsch --> egal!) Der ESP8266 fällt
zurück und der AP startet --> soweit auch OK! Der AP ist auch
ersichtlich in den Netzwerkschnittstelen eines Clients (Smartphone,
Computer) beim Versuch zu verbinden....passiert dann einfach nix mehr!
Keine Ausgaben mehr am Serial Monitor, keine Verbindung...einfach nix!
So als ob das ganze Gewerk einfach einfriert so wie es ist. Es kommt zu
meinen Verwundern auch zu keinen Absturz des ESP's. Irgendwann nach ca.
5min ist wieder der AP ersichtlich, ich kann ich mich wieder Verbinden
aber es ist wieder keine Verbindung möglich. Das ganze Spiel kann man
dann länger so treiben aber es kommt zu keinen Erfolgreichen
Verbindungsaufbau und Serial.println() liefert keine Infos mehr. Der ESP
bleibt bis ich ihn Resette so stehen! Mittels getfreeHeap() konnte ich
ca. 20kB freien Speicher Feststellen im Status von _size=5. "Sollte"
reichen!
Frage:
Beim Wechsel von STA zu AP Mode zur Runtime hin: Muss ich bzw. kann ich
hier den WiFi Stack noch irgendwie freigeben? Kann das Eventuell daran
liegen?
Der "freeze" ohne Absturz: Wie kann ich u.U. diesen Aufheben? Im Netz
lese ich des öfteren von "yield()" sozusagen als ultimatives
Laserschwert für all Probleme des ESP8266. Mein Versuch diese Funktion
zu implementieren scheiterte allerdings IMMER in einen Absturz es ESP's
und machte das Laufzeit verhalten eher schlechter als besser!
Die Funktion zum Speicher reservieren ist:
Johannes R. schrieb:> Beim Wechsel von STA zu AP Mode zur Runtime hin: Muss ich bzw. kann ich> hier den WiFi Stack noch irgendwie freigeben? Kann das Eventuell daran> liegen?
Nein
> Der "freeze" ohne Absturz: Wie kann ich u.U. diesen Aufheben?
Durch einen Hardware reset
Zeige mal ganz detailliert, wie die Stromversorgung des Moduls gebaut
hast. Ich will Fotos vom Aufbau sehen, vom man die Leitungen bis zur
Quelle (Netzteil?) sehen kann.
Steve van de Grens schrieb:> Durch einen Hardware reset>> Zeige mal ganz detailliert, wie die Stromversorgung des Moduls gebaut> hast. Ich will Fotos vom Aufbau sehen, vom man die Leitungen bis zur> Quelle (Netzteil?) sehen kann.
Danke Steve für deine Rückmeldung!
Mit einen Bild kann ich dir derzeit nicht dienen - es wird auch nicht
viel Aussagekraft haben! Ich betreibe den USB-Prommer mit ESP01 direkt
an einen Microsoft Surface Pro 5 USB 3.0-Port.
Ein Hardware Reset kann doch nicht des Rätsels Lösung sein auf Dauer?
mfg
Johannes R. schrieb:> Die Funktion für das WiFi funktioniert mit z.B. _size=5 Wunderbar!> Bereits beim wechseln auf _size= 10 kommt es zu Probleme.
Es ist unklar wie systemParameters.numLEDlenght befüllt wird, d.h. ob
der Wert zur Compile-Time bekannt ist oder nicht und ob er sich zur
Runtime jemals ändern könnte.
Die Funktion updateLength() fehlt im obigen Code Listing.
Passt die Reihenfolge der Aufrufe von updateLength() und
initializeArray()?
Eine Analyse ist immer schwierig, wenn wichtige Code-Teile fehlen.
Warum gibst du den Speicher frei und allokierst gleich wieder neuen? Das
kann zur memory fragmentation führen und das ist genauso schlimm wie ein
memory leak. Solange sich die Größe nicht ändert ist das unnötig.
Warum allokierst du den Speicher überhaupt dynamisch und verwendest
nicht ein globales statisches Array? Probier mal den Speicher als
globales statisches Array ggf. mit der maximalen Größe anzulegen.
Michael
Michael D. schrieb:> Es ist unklar wie systemParameters.numLEDlenght befüllt wird, d.h. ob> der Wert zur Compile-Time bekannt ist oder nicht und ob er sich zur> Runtime jemals ändern könnte.
Der Wert von numLEDlenght wird aus den nvs gelesen/geschrieben.
Entschuldige die Unanehmlichkeit!
Das war noch aus einer Debug funktion (Test: vor ändern des WiFi Modus
-->> Speicher komplett freigeben ->> danch wieder neu anlegen) Der Code
ist so von der Funktion nachstehend richtig gestellt.
Michael D. schrieb:> Warum gibst du den Speicher frei und allokierst gleich wieder neuen? Das> kann zur memory fragmentation führen und das ist genauso schlimm wie ein> memory leak. Solange sich die Größe nicht ändert ist das unnötig.>> Warum allokierst du den Speicher überhaupt dynamisch und verwendest> nicht ein globales statisches Array? Probier mal den Speicher als> globales statisches Array ggf. mit der maximalen Größe anzulegen.
naja, die Idee ist eben zur Laufzeit hin das Array nur mit der
entsprechenden Größe anzulegen und nicht mehr. Der Hintergrund davon war
eben genau solche Performance Probleme zu umgehen. Wenn diese Funktion
aufgerufen wird, wird sich auch zu 100% die Größe des Array's ändern.
Michael D. schrieb:> Warum allokierst du den Speicher überhaupt dynamisch und verwendest> nicht ein globales statisches Array? Probier mal den Speicher als> globales statisches Array ggf. mit der maximalen Größe anzulegen.
Achso: Und ich wollte eig. Speicher im globalen sketch sparen um OTA
Funktion zu ermöglichen!
Beim ESP8266 habe ich zumindest bei Arduino mal gelesen, daß es für
seine Funktion kritisch ist, daß die interne Firmware alle paar ms
Prozessorzeit bekommt, da er sonst abschmieren kann. Keine Ahnung, ob
das beim hier gewählten Weg der Programmierung eine Rolle spielt, aber
vielleicht ein Denkansatz.
Johannes R. schrieb:> Mit einen Bild kann ich dir derzeit nicht dienen
Benutzt du diesen Programmieradapter zur Stromversorgung?
Erfahrungsgemäß laufen die Module damit nicht stabil, weil der
Spannungsregler nicht genug Strom liefert, um die Funkschnittstelle zu
versorgen. Bedenke das, wenn dein Fehler sporadisch auftritt.
Wenn er 100% nachvollziehbar ist, und ein anderer Sketch der WLAN aktiv
nutzt, einigermaßen stabil läuft, dann ist der Adapter wohl nicht deine
Problemursache.
> Ein Hardware Reset kann doch nicht des Rätsels Lösung sein auf Dauer?
Wenn ein Computer mit einer Fehlfunktion abstürzt dann muss man ihn
resetten. Daran führt kein Weg vorbei. Viele Fehler erkennt der Watchdog
im ESP Chip automatisch und löst dann einen Reset aus. Leider klappt das
aber nicht immer.
Ben B. schrieb:> Beim ESP8266 habe ich zumindest bei Arduino mal gelesen, daß es für> seine Funktion kritisch ist, daß die interne Firmware alle paar ms> Prozessorzeit bekommt, da er sonst abschmieren kann. Keine Ahnung, ob> das beim hier gewählten Weg der Programmierung eine Rolle spielt, aber> vielleicht ein Denkansatz.
Ja, das spielt eine Rolle. Die delay() Funktion macht es einem hier
allerdings leicht, denn sie enthält einen Aufruf von yield().
Der Nachteil dieser schön einfachen Methode ist, dass man mehr Stack
braucht, als wenn man so etwas über endliche Automaten (ohne delay)
programmiert. Und Stack hat der ESP nicht viel. Etwa 5 kB sind für den
Sketch nutzbar.
Steve van de Grens schrieb:> Benutzt du diesen Programmieradapter zur Stromversorgung?
Derzeit ja um mit dem Serial-Monitor arbeiten zu können und die Embedded
Tests auf der Hardware durchführen zu können. Später werde ich dann den
ESP8266 in eine Adapterplatine betreiben und mit einen 5V Smartphone
Ladegerät betreiben.
Wie du bereits gesagt hast - ich vermute nicht dass es am Netzteil und
der Spannungsversorgung liegt!
Steve van de Grens schrieb:> Erfahrungsgemäß laufen die Module damit nicht stabil
Nur als Frage nebenbei (möchte aber nicht vom Thema ablenken):
Was würdest du mir für einen Prommer empfehlen welcher genug Strom
liefert um die ESP8266 ausreichend und stabil zu versorgen?
mfg
Johannes R. schrieb:> Was würdest du mir für einen Prommer empfehlen welcher genug Strom> liefert um die ESP8266 ausreichend und stabil zu versorgen?
Ich kenne keinen guten Adapter. Zum programmieren reicht der obige ja
auch, nur halt nicht für stabilen Betrieb.
> AdapterPCB.jpg
Ob der besser ist, kann ich nicht einschätzen. Spontan fehlt mir da ein
"dicker" Elko mit 100 oder 200 µF direkt an VCC und GND vom ESP Module.
Kann man leicht ergänzen.
Der Spannungsregler scheint dieser zu sein:
https://datasheet.lcsc.com/lcsc/2111081830_Shenzhen-Fuman-Elec-4A2D_C2832127.pdf
Die 3,3V Version ist für maximal 500mA spezifiziert, das reicht.
Johannes R. schrieb:> Wie du bereits gesagt hast - ich vermute nicht dass es am Netzteil und> der Spannungsversorgung liegt!
Die meisten "Absturzprobleme" die mir bekannt sind, liegen beim ESP8266
an einer schlecht dimensionierten Versorgungsspannung. Meine ESP8266-01
Projekte erhalten alle einen eigenen Regler (AMS1117-3,3) für die
3,3Volt. Und entsprechende Elkos für die Stromspitzen.
Johannes R. schrieb:> Achso: Und ich wollte eig. Speicher im globalen sketch sparen um OTA> Funktion zu ermöglichen!
Um für alle Anforderungen genügend Platz zu haben, wurde auf meinen
ESP-01s der Speicherbaustein gewechselt. Damit habe ich auf dem ESP 4MB
zur Verfügung.
Das ist ohne den vollständigen Quelltext kaum nachvollziehbar. Wo du den
"Endlos langen Output" erwähnst: Schau dir mal diese Hinweise an:
http://stefanfrings.de/esp8266/index.html#fallstricke
Eventuell dauert die Ausgabe zu lange oder du hast die String Klasse
(unbewusst) auf eine Weise verwendet, die den Speicher fragmentiert.
Diesen Blog von stefanfrings kenne ich soweit, habe jetzt auch in den
längern Loops überall ein yield() eingebaut um sicher zu gehen dass es
nicht an zuwenig Rechenressourcen für die firmware liegt.
Steve van de Grens schrieb:> Eventuell dauert die Ausgabe zu lange oder du hast die String Klasse> (unbewusst) auf eine Weise verwendet, die den Speicher fragmentiert.
Hmmm.....einfach nochmal für mein Verständnis zum Thema string:
Ich nehme jetzt nochmal willkürlich eine Funktion aus meinen Code raus:
1
StringSSEWrapper::toStringIp(IPAddressip){
2
Stringres="";
3
for(inti=0;i<3;i++){
4
res+=String((ip>>(8*i))&0xFF)+".";
5
}
6
res+=String(((ip>>8*3))&0xFF);
7
DEBUG_SERIAL.print("toStringIP: ");
8
DEBUG_SERIAL.println(res);
9
returnres;
10
}
Ich muss ja mit den String "res" am Ende der Funktion nichts mehr machen
vor "return" oder doch?
Warum frage ich das: Ich hatte mal ein Thema auf einen WindowsCE
basierten HMI wo von seitens Support eine definitve Anweisung gekommen
ist ich muss lokale Variablen am Ende einer Funktion immer "Bewusst"
zerstören um hier den Speicher freizugeben!
Johannes R. schrieb:> Ich nehme jetzt nochmal willkürlich eine Funktion aus meinen Code raus:
Ich kann nicht einschätzen, ob dein Code den Speicher fragmentiert. Das
hängt ganz stark davon ab, wie der Speicher vor dem Aufruf der Funktion
aussah (kann der belegte Speicher vom String vergrößert werden, oder ist
ein neuer Block nötig?) und was danach mit dem String gemacht wird.
Ich wollte nur darauf hinweisen, dass das ein möglicher Fehler ist.
Wirklich herausfinden wirst du es nur, wenn du den Heap zur Laufzeit
analysierst. Der ESP8266 hat aber leider keinen Debugger-Anschluss.
Ja OK danke dir für den Input!
Das der keinen Debugger nutzen kann nervt mich mittlerweile total!
Steve van de Grens schrieb:> Ich kann nicht einschätzen, ob dein Code den Speicher fragmentiert.
Wie würde den so etwas aussehen? Ich habe jetzt sämtliche malloc // free
Befehle entfernt.
Johannes R. schrieb:> Das der keinen Debugger nutzen kann nervt mich mittlerweile total!
Einer der Vorteile des ESP32, den kann man debuggen :-)
Michael
Johannes R. schrieb:> Wie würde den so etwas aussehen?
Ich bin jetzt unsicher, ob ich deine Frage richtig verstanden habe. Die
Erklärung wie das aussieht war schon in dem verlinkten Artikel:
https://www.mikrocontroller.net/articles/Heap-Fragmentierung
Ich vermeide die funktions-übergreifende Nutzung von String Variablen.
Die Standard C Bibliothek enthält genug alternative Funktionen, um auf
Strings verzichten zu können und stattdessen char[] zu benutzen. Das ist
zwar umständlicher, aber dann hast du die Speicherverwaltung besser
unter Kontrolle. Denn die Standard C Funktionen für char[] allokieren
nicht im Hintergrund Speicher, wie es die String Klasse tut.
Ohne die String Klasse kannst du allerdings nicht mehr ganz so einfach
einen String (bzw eine Zeichenkette) zurück geben. Ich würde dem Muster
der C Bibliothek folgen, zum Beispiel wie bei snprintf(). Sprich: Der
Aufrufer von SSEWrapper::toStringIp() müsste einen Puffer bereitstellen,
der dann von der Methode gefüllt wird. Etwa so:
So entfällt die dynamische Nutzung des Heap. Die Variable ipStr gehört
zum Stack der aufrufenden Funktion und wird von ihr (beim Verlassen)
auch wieder freigegeben.
Nachtrag: falschen Link korrigiert
Johannes R. schrieb:> Ich muss ja mit den String "res" am Ende der Funktion nichts mehr machen> vor "return" oder doch?
Das sollte aus C++ Sicht so passen, sofern die String-Klasse damit
korrekt umgeht. Es
> Warum frage ich das: Ich hatte mal ein Thema auf einen WindowsCE> basierten HMI wo von seitens Support eine definitve Anweisung gekommen> ist ich muss lokale Variablen am Ende einer Funktion immer "Bewusst"> zerstören um hier den Speicher freizugeben!
Für C++ Objekte welche in ihrem Destruktor den von ihnen allokierten
Speicher korrekt freigeben musst du das nicht machen.
Beim Suchen nach der Fehlermeldung "Fatal exception
29(StoreProhibitedCause):" habe ich nur Beispiele mit
Speicher-Überläufen gefunden, z.B. folgenden:
https://stackoverflow.com/questions/76030246/calling-a-routine-on-esp8266-decreases-progressively-the-free-heap
Du verwendest nicht zufällig wifi_softap_get_station_info() oder
ähnliche Funktionen deren Rückgabewert explizit freigegeben werden muss?
Da wir den kompletten Code nicht sehen, ist das nur ein Blick in die
Glaskugel.
Michael
Steve van de Grens schrieb:> void SSEWrapper::toStringIp(char* buffer, int maxLen, IPAddress> ipAddress) {> ...> }> void aufrufer() {> IPAddress ipAddress = ...;> char buffer[20];> sseWrapper.toStringIp(buffer, sizeof(buffer), ipAddress);> // buffer contains the IP address now> }
Ich bin nun dazu übergegangen und habe so ziemlich jede String operation
durch ein char Puffer ersetzt bzw. ersetzen können. Ich stelle jetzt
global 3 Puffer zur Verfügung mit denen ich arbeite. Das Programm hat
sich vom Sketch Size her fast gar nicht geändert. Die Stabilität hat
sich wenn ihr mich fragt verbessert - die Responsezeit von einem SSE
Reqeuest allerdings etwas verringert aber damit kann ich leben! Nach wie
vor erhalte ich abstürze beim umschalten vom STA Mode -->> AP Mode -->>
STA Mode des ESP!
Michael D. schrieb:> Du verwendest nicht zufällig wifi_softap_get_station_info() oder> ähnliche Funktionen deren Rückgabewert explizit freigegeben werden muss?
Hmm....nicht das ich wüsste!
Ich Scanne nach Erreichbaren Netzwerken beim Umschalten vom STA Mode auf
den AP Mode und nutze die WiFi.scanNetworks(true, true); Methode sowie
die WiFi.scanDelete(); Methode sollten die Elemente nicht mehr gebraucht
werden.
Mir ist noch etwas anderes in den Sinn gekommen:
Könnte es sein, dass ich mir mit der Verwendung von Preferences.h den
Speicher Zustelle?
Ich nutze die Funktion zum Speichern von Parametern
1
preferences.putInt(RED_KEY,systemParameters.r);
2
preferences.putInt(GREEN_KEY,systemParameters.g);
3
preferences.putInt(BLUE_KEY,systemParameters.b);
könnte dies Dazu führen dass ich Elemente überschreibe und mir so die
WiFi Funktion "unnutzbar" mache?
Generell nochmal die Frage: Kann ich für diese Preferences eine Partiton
einrichten wo die Datenliegen? Ich komm da aber nciht ganz weiter wie
ich eine Custom Partition Tabelle einrichte wo ich einerseits die .html
File Speichere für den Webserver und anderer Seits die NVS Partiton
anlege für die preferences sowie Ota Funktionalität nutzen möchte
(Between: neue Flash Chips mit 4MB sind bestellt damit ich genug speiche
habe für das Testen!)
Johannes R. schrieb:> Könnte es sein, dass ich mir mit der Verwendung von Preferences.h den> Speicher Zustelle?
Normalerweise nicht, da du diese Daten in einem anderen Bereich
(LittleFS) speicherst, als deinen Sketch.
Wenn du mit der Arduino IDE arbeitest, kannst du auch die
Speicherzuordnung anpassen.
Bei anderen Systemen musst du schauen.
Dieter S. schrieb:> Normalerweise nicht, da du diese Daten in einem anderen Bereich> (LittleFS) speicherst, als deinen Sketch.> Wenn du mit der Arduino IDE arbeitest, kannst du auch die> Speicherzuordnung anpassen.> Bei anderen Systemen musst du schauen.
Hm, ja stimmt.
Unter PlatformIO nutzt man hier die Linker Scripts um die
Speicherzuordnung festzulegen. Dabei nutze ich das derzeit noch das
"esp8266.flash.1m128.ld" Linkerskript. (Wird geändert wenn ich den 4MB
Flash Chip setze!)
Wobei mir hier immer noch Schleierhaft ist wo die Daten welche ich
in/aus dem NVS Storage beziehe dann "rauskommen"? Ist das die eeprom
Partition dann mit 20KB?
Ich glaube ja mittlerweile generell, dass ich ein falsches Linkerskript
verwende oder? Wo wird denn in diesem Linkerskript der OTA Speicher
reserviert? Ich weiß bei Arduino gibt es eine Auswahl mit OTA und
LittleFS size auszuwählen...