In Dauerschleif eine Aktion alle 30s durchführen oder direkt am Start

Gast #6794128
Lesenswert?

Hallo,

in meinem arduino code möchte ich alle 30s eine Aktion durchführen oder 
beimersten Druchlauf, dafür habe ich folgenden Code geschrieben:
1
const unsigned long statusMessageInterval = 1000*30* 1; // 30s
2
unsigned long statusMessageTimer = 0;
3
void loop(){
4
if ( ((millis() - statusMessageTimer) >= statusMessageInterval ) || statusMessageTimer == 0)
5
  {
6
//doing
7
statusMessageTimer = millis();
8
}
9
}

"doing" wird aber nur alle 30s durchgeführt und nicht beim ersten 
durchlauf...Was mache ich falsch??

Grüße
Gast #6794153
Lesenswert?

nichts...

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
}

const unsigned long statusMessageInterval = 1000  3  1; // 3s
unsigned long statusMessageTimer = 0;

void loop() {
  if ( ((millis() - statusMessageTimer) >= statusMessageInterval ) || 
statusMessageTimer == 0)
  {
    Serial.print("statusMessageTimer = 
");Serial.println(statusMessageTimer);
    Serial.print("millis() = ");Serial.println(millis());
    statusMessageTimer = millis();
  }
}
Seraller Monitor:
statusMessageTimer = 0
millis() = 54
statusMessageTimer = 54
millis() = 3054
statusMessageTimer = 3054
millis() = 6054

habs mal auf 3 sekunden reduziert..
#6794188
Lesenswert?

Geil wäre ja, einfach einfach zu programmieren.
1
zeitMerken();
2
doWhatever();
3
wartenBisZeitAbgelaufen();
in der Mainloop sollte da reichen.

Den Trick mit dem warten auf Millis und dem möglichen Überlauf alle paar 
Wochen muss man natürlich beachten, oder man nutzt simpel delay() wenn's 
auf die 30s nicht so ankommt.
Gast #6794287
Lesenswert?

Nur als Anmerkung, da steckt ein kleiner systematischer Fehler drin:
Timervariable wird für Timerwert und Timerstatus (status==0) 
missbraucht.
1
if ( ((millis() - statusMessageTimer) >= statusMessageInterval ) || statusMessageTimer == 0)
2
  {
3
    doit();
4
    statusMessageTimer = millis();


Fällt der Rückgabewert von millis() zufälligerweise auf den Wert 0, wird 
nicht gewartet und sofort die Routine ausgeführt.
Da der Rückgabewert 32Bit hat, dürfte das sehr selten passieren.

Aber wenn jemand auf die tolle Idee kommt, den Code für einen 100ms 
Zyklus zu nutzen , steigt die Wahrscheinlichkeit natürlich an.
Dann wundert man sich, dass das System nach 30Tagen abstürzt oder 
unerklärliche Dinge tut...

Besser wäre es in setup() den ersten Durchlauf aufzurufen und die 
zyklischen im Loop.
Dann ohne das Konstrukt '|| statusMessageTimer == 0'.

Oder man baut eine Zustandsmaschine mit extra Variable.
Gast #6794329
Lesenswert?

>Dann setze eben den Startwert so, daß die Bedingung beim Eintritt in
>loop() erfüllt ist.

Logisch.

>unsigned long statusMessageTimer = millis() + 30 Sekunden;

Besser:

unsigned long statusMessageTimer = millis() - 30 Sekunden;

Noch besser:

unsigned long MilliSecTick = millis() - MILLISEC_TICK_INTERVAL;
#6794380
Lesenswert?

Falk B. schrieb:
> Man könnte ja die Funktion einfach auch in setup() einmalig aufrufen . .

Ich glaub das ist Geschmackssache, lässt sich drüber streiten ob das 
schön ist :-)

Wie wäre es mit sowas, da könnte man dann auch weitere "timeouts" 
hinzufügen.

Und as Problem mit dem Überlauf bei Tag 49 fällt auch weg.
1
const uint32_t INTERVAL = 3000;    // 3 Sek.
2

3
uint32_t ms;
4
uint32_t last_ms;
5
uint32_t timeout;
6

7
void update_timeout(void)
8
{
9
  uint32_t diff_ms;
10

11
  ms = millis();
12
  
13
  if (last_ms != ms)
14
  {
15
    diff_ms = (ms - last_ms);
16
    last_ms = ms;
17
    
18
    if (diff_ms > timeout)
19
      timeout = 0;
20
    else
21
      timeout -= diff_ms;
22
  }
23
}
24

25
void setup()
26
{
27
  Serial.begin(115200);
28
}
29

30
void loop()
31
{
32
  update_timeout();
33

34
  if (!timeout)
35
  {
36
    timeout = INTERVAL;
37
    
38
    Serial.print("millis() = ");Serial.println(millis());
39
  }
40
}
Gast #6794409
Lesenswert?

Das 'if (last_ms != ms)' in der Update Funktion kann man auch weglassen. 
Macht vermutlich an der Performance nicht so viel aus.

Ich würde den SOftwaretimer ggf noch in eine Klasse oder eine Template 
kapseln.
Gast #6794457
Lesenswert?

1
#include <INTERVAL.h>
2

3
#include <Streaming.h> // die Lib findest du selber ;-)
4
Stream &cout = Serial; // cout Emulation für AVR Arduinos
5

6
void setup() 
7
{
8
  Serial.begin(9600);
9
  cout << F("Start: ") << F(__FILE__) << endl;
10
  
11
}
12

13
void loop() 
14
{
15
  //INTERVAL(Wiederholung,Startzeit) { // tuwas();}
16
  INTERVAL(30000,0) cout << millis() << endl;
17
}
Angehängte Dateien:
#6794506
Lesenswert?

Eigentlich sollte das kein Problem sein.
Wie bereits gesagt, lagere, alles was Du zyklisch vorhast in eine 
Funktion aus. Z.B. in HauReinOnkelOtto().
Dann sieht Dein Programm folgendermaßen aus:
In setup(), nachdem die Initialisierung abgeschlossen ist einmalig 
HauReinOnkelOtto() aufrufen. Nicht vergessen die Referenzzeiten in die 
Ausgangsstellung bringen.

Später, in der Hauptschleife:

Frage die Zeitreferenz ab;
Endzeit erreicht?
Ja: HauReinOnkelOtto();
    Zeit in Ausgangsstellung bringen;

Den anderen Kram machen, der so anliegt.
Hauptschleife Ende
Gast #6794543
Lesenswert?

EAF hat im Beitrag #6794457 folgenden Anhang gepostet:
1
// Start of Interval Macro
2
#define INTERVAL_2(interval,firstInterval)     \
3
{                                              \
4
  static unsigned long lastHit = 0;            \
5
  static bool firstRun = true;                 \
6
  DoINTERVAL = millis() - lastHit >= (firstRun?(firstInterval):(interval)); \
7
  if(DoINTERVAL)                   \
8
  {                                \
9
     lastHit = millis();           \
10
     firstRun = false;             \
11
  }                                \
12
}                                  \
13
if(DoINTERVAL)
14
// End of Interval Macro

Ja, so kann man es machen und ist in keinster Weise falsch.
Aber ehrlich, Arduino ist C++11.

Ich würde hier wirklich ein class oder template Konzept nutzen.
Es gibt hunderte von Varianten im Netz. Z.B.

https://www.arduino.cc/reference/en/libraries/simpletimer/

Preprocessor Makros sollten wirklich nur da genutzt werden,
wo es keine bessere Möglichkeit gibt.
Function Macros nutze ich nur bei Androhung von Gewalt oder wenn ich 
eine Sauerei mit \_\_FILE\_\_ und \_\_LINE\_\_ benötige...
Gast #6794554
Lesenswert?

Martin Teuer schrieb:
> "doing" wird aber nur alle 30s durchgeführt und nicht beim ersten
> durchlauf...Was mache ich falsch??

Du gehst davon aus, dass der Timer beim ersten Aufruf der if-Abfrage 
noch auf 0 steht. Bis dahin wird er aber schon etwas weiter sein.

Zwischen dem Start des Timers und dem Ausführen der loop() Funktion 
passiert je nach Projekt noch einiges mehr, was du in deinem eigenen 
Quelltext nicht sehen kannst.
Gast #6794578
Lesenswert?

BeBe schrieb:
> Aber ehrlich, Arduino ist C++11.
Nicht nur.
Der Core ist größtenteils in C und etwas ASM
Das Macro sollte auch in C funktionieren.

BeBe schrieb:
> Ich würde hier wirklich ein class oder template Konzept nutzen.
Ja!
Kann man durchaus tun.
(habe ich auch noch im Köcher)
Wird aber nicht sparsamer, in Sachen RAM/FLASH
1
// Wieder eine Variation des BlinkWithoutDelay
2

3
#include <CombieTimer.h>
4

5
Combie::Timer::Pulsator puls(1000); // liefert alle 1000 ms einmal true sonst false
6

7

8

9
void setup()
10
{
11
  pinMode(LED_BUILTIN,OUTPUT);
12
  // puls.start(); // ohne start() beginnt puls mit Pause, sonst mit Impuls
13
}
14

15
void loop()
16
{
17
  if(puls) digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
18
}
#6794637
Lesenswert?

EAF schrieb:
> BeBe schrieb:
>> Ich würde hier wirklich ein class oder template Konzept nutzen.
> Ja! Kann man durchaus tun. (habe ich auch noch im Köcher)
> Wird aber nicht sparsamer, in Sachen RAM/FLASH

Das ist aber nicht der springende Punkt.
Es ist normalerweise nicht unbedingt sparsamer aber normalerweise auch 
nicht größer!  (Alles in Macros geht auch in Templates.)
Dafür hat man besseres Diagnose/Warnungen, Typprüfung in IDEs 
kontextsensitive Hilfen, besseres Debugging und anderes.

Aber Du kennst beide Seiten (C und C++), das brauch ich Dir nicht zu 
sagen.
Es ging mit um Mitleser, die später an Makro Code verzweifeln könnten.
Gast #6794647
Lesenswert?

Klaus H. schrieb:
> EAF schrieb:
>> BeBe schrieb:
>>> Ich würde hier wirklich ein class oder template Konzept nutzen.
>> Ja! Kann man durchaus tun. (habe ich auch noch im Köcher)
>> Wird aber nicht sparsamer, in Sachen RAM/FLASH
>
> Das ist aber nicht der springende Punkt.
> Es ist normalerweise nicht unbedingt sparsamer aber normalerweise auch
> nicht größer!  (Alles in Macros geht auch in Templates.)
> Dafür hat man besseres Diagnose/Warnungen, Typprüfung in IDEs
> kontextsensitive Hilfen, besseres Debugging und anderes.
(Firma: irre) #6794682
Lesenswert?

BeBe schrieb:
> Es gibt hunderte von Varianten im Netz. Z.B.
>
> https://www.arduino.cc/reference/en/libraries/simpletimer/

Habe ich mir gerade angesehen, kann aber nicht wirklich erkennen, warum 
dort 64 Bit Intervalle genutzt werden.

Dieses kommt mit 32 Bit aus.
1
    class SimpleTimer
2
    {
3
      private:
4
      uint32_t timeStamp;      // Zeitmerker
5
      bool reached;                // default Status: timer abgelaufen
6
    
7
      public:
8
      SimpleTimer():timeStamp(0),reached(true){}
9
      
10
      void start()
11
      {
12
        timeStamp   = millis();
13
        reached     = false;
14
      }
15
      
16
      void reset()
17
      {
18
         reached     = true;
19
      }
20
    
21
      bool operator()(const uint32_t interval) 
22
      {
23
        if(!reached) reached = millis() - timeStamp >= interval;
24
        return reached;
25
      }
26
    };

Dieser "SimpleTimer" ist die Kern von dem vorher gezeigtem Pulsator
1
    
2
    class Pulsator   // liefert alle X ms einen HIGH Impuls
3
    {
4
      protected: 
5
      SimpleTimer timer;
6
      uint32_t interval;
7
      
8
      public:
9
      Pulsator(uint32_t interval):interval(interval){}
10
      
11
      void setInterval(const uint32_t interval)
12
      {
13
         this->interval =  interval;
14
      }
15
      
16
      void start()
17
      {
18
        timer.start();
19
      }
20
      
21
      operator bool()
22
      {
23
          bool result = false;
24
          if(timer(interval))
25
          {
26
            result = true;
27
            timer.start();
28
          }
29
          return result;
30
      } 
31
    };
Beitrag #6794705 wurde von einem Moderator gelöscht.
Gast #6794733
Lesenswert?

Minus M. schrieb:
> Habe ich mir gerade angesehen, kann aber nicht wirklich erkennen, warum
> dort 64 Bit Intervalle genutzt werden.

Nicht nur das!
Sie enthält einen massiven Fehler!
1
bool SimpleTimer::isReady() {
2
    return _start + _interval <= millis();
3
}

Angenommen, millis() steht ganz kurz vor dem Überlauf, _start wird 
gleich millis() gesetzt und interval ist hinreichend groß.

Jetzt ist die 64Bit Summe aus _start + _interval größer als der 32Bit 
Wertebereich von millis().

Resultat:
Die Methode wird niemals wieder ein true liefern.
Die Chancen stehen also recht gut, dass das Gerät nach 49 Tagen einen 
Reset benötigt.
Gast #6794839
Lesenswert?

wennst deinen "previousMillis" statt mit 0 schon einfach alt genug 
initialisiert, ist deine Bedingung beim ersten Lauf schon true

const unsigned long statusMessageInterval = 1000*30* 1; // 30s
unsigned long statusMessageTimer = 0 - statusMessageInterval;
Gast #6795076
Lesenswert?

LostInMusic schrieb:
> Zuweisung eines negativen Werts an eine "unsigned"-Variable?

unsigned long statusMessageTimer = 0UL - statusMessageInterval;
Macht die Sache geringfügig offensichtlicher, da der Unsigned Unterlauf 
durchaus vom Standard abgedeckt wird.

Was mich da eher stört:
Der Urknall, in der Welt eines µC, ist der PowerOn.
Dort gibt es eigentlich noch gar keine vergangenen Zeitpunkte. Von daher 
scheint es mir unnatürlich einen Zeitpunkt in die Vergangenheit zu 
setzen.

Ist das nicht eher ein dirty Hack?
Gast #6795476
Lesenswert?

Oliver S. schrieb:
> Man muss nicht alles selber machen:
> https://github.com/sstaub/Ticker
Da hast du Wahr!

Aber auch da findet sich ein Haar in der Suppe.
Die Klasse macht zu viel.
Sie hat dadurch recht viele Eigenschaften(Variablen).

Zudem:
1
        uint32_t Ticker::elapsed() {
2
  if (resolution == MILLIS) return millis() - lastTime;
3
  else return micros() - lastTime;

Hier sieht man dem Methodenaufruf nicht an, ob man einen Wert in Milli- 
oder Mikrosekunden zurück erhält.
Das ist abhängig von der Instanziierung, und die ist ganz woanders.
Das Verhalten zieht sich wie ein roter Faden durch die ganze Klasse.
Das ist nicht so schön......

Aber natürlich, nutzbar wird das schon sein.



noiasca schrieb:
> IMHO nein. Die Situation ist die gleiche wie in 49 Tagen nach dem
> millis() rollover

Ja, sie ist ähnlich.
So ähnlich, dass es funktionieren wird.

Aber schön ist das nicht, aus meiner Sicht.
Ins Besondere, wenn es auch anders geht.
Gast #6795484
Lesenswert?

EAF schrieb:
> Die Klasse macht zu viel.

Nachdem die Source öffentlich ist und sehr klein und
übersichtlich, dürfte es ein Leichtes sein sie so zu strippen
dass sie auch dir klein genug ist.

Wenn man Glück hat macht das sogar der Compiler durch Weglassen
unbenutzer Funktionen bzw. Methoden.
Gast #6795510
Lesenswert?

mitlesa schrieb:
> Nachdem die Source öffentlich ist und sehr klein und
> übersichtlich, dürfte es ein Leichtes sein sie so zu strippen
> dass sie auch dir klein genug ist.
Ja!

Steht aber im Widerspruch zu:
Oliver S. schrieb:
> Man muss nicht alles selber machen

Zudem bin ich mit solchen "Dingen" schon bestens ausgestattet.
Zwei Varianten gabs hier schon zu sehen, aber gerne auch noch eine 
Dritte(ein Ausschnitt):
1
class TuwasTask: public Task
2
{
3
  protected:
4
    unsigned long interval;
5
  
6
  public:
7
    TuwasTask(const unsigned long interval):interval(interval){}
8
    
9
    virtual void run() override
10
    {
11
      TaskBlock
12
      {
13
        while(1)
14
        {
15
       //   tuwas();
16
          taskPause(interval);
17
        }  
18
      }
19
    }
20
};
Gast #6795542
Lesenswert?

EAF schrieb:
> Aber schön ist das nicht, aus meiner Sicht.

Schönheit ist ja sehr was individuelles.

c unsigned overflow ist "defined behavior" daher kann man das schon 
nutzen.

Die Variable startet bei mir mit
statusMessageTimer = 4294964296

Die Subtraktionsvarianten bringen jedenfalls den Variableninhalte bzw 
Millis() in eine Kombination, wie sie auch zur Laufzeit auftreten 
können.

Die Additionsvarianten funktionieren im konkreten Beispiel auch. Eine 
Kombination millis() = 0 und statusMessageTimer = 3000 wird aber zur 
Laufzeit nicht vorkommen. Daher finde ich das nicht besser.

Mit millis() initialisieren sehe ich keinen Grund, kostet auch etwas 
Flash und daher kann man das auch dem Compiler überlassen.

Das 0UL ... wenn man mag. Bei mir hat der Compiler aber auch richtig 
4294964296 eingesetzt.
1
// https://www.mikrocontroller.net/topic/523549
2
void setup() {
3
  Serial.begin(115200);
4
}
5

6
const unsigned long statusMessageInterval = 1000 * 3 * 1; 
7

8
//unsigned long statusMessageTimer = millis() + statusMessageInterval; //  Michael U. (amiga) 19.08.2021 09:07      1932/226
9
//unsigned long statusMessageTimer = millis() - statusMessageInterval; //  LostInMusic (Gast) 19.08.2021 11:53    1932/226
10
unsigned long statusMessageTimer = 0 - statusMessageInterval;        //  noiasca (Gast) 19.08.2021 19:18        1890/226
11
//unsigned long statusMessageTimer = 0UL - statusMessageInterval;      //  EAF (Gast) 20.08.2021 00:11            1890/226
12

13
void loop() {
14
  if ( ((millis() - statusMessageTimer) >= statusMessageInterval ))
15
  {
16
    Serial.print("statusMessageTimer = "); Serial.println(statusMessageTimer);
17
    Serial.print("millis() = "); Serial.println(millis());
18
    statusMessageTimer = millis();
19
  }
20
}
Gast #6795578
Lesenswert?

noiasca schrieb:
> Das 0UL ... wenn man mag. Bei mir hat der Compiler aber auch richtig
> 4294964296 eingesetzt.
Ja, das macht er richtig.
Das UL hat dort eher dokumentierenden Charakter.

Hier fände ich das noch viel wichtiger!
Martin Teuer schrieb:
> const unsigned long statusMessageInterval = 1000*30* 1; // 30s


Denn die Berechnung steht (ohne UL) mit den Werten schon ganz kurz vorm 
Überlauf.
 const unsigned long statusMessageInterval = 1000*40* 1; // 40s
klappt schon nicht mehr wirklich
>  warning: integer overflow in expression of type 'int'

Besser:
 const unsigned long statusMessageInterval = 1000UL  30  1; // 30s

---

Aber davon abgesehen, ich wüsste nicht wie ich den Hack dokumentieren 
sollte, wenn ich ihn bei mir anwenden wollte....(ohne mich zu schämen)
OK, das war jetzt arg subjektiv.
Wie auch immer, es ist ein freies Land, mit freier Fahrt, wo jeder das 
machen kann, was er gerne tut.
Die Grenzen werden einem schon aufgezeigt, oder eben auch nicht.

Ich für mich weiß nur, dass man sich an solche Hacks gewöhnt. Dann auch 
mal gerne zum Prinzip erhöht.
#6795689
Lesenswert?

EAF schrieb:
> ich wüsste nicht wie ich den Hack dokumentieren
> sollte, wenn ich ihn bei mir anwenden wollte....(ohne mich zu schämen)
1
statusMessage=1;
2
void loop() {
3
if (statusMessage)
4
  {
5
    Serial.print("statusMessageTimer = ");   Serial.println(statusMessageTimer);
6
    Serial.print("millis() = "); Serial.println(millis());
7
    statusMessageTimer = millis();
8
    statusMessage = 0;
9
  }
10
if((millis() - statusMessageTimer) >= statusMessageInterval ) statusMessage = 1;
11
}
Auch nich wirklich hübsch aber keine krude "Geheimzahl"...?-/
Gast #6795705
Lesenswert?

Ich arbeite immer mit 1 Variable dann brauch ich mir das ganze nicht 
antun.

Neu_millis = millis() + Zeit_ich_will '(in Millis)

If millis() > Neu_millis then
  mach_was_du_machen_sollst
  Neu_millis = millis() + Zeit_ich_will.
end if


Fertig.

Beim Start setzt ich neu_millis auf den passenden Wert und das wars.
Wenn ich den Überlauf abfangen will brauche ich nur den Max_wert zu 
überprüfen und den Wert passend zu setzen. Ist eine Zeile mehr.

Wieso macht ihr da so ein Aufwand.
Gast #6795897
Lesenswert?

Schlaumaier schrieb:
> Wenn ich den Überlauf abfangen will
Du hast da 3 (potentielle) Überläufe!
Für jeden eine Extrawurst backen...

Schlaumaier schrieb:
> Ist eine Zeile mehr.
Dann zeige das doch mal....

Schlaumaier schrieb:
> brauche ich nur den Max_wert zu
> überprüfen und den Wert passend zu setzen.
Ich sehe keinen Max_wert.
Und bei unseren Geschichten muss man den Überlauf überhaupt nicht 
prüfen.
Wozu auch?
Solange das Interval kleiner als 49 Tage ist, kein Problem.
Und 30 Sekunden sind deutlich drunter.



Schlaumaier schrieb:
> Wieso macht ihr da so ein Aufwand.
Tja....
Vielleicht, weil man auch bei einem solchen Trivialproblen noch Bockmist 
bauen kann?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren