Absturzstelle finden

Hallo,
ich habe einen MEGA2560 mit einem sehr umfangreichen Sketch.
Dieser bleib von Zeit zu Zeit hängen. Damit es wieder weiter geht, habe ich den Watchdog aktiviert.
Funktioniert alles bestens soweit.
Nun möchte ich aber wissen, wo genau der Absturz stattgefunden hat.
Meine Idee ist, einer Variablen an verschiedenen Programmpositionen jeweils einen eindeutigen Wert zur Identifizierung zuzuweisen.
Jetzt ist nur noch ein Problem! Wie komme ich nach dem Neustart, der vom Watchdog ausgelöst wurde, an den Wert vor dem Auslösen des Neustarts?
EEPROM geht nicht, der geht kaputt.
Meine Idee wäre, das SRAM. Wenn ich auf ein bestimmtes Byte im SRAM zugreifen könnte, dann müsste der Wert vorm Neustart auch der Wert nach dem Neustart sein.
Den bei einem Watchdog-Reset wir das SRAM ja nicht durch einen Spannungsabfall gelöscht.
Kennt jemand eine Möglichkeit, ein bestimmtes Byte im SRAM anzusprechen?
Dann könnte ich das einmal testen.
LG

Einfach einen Zeiger auf eine bestimmte Adresse initialisieren:

byte* address = reinterpret_cast<byte*>(0x0FFF);

Ein Problem ist es dann noch eine passende Adresse im RAM dafür auszuwählen. Dazu sollte man verstehen wie das organisiert ist:
https://www.nongnu.org/avr-libc/user-manual/malloc.html

Serenifly, danke für deine Antwort.

Leider weis ich jetzt nicht was ich mit deiner Code-Zeile anfange.

Ich brauche so etwas wie:

void setup(){
  Absturzposition = von_RAM_lesen();
  //Code
}

void loop(){
Sketch_Pos_in_RAM(1);

//Code

Sketch_Pos_in_RAM(2);

//Code

Sketch_Pos_in_RAM(3);

//Code

Sketch_Pos_in_RAM(4);

//usw  

}

void Sketch_Pos_in_RAM(Pos){
  //keine Ahnung was da stehen muss!
}

byte von_RAM_lesen(){
  byte Speicherinhalt;
  //keine Ahnung was da stehen muss!
  return Speicherinhalt;
}

https://de.wikibooks.org/wiki/C%2B%2B-Programmierung/_Weitere_Grundelemente/_Zeiger

Meine allererste Maßnahme bei der Fehlersuche ist, für hübschen Code zu sorgen. Vielleicht hilft das auch in Deinem Fall.

Ansonsten …

ar182:
EEPROM geht nicht, der geht kaputt.

… nehme ich auch mal das EEPROM, um Absturz- bzw. Fehlerquellen so zu suchen, wie Du es Dir zuerst vorgestellt hast. Das EEPROM ist für mind. 100.000 Schreibzugriffe spezifiziert und wenn Du nicht immer nur eine einzige Speicherzelle manipulierst, kann das EEPROM sehr lange halten.

Wieviele loop()-Durchgänge hast Du denn pro Sekunde?

Gruß

Gregor

Es sind 10 bis 25 Durchläufe / Sekunde.
Ein Tag hat 86400 Sekunden.

Ich hab jetzt einen Zeiger auf die letzte RAM-Stelle.
Im Testprogramm klappt es.

#include <avr/wdt.h> 

#define LED  13

uint8_t vProgPos;

uint8_t *zProgPos;

void setup(){ 
  zProgPos = 0x1FFF;
  wdt_reset();
  Serial.begin(9600);
  pinMode(LED,OUTPUT);
  randomSeed(analogRead(0));
  wdt_enable(WDTO_4S);
  Serial.println(); 
  Serial.print("Start... ");
  Serial.println(*zProgPos);
} 

int x = 1;
void loop(){ 
  Serial.print(x);
  for(int i=0;i<x;i++){
    digitalWrite(LED,1);
    delay(500);
    digitalWrite(LED,0);
    delay(500);
  }
  x++;
  uint8_t vZuF = random(1, 254);
  Serial.print(" ");Serial.println(vZuF);
  *zProgPos = vZuF;
  wdt_reset(); 
}

Könnte es sein, dass Dein x und damit als Folge Dein i überläuft?

Gruß Tommy

ar182:
Ich hab jetzt einen Zeiger auf die letzte RAM-Stelle.

Gerade das ist wahrscheinlich nicht gut. Das Ende des RAMs ist der Anfang des Stacks Der Stack wächst von hohen auf niedrige Adressen! Deshalb hatte ich dir den Link auf die AVR libc Seite gegeben

Das sieht man auch leicht wenn man die Adressen von zwei Variablen ausgibt. Die zweite hat eine niedrigere Adresse

