Alternative zum delay

Hi Forum :slight_smile:

ich habe wohl schon gemerkt, dass es sehr wichtig ist, an welchen Stellen man delays benutzt. Besonders wenn man möchte, das Sensoren anständig und kontinuierlich Ihre Werte messen.

Jetzt stehe ich allerdings vor einem Fragezeichen und muss das Forum mal dazu ziehen.

Gibt es alternativen zum Delay Befehl?

Ich schalte über eine Lichtschranke ein Relais, welches eine LED-Leiste mit 12V versorgt. Als Beispiel soll die LED, sobald die Lichtschranke ausgelöst wird 2 Sekunden lang aufleuchten. Zudem wird auf einem LCD Display die Anzahl der Auslösungen angezeigt und auf addiert.

Jetzt kommt der Knackpunkt.

Währenddessen der Delay ausgeführt wird, reagiert die Lichtschranke nicht mehr, bis der Delay vorbei ist.

Es gibt sicherlich Tricks, indem man z.B. Variabeln hochlaufen lässt, bis etwa 2 sekunden erreicht sind und dann resetet werden, sobald der Sensor erneut auslöst. Allerdings ist das wieder so offensichtlich unsauber und dabei werden ja meist auch nicht wirklich immer 2 Sekunden erreicht...

Wie würdet Ihr sowas lösen? Gibt es keine elegantere Lösung? Der Delay ist natürlich schön einfach, keine Frage. Eine Methode über eine Variable wär denkbar, jedoch auch aufwendiger und irgendwie gefuscht?

Danke für eure Gedanken.

Hier wirst du fündig.
Delay Ersatzstoff in vielen Varianten!

Ablaufsteuerung
Meine Standardantwort zu Ablaufsteuerungen:

Eine Stichworte Sammlung für Google Suchen:
Endlicher Automat,
State Machine,
Multitasking,
Coroutinen,
Ablaufsteuerung,
Schrittkette,
BlinkWithoutDelay,

Blink Without Delay
Die Nachtwächter Erklärung

Intervall Macro
Multitasking Macros
INTERVAL Ersatzstoff
CooperativeTask


Das würde ich allerdings anders angehen:

Ich schalte über eine Lichtschranke ein Relais, welches eine LED-Leiste mit 12V versorgt. Als Beispiel soll die LED, sobald die Lichtschranke ausgelöst wird 2 Sekunden lang aufleuchten. Zudem wird auf dem Display die Anzahl der Auslösungen addiert.

Siehe die Beispiele im Anhang.
Für die 2_sec bietet sich der FallingEdgeTimer an, und der Counter aus den Tools, für die Zählungen und die Ereignisauslösung..

CombieLib.zip (76.1 KB)

mrzortrax:
Wie würdet Ihr sowas lösen? Gibt es keine elegantere Lösung? Der Delay ist natürlich schön einfach, keine Frage. Eine Methode über eine Variable wär denkbar, jedoch auch aufwendiger und irgendwie gefuscht?

Wenn mehrere Dinge quasi gleichzeitig gemacht werden sollen, geht das meiner Erfahrung nach am besten mit einem endlichen Automaten. Dieses „Strickmuster“ ist zunächst nicht ganz einfach zu verstehen. Kurz: loop() läuft ungebremst durch und prüft bei jedem Durchlauf, ob es Zeit für eine Aktion ist oder ob ein Sensor oder Schalter einen bestimmten Zustand erreicht hat. Guck z. B. nach der „Nachtwächtererklärung“ oder hier.

Gruß

Gregor

Für dein Problem gibt es mehrere 'saubere' Lösungen, von denen einige schon angesprochen wurden. Auch in meinen MobaTools ist ein Beispiel enthalten, das exakt das macht, was Du willst. Nur mit einem Taster anstelle der Lichtschranke. Im Beispiel sind es sogar 2 Taster und 2 Leds mit jeweils unabhängigen Einschaltzeiten.

Auch in meinen MobaTools ist ein Beispiel enthalten,

Noch habe ich deine Tools nicht genau untersucht, bisher eher überflogen.

