Arduino Mega hängt wenn String nicht global definiert ist

OP #6305685
Lesenswert?

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?
Gast #6305691
Lesenswert?

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
OP #6305701
Lesenswert?

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
Gast #6305702
Lesenswert?

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.
OP #6305704
Lesenswert?

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...
#6305735
Lesenswert?

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.
OP #6305758
Lesenswert?

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
Angehängte Dateien:
OP #6305775
Lesenswert?

Peter D. schrieb:
> Mach die Strings global und schau die Compilerausgabe an:
>
1
> AVR Memory Usage
2
> ----------------
3
> Device: at90can128
4
> 
5
> Program:    6798 bytes (5.2% Full)
6
> (.text + .data + .bootloader)
7
> 
8
> Data:         58 bytes (1.4% Full)
9
> (.data + .bss + .noinit)
10
>

Sieht bei mir gaanz anders aus:

lokal:
1
Sketch uses 32938 bytes (12%) of program storage space. Maximum is 253952 bytes.
2
Global variables use 1774 bytes (21%) of dynamic memory, leaving 6418 bytes for local variables. Maximum is 8192 bytes.
3

4
global:
5
[pre]Sketch uses 32750 bytes (12%) of program storage space. Maximum is 253952 bytes.
6
Global variables use 1792 bytes (21%) of dynamic memory, leaving 6400 bytes for local variables. Maximum is 8192 bytes.
Gast #6305802
Lesenswert?

Welche "string definition" ist denn die eine, um die es geht?

Programmcode gehört in *.c Dateien, nicht in *.h Dateien.
1
unsigned int uiSensor01Intensity = 0.0;
2
unsigned int uiSensor01DiodeTemperature = 0.0;
3
unsigned int uiSensor01DiodeTemperature1 = 0.0;
4
unsigned int uiSensor01DiodeTemperature2 = 0.0;
5
unsigned int uiSensor01uCTemperature = 0.0;
6
unsigned int uiSensor05Intensity = 0.0;
7
unsigned int uiSensor05DiodeTemperature = 0.0;
8
unsigned int uiSensor05DiodeTemperature1 = 0.0;
9
unsigned int uiSensor05DiodeTemperature2 = 0.0;
10
unsigned int uiSensor05uCTemperature = 0.0;
11
unsigned int uiSensor07Intensity = 0.0;
12
unsigned int uiSensor07DiodeTemperature = 0.0;
13
unsigned int uiSensor07DiodeTemperature1 = 0.0;
14
unsigned int uiSensor07DiodeTemperature2 = 0.0;
15
unsigned int uiSensor07uCTemperature = 0.0;

Warum speicherst du Fließkommazahlen in Integer Variablen?
Gast #6305817
Lesenswert?

> 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:
1
#define DEBUG_MESSAGES

oder eben nicht, wenn du sie deaktvieren willst.
OP #6305821
Lesenswert?

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
> #ifdef DEBUG_MESSAGES
2
>   printf(...);
3
> #endif
4
>
>
> Und dann schreibst du in deiner *.ino Datei ganz oben noch vor dem
> Include:
>
>
1
> #define DEBUG_MESSAGES
2
>
>
> oder eben nicht, wenn du sie deaktvieren willst.

So ähnlich hab ich das auch schon Stellenweise gemacht:
1
#define WEB_SERVER_DEBUG 0
und dann immer so
1
if (WEB_SERVER_DEBUG)Serial.println("\n\rnew WebClient");

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.
Gast #6305826
Lesenswert?

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?
Gast #6305838
Lesenswert?

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.
OP #6305847
Lesenswert?

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
Gast #6305916
Lesenswert?

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!
Gast #6306098
Lesenswert?

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 ...
OP #6306498
Lesenswert?

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)
OP #6306499
Lesenswert?

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 ;-)
Gast #6306516
Lesenswert?

> 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 ...
Gast #6306544
Lesenswert?

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:
1
    sHtmlData  = a;
2
    sHtmlData += b;
3
    sHtmlData += c;
4
    sHtmlData += d;
5
    sHtmlData += e;
6
    WebClient.print(sHtmlData);

Neu:
1
    WebClient.print(a);
2
    WebClient.print(b);
3
    WebClient.print(c);
4
    WebClient.print(d);
5
    WebClient.print(e);
Gast #6306763
Lesenswert?

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
Gast #6306808
Lesenswert?

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
Gast #6306839
Lesenswert?

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)... ;-)
Gast #6306946
Lesenswert?

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!
Gast #6306958
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren