[Projekt] INTERVAL Ersatzstoff

Das mein ich nicht. Muss ja auch so sein, weil die loop ja sonst nix zu tun hat.

Ja, jetzt sehe ich was du meinst...

Da ist ein Verzug drin.

Aber das ist doch beim INTERVAL ganz genau so!
Oder?

Ja, mal schauen, ob das da noch rein passt......

ja, da ist es genauso.

Mal angenommen, das Problem wäre, dass du unbedingt delay() einsetzen möchtest:

#include "CombieTimer.h"
using Combie::Pulsator;

Pulsator puls(1000);

void setup(void) 
{
  Serial.begin(9600);
  Serial.println();Serial.println("Start");Serial.println();

}

void yield(void) 
{
  if(puls) Serial.println(millis());
}

void loop(void) 
{
 delay(4711);
}

Das ist aber nicht in deinem Sinn!
(nehme ich mal an)

Hätte aber den Vorteil, dass du in deine langlaufenden Dinge nur ein paar yield(); Aufrufe einstreuen müsstest, damit sich das Timing verbessert.

Vorsicht mit yield()!
Man hat sich da ganz schnell eine indirekte Rekursion gebaut.

Jetzt ist es so, dass die Zeiten evtl. mal länger sind, aber nie kürzer.

Würde ich jetzt SimpleTimer modifizieren, dann wäre es so:
Manchmal sind die Zeiten kürzer, dann wenn aufgeholt wird, und manchmal länger, wenn andere lang laufende Dinge im Weg stehen. Im Mittel, würde es dann passen.

Ich denke, das ist eine Aufgabe für einen eigenen "AufholPulsator"

In SimpleTimer oder den Pulsator möchte ich es nicht aufnehmen.
Denn in SimpleTimer hat es nichts zu suchen, da es für die anderen Timer nicht gebraucht wird.
Und der Pulsator zeigt jetzt (noch) das gewohnte INTERVAL Verhalten.

Ein Problem für Morgen....

delay verwende ich prinzipiell nicht, ausser ich weiss genau was ich tue oder eben bei quick and dirty.

In dem Beispiel habe ich delay genommen, um zu zeigen, was passiert, wenn eine loop mal länger dauert. Bei thinkspeak z.b. warte ich auf die Antwort, anstatt es mit einem endlichen Automaten zu machen. Sollte ich vielleicht auch mal umschreiben.
eine loop sieht z.B. so aus:

  seriell_lesen();
  EingaengeAbfragen();
  TasterAbfragen();
  SR04auslesen();
  lcdaktualisieren(0);
  lcduntenaktualisieren();
  Serverroutine();
  DS1820auslesen();
  schalterfunktionen();
  WasserhochpumpenSR04();
  uhrAbfragen();
  filterSpuelen();
  alarme();
  Automat();
  AusgaengeSchreiben();
  checkUDP();
  intervalle();

nichts von alledem ist wirklich blockierend, aber doch können schonmal über 100ms zusammenkommen, wenns dumm läuft. Innerhalb Funktionen nehm ich dann INTERVAL
Aber wirklich gestört hat der Verzug bei INTERVAL ja auch nicht.

Nöö, das ist schon alles ganz ok so.

Was du meinst, dreht sich eher um Übersetzungseinheiten.
Wenn einen statische Eigenschaften zu sowas zwingen.

Wenn man Libraries bauen möchte, die nicht jedes mal übersetzt werden sollen.
Dann beschleunigt es den Übersetzungsvorgang des gesamt Projektes.

Anderes Beispiel:
Es bietet eine Gelegenheit den Quellcode nicht mit ausliefern zu müssen, sonder nur die *.h und die *.a Datei
Aber so geheim ist mein Zeugs nicht.

ElEspanol:
ja, da ist es genauso.

Ich habe einige Zeit über das Problem nachgedacht...

Auf folgendes bin ich gekommen:

class AufholPulsator
{
      private:
      unsigned long timeStamp = 0;
      unsigned long interval;

      public:
      AufholPulsator(unsigned long interval):interval(interval){}

      void start()
      {
        timeStamp = millis();
      }

      void setInterval(const unsigned long _interval)
      {
         interval = _interval;
      }

      operator bool()
      { 
        bool abgelaufen = millis() - timeStamp >= interval;
        if(abgelaufen) timeStamp += interval;
        return abgelaufen;
      }
};


AufholPulsator puls(1000);


void setup(void) 
{
  Serial.begin(9600);
}

void loop(void) 
{
   if(puls) Serial.println(millis());
   delay(137);
}

So holt es den Verzug wieder auf.

Zumindest akkumuliert sich der Verzug sich nicht mehr, von Puls zu Puls.
Solange der Verzug kürzer, als das Intervall ist, gelingt das auch.

Naja....

Das ganze als Github wär nett.

Da sprichst du was an…

Hilfst du mir beim striegeln, und bürsten?
Doku schreiben, usw…
Beim Seite bauen, im Arduino Playground?

Etwas Fleißarbeit ist auch noch nötig, damit das in den Librarymanagern aller Arduinoinstallationen weltweit automatisch auftaucht.

Kein Scherz!
Die Arbeit steht an, wenn man das tun möchte.

CombieLib.zip (13.7 KB)

Ich kann nur sehen was Jomelo da hat:

So viel ist es ja nicht und im ersten Post hast du schon deine Doku :slight_smile:
Seite bauen und Playground ist doch nicht nötig, aufs Github drauf wie Jomelo und gut is.
Dann kann Jomelo auch den Hinweis ändern:

Attention:

when you change from v2.3.4 to 3.0.0 ** the backend system is removed from this lib use this when you need a task system: Arduino Playground - TaskScheduler

so ähnlich könnts ausschauen:

Attention:

when you change from v2.3.4 to 3.0.0 ** the backend system is removed from this lib use this when you need a task system: https://github.com/Combie/CombieLib

ElEspanol:
delay verwende ich prinzipiell nicht, ausser ich weiss genau was ich tue oder eben bei quick and dirty.

Ich genauso.
Meine Timings erledige ich meistens so:

    if (millis() / 1000 % 600 == 0)        // every 10 Minutes
    {

    }
    if (millis() / 1000 % 600 == 1)        // every 10 minutes, one second later
    {

    }

   if (millis() / 100 % 10 == 0)        // every second
    {

    }
    if (millis() / 100 % 10 == 1)        // every second, 100ms later
    {

    }

oder auch

switch (millis() / 100 % 10)
{
  case 0:
   // every second
  break:
  case 1:
   // every second 100mS later
  break:
  case 2:
  // every second 200mS later
  break:
  // and so on...
}

Das ganze noch mit "done" Semaphoren, die im Interrupt zurückgesetzt werden wenn die Aufgabe pro Zyklus nur einmal erfolgen darf....

Somit kann man unkompliziert und intuitiv die gesamte Last fein säuberlich verteilen.

Aber: est natürlich immer noch "collaborative" die jew. Aufgabe darf nicht über den Takt sabbern.

if (millis() / 1000 % 600 == 0) // every 10 Minutes

ist leider falsch, wenn man möchte, dass es alle 10 Minuten einmal drankommt.

Nachtrag:
Und wenn man das gemerkt hat, und deswegen mit done Semaphoren oder ähnlichem hantiert, kann man es auch gleich "richtig" nach dem BlinkWithoutDelay-Schema machen.

michael_x:
ist leider falsch, wenn man möchte, dass es alle 10 Minuten einmal drankommt.

Nachtrag:
Und wenn man das gemerkt hat, und deswegen mit done Semaphoren oder ähnlichem hantiert, kann man es auch gleich "richtig" nach dem BlinkWithoutDelay-Schema machen.

