Go Down

Topic: BetweenLoop - Event (Read 722 times) previous topic - next topic

eddyst

Hallo,

ich beschäftige mich noch nicht lange mit Arduino, habe mir aber z.B. schon viele Beispiele angeschaut und eine eigene Lib gebastelt, die einen Timer und eine LED kombiniert - nichts kompliziertes. Mich stört, das ich in meinem Sketch für viele Libs irgendwelchen Code in jedem loop aufrufen muß. Also ich meine nicht das er aufgerufen wird, sondern das ich es tun muß.

Das fast gute Beispiel ist Serial. Da steckt der Aufruf direkt in void main() - der Anwender muß sich nicht darum kümmern.

Ich würde mir ein BetweenLoop - Event wünschen, für das sich eine Library subscriben kann. z.B. als einfache verkettete Liste aus Funktionszeigern. Die Lib definiert eine Funktion, und fügt mit der Initialisierung einen Zeiger darauf an die Liste an.

Gibt's sowas evl. schon? Was haltet Ihr von der Idee? Oder wie handhabt Ihr sowas?

Viele Grüße

jurs


Gibt's sowas evl. schon? Was haltet Ihr von der Idee? Oder wie handhabt Ihr sowas?


Keine Ahnung. Denn ich verstehe überhaupt nicht, was Du meinst.

Ein normales Programm-Design sieht so aus: Kein delay(), es sei denn vielleicht mal im Notfall ganz kurze bis hoch zu delay(5), ansonsten alles mit nicht-blockierenden Funktionen realisieren!

Und dann ist Deine loop eine enge loop, die zig- bis tausende mal pro Sekunde durchläuft, z.B. für eine Robotersteuerung:

void loop()
{
  Sensoren_auswerten();
  Funkbefehle_auswerten();
  Blinken();
  Lenken();
  Fahren();
  Status_melden();
}

Keine der Funktionen darf
- ein delay() enthalten (es sei denn extrem kurze)
- eine länger laufende Schleife enthalten

Und eine Funktion wie Funkbefehle_auswerten() darf NICHT so anfangen:

- Warte_auf_Funkbefehl()
- Führe Funkbefehl aus()

Sondern so:
- wenn kein Funkbefehl bereit, dann Funktion verlassen
- Funkbefehl lesen
- Statusvariable zur Befehlsausführung setzen

Und wenn der Funkbefehl beispielsweise "Blinken()" lautet, dann darf das nicht so aussehen
- Blinker an
. delay(xy)
- Blinker aus
- delay(xy)

Sondern so
- wenn (xy Zeit) seit letzter Umschaltung vergangen, dann Umschaltung des Blinkstatus

Und wenn Dein Programmdesign korrekt ist und Du beispielsweise 10 Unterfunktionen in Deiner loop drin hast, von denen jede maximal 10 ms Ausführungszeit benötigt, wären das 10*10 = 100 ms und Deine gesamte Loop wird dennoch mindestens zehnmal pro Sekunde komplett durchlaufen, ohne dass irgendwo noch Aufrufe in andere Unterfunktionen eingefügt werden brauchen.

Falls das mit den zu lange laufenden Funktionen mit delay() und langlaufenden Schleifen nicht das Problem ist, mach mal ein etwas konkreteres Beispiel! Die einfache "Liste mit Funktionszeigern" ist normalerweise immer die loop und die ist doch bereits vorhanden!



mkl0815

Hmm das ist vermutlich nicht so zielführend.
Zum Einen hat der User weniger Kontrolle darüber wann bestimmte Aktionen durchgeführt werden sollen, zum Anderen kann die Lib ja nie wissen wie lange ein loop() Durchlauf dauert und ob das immer gleich lang ist.
Wenn Du so eine Funktionalität brauchst, kannst Du das ja selbst in eine Klasse (z.B. LibTool) giessen, die dann am Ende von loop() einmal ein "LibTool->process()" aufruft und alle Funtionen abklappert die sich mit "void LibTool::addFunction(const void *fnPointer)" angemeldet haben, bzw. angemeldet wurden.

eddyst

Erstmal danke für eure Antworten.

In Bezug auf den Programmablauf gebe ich jurs erstmal recht. Konkret war ich auf der Suche nach einer simplen Möglichkeit zu sehen was mein Programm gerade tut. Serial ist vom Optokoppler belegt der mit meiner Heizung spricht. LCD hatte ich bestellt, ist aber noch nicht da (von China dauert das eben). Also Blinkzeichen per LED - angelehnt an das BlinkWithoutDelay Beispiel. Die ersten 2 LED - naja kein schöner Code aber geht erstmal. Bei Nummer 5 von den 8 vorhandenen war das dann so unübersichtlich das ich das in eine kleine Lib ausgelagert habe. Als minibeispiel sieht der Aufruf jetzt so aus:
Code: [Select]

#include <LEDTimer.h>
LEDTimer step1(13);

void setup() {
}

void loop() {
  step1.doEvents(); 

  Wenn irgendwas dann:   step1.blinkBegin( 1000, 500, 10); //Blinke 10 mal abwechselnd 1000 dann 500 ms

}

Bleiben also noch die überflüssigen Aufrufe von doEvents aus dem Usercode wegzuoptimieren. Und optimal wäre es natürlich wenn sich das nach dem letzten blinken wieder deregistriert.

zweites Beispiel: Serial
main() macht bei jedem Durchlauf der Hauptschleife das folgende:
Code: [Select]

if (serialEventRun) serialEventRun();

Das nenne ich einen "bösen Hack". Warum kann es sich nicht mit Serial.begin registrieren?
Der User hat hier auch keine Kontrolle. Braucht er sie? Nö! Der der Serial entwickelt hat, hat entschieden das das eben ab und zu gemacht werden muß. Aber es ist eigentlich Aufgabe der Lib dafür zu sorgen das es ausgeführt wird. Vom core würde ich nur einen Mechanismus erwarten der das (allgemein für alle die Libs die diese Funktionalität brauchen) ermöglicht.

drittes Beispiel: Ethernet
Keines der Beispiele verwendet Ethernet.maintain() auch wenn einige DHCP nutzen. Nach einigem suchen habe ich gelesen man kann es ruhig in jedem loop aufrufen. Warum gibt man dan nicht der Lib die Möglichkeit das zu tun? Wird sonst eh vergessen - macht aber irgendwann Probleme.

@mkl0815:
Die Idee mit der Klasse ist eine gute (Not-) Lösung. Das werde ich wohl auch erstmal so machen.

Ich gebe Dir Recht:
Quote
der User hat weniger Kontrolle darüber wann bestimmte Aktionen durchgeführt werden sollen

ABER: Braucht er die? (Siehe Serial)

Quote
zum Anderen kann die Lib ja nie wissen wie lange ein loop() Durchlauf dauert und ob das immer gleich lang ist.

ABER: Ob das schlecht oder egal ist, muß der Entwickler der Lib entscheiden. Bis jetzt kann er aber nichtmal beeinflussen ob seine Funktion überhaupt aufgerufen wird.

Ich hoffe ich konnte meine Überlegungen ungefähr rüber bringen.

michael_x

Einerseits hast du recht, und
Quote
Vom core würde ich nur einen Mechanismus erwarten der das (allgemein für alle die Libs die diese Funktionalität brauchen) ermöglicht.

... ist eine schöne Idee. Aber da es nun mal nicht im Core ist, ist mkl0815's Vorschlag keine Notlösung, sondern ein schöner Ansatz.

Andererseits sollte eine Library eigentlich nur Funktionalität zur Verfügung stellen, und nur laufen, wenn sie aufgerufen wird.
Deine doEvents() Methode ist doch prima dafür geeignet.
Bist du neidisch, dass der Serial-Entwickler Zugriff auf main() hat und sich wünschen durfte, dass sein doEvents() (=serialEventsRun) dort versteckt wird, um unbedarfte Hobby-Programmierer nicht zu verwirren, und du nicht? ;)

Sieh es doch anders: setup() und loop() sind nur zwei Teile von main, mit denen du konfigurieren kannst, was einmal und was jedesmal gemacht werden muss. Deine eigentliche "Usercode" - Logik gehört in eine eigene Datei, wenn meinProjekt.ino zu groß und unübersichtlich wird.

Oder auch überlegenswert: Du musst ja nicht die Arduino IDE nehmen, wenn sie dich zu sehr einengt, statt dir das Leben zu vereinfachen.

eddyst

Quote
Bist du neidisch,

JAAAAAAAAAAAAAAAAAAAAA :0 :0 :0 :0 :0 :0 war das erste was ich dachte, Der Mann versteht mich!

Aber jetzt, ein paar Minuten später? Nein - nicht neidisch sondern entrüstet über so viel Egoismus.

Wie sagt Massimo? build it, hack it and share it, bacause Arduino is you.

Also ab mit dem Gedanken in den großen Ideenkochtopf und schau mer mal was die nächsten Versionen bringen.


mkl0815

Mal noch ein anderer Gedanke dazu. Wir reden hier ja über einen Mikrocontroller mit begrenzten Ressourcen. Teilweise muss an jedem Byte gespart werden. Eine verkettete Liste mit Funktionspointern anzulegen bedeutet minimal 4 Byte pro registierter Funktion (2 Byte Zeiger auf die Funktion, 2 Byte Zeiger auf den nächsten Eintrag), um am Ende den Quelltext schicker zu machen, weil die Aufrufe der Funktionen nicht in der loop() eingetragen sind, sondern "automatisch" oder "halbautomatisch" aufgerufen werden, könnte man fast schon als "dekadent" ansehen  :)
Vom zusätzlichen "Zeitverbrauch" durch das durchlaufen der Liste und der Reihenfolge der Aufrufe die maßgeblich von der Reihenfolge der Registierung der Callback-Funktionen abhängt und dem damit ggf. verbundenen Seiteneffekten die für den Nutz unerklärlich sind, weil er die Aufrufe nicht sieht, mal ganz abgesehen.

Bitte nicht falsch verstehen, ich finde die Idee auch interessant, möchte aber auch auf mögliche Bedenken hinweisen.

Am Ende ist der Arduino ja auch eine Art Lernplattform. Und das bedeutet für mich auch, das der Nutzer sehen soll wie etwas funktioniert. Die IDE versteckt ja schon jede Menge Details, um den Einstieg zu erleichtern.
Aber das ist wie gesagt, Ansichtssache.

Mario.

Go Up