Mega2560 RAM kaputt?

Hi, bin nun schon seit einigen auf der Suche nach einem blöden Fehler. Nach Stunden fehlerfreier Funktion kommt plötzlich ein ovf und er hängt.
Ist ein sehr umfangreicher Sketch, der seit nun schon Jahren im Dauer provisorisch Betrieb läuft.
Das Problem begann, als ich von dht22 auf bme280 mit adafruit lib umgestiegen bin. Die bme280 Routine läuft standalone tagelang ohne Probleme.
Ich habe viele debug Ausgaben drin, auch die Ausgabe von free Ram. Normalerweise so 3300 bytes free. Teilweise kam dann 22 bytes free und das wars, aufgehängt, manchmal noch eine ovf in der debug ausgabe von einer Variablen.
Er hängt sich auch immer an verschiedenen Stellen auf, zumindest lassen die debug Ausgaben darauf schließen. Aufgrund exzessivem Gebrauch von INTERVAL konnte ich jedoch bisher nicht sicher herausfinden, ob es vielleicht doch eine bestimmte Stelle im Sketch ist.

Kommentiere ich nun im loop genügend Programmteile aus, läuft alles. Unabhängig was ich deaktiviere, Hauptsache der Programmteil ist gross genug.

Der Umstieg auf bme280 fiel auch mit einem Tausch des Mega2560 zusammen. Daher kommt mir der Verdacht, ob evtl. die Hardware was hat.

Irgend eine Idee, was ich noch probieren kann?
Momentan hab ich leider keinen 2. Mega zum tauschen verfügbar.

Gibt es einen Testsketch, der das RAM prüft?

P.s.: Der Sketch ist dermaßen umfangreich, macht zunächst noch keinen Sinn, den zu posten. String Objekte werden nicht benutzt.

Normalerweise so 3300 bytes free.
...
dann 22 bytes free und das wars, aufgehängt,

Das riecht nach Heap-Stack Kollision.
Rekursion?
Memory Leak?

Aber wie immer, wer den Code geheim hält, muss selber suchen.

Tipp:
Der Fehler ist dort, wo du ihn bisher nicht gesucht hast.
Da, wo du noch nicht richtig hingeschaut hast.


Daher kommt mir der Verdacht, ob evtl. die Hardware was hat.

:o :o Natürlich. :o :o

Ich kann den Code schon posten, aber ich mach jede Wette, dass du ihn nicht komplett durchsiehst. Ich nehm später die persönlichen Daten raus und packe es in ein zip.

Rekursion benutze ich hier nicht.

Wie gesagt, es kam ja schon ovf, stack overflow anstatt des Variablenwertes bei der debug Ausgabe.
Richtig hingeschaut hab ich auf die Änderung beim einpflegen des bme280 codes. Der Rest blieb ja unberührt.

Hier nun der Sketch.
Hardware:
Mega
5100 Shield
18b20 sensoren
Sr04 Sensoren
bme280 Sensor
lcd 1602
lcd 2004
zig Taster
zig Relais, OK, LEDs

Wobei ovf ja nur heisst, dass die Zahl für die print Routine zu gross ist.

Edit: korrigierte pool.zip hochgeladen

pool.zip (25.8 KB)

ElEspanol:
Irgend eine Idee, was ich noch probieren kann?

nur was mir so auffällt, was zwar nicht die Ursache sein muss - aber sicher nicht förderlich ist:
Das F-Makro wendest du nicht konsequent an, und geschätzte 10% deiner Fixtexte stehen noch im RAM
Im Webclient machst du mir zu viele Ausgaben