Manchmal juckt es keiner, wenn es mehrmals passiert. Zum Blinken z.B. wäre es völlig wurscht.

Der BlinkWithoutDelay-Schema wird, wenn man eine Staffelung wie "// every 10 minutes, one second later" benötigt, komplett unübersichtlich.

Hallo,

seltsame Herangehensweise. Entweder es funktioniert richtig oder gar nicht. Halbe Sachen gibts bei mir nicht. Übersicht behalte ich, wenn ich die zeitliche Bedingung in die Funktion schreibe. Das sind 4 Zeilen. Dann erhalte ich eine aufgeräumte loop. Mehr Übersicht geht nicht. Meine Meinung.

Doc_Arduino:
wenn ich die zeitliche Bedingung in die Funktion schreibe. Das sind 4 Zeilen. Dann erhalte eine aufgeräumte loop. Mehr Übersicht geht nicht.

Ich schreibe die sogar teilweise mit in die Funktion, wenn die Funktion nur in diesem Zusammenhang benutzt wird. Ausgelagert in ein Tab und ich habe in der loop nur den Funktionsaufruf.

Der BlinkWithoutDelay-Schema wird, wenn man eine Staffelung wie “// every 10 minutes, one second later” benötigt, komplett unübersichtlich.

Du kannst innerhalb des Zyklus jederzeit abfragen, wo du gerade stehst, und z.B. die ersten Sekunde lang die LED anschalten, und sie danach ausmachen. Oder so…

Stimmt, es gibt Fälle wo es egal ist, wie oft eine “Aktion” wiederholt wird. Aber wenn du etwas absichtlich (warum?) erst später ausführen willst… ?

const unsigned long INTERVALL=600*1000L; // 10 Minuten
static unsigned long last;
if (millis() - last > INTERVALL) {
   last = millis();
   // Einmal-Aktion
}

digitalWrite(LED,  millis() - last < 2000 ); // Im Intervall 2 sec HIGH, danach LOW

static bool OneSecond;
if (millis() - last > 1000 ) {
   if (! OneSecond)  { /* Einmal die verzögerte Aktion. */ }
   OneSecond = true;
} else OneSecond = false;

Ob das nach loop() oder in eine separate Funktion gehört, ist natürlich zu überlegen…
Stimme ich auch zu.

michael_x:
Aber wenn du etwas absichtlich (warum?) erst später ausführen willst... ?

Z.B. Sensor.request und dann 2 Sekunden danach die Temperaturabfrage bei einem DS18B20, etc.

michael_x:
...Aber wenn du etwas absichtlich (warum?) erst später ausführen willst... ?...

Warum? um die Last zeitlich zu verteilen.
Wenn man schnelle und langsamen Aufgaben erledigen will, ist es vorteilhaft, wenn die langsame auch nicht anfangen wenn die schnelle gerade dran sind...

Jetzt hast du aber glaube ich einen Denkfehler. Der Arduino macht nur alles nacheinander. Und langsamere Aufgaben gibt es nicht, der macht immer gleich schnell. Du meinst wahrscheinlich umfangreichere, länger dauernde Aufgaben. Und wenn diese eben zu lange Dauern, musst du sie aufteilen und im loop immer nur einen Teil davon machen. Stichwort: endlicher Automat, Etc.

Will sagen, wenn du eine „langsame“ Aufgabe hast, die dir wegen ihrer Ausführungsdauer Probleme macht, ist der Sketch nicht optimal designed.

ElEspanol:
...und wenn diese eben zu lange dauern, musst du sie aufteilen und im loop immer nur einen Teil davon machen...

Genau das erfolgt mit dem "// every 10 minutes, one second later"

Das ist kein Denkfehler, das ist das Konzept.

Oder alle 100 loops in den nächsten Teil gehen, oder, ....

Da gibt es mehrere Möglichkeiten.