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!
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.
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?
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.
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.
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.
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.
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.
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.
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!
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?
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.
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.
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.
So,
habe das ganze heute mal versucht indem ich das Array Global definiere und ohne free()/malloc().
Der AP startet Einwandfrei und es ist soweit auch alles gut und genau so wie es sein soll.
Wenn ich mich mit einen Client darauf Verbinde, kommt ein Endlos langer Output und der WiFi AP wird nicht mehr angezeigt. Der ESP8266 verfängt sich irgendwo und es passiert wie beschrieben wieder "nix-mehr"! Ich habe den Output mal angeführt:
Ganz zum schluss kommt dann nach ca. 5min Wartezeit irgendwann mal "Fatal exception 29(StoreProhibitedCause):" Kann jemand mit dem etwas anfangen bzw. wie kann ich dieses Problem in den Griff bekommen?
Habe jetzt auch mal ganz am Ende der Liternei: "mac 985" erhalten!
Das ist der Output wenn ich im Setup auch setDebugOutput aktiviert habe...
1
DEBUG_SERIAL.begin(115200);
2
DEBUG_SERIAL.setDebugOutput(true);
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.
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!
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.
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.
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.
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!
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.
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!)
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.
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...
So, habe jetzt ewig probiert.
Memory Fragmentation kann ich definitiv ausschließen, da ich jetzt alles umgebaut habe auf char[] Array's und keine Strings mehr verwende.
Das Problem besteht nun ganz klar darin, dass im AP Mode nach Neustart des ESP8266 ein komplett unerwartetes Verhalten auftritt wo der AP nicht mehr ersichtlich ist.
Wenn ich zur Laufzeit hin im STA-Mode Starte -->> Umschalte auf den AP-Mode und dann wieder auf den STA-Mode schalte funktioniert das ganze OHNE Probleme!
Diese Wird im Setup ganz am Ende 1x aufgerufen und anschließend in der Loop geprüft ob sich der WiFi State changed. Hat jemand eine Idee? Manchmal kann ich beim crash auch beobachten, dass ein mac 684 Error kommt gefolgt von einen wdt Reset was aber NICHT IMMER der fall ist!