2.Pointer auf Funktion in verket. Liste (Task Scheduler) führ zu Reset, 1.OK

Hallo,

ich bin dabei mir einen Task Scheduler (aus Lernzweck) selbst zu basteln mit einer verketten Liste und Klassen. (eines meiner ersten OOP-Projekte).

Nun gibt es Tasks, (heißen bei mir FCN für Funktion) die sollen 2 Pointer zu Callback-FCN bekommen und diese abwechselnd ausführen (z.B. Toggeln einer LED, Callback1: LED-on, Callback2: LED-off)

Nur irgendwas geht schief, sobald ich zulasse, dass der 2. Callback ausgeführt wird, resettet sich der Mega2560. Arduino 1.6.4 unter Ubuntu 14.04.

Der entsprechende Aufruf der 2. Callback-FCN steh im .ino Zeile 368 p->fcn.pFCN2(); //Execute 2.FCN here! VERURSACHT RESET! FEHLER!

Gesamte Code habe ich mal der Übersicht halber Attached.
Vielen Dank für Hinweise und Tipps! :slight_smile:

_150703_evListe_V008.ino (20.8 KB)

myHelpers.cpp (5.97 KB)

myHelpers.h (6.2 KB)

Bevor man einen Funktionszeiger ausführt sollte man unbedingt abfragen dass er ungleich NULL ist

Vielen Dank für den Hinweis. Hört sich vernünftig an. Habe es gleich mal umgesetzte:

if(p->fcn.pFCN2 != NULL){
p->fcn.pFCN2(); //Execute 2.FCN here! VERURSACHT RESET! FEHLER!
}

Leider ohne Erfolg. Das gleich Fehlerbild. Also mache ich den Fehler vielleicht beim zuweisen der 2. Callback-FCN?

FL.addFCN(&F9, &F9b,2,1); // Toggle FCN

wobei die Callbacks wie folgt aussehen:

void F9() { Don(9);  DEBUG_1("F 9a",0,0 ,0 );int i = random(1, MAXDELAY); delay(i);} // Pin 9 LED ein
void F9b(){ Doff(9);  DEBUG_1("F 9b",0,0 ,0 );int i = random(1, MAXDELAY); delay(i);} // Pin 9 LED aus

Edit: Auch wenn ich andere Callbacks (die sonst ohne Probleme laufen) oder NULL Zeiger hinzufüge, schmiert der Mega ab. Und dass trotz der Prüfung ob Pointer != NULL!! Scheint also doch nicht ganz so einfach oder es ist schon zu spät für mich den Fehler zu sehen. :(

Weiß nicht. Sieht auf den ersten Blick eigentlich in Ordnung aus. Ist aber schwer das alles zu überblicken, da mir die Übersicht fehlt.

Was völlig anderes; du solltest structs und Objekte nie per value übergeben, sonder per reference. Damit meine ich den Listenelement Konstruktor. So wird unnötigerweise eine Kopie erstellt. Besser:

Listenelement(Fcn &fcn)

Danke dass Du mal drauf geschaut hast Serenifly. Das mit dem Überblick ist schon ein Problem. Wenn ich ein paar Tage nicht dran gesessen habe fällt es sogar mit schwer. Daher würde ich den TaskScheduler sobald er funktioniert auch gerne in extra .h und .cpp "verstecken".

Bei der Integration weitere Funktionen/Funktionalitäten habe ich auch noch Fragen ob (als Klasse) und wie (Datenaustausch/Zugruff) diese am günstigsten einbezogen werden. Grundlegend habe ich den Code für meinen Datenlogger schon fertig, auf 2500 unübersichtlichen Zeilen Spagetticode und endlosen globalen Variablen. Da werd ich noch konkret im Forum zu fragen.

Danke auch noch für den Tip mit der Referenz. Anfängerfehler/Problem mit Zeigern/Referenzen. Wede ich morgen mal ändern.

Habe gerade noch einen Fehler gefunden, den ich mir nicht erklären kann, außer es ist ein Bug??

//void rtc_isr(void){//kommentar} // Fehler:_150704_evListe_V008b.ino:610:1: error: expected '}' at end of input
                                  //       a function-definition is not allowed here before '{' token

void rtc_isr(void){               // OK
  //kommentar
}

void F13(){}      // OK

void F12(){ Dtog(12); DEBUG_1("F 12",0,0 ,0 );Ram_max();int i = random(1, MAXDELAY); delay(i);} // Pin 12 LED toggle
//...

je nachdem ob ein Kommentar und die geschweiften Klammern in einer Zeile stehen (->Fehler) oder
verteilt über mehere Zeilen (-> OK). Gibt es dafür eine Erklärung oder mach der Compiler ‘Hitzefrei’? :o

Wenn du die ersten Kommentarstriche entfernst kommt natürlich der Fehler, weil die schließende Klammer fehlt. Bei Kommentaren wo eigentlich noch Code danach kommt verwendet man besser /* */

Verwendest du die Arduino IDE? Bei so komplexem Code solltest du dir vielleicht mal Gedanken darüber machen auf eine richtige IDE wie Visual Studio + Visual Micro umzusteigen. Da sieht man solche Dinge sofort, da sich bei Kommentaren schon während dem Tippen die Farbe ändert.

Serenifly: Wenn du die ersten Kommentarstriche entfernst kommt natürlich der Fehler, weil die schließende Klammer fehlt.

ohh man na klar!! Wie dumm von mir! :blush:

Serenifly: Verwendest du die Arduino IDE? Bei so komplexem Code solltest du dir vielleicht mal Gedanken darüber machen auf eine richtige IDE wie Visual Studio + Visual Micro umzusteigen. Da sieht man solche Dinge sofort, da sich bei Kommentaren schon während dem Tippen die Farbe ändert.

Ich nutze sublime Text 3 mit dem Arduino addon, aber da ich das ganze nicht richtig zum laufen bekommen habe editiere ich den Code in sublime und öffne ihn dann mit der Arduino IDE (1.6.4) und test-kompiliere bzw. lade ihn dort nur hoch.

Hat mich aber nicht vor dem dumme Fehler mit der } hinter //... bewahrt. ::)