aus

              client.print(F("<td align='center' bgcolor='#ff0000' width='200'>"));
              client.print(F("<span style='font-size:2em'>Pumpe</span>
"));
              client.print(F("<form method=get><input type='submit' name=4 value='ein' style='height:100px; width:140px; font-size:200%'>"));

kann man so machen

              client.print(F("<td align='center' bgcolor='#ff0000' width='200'>" 
                            "<span style='font-size:2em'>Pumpe</span>
" 
               "<form method=get><input type='submit' name=4 value='ein' style='height:100px; width:140px; font-size:200%'>"));

das bringt auch massive Einsparung in den zu übertragenden Bytes auf Netzwerk-Ebene

Die Routinen, in denen noch String Objekte sind, werden momentan nicht genutzt, werde es aber mit der Zeit noch umbauen.
Die paar Bytes wegen dem F Macro reissen es nicht raus, muss ich aber auch mal noch ändern. Sind aber noch über 3000 bytes frei.

Ich habe jetzt an vielen Stellen noch eine Routine angesprungen, die verschiedene Statusmeldungen ausgibt, wenn freememory<100. und noch mehr debug Ausgaben.

Nun läuft das Ding schon fast 24 Stunden ohne Fehler.

String lcdcorrecting(String fehler)

Referenzen solltest du inzwischen eigentlich kennen und wissen dass man Objekte i.d.R. nicht als Kopie übergibt

Der Compiler ist normal in der Lage die Kopie beim Rückgabewert wegzuoptimieren (Stichworte "copy elision" und "return value optimization"), aber das ist dann auch unnötig, da man den übergebenen String in der Funktion ändern kann

Evtl. muss man dann aber Teil-Strings zwischenspeichern. Ist also vielleicht nicht unbedingt so viel besser

Diese Altlasten in dem Sketch muss ich noch beseitigen, da sind teilweise noch Programmteile aus meinen Anfangszeiten drin. Ich finde es klasse, dass sich doch jemand diesen umfangreichen verschachtelten Code ansieht. Aber ich bin mir ziemlich sicher, dass das alles nicht die Ursache des Problems ist. Der Sketch lief jetzt das letzte Mal über 90.000 Sekunden, bis er sich verabschiedet hat. Vor dem Einbau der weiteren debug Massnahmen lief er wesentlich kürzer.

Und das freememory war IMMER gleich mit 3.306 oder so, deswegen glaube ich nicht an die String Theorie. Das Logfile hat fast 16 Megabyte. Werde das letzte Mb nachher mal hochladen. Die Meldung mit freememory = 22 bekomme ich nicht mehr. Vermutlich kackt er vorher ab.
Bevor ich nun weitere Änderungen mache, will ich den nächsten Absturz abwarten, was das Log sagt.

Und wenn alles nix hilft, aus einer anderen Installation testweise einen Mega entführen.

Ich muss gestehen, dass ich mir das auch angesehen habe, aber nicht durchblicke.
Offensichtliche Fehler kann ich so nicht entdecken.

Also habe ich nur ein paar allgemeine Aussagen für dich.

Du arbeitest viel mit Buffern, und füllen der Buffer.
Dort eine Bereichsüberschreitung, und es knallt.
Aus eigener leidlicher Erfahrung weiß ich, wie gut sich solche Fehler verstecken können.

availableMemory() sagt zwar wie groß der größte zu reservierende Block ist, sagt aber nichts über den Stackbedarf. Auch wenn ich/du keine offensichtliche Rekursion sehen, heißt es nicht, dass keine da wäre.

Tipp zu Verwendung der String Klasse:
Man kann in setup() dafür Platz reservieren, so dass die automatische Reservierung vermeidbar ist.

So, nun kam der heutige Absturz.

Log von heute (putty-verkurzt.log, ca. 8,5 Mbyte gross, keine Auffälligkeiten, bis dann am Schluss die Sache komisch wird.
Die "St=0^^^^^^^^^ ..... ^㠠 Free RAM: 3309" ist eine Zeile mit fast 200.000 Zeichen Länge
Die Meldung "Free RAM: 3309" ist eine andere serielle Ausgabe, wurde nicht zusammen mit der St= geschickt.

Nach millis=45.406.532, das sind ca. 15 Stunden

Log von gestern, putty1-verkurzt:
Auch hier die letzten Zeilen ewig lang. Absturz nach über millis=90.104.677, das sind ca. 30 Stunden

Die Hardware wurde nicht angefasst, sie steht remote in einem Technikraum.

putty1-verkurzt.log.txt (545 KB)

putty-verkurzt.log.txt (969 KB)

Ja, das riecht nach Bufferüberlauf, oder Stack Heap Kollision.

Ich hab jetzt mal ein char array mit 2000 Zeichen angelegt, so dass nur noch 1265 freememory da sind. Mal sehen, was passiert.

"Der Sketch verwendet 82170 Bytes (32%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 6755 Bytes (82%) des dynamischen Speichers, 1437 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.
Wenig Arbeitsspeicher verfügbar, es können Stabilitätsprobleme auftreten."

Ach, warum magst du mir nicht zuhören...?

Hier mal ein Beispiel, für einen potentiellen Bufferüberlauf.
Zumindest lässt du keinen Platz für das Null Byte, welches als Stringende unabdingbar ist.

Udp.read(incomingbuffer, sizeof(incomingbuffer));
Und der darauffolgende
pos = strstr(incomingbuffer, "#200M");
Kann Vollgas vor die Wand fahren.
Bzw., bis ins endlose, bis er ein zufälliges Nullbyte findet.

Tipp 1:
Udp.read() hat einen Rückgabewert, nutze diesen als Richtwert für das zu setzende Nullbyte. Dann sparst du dir auch das ewige löschen des Buffers.

Tipp 2:
Um Platz zu schaffen, für das Nullbyte, welches du beständig ausblendest:
-> Udp.read(incomingbuffer, sizeof(incomingbuffer)-1);


Soll ich noch mehr solcher Problemstellen suchen?

OK hier ist noch eine mögliche....
Ganz sicher bin ich mir noch nicht.

Aber mir scheint, als könnte:

void PString::write(uint8_t b)
{
  if (_cur + 1 < _buf + _size)
  {
    *_cur++ = (char)b;
    *_cur = '\0';
  }
}

ein Byte über das Ende hinausschreiben.
(muss ich mal in Ruhe testen)

Nachtrag:
Getestet, und funktioniert.
Also Entwarnung

Zwischenbericht:

mit den zusätzlich als Array definierten 2000 Byte läuft der Mega nun schon millis=106.707.528, was knapp 30 Stunden sind.

Die udp Einleseroutine habe ich mit Telegrammen von 7168 Byte beschossen, hat nichts ausgemacht.

Zum Testen: Setze später mal das 2000-Byte-Array ganz an den Anfang als erste Deklaration, so dass es zwar den Speicher verknappt, aber keinen Raum für Überlaufe schafft.

Gruß Tommy

Da habe ich es hingesetzt.

char speicherplatz_verschwenden[2000];

und mit

memcpy(speicherplatz_verschwenden,65,1999);

hoffentlich richtig befüllt.

Du meinst wohl memset()

ElEspanol:
Da habe ich es hingesetzt.

Wo im Code ist da?

Gruß Tommy

da:

ganz an den Anfang als erste Deklaration

Klar, memset()

Vielleicht doch einen 3. Wahl Chip mit defektem RAM erwischt?