Es gibt dazu auch fertige Konstanten wie RAMSTART, SP (Stackpointer), RAMEND, __bkval (Ende des Heaps) und __heap_start (Anfang des Heaps). Letzere sind aber nur ungleich 0 wenn man dynamischen Speicher verwendet

Wobei bei einem UNO RAMEND 0x8FF ist und auf einem Mega 0x21FF

 zProgPos = 0x1FFF;

Warnung hast du bei dir auch nicht aktiviert. Der reinterpret_cast stand da nicht ohne Grund dort. Ich hatte nur ein paar Klammern vergessen

Ich nehme mal an, 's ist kein AVRICE oder änliches vorhanden? Ev. Oszi und mit ein paar Pins wackeln?

Meine Idee wäre, das SRAM. Wenn ich auf ein bestimmtes Byte im SRAM zugreifen könnte, dann müsste der Wert vorm Neustart auch der Wert nach dem Neustart sein.

Du kannst dem Compiler sagen, dass er Variablen in die "gcc AVR .noinit Section" legen soll.
Zeiger oder sonstige Klimmzüge sind nicht nötig.
Auch um den Linker muss man sich nicht kümmern, wenn der Bereich nicht von Hauptprogramm und Bootloader gemeinsam verwendet werden soll.

Absturzstelle finden

Über GSM die GPS Koordinaten schicken?
Entschuldige, konnte nicht wiederstehen.

Ich würde mal sagen, daß Du uns den Sketch zeigen solltest.
Wir docktern nur an den den Symptomen herum.

Grüße Uwe

combie:
Du kannst dem Compiler sagen, dass er Variablen in die "gcc AVR .noinit Section" legen soll.
Zeiger oder sonstige Klimmzüge sind nicht nötig.
Auch um den Linker muss man sich nicht kümmern, wenn der Bereich nicht von Hauptprogramm und Bootloader gemeinsam verwendet werden soll.

Hallo combie,
deine Antwort hat mich auf die richtige Spur gebracht.
Hier ist ein besserer "Link"

Fein!
Dann habe ich aus meiner Sicht, alles richtig gemacht.

Und über “besser” kann man sich bekanntlich vortrefflich streiten.
Denn das liegt im Auge des Betrachters.

Ich würde mal sagen, daß Du uns den Sketch zeigen solltest.
Wir docktern nur an den den Symptomen herum.

Das ist auch eine durchaus akzeptable Sicht, würde ich mal sagen.

Aber wenn der Code geheim bleiben soll, stört mich das auch nicht.
Mir ist im Grunde lieber, wenn der Fehler vom Ersteller selber gefunden wird.


Allerdings glaube ich weniger, dass dieses .noinit Spielchen den Bock ans Licht bringt.
Kann: Ja!
Muss: Ganz und Gar nicht!

Denn:
Die Fehler verursachende Stelle ist bis zu 8 Sekunden von dem WDT Reset entfernt.
8 Sekunden ist eine kleine Ewigkeit, für einen µC.

Du musst das so sehen.
Deine Seite ist was für Leute, die vergessen haben wies geht.
Meine Seite ist was für mich, der von .noinit noch nie was gehört hat.

Mein Sketch hat fast 3000 Zeilen (Leerzeilen und Kommentare nicht mitgezählt)
und ist ohne seine Hardware nicht lauffähig.
Also geheim ist er nicht, aber sinnlos an dieser Stelle.

uint8_t zProgPos __attribute__ ((section (".noinit")));

#include <avr/wdt.h> 
#include "Streaming.h"
#define LED  13


void setup(){ 
  wdt_reset();
  Serial.begin(9600);
  pinMode(LED,OUTPUT);
  randomSeed(analogRead(0));
  wdt_enable(WDTO_4S);
  Serial<<"letzte Pos "<< zProgPos<<ende;
} 

int x = 1;
void loop(){ 
  Serial.print(x);
  for(int i=0;i<x;i++){
    digitalWrite(LED,1);
    delay(500);
    digitalWrite(LED,0);
    delay(500);
  }
  x++;
  uint8_t vZuF = random(1, 254);
  Serial.print(" ");Serial.println(vZuF);
  zProgPos = vZuF;
  wdt_reset(); 
}

Also geheim ist er nicht, aber sinnlos an dieser Stelle.

Interessant!

Ich übersetze:
Du hast einen Code, welcher einen Fehler verursacht.
Du möchtest Hilfe/Unterstützung bei dem Versuch diesen Fehler zu finden.
[ironie=überzogen]
Mittlerweile bist du in Sachen Kompetenzstufenentwicklung auf dem aller höchsten Level angekommen, so dass deine Entscheidung, was sinnlos ist, unanfechtbar fest steht.
Wir kleinen Lichter können damit sowieso nichts anfangen.
[/ironie]