Nichts desto Trotz:
Ich nehme sie mal in meine Standard Antwort auf, da die Lib ja auch dieses Thema angeht.

Hallo,

es gibt, wie bereits geschrieben, mehrere Möglichkeiten. Der Einsatz von Lib´s und Tool´s ist manchmal der schnellste und einfachsteWeg. Wenn Du es aber verstehen willst worauf das Prinzip dann must Du dich mit dem Thema millis() und der "Nachtwächter Erklärung" beschäftigen. Unter Beispiel in der IDE findest du das Beispiel "BlinkWithoutDelay" probiere es aus und verstehe es.

was millis() ist findest Du hier z.B link

den Nachtwächter kannst du hier im Forum suchen link

Heinz

Rentner:
Wenn Du es aber verstehen willst worauf das Prinzip dann must Du dich mit dem Thema millis() und der "Nachtwächter Erklärung" beschäftigen.

Das Grundprinzip des 'Nachtwächters' ( oder einer anderen Erklärung dazu ) muss man auch bei Einsatz einer Lib verstanden haben. Da geht es einfach um die Grundlagen, wie man blockadefrei programmiert.
Nur den doch etwas 'sperrigen' direkten Umgang mit den millis() kann man sich erleichtern. Das finde ich aber durchaus legitim, so sind je eingentlich die ganzen Arduino-Libs gedacht. Im Prinzip braucht man auch kein digitalRead oder digitalWrite, pinMode oder sonstwas. Aber es ist eben doch bequemer.

@combie: danke für die Werbung :wink: :slight_smile:

Vielen Dank für eure Antworten.

Dann fange ich mal an mich da durch zu hangeln. Wahrscheinlich gibt es auch hier nicht den "goldenen richtigen" Weg, weils je nach Anwendung viele Möglichkeiten gibt wie Ihr aufgezählt habt.

Danke dafür.

Gut hab ich mir jetzt direkt mal angesehen das Beispiel mit dem Nachtwächter dem endlichen Automat und der Blinki winki LED ihr wisst schon was Geschichte.

Also gut, versteh ich soweit. Durch Berechnen der fortlaufenden Systemstartzeit + Wartezeit ergibt sich dann eine neue Aktionszeit wenn man so will. Da gibts ja mehrere Möglichkeiten millis() zu nutzen, wie ich es aus meiner Logik herleite.

Frage zur folgenden Variable und ob ich den Teil richtig verstanden habe...

"unsigned long int millisMem;"
unsigned erhöht die Variable long indem sozusagen die Minuszahlen ausgeschlossen werden und in Pluszahlen potenziert also verdoppelt werden richtig?

millies() gibt dann dementsprechend die laufende Systemzeit in ms zurück, korrekt?

Jetzt bin ich gedanklich schon bei einem überlauf der Variablengröße von millisMem.

Ich meine ich habs auch irgendwo gelesen das es so ist. Nach meinen Berechnungen ist nach 49,7 Tagen der Maximalwert der Variablen erreicht. Was geschieht danach? Ein Systemneustart um von vorne anzufangen? Was passiert mit den Millies über diese Zeit hinaus? Läuft die im Hintergrund einfach weiter unaufhörlich weiter?

Das wäre noch etwas, das ich momentan noch nicht so greifen kann.

:slight_smile: Bitte um Erklärung. Und dann noch, ob Millis von den genannten Möglichkeiten denn die Beste Wahl ist. Oder wenn man das nicht beantworten kann, was für euch kurz und knapp die Beste einfachste Wahl ist.

Danke

Ich meine ich habs auch irgendwo gelesen das es so ist. Nach meinen Berechnungen ist nach 49,7 Tagen der Maximalwert der Variablen erreicht. Was geschieht danach? Ein Systemneustart um von vorne anzufangen? Was passiert mit den Millies über diese Zeit hinaus? Läuft die einfach weiter?

Ja, der Überlauf....

Die von dir gelernten Verfahren, wenn man sie richtig anwendet, überleben auch den Überlauf.
Bedingung: Die zu verwaltende Zeit muss kleiner als 49,X Tage sein

... :smiley: und ne Lösung zu diesem Problem? (Jetzt für zukünftige Anwendungen mein ich)

Hab ich übrigends schon wunderbar für mein Beispiel sofort in die Praxis umgesetzt.

Bei Auslösung der Lichtschranke zählt jetzt schön die Variable hoch, schaltet das Licht über das Relais ein und setzt den Timestamp wie ich Ihn nenne (viel ästetischer der name wie ich finde), das Licht bleibt an, bis der Timestamp 2000 ms erreicht hat. Klappt

(Als kleine Anmerkung noch... Ganz streng genommen ist die Timstamp - millis() > 2000 auch wieder nicht 100%ig genau. Ein == funktioniert schon nicht, weil dann der Code genau bei 2000ms durchlaufen werden müsste. Ich denke aber für die meisten Anwendungen würde das trotzdem reichen.)

... :smiley: und ne Lösung zu diesem Problem? (Jetzt für zukünftige Anwendungen mein ich)

Wie so eine Lösung bei mir aussehen könnte/würde:
(Lichtschranke habe ich nicht, darum ein Taster)

#include <CombieTypeMangling.h>
#include <CombiePin.h>
#include <CombieTimer.h>
#include <CombieTools.h>

using namespace Combie::Millis;
using namespace Combie::Tools;
using namespace Combie::Timer;
using namespace Combie::Pin; 

using CounterType = Counter<byte>;

TasterGND<2>      taster; // Taster gegen GND geschaltet
OutputPin<13>     led;
EntprellTimer     entprellen {20_ms};
FlankenErkennung  flankenerkennung;
FallingEdgeTimer  toff     {2_sec};  // nachleucht dauer
CounterType       counter;


void setup() 
{
    Serial.begin(9600);
    taster.initPullup();
    led.init();
    counter.onCount([](CounterType &counter)
                    { 
                      Serial.print("Ereignis Nr: ");
                      Serial.println(counter);
                    });
}

void loop() 
{ 
  bool ereignis = flankenerkennung = entprellen = taster;
  counter       = ereignis;
  led           = toff             = ereignis;
}

mrzortrax:
(Als kleine Anmerkung noch... Ganz streng genommen ist die Timstamp - millis() > 2000 auch wieder nicht 100%ig genau. Ein == funktioniert schon nicht, weil dann der Code genau bei 2000ms durchlaufen werden müsste. Ich denke aber für die meisten Anwendungen würde das trotzdem reichen.)

Dafür kann man Relation und Gleichheit mittels ">=" kombinieren, also jetzt - vorhin >= intervall

Diese Variante ist auch nach ca. 49 Tagen im Zusammenhang mit einer Ablaufsteuerung überlaufsicher.

mrzortrax:
... :smiley: und ne Lösung zu diesem Problem? (Jetzt für zukünftige Anwendungen mein ich)

Dazu müßte man Code von Dir sehen :slight_smile:

combie:
Wie so eine Lösung bei mir aussehen könnte/würde:
(Lichtschranke habe ich nicht, darum ein Taster)

#include <CombieTypeMangling.h>

#include <CombiePin.h>
#include <CombieTimer.h>
#include <CombieTools.h>

using namespace Combie::Millis;
using namespace Combie::Tools;
using namespace Combie::Timer;
using namespace Combie::Pin;

using CounterType = Counter;

TasterGND<2>      taster; // Taster gegen GND geschaltet
OutputPin<13>    led;
EntprellTimer    entprellen {20_ms};
FlankenErkennung  flankenerkennung;
FallingEdgeTimer  toff    {2_sec};  // nachleucht dauer
CounterType      counter;

void setup()
{
    Serial.begin(9600);
    taster.initPullup();
    led.init();
    counter.onCount([](CounterType &counter)
                    {
                      Serial.print("Ereignis Nr: ");
                      Serial.println(counter);
                    });
}

void loop()
{
  bool ereignis = flankenerkennung = entprellen = taster;
  counter      = ereignis;
  led          = toff            = ereignis;
}