Danke!

PS: Call by Reference ist auch realisiert. Danke!

jim_beam: Nur irgendwas geht schief, sobald ich zulasse, dass der 2. Callback ausgeführt wird, resettet sich der Mega2560. Arduino 1.6.4 unter Ubuntu 14.04.

Ziemlich umfangreicher Code, um da einen Fehler zu suchen.

Für mich sieht das irgendwie danach aus, als wenn Du im RAM eine "dynamisch verkettete Liste mit zur Laufzeit zu erstellenden Listenelementen" anlegen möchtest.

Allerdings frage ich mich, wo da die RAM-Speicherverwaltung stattfinden soll?

Entweder müssen die Listenelemente zur Compilezeit statisch im RAM definiert sein, z.B. als statisches Array.

Oder Du müßtest jedesmal, wenn ein neues Listenelement in der Liste hinzugefügt werden soll, dynamisch neuen RAM-Speicher für das neue Listenelement reservieren. Oder wenn Du objektorientiert programmierst, eine neue Instanz einer Objektklasse dynamisch erzeugen und in die Liste einklinken.

Ich kann in Deinem Code keine Stelle entdecken, wo - entweder dynamisch RAM für ein neues Listenelement reserviert wird oder - dynamisch eine neue Instanz aus einer Listenelement-Klasse erstellt wird

Ich sehe nur Pointer, die munter wohl irgendwo im freien (oder auch bereits verwendeten) RAM-Speicher aufgehängt werden, wenn neue Listenlemente "geaddet" werden sollen.

Aber vielleicht ist der Code auch nur viel zu unübersichtlich für mich, um zu erkennen, wo Dein Code den RAM-Speicher für neue Listenelemente reserviert?

Ich dachte irgendwo ein new erspäht zu haben:

Listenelement *neuesListenelement = new Listenelement(fcn);

Dabei aber auch nicht vergessen den Speicher wieder freizugeben wenn der Zeiger auf das Element überschrieben wird!

Serenifly: Ich dachte irgendwo ein new erspäht zu haben:

Listenelement *neuesListenelement = new Listenelement(fcn);

OK, also angefordert wird neuer Speicher für neue Listenelemente schon mal. Die Frage ist dann als nächstes, ob dann auch alles korrekt verwendet wird.

jurs: Ziemlich umfangreicher Code, um da einen Fehler zu suchen.

Das ist auch mein Problem als absoluter Neuling in OOP und mit wenig Erfahrungen im Programmieren; wie gestalte ich meinen Code übersichtlich? Wie gesagt, mein Datenlogger/Smarthome-Steuerung existiert bereits in über 2500 Zeilen Spagetti-Code. Aber weil mich genau das gestört hat, habe ich angefangen mit Klassen ect. Gibt es da ein gutes Tutorial oder Buch zu wie gestalte ich den Code übersichtlich/Portionsweise?

jurs: Für mich sieht das irgendwie danach aus, als wenn Du im RAM eine "dynamisch verkettete Liste mit zur Laufzeit zu erstellenden Listenelementen" anlegen möchtest.

Genau. Das ganze habe ich mir grundlegend bei der TimeAlarm Library abgeschaut und konkret aus folgendem Beispiel abgeleitet: virtual-maxim Dynamische Datenstrukturen – Einfach verkettete Liste

Die TimeAlarm ist schon sehr gut. Aus 1+3 Gründen mache ich das ganze dennoch selbes: 1. ich möchte Tasks eine Start-Offset mitgeben 2. mein uC soll im Low-Power-Mode schlafen, wenn es nix zu tun gibt 3. Lerneffekt 4. Spaß an der Freude (wenn mal wieder ein Stück mehr funktioniert)

jurs: Entweder müssen die Listenelemente zur Compilezeit statisch im RAM definiert sein, z.B. als statisches Array.

Oder Du müßtest jedesmal, wenn ein neues Listenelement in der Liste hinzugefügt werden soll, dynamisch neuen RAM-Speicher für das neue Listenelement reservieren. Oder wenn Du objektorientiert programmierst, eine neue Instanz einer Objektklasse dynamisch erzeugen und in die Liste einklinken.

Mir reicht es völlig dass zur Compilezeit alles statisch fest steht, (im Moment) habe ich nicht vor zur Laufzeit etwas hinzu zu fügen oder zu entfernen. Aber fand das Beispiel sehr anschaulich und (weitestgehend) nachvollziehbar. Wüsste nicht wie ich es anders machen würde und es tut ja auch sehr gut (bis auf der 2. Callback-Pointer).

jurs: Aber vielleicht ist der Code auch nur viel zu unübersichtlich für mich, um zu erkennen, wo Dein Code den RAM-Speicher für neue Listenelemente reserviert?

Sorry und vielen Dank dass Du, Serenifly und alle Anderen sich meinem Problem annehmen! :) Wie schon erwähnt, würde gerne mehr darüber lernen wie man das eine oder andere "besser" programmiert/gestaltet.

VG

jim_beam: Mir reicht es völlig dass zur Compilezeit alles statisch fest steht, (im Moment) habe ich nicht vor zur Laufzeit etwas hinzu zu fügen oder zu entfernen. Aber fand das Beispiel sehr anschaulich und (weitestgehend) nachvollziehbar.

Ja, verkettete Listen sind was schönes und auch relativ einfach was dynamische Datenstrukturen angeht. Und sie sind schön zum Üben und Lernen. Aber hier Overkill. Wenn alles zur Compile-Zeit fest steht kannst du deine structs auch einfach in ein Array packen und dann über das Array iterieren.

Serenifly: Ja, verkettete Listen sind was schönes und auch relativ einfach was dynamische Datenstrukturen angeht. Und sie sind schön zum Üben und Lernen. Aber hier Overkill. Wenn alles zur Compile-Zeit fest steht kannst du deine structs auch einfach in ein Array packen und dann über das Array iterieren.

Hast Du ev. eine Empfehlung für ein konkretes Beispiel für mich?

Aber es bleibt die eingangs gesellte Frage, warum Reset beim ausführen des 2. FCN-Pointer? ich mache das gleiche wie beim 1.?? :confused:

Mir ist aufgefallen, dass die Abfrage ob er nicht NULL ist auch true wird, wenn ich ihn mit NULL initialisiere. scheinbat geht bei der Zuweisung des 2. ptr schief. kann ich mir ansehen auf welchen Speicherbereich er zeigt und das vergleichen mit der Adresse der eigentlichen Callback-Funktion?

kann ich mir ansehen auf welchen Speicherbereich er zeigt und das vergleichen mit der Adresse der eigentlichen Callback-Funktion?

Klar. Ein Funktionsname ist ein Zeiger (auf den flash-Speicher).

Wie wäre es, wenn du dein Problem (möglichst auf das Problem reduziert) hier postest?

michael_x: Wie wäre es, wenn du dein Problem (möglichst auf das Problem reduziert) hier postest?

Ja, dass ist mir vielleicht nich nicht so gut gelungen, aber mir ist auch nicht ganz klar, wie ich so eine einfach verkette Liste die als TaskScheduler fungiert simpler machen kann.

Eigentlich ist es "nur" dieses einfach verkettete Liste-Beispiel: http://www.virtual-maxim.de/dynamische-datenstrukturen-%E2%80%93-einfach-verkettete-liste/

das ich mit 2 pointern auf Callback-Funktion ergänzt/umgebastelt habe. Nun gibt es mit dem ersten Pointer überhaupt keine Probleme, aber der 2. fürt zum Rest. Wobei ich mit dem 2. (so denke ich) genau das gleiche gemacht habe wie beim ersten Callback-Pointer/Funktion. :confused:

Das Problem liegt also beim 2. Callbck-Pointer und scheibar bei dessen Initialisierung. Da scheint etwas flasch zu laufen, eine falsche Adresse drinn zu stehen. Was zum rset führt.

Pointer Adresse ansehen ist dann einfach: Serial.print(p->fcn.pFCN2);? Konnte/kann es leider noch nicht testen, vielleicht am Wochenende.

In jedem Fall Vielen Dank!

Also, in dem Code von #1 fehlt die Initialisierung von fcn.pFCN2 im Listenelement c'tor ( Zeile 74 ff )

Oft sind es ja einfache Fehler, für die man blind ist

michael_x: Also, in dem Code von #1 fehlt die Initialisierung von fcn.pFCN2 im Listenelement c'tor ( Zeile 74 ff ) Oft sind es ja einfache Fehler, für die man blind ist

SOLVED! Tausend Danke!! Ich muss gestehen, dass ich als Neuling auf dem Gebiet OOP und Klassen noch nicht ganz den Überblick habe was ich wo wie machen muss. Wieder dazu gelernt.

Vielen Dank auch an alle anderen die sich die Mühe gemacht haben mit dem langen Code.

;)

Verkettete Listen sind hübsch, aber normalerweise nur Jux bei der Programmierung kleiner Microcontroller.

Die Anzahl Elemente steht doch üblicherweise schon vor dem Compilieren fest, und/oder ist durch die kleine RAM-Größe beschränkt. Bei einem Array sparst du dir die Ketten-Zeiger.

michael_x:
Die Anzahl Elemente steht doch üblicherweise schon vor dem Compilieren fest, und/oder ist durch die kleine RAM-Größe beschränkt. Bei einem Array sparst du dir die Ketten-Zeiger.

Das sehe ich (inzwischen) auch so. Musste aber erst mal bis hier her kommen um das alles so zu verstehen. Im Beispiel der TimeAlarm Lib ist’s dynamisch möglich, daran hatte ich mich orientiert, aber für mich nicht notwendig.

Als Anfänger/Quereinsteiger mit einigen Grundkenntnissen, aber auch vielen Lücken ist es sehr hilfreich, wenn Beispielcode zur Verfügung steht, das war mit dem oben genannten Beispiel von virtual-maxin der Fall.

Jetzt müsste ich mal vergleichbare Bsp. für Array-Variante suchen/finde. Stelle mir das in etwa so vor:

const byte ANZAHL_TASKS = 2;
typedef void(*pF)(void);		// Pointer to void void FCN

pF arry[ANZAHL_TASKS] = {&Callback1, &Callback2,...);

//...

execute_all_Tasks(){

for(byte i=0, i<=ANZAHL_TASK;i++){
arry[i]      // wie führe ich hier die Callabck-FCN aus?

Sehr schöne finde ich bei “meiner” jetzigen Variante, dass ich eine Task/Funktion nur ein mal im Code “einhängen/adden” muss und nicht an mehreren Stellen Änderungen vor nehmen muss bzw. anganben darüber wie viele Tasks/FCN es gibt. Aber vielleicht ist das bei Array Variante auch möglich?