Ich weiss, man sollte die Arduino Strings meiden, aber ich hab schnell
einen Webserver einbauen wollten und dafür Beispielcode angepasst.
Hat auch soweit ganz gut geklappt, nur im Zuge der Weiterentwicklung
ging es dann irgendwann doch nicht mehr gut!
Das Programm blieb nach einem Loop-Durchlauf einfach hängen. Serielle
Debug-Ausgaben und ein paar Änderungen haben bewirkt dass es mal 2 oder
3 loops gut ging, aber sonst wurde es nicht besser und ich kam dem
Problem nur sehr rudimentär auf die Schliche. Einmal lief die loop
dauerhaft durch, aber nach ein paar Durchläufen wurde die zyklische
serielle Ausgabe minimal korrumpiert. Ein Zeichen hat geändert (und
blieb dann so).
Mit meiner nicht all zu großen Erfahrung hab ich auf ein Speicherproblem
getippt. Irgendwo wird in einen Bereich geschrieben wo Programmcode
steht...
Hab zwischendurch alles in Atmel Studio importiert um debuggen zu
können. Mit dem Compiler tritt das Problem erst gar nicht auf!
Also zurück zur Arduino IDE...
Ich konnte es dann auf den Aufruf vom Webserver reduzieren, konkret auf
die String-Definitionen in der Funktion (alles andere auskommentiert).
Da ich ein Speicherproblem vermute und von der Anfälligkeit der String
weiss war das mein erster Verdächtiger.
Als ich dann die String-Definitionen in der Datei global gemacht hab ist
das Problem verschwunden!
Erklären kann ich es mir nur so halb: Dadurch dass die Strings lokal
waren hat das das Speichermanagement durcheinander gebracht und der µC
hing irgendwo fest.
Hat mich ein gutes Stündchen (oder mehr) gekostet, deswegen würde mich
interessieren ob ich mit meiner Vermutung richtig liege?
Bob A. schrieb:> Erklären kann ich es mir nur so halb: Dadurch dass die Strings lokal> waren hat das das Speichermanagement durcheinander gebracht und der µC> hing irgendwo fest.
An den lokalen Strings liegt das sicher nicht. Du hattest wahrscheinlich
einen Speicherueberlauf. Oder Serial.print in einer ISR.
leo
Bob A. schrieb:> deswegen würde mich> interessieren ob ich mit meiner Vermutung richtig liege?
Keine Ahnung, was du da falsch gemacht hast....
Und zeigen tust du es ja nicht.
Als wenn dich eine fundierte Problemanalyse überhaupt nicht
interessieren würde.
> So viel Text, so wenig Code...
Meinst du meinen Beitrag, oder dass ich im sketch viel Text (html-Seite)
und wenig Code habe?
Das Ganze ist recht umfangreich und geht über mehrere Dateien. Da wusste
ich nicht was ich alles hier zeigen soll. Was für ein Teil wäre denn
interessant zu sehen?
leo schrieb:> Bob A. schrieb:>> Erklären kann ich es mir nur so halb: Dadurch dass die Strings lokal>> waren hat das das Speichermanagement durcheinander gebracht und der µC>> hing irgendwo fest.>> An den lokalen Strings liegt das sicher nicht. Du hattest wahrscheinlich> einen Speicherueberlauf. Oder Serial.print in einer ISR.>> leo
Keine ISR, hab wirklich nur die String-Definitionen in der Datei
verschoben und sonst nix geändert
Bob A. schrieb:> Meinst du meinen Beitrag, oder dass ich im sketch viel Text (html-Seite)> und wenig Code habe?
Deinen Beitrag, wir können nichts prüfen, was wir nicht sehen.
Bob A. schrieb:> Das Ganze ist recht umfangreich und geht über mehrere Dateien. Da wusste> ich nicht was ich alles hier zeigen soll. Was für ein Teil wäre denn> interessant zu sehen?
Naja im Idealfall baust du ein Minimal-Beispiel, bei dem der Fehler
auftritt.
Wenn das nicht klappt, hilft's nichts, dann eben alles posten.
Arduino Fanboy D. schrieb:> Bob A. schrieb:>> deswegen würde mich>> interessieren ob ich mit meiner Vermutung richtig liege?>> Keine Ahnung, was du da falsch gemacht hast....> Und zeigen tust du es ja nicht.>> Als wenn dich eine fundierte Problemanalyse überhaupt nicht> interessieren würde.
Es ist halt recht viel Code, dachte das sprengt den Rahmen zum Zitieren
dann hier...
Bob A. schrieb:> Es ist halt recht viel Code, dachte das sprengt den Rahmen zum Zitieren> dann hier...
also her damit, sonst ist der Speicher für den Thread verschwendet.
Bob A. schrieb:> Es ist halt recht viel Code, dachte das sprengt den Rahmen zum Zitieren> dann hier...
Einfach mal die Scheuklappen ablegen und über das Eingabefenster
schauen:
Antwort schreiben
Wichtige Regeln - erst lesen, dann posten!
Groß- und Kleinschreibung verwenden
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
usw.
Falls wir hier von einem ESP8266 reden:
Lokale Variablen liegen auf dem Stack, der sehr viel kleiner ist, als
der Speicher für globale Variablen. Siehe
http://stefanfrings.de/esp8266/index.html#fallstricke
Das ist bei Arduino mit AVR ganz anders, da darf der Stack den gesamten
freien RAM nutzen.
Peter D. schrieb:> Einfach mal die Scheuklappen ablegen
waren in meinem Fall wohl bis vor die Augen gerutscht - sorry!
Das Projekt als zip hab ich angehängt - ein wenig eingestampft
So wie es ist erden einmal die Sensorwerte ausgegeben und es hängt.
Schiebe ich die 3 String-Definitionen aus handleWebRequest nach oben in
die Servers.h (global definiert) dann läuft es durch
Ah, ok, war erstaunt da ich das schnell fand ;-)
Was meinst du mit Puffer?
Eingangspuffer für Anfragen an den Webserver?
Das Phänomen tritt auch mit abgezogenem LAN-Kabel auf.
Stefan ⛄ F. schrieb:> Programmcode gehört in *.c Dateien, nicht in *.h Dateien.
In der Arduino-IDE ist das alles etwas sonderbar...
Stefan ⛄ F. schrieb:> Welche "string definition" ist denn die eine, um die es geht?
Stefan ⛄ F. schrieb:> Warum speicherst du Fließkommazahlen in Integer Variablen?
Das waren mal floats... Ist mir wohl entgangen das mit zu ändern
Krass dass es da kein Gemecker vom Compiler gibt (-Wall)
> Krass dass es da kein Gemecker vom Compiler gibt (-Wall)
Vermutlich gilt für die Null eine Ausnahme.
Mir ist aufgefallen, dass du eine größere Menge Debug Meldungen
eingebaut und wieder auskommentiert hast. Anstatt sie immer wieder
mühsam zu aktivieren und zu deaktivieren, könntest du ein Makro
verwenden. Zum Beispiel:
1
#ifdef DEBUG_MESSAGES
2
printf(...);
3
#endif
Und dann schreibst du in deiner *.ino Datei ganz oben noch vor dem
Include:
Kurzer Ausflug ins offtopic
Stefan ⛄ F. schrieb:> Mir ist aufgefallen, dass du eine größere Menge Debug Meldungen> eingebaut und wieder auskommentiert hast. Anstatt sie immer wieder> mühsam zu aktivieren und zu deaktivieren, könntest du ein Makro> verwenden. Zum Beispiel:>>
1
>#ifdefDEBUG_MESSAGES
2
>printf(...);
3
>#endif
4
>
>> Und dann schreibst du in deiner *.ino Datei ganz oben noch vor dem> Include:>>
1
>#defineDEBUG_MESSAGES
2
>
>> oder eben nicht, wenn du sie deaktvieren willst.
So ähnlich hab ich das auch schon Stellenweise gemacht:
Find ich irgendwie angenehmer da man nur eine Zahl ändern muss (oder
halt true und false)
Und ich kann es auf mehrere debug-Ausgaben aufteilen die ich separat
ein- und ausschalten kann.
Bob A. schrieb:>> Welche "string definition" ist denn die eine, um die es geht?> String sHtmlHeader;> String sHtmlData;> String sHtmlFooter;> in Servers.h
Da sammelst du ziemlich viele Daten ein.
Globale Variablen liegen zusammen mit dem Heap an einem Ende des RAM.
Der Stack beginnt am anderen Ende. Beide wachsen aufeinander zu.
Prüfe mal, wie viel Platz dazwischen noch frei ist, nachdem du die
langen Strings befüllt hast.
https://playground.arduino.cc/Code/AvailableMemory/
Ich weiß nicht, wie viel RAM das Arduino Framework braucht. Die avr libc
braucht etwas weniger als 100 Bytes. Weniger als 100 Bytes sollten es
sicherheitshalber nicht sein.
Wenn du ganz knapp an der Grenze bist, könnte es Zufall sein, dass das
Verschieben der Variablen zum Erfolg führte.
Was ich noch prüfen würde: Wenn WebClient.println(x) fertig ist, wird x
danach noch gebraucht, oder kannst du den Speicherplatz danach anders
benutzen? Es könnte sein, dass WebClient.println() nur ein Ethernet
Paket (also einen Teil des Strings) sofort sendet und den Rest später im
Hintergrund abarbeitet. Greift er dann später noch auf die
Speicherzellen des Strings zu? Oder legt er gar eine weitere Kopie an?
Bob A. schrieb:> Stefan ⛄ F. schrieb:>> Programmcode gehört in *.c Dateien, nicht in *.h Dateien.> In der Arduino-IDE ist das alles etwas sonderbar...
Nö, da ist auch nicht anders. Die einzige Besonderheit ist, dass es
zusätzlich noch .ino Dateien gibt.
Da muss man dann aufpassen in welcher Reihenfolge das alles eingebunden
wird.
was schrieb:> Bob A. schrieb:>> Stefan ⛄ F. schrieb:>>> Programmcode gehört in *.c Dateien, nicht in *.h Dateien.>> In der Arduino-IDE ist das alles etwas sonderbar...>> Nö, da ist auch nicht anders. Die einzige Besonderheit ist, dass es> zusätzlich noch .ino Dateien gibt.> Da muss man dann aufpassen in welcher Reihenfolge das alles eingebunden> wird.
IRgendwas war, dass ich .c un .h nicht so eingebunden bekam wie ich es
gewohnt bin.
Plane eh den Umstieg auf Atmel Studio, dann wird das wieder ordentlich
gemacht
Bob A. schrieb:> In der Arduino-IDE ist das alles etwas sonderbar...
Nein!
Bob A. schrieb:> Das waren mal floats... Ist mir wohl entgangen das mit zu ändern> Krass dass es da kein Gemecker vom Compiler gibt (-Wall)
Teste:
Habe den Code mal überflogen....
Im I2C Teil kann ein Bufferüberlauf stattfinden.
Achte auf die 24 und 29.
Ansonsten stört mich die inflationäre Verwendung von String.
Vieles davon lässt sich mit "Streamig" vermeiden.
Unmengen an Definitionen in *.h Dateien.
No!
Im Vorraus: hab von Arduinos Web-API keine Ahnung, was mir aber
aufgefallen ist:
1
if(WebServer.available())
2
{
3
WebClient=WebServer.available();
4
...
5
WebClient.stop();
6
}
Das sieht verkehrt aus. Das .available() scheint einen Client
zurückzuliefern, der explizit mit .stop() wieder freigegeben werden muß.
Hier wird zweimal .available() aufgerufen aber nur einmal .stop().
Könnte ein Memory/Socket-Leak erzeugen (aber wie gesagt, keine Ahnung
von der API - die Doku ist, wie üblich bei Arduino, unterirdisch).
Die Beispiele im Netz haben immer diese Form:
1
WebClient=WebServer.available();
2
if(WebClient)
3
{
4
...
5
WebClient.stop();
6
}
Außerdem: auf einem Mikrocontroller mit 8kB RAM so mit Strings
rumzuaasen zeugt von ziemlicher Blauäugigkeit ...
Bob A. schrieb:> Krass dass es da kein Gemecker vom Compiler gibt
Bei euch Arduino-Hirnis schüttelt der Compiler nur leise den Kopf. Das
Meckern hat er aufgegeben.
Arduino Fanboy D. schrieb:>
> Im I2C Teil kann ein Bufferüberlauf stattfinden.> Achte auf die 24 und 29.
Oha! Danke! Böser, vermeidbarer Fehler!
> Ansonsten stört mich die inflationäre Verwendung von String.
Wie gesagt, bin mir bewusst dass die Arduino Strings Probleme machen
können. Ich wollte nur schnell eine Übersicht der Werte bauen. In der
definitiven Fassung wird der eh nicht mehr drin sein.
> Vieles davon lässt sich mit "Streamig" vermeiden.
Beziehst du dich auf die Stream-Klasse der Arduinos? Nur dass ich weiss
wo ich lesen soll
> Unmengen an Definitionen in *.h Dateien.> No!
Was ist daran schlecht? (Sorry, bin halt nur Hobby-Programmierer)
foobar schrieb:> Das .available() scheint einen Client zurückzuliefern, der explizit mit> .stop() wieder freigegeben werden muß.
Falls ich es richtig verstanden hat liefert der nur wieviel Zeichen im
Puffer stehen. Was das .stop macht erschliesst sich aus der Doku nicht
wirklich.
> Außerdem: auf einem Mikrocontroller mit 8kB RAM so mit Strings> rumzuaasen zeugt von ziemlicher Blauäugigkeit ...
War nur quick&dirty um schnell zum Ziel zu kommen. Dazu nutzt man ja
Arduinos ;-)
> Falls ich es richtig verstanden hat liefert der nur wieviel Zeichen im> Puffer stehen.
Die macht definitiv mehr (ja, der Name ist beschissen gewählt).
Verwende sie halt so, wie es in den Beispielen gemacht wird. Ist ja
nicht so, dass deine Variante irgendwie sinnvoll wäre ...
Bob A. schrieb:>> Vieles davon lässt sich mit "Streamig" vermeiden.> Beziehst du dich auf die Stream-Klasse der Arduinos?
Ich denke nicht. Er meinte damit, dass die ganzen String Verkettungen
überflüssig sind. Du kannst die Antwort nach und nach generieren während
sie übertragen wird. Das nennt man Streaming.
Alt:
Streaming, nicht streamig, hatte da ein n vergessen.
Streaming tuts mit allen Klassen, welche Stream oder Print
implementieren.
Also z.B. auch mit Serial, Wire, LCD, und eben mit allen Web Clients
und Servern.
usw.
Kurzfassung:
1
#include<Streaming.h> // Die Lib findest du selber
2
3
/*
4
sHtmlData = a;
5
sHtmlData += b;
6
sHtmlData += c;
7
sHtmlData += d;
8
sHtmlData += e;
9
WebClient.print(sHtmlData);
10
*/
11
WebClient<<a<<b
12
<<c<<d
13
<<e;
Und schon ist sHtmlData flüssiger als Wasser.
Überflüssig
das sieht mal sehr elegant aus!
Danke für den Tipp!
P.S.: das ist jetzt C++? Kenn mich eigentlich nur mit "normalem" C aus.
Wobei auskennen hier ein riesiger Begriff ist für die tatsächlichen
Kenntnisse ;-)
Bob A. schrieb:> das ist jetzt C++?
Ja, Arduino, und seine Libs, ist in Hauptsache C++
Der Core besteht viel aus C
Assembler, ist eher weniger vertreten.
Ich bin immer wieder verwundert, wie hier Leute runtergemacht werden,
Threads geentert werden...................
Und dann mal wieder so was wie hier.
Konkrete und fachmännische Hilfe, TOLL.
Hut ab.
>Danke
Solar schrieb:> wie hier Leute runtergemacht werden,
"Nicht wissen", ist nicht schlimm.
Aus der Ecke kamen wir alle mal.
Sobald "Nicht wissen" mit Arroganz und Uneinsichtigkeit im Verbund
vorkommen, gehts ab....
Arduino Fanboy D. schrieb:> Sobald "Nicht wissen" mit Arroganz und Uneinsichtigkeit im Verbund> vorkommen, gehts ab....
Gleiches gilt aber auch für "Wissen" in Verbund mit Arroganz und
Überheblichkeit...
Bis vor nicht allzu langer Zeit, hat die bloße Erwähnung des Wortes
"Arduino" einen reflexartigen shit storm ausgelöst.
Es hat etwas gedauert, die Meute zu dressieren(um konditionieren)... ;-)
Arduino Fanboy D. schrieb:> WebClient << a << b> << c << d> << e;>> Und schon ist sHtmlData flüssiger als Wasser.> Überflüssig
Was aber nicht heißen muß, daß es besser mit RAM haushaltet. Die Strings
a..e müssen ja trotzdem irgendwo zwischen gespeichert werden, bis die
Ausgabe abgeschlossen ist. Vermutlich wird der Platz auf dem Stack
angelegt.
Solar schrieb:> Arduino Fanboy D. schrieb:>> Es hat etwas gedauert, die Meute zu dressieren(um konditionieren)... ;-)> Und das findest Du nicht "arrogant"?
Kann man durchaus so sehen(wenn man denn ernstlich will).
Ich halte das eher für "provozierend".
Am Rande:
Als ich hier einschlug, noch mit anderem Namen.
Wollte einem Arduino Jünger helfen.
Und war über den Shistorm überrascht.
Wie ich dann merken musste, kein Einzelfall, sondern die Normalität.
Auch und gerne von den(einigen) Moderatoren befeuert.
Da habe ich mich hier abgemeldet und gedacht: Arschlöcher!
Andererseits, finden hier interessante und lehrreiche Dialoge statt.
Eigentlich nicht das, worauf ich verzichten wollte.
Also hingesetzt, versucht die Situation zu analysieren...
Viele hier sind, zumindest bei einigen Ansichten, das was man "frozen"
nennt. Eigentlich normal, hat wohl jeder irgendwo, aber für einen
fruchtbaren Dialog ist das nicht unbedingt förderlich.
Die Kernfragen waren:
Wie kommt man an die militanten Arduinohasser ran?
So dass sie sich selber, ihren Hass, überdenken...
Versucht man einen militanten mit Argumenten zu überzeugen, wird er
gegen argumentieren und damit die festigkeit seines "frozen" Status
trainieren.
Da war die Idee schnell geboren!
"Überzeichnen"
Mich selber "Arduino Fanboy" nennen.
Damit war ich mir der Aufmerksamkeit bewusst.
Habe mich auf "viel Haue" eingestellt.
(hab recht behalten)
Die Hasser kamen also von selber, darüber brauchte ich mir mit dem Trick
keine Sorgen machen.
Die Strategie:
Einfach nur Präsenz zeigen.
Möglichst korrekte Antworten geben.
Das Feuer, was sonst auch die anderen Arduino User trifft, auf mich
lenken.
Und sich ab und an mal einen der Hasser Fraktion greifen und mit
möglicht fachlichen Argumenten öffentlich zerlegen.
Das ist übrigens meist recht einfach...
Arduinohasser mögen meist kein C++, und haben sowieso wenig Ahnung von
der IDE und den sonstigen Zusammenhängen in dieser konkreten Welt. Wenn
dann noch Überheblichkeit, Arroganz usw. dazu kommt dann gelingt das um
so besser.
Logischer weise macht man sich so nicht nur Freunde!
Sieht man z.b. daran, dass viele meiner Beiträge negativ bewertet
werden, obwohl sie sachlich, fachlich und korrekt sind.
Ich schätze mal, das sind hauptsächlich Leute, denen ich so schon mal
derbe an die Karre gefahren bin(hoffentlich fachlich), und jetzt zu
feige sind sich öffentlich zu stellen, weil sie genau wissen, wie so
eine Konfrontation aus geht.
Solar schrieb:> Und das findest Du nicht "arrogant"?
Kann man durchaus so sehen(wenn man denn ernstlich will).
War ich das alleine?
Ganz sicher nicht.
Mittlerweile gehts immer mehr in diese Richtung:
Wo ich richtig liege, werde ich bestätigt, und wo ich falsch liege,
gibts Haue.
Ich denke mal, so soll es sein, in einem Forum!
Danke!
Peter D. schrieb:> Was aber nicht heißen muß, daß es besser mit RAM haushaltet. Die Strings> a..e müssen ja trotzdem irgendwo zwischen gespeichert werden, bis die> Ausgabe abgeschlossen ist. Vermutlich wird der Platz auf dem Stack> angelegt.
"besser" ist immer so eine Frage...
Es bieten sich folgende Möglichkeiten:
Globaler Buffer, Stack, Heap.
Mehr Möglichkeiten sehe ich nicht.
Denn durchs Ram muss es sowieso.
Dass Heap auf kleinen Arduinos die schlechteste Möglichkeit ist, setze
ich mal als Fakt fest.
Die vorher vom TE verwendete String Klasse, huddelt wie die bekloppte
mit Heap rum.
String::reserve() mildert das Problem.
Bleibt also noch global, oder Stack
Globale, sollte man vermeiden, wenn nahezu kostenlos möglich.
Hier scheint mir Stack angemessen und brauchbar.