Ah interessant... Danke! =) Zwischenfrage hierzu. Mir persönlich fällt direkt auf, das in diesem Fall die Syntax anders zu sein scheint. Woran liegt das? Ist das nur eine Stilfrage? Oder liegt das an den eingebundenen Bibliotheken?

agmue:
Dafür kann man Relation und Gleichheit mittels ">=" kombinieren, also jetzt - vorhin >= intervall

Diese Variante ist auch nach ca. 49 Tagen im Zusammenhang mit einer Ablaufsteuerung überlaufsicher.
Dazu müßte man Code von Dir sehen :slight_smile:

Gute Ergängung dazu. =) Prima.

Aber diese Variante hat doch im Prinzip nichts mit einem eventuellen Überlauf zu tun? Oder hab ich das jetzt falsch verstanden? Was genau meinst du mit Ablaufsteuerung?

Stelle ich mir jetzt so vor, das im Loop eine Prüfung der Laufzeit stattfindet und frühzeitig mit einem Befehl sozusagen ein software reset durchgeführt wird, damit das System neustartet?

Wenn das so gemeint war wäre interessant zu wissen, mit welchem Command ich eigentlich Softwareseitig einen reset durchführen kann, oder zumindest wenn keinen kompletten reset des gesamten Controllers, eventuell nur einen reset der Systemlaufzeit.

Damit wäre das Thema dann weitestgehend langfristig gesehen Wasserdicht.

Das Thema ist auch ohne Reset wasserdicht. jetzt-vorher >= Abstand funktioniert auch beim Überlauf, solange Abstand nicht > 49,x Tage ist.

Das wurde Dir aber schon geschrieben.

Ich habe das hier mal mit Byte nachgebildet, damit Du nicht so lange warten musst. Probiere es aus.

Gruß Tommy

Hallo,

mir scheint Du hast die Verwendung von millis() noch nicht ganz verstande. Alle 49 Tage einen Reset machen ist Unsinn .Bei der üblicherweise verwendeten und bewährten Abfrage

if(millis()-letzte >=intervall){
   letzte=millis();
   // tu was auch immer das sein mag
}

passiert bei einem Überlauf von millis() nichts, es geht ganz normal weiter.

Heinz

Wenn das so gemeint war wäre interessant zu wissen, mit welchem Command ich eigentlich Softwareseitig einen reset durchführen kann, oder zumindest wenn keinen kompletten reset des gesamten Controllers, eventuell nur einen reset der Systemlaufzeit.

Es gibt keinen SoftReset!
Nada!
Völlig unnötig.
Ein Irrweg.

Den WDT könnte man dazu missbrauchen.


Ah interessant... Danke! =) Zwischenfrage hierzu. Mir persönlich fällt direkt auf, das in diesem Fall die Syntax anders zu sein scheint. Woran liegt das? Ist das nur eine Stilfrage? Oder liegt das an den eingebundenen Bibliotheken?

Es ist C++.

Nur die Betrachtungsweise ist eine andere.

Viele neigen dazu Programmflusspläne zu malen. bzw. den "Kontrollfluss" zu denken.
Kommen damit auf die lustigsten if Kaskaden und Schleifen.
Alles so machbar....

Aber es gibt eben auch andere Betrachtungsweisen:
Dort habe ich mich auf den Datenfluss konzentriert.
Und natürlich die beteiligten Komponenten(Libs/Klassen) so geformt, dass sich dieser Datenfluss abbilden lässt.
Die Programmlogik selber, steckt in den einzelnen Komponenten und in ihrer Anordnung in den Datenfluss Verkettungen.

In loop() klar zu sehen, wie die Information von rechts nach links fließt.

bool ereignis = flankenerkennung = entprellen = taster;

Taster ist die Quelle, und Ereignis das Ziel.
Dazwischen liegen die Transformationen: Entprellen und Flankenerkennung

Bei den Timern werkelt innen das "BlinkWithoutDelay" Prinzip.
Nur anders verpackt.

Alles Standard C++11 kompatibel.
Nix andere Syntax.
Syntax, wie im dicken modernen Buch erklärt.

Ok... Wenn ihr das sagt ist das schonmal sehr gut. Nur verstehen tue ich das jetzt noch nicht. Wenn mir das jemand noch verständlich erklären kann, bin ich weitestgehend glücklich. Denn es erscheint mir aktuell als unlogisch und das befriedigt mich noch nicht. :stuck_out_tongue:

Ich speichere ja die Laufzeit von millis() (sind ja wohl aufaddierte millisekunden) in der genannten Variablen (unsigned long int timestamp;)

Diese kann ja einen Wert von maximal dieser Größe speichern: 4,294,967,295

Also das Ührchen läuft und läuft, ich speicher nach jedem Durchlauf die aktuelle Systemzeit neu in die Variable. Doch irgendwann übersteigt doch die Laufzeit, die maximale Länge der oben genannten Zahl und damit würde ja automatisch ein Überlauf entstehen. Das hätte ja aus meiner Sicht erstmal nichts mit der Verarbeitung der gespeicherten Zahl zu tun, sondern das rein aus der Logik das speichern der Systemzeit in der Variable aufgrund der Größenüberschreitung nicht mehr möglich wäre.

So... Und DAS muss mir jetzt bitte einer erklären, warum das doch geht und wie so eine Zahl dann aussieht wenn eine Überschreitung stattgefunden hat.

xD ja, ich sage ja ich stehe noch relativ am Anfang meiner C++ karriere. Von daher bin ich noch nicht so weit um das alles greifen zu können. Aber schön auch andere Varianten zu sehn. Irgendwann kommt dann mal der Punkt wo ich vielleicht sag. Ach das hat der damals damit gemeint. :slight_smile: Wie das dann so eben ist...

Danke. (Ich belass es vorerst bei den millis, das versteh ich jetzt und kann das auch dank euch anwenden.)

combie:
Es gibt keinen SoftReset!
Nada!
Völlig unnötig.
Ein Irrweg.

Den WDT könnte man dazu missbrauchen.


Es ist C++.

Nur die Betrachtungsweise ist eine andere.

Viele neigen dazu Programmflusspläne zu malen. bzw. den "Kontrollfluss" zu denken.
Kommen damit auf die lustigsten if Kaskaden und Schleifen.
Alles so machbar....

Aber es gibt eben auch andere Betrachtungsweisen:
Dort habe ich mich auf den Datenfluss konzentriert.
Und natürlich die beteiligten Komponenten(Libs/Klassen) so geformt, dass sich dieser Datenfluss abbilden lässt.
Die Programmlogik selber, steckt in den einzelnen Komponenten und in ihrer Anordnung in den Datenfluss Verkettungen.

In loop() klar zu sehen, wie die Information von rechts nach links fließt.

bool ereignis = flankenerkennung = entprellen = taster;

Taster ist die Quelle, und Ereignis das Ziel.
Dazwischen liegen die Transformationen: Entprellen und Flankenerkennung

Bei den Timern werkelt innen das "BlinkWithoutDelay" Prinzip.
Nur anders verpackt.

Alles Standard C++11 kompatibel.
Nix andere Syntax.
Syntax, wie im dicken modernen Buch erklärt.

So... Und DAS muss mir jetzt bitte einer erklären, warum das doch geht und wie so eine Zahl dann aussieht wenn eine Überschreitung stattgefunden hat.

unsigned long
0xFFFFFFFF + 1 = 0

irgendwann übersteigt doch die Laufzeit, die maximale Länge der oben genannten Zahl und damit würde ja automatisch ein Überlauf entstehen.

Korrekt!

Was zwingend dazu führt, dass die vorher mal gespeicherte Zeit größer, als die JetztZeit ist!

Jetzt kommt einer da her und rechnet: (kleineJetztZeit - großeGemerkteZeit)

Es findet bei der Subtraktion ein Unterlauf statt.
Dieser Unterlauf kompensiert den vorherigen Überlauf.

Was dazu führt, dass (kleineJetztZeit - großeGemerkteZeit) die korrekte Zeitdifferenz als Ergebnis hat.
Damit liefert Zeitdifferenz >= Intervall immer richtige Ergebnisse.
Über alle Überläufe und Unterläufe hinweg

Male es dir auf Papier auf.