Im Grunde ist das voll ok.
Mich persönlich interessiert der Fehler/Code eher nicht.

Könnte aber mal drüber schauen...
Manchmal springt einen sowas dann ja an.
Hat auch mit Erfahrung zu tun, also mit selber schon mal DIESEN Fehler gemacht zu haben.

Du musst das so sehen.

Muss nicht, aber darf... :slight_smile:

Du willst mir doch nicht erzählen, das du 3000 Zeilen Code durchackern wirst.
Der dann in deinen Augen auch noch super schlecht geschrieben ist.
Ich würde dir empfehlen, einmal das Haus zu verlassen evt. scheint bei dir sogar die Sonne. 8)

Welchen Sinn hat der Sketch den Du uns zeigst?

Wie .noinit arbeitet?

Wenn der Watchdog alle x Sekunden resetiert werden muß und Du delay() in nicht vernachlässigbarer Größe verwendest, dann ist früher oder später die Zeit zwischen 2 Reset des Watchdogs zu groß und dieser schlägt an.

Die Lösung ist es mittels Deines Sketches den Teil zu finden der dies macht und zu verbessern.

Frage: Was macht

uint8_t zProgPos __attribute__ ((section (".noinit")));

beim Einschalten (anlegen der Versorgungsspannung) des Arduinos?

Die Werte im RAM sind pseudozufällig, das heißt die Speicherzellen haben beim Einschalten eine Präferenz, die aber nicht für alle Zellen und immer 100% wiederholbar ist, welchen Status (HIGH oder LOW) sie haben. Es ist nicht gesagt, daß die SPeicherstelle, wo Du die Variable ablegst und wieder herausholst beim Einschalten den Wert 0 oder einen irgendwie vorhersehbaren Wert hat. Du kannst also nicht sicher unterscheiden ob sich Dein Sketch im Zustand nach dem Einschalten (Zuführen der Versorgungsspannung) oder nach einem Reset befindet und ob darum der Wert der Variablen gültig oder nur ein fast zufälliger Wert ist.

Darum sehe ich die von Dir gefundene Lösung nicht als gangbaren weg.

Grüße Uwe

ar182:
Du willst mir doch nicht erzählen, das du 3000 Zeilen Code durchackern wirst.
Der dann in deinen Augen auch noch super schlecht geschrieben ist.
Ich würde dir empfehlen, einmal das Haus zu verlassen evt. scheint bei dir sogar die Sonne. 8)

Überlaß die Entscheidung uns.
Außerdem regnet es seit 3 Tagen ( Freitag hat es auch bis ins Tal (260m Meereshöhe) 2 cm Schnee gegeben).
Die Wettervorhersagen geben für die nächsten Tage Regen/Schnee. Im moment hält sich der Schnee oberhalb 800m (aus dem Fenster geschaut; nicht mal vom PC aufgestanden).

Zwingen uns den Sketch zu zeigen will ich Dich nicht, nur Deine Argument, wieso nicht, entkräften.

Grüße Uwe

Ich weis nicht was ihr habt?

In meine kleinen Test- Sketch ist die Variable zProgPos
nach dem auslösen des Watchdogs immer noch beim Neustart die gleiche.

In meinen Problem-Sketch weise ich zProgPos am Anfang jeder Subroutine einen eindeutigen Wert zu.
Dieser steht dann beim Neustart in meiner Protokolldatei.

sprintf_P(vText, PSTR("~~~~~~ N e u s t a r t ~~~~~~ Absturzpostion = %d "), zProgPos); ProtokollAufSD(true);

Habe ich dann eine Subroutine als Problemfall identifiziert, so bekommt zProgPos vor jeder markanten Stell
in dieser Subroutine einen neuen Wert. Der dann wieder nach dem Neustart in meiner Protokolldatei steht.

Und so taste ich mich an den Fehler heran.

Zwingen uns den Sketch zu zeigen will ich Dich nicht nur Deine Argument wieso nicht entkräften.

Ich auch nicht.
Wobei ich dazu sagen muss, dass der Code wohl schlimm sein muss.
Wenn er richtig denkt, was ich denken werde, nach dem ich den Code gesehen habe.

Die Peinlichkeit scheint sich unser ar182 nicht geben zu wollen.

Wie schon gesagt:
Total menschlich, und somit völlig ok.
Allerdings durchaus noch entwicklungsfähig....

Habe ich dann eine Subroutine als Problemfall identifiziert, so bekommt zProgPos vor jeder markanten Stell
in dieser Subroutine einen neuen Wert. Der dann wieder nach dem Neustart in meiner Protokolldatei steht.

Und so taste ich mich an den Fehler heran.

Kann klappen, muss aber nicht.
So findest du Symptome, nicht unbedingt die Ursache!