Millis() Timer pausieren und wieder starten

Neues Projekt, neue Fragen.

Das Vorhaben: Auf einem LCD Display sollen zwei verschiedene Zähltimer angezeigt werden, welche mit Buttons umgeschaltet werden.

Also:
Arduino starten: Noch nichts passiert.
Button1 drücken: Timer 1 startet, zählt in Sekunden hoch, Timer 2 macht noch nichts.
Button2 drücken: Timer 1 pausiert, Timer 2 fängt an zu zählen.
Button1 drücken: Timer 1 startet wieder, und zwar da wo er pausiert hat, Timer 2 pausiert wieder
usw...

Ich habe probiert das ganze mit millis() zu realisieren und habe alle möglichen Rechnungen getestet, habe aber keine sinnvolle Lösung gefunden, ohne für jede eventuell notwendige Pause eine extra Variable anzulegen welche die Pausenzeit speichert. Das wäre nur eine absolute Notlösung, da die Anzahl der Pausen/Umschaltungen nicht vorhersehbar ist.
Gibt es dafür eventuell irgendwelche Libraries die das ganze vereinfachen oder einen Denkansatz auf den ich noch nicht gekommen bin? Meine Recherche lief leider bisher ins Leere...

Vielen Dank schonmal

Frage: Müssen die Zähler exakte Sekunden ab Tastendruck zählen oder würde es reichen, wenn sie sich an einen unabhängig laufenden Sekundenzähler sozusagen anhängen?


Gruß Walter

const byte tast = 10;
enum {warten, time1, time2};
byte schritt = warten;
unsigned long zeitmerker[2] = {0};
void setup() {
  Serial.begin(115200);
  Serial.println(F("Start...."));
  pinMode(tast, INPUT_PULLUP);
}

void loop() {
  if (!digitalRead(tast))
  {
    schritt++;
    if (schritt > time2) schritt = time1;
  }
  zeit(schritt);
  ausgabe(schritt);
}

void ausgabe(byte timerauswahl)
{
  if (timerauswahl == 1)
  {
    Serial.println(zeitmerker[0]);
  }

  if (timerauswahl == 2)
  {
    Serial.println(zeitmerker[1]);
  }

}

void zeit(byte lastschritt)
{
  switch (lastschritt)
  {
    case warten:
      Serial.println(F("nichts passiert"));
      break;
    case time1:
      Serial.println(F("hier zaehle Time1"));
      if (millis() % 1 == 0) {
        zeitmerker[0]++;
      }
      break;
    case time2:
      Serial.println(F("hier zaehle Time2"));
      if (millis() % 1 == 0) {
        zeitmerker[1]++;
      }
      break;
  }
}

Mach was draus.

Achtung, evtl. schiebt es Dir den Seriellen Monitor voll. Also nicht auf einer empfindlichen Kiste ausprobieren.

Hallo,

@ TO:
hast du was mit User h_and_y zu tun? Sei ehrlich.

Du musst nicht für jeden Stop und Resume eine Variable anlegen. Du musst diese nur einmalig wenn er weiterzählen soll verrechnen.

Bei Stopp speicherst du den aktuellen millis Wert meinetwegen in 'stopp'.
Wenn er weiterzählen soll, bildest du die letzte Differenz und ziehst diese vom dann aktuellen millis ab. Du legst deine Startzeit um die alte Differenz zurück.

start = millis() - (stopp - start);

Danach subtrahierst du wieder wie gewohnt mit (millis - start) und hast deine laufende Zeit inkl. korrigierten Resume Wert. Weil dieser steckt ab dem Zeitpunkt in start drin.

Moin
Was soll passieren, wenn die Knöpfe wieder losgelassen werden?

Danke für die rege Beteiligung.

@wno158
Es wäre schön wenn die Zählung der beiden Timer relativ exakt ist, es kommt aber nicht auf 2-3 Sekunden in der Gesamtzählung an.

@my_xy_projekt
Danke, werde ich dann versuchen umzusetzen. Ich hatte bereits versucht eine Variable hochzählen zu lassen gebunden an die millis()-Funktion, das hat mit meiner Variante aber noch nicht funktioniert.

@Doc_Arduino
Falls du mit "TO" mich meinst (diese Abkürzung ist mir neu, wofür genau steht sie?): Nein, diesen User kenne ich nicht.
Das habe ich ebenfalls bereits versucht, klingt auch erstmal logisch. Ich prüfe nochmal ob in meiner Verschachtelung ein Fehler drin war.

@paulpaulson
Wie gesagt:
Knopf 1: Timer 1 läuft, Timer 2 pausiert
Knopf 2: Timer 1 pausiert, Timer 2 läuft weiter
Knopf 1: Timer 1 läuft, Timer 2 pausiert wieder
usw... Also Der Knopfdruck schaltet die Timer jeweils um, es geht nicht darum zu erfassen wie lange die Taster gedrückt sind.

Hallo
und Danke für deine Antwort.
Ich schaue mal was ich meiner Sketckkiste über die Anwendung von Buttons, Timer und Zähler finde.

suche mal nach "Arduino Schachuhr" auch auf Englisch, du wirst einige Sketche finden.

TO heißt Tread Owner (Hoffe ich habe es richtig geschrieben) also Tread-besitzer, der den Tread angefangen hat.

[quote="disorder218, post:1, topic:855936"]
Das wäre nur eine absolute Notlösung, da die Anzahl der Pausen/Umschaltungen nicht vorhersehbar ist.[/quote]
Wieso? Du brauchst ja nicht die Anzahl bzw die Dauer der einzelnen Pausen sondern nur deren Summe. Da reicht 1 Variable.
Grüße Uwe

einfach mal so runtergeschrieben:

// Schachuhr
// https://forum.arduino.cc/t/millis-timer-pausieren-und-wieder-starten/855936
// by noiasca
// 2021-05-01 

int8_t activeTimer = -1;               // which timer is currently active

struct Timer {
  const byte buttonPin;                // pin to GND starts timer
  uint32_t previousMillis;             // last active millis of this timer
  uint32_t millis;                     // millis
};

Timer timer[] {
  {A0, 0, 0},                          // buttonPin, previousMillis, millis
  {A1, 0, 0},
};

const byte noOfTimer = sizeof(timer) / sizeof(timer[0]); // calculate number of timers

void setup() {
  Serial.begin(115200);
  Serial.println(F("Schachuhr"));
  for (auto &i : timer)
    pinMode(i.buttonPin, INPUT_PULLUP);
}

void loop() {
  // read button
  for (size_t i = 0; i < noOfTimer; i++)
  {
    if (digitalRead(timer[i].buttonPin) == LOW && activeTimer != (int8_t)i) activateTimer(i); // if different button, activate timer
  }
  // output
  updateSerial();
}

void activateTimer(byte i)                                          // sets timer i to active
{
  timer[i].previousMillis = millis();
  Serial.print(F("Button pressed ")); Serial.println(i);
  activeTimer = i;
}

uint32_t getMillis(byte i)                                          // returns the accumulated millis of one timer
{
  if (activeTimer == i)                                             // update millis before return
  {
    uint32_t currentMillis = millis();
    timer[i].millis += currentMillis - timer[i].previousMillis;     // add passed time to old millis
    timer[i].previousMillis = currentMillis;                        // remember the timestamp of the last millis update
  }
  return timer[i].millis;
}

void updateSerial()                                                 // demo output to serial
{
  static uint32_t previousMillis = 0;
  uint32_t currentMillis = millis();
  if (millis() - previousMillis > 500)
  {
    for (size_t i = 0; i < noOfTimer; i++)
    {
      Serial.print(F("Timer")); Serial.print(i); Serial.print(" "); Serial.print(getMillis(i)); Serial.print("\t");
    }
    Serial.print(F(" active=")); Serial.println(activeTimer);
    previousMillis = currentMillis;
  }
}

mit getMillis(i) bekommt man die jeweiligen millis retour.

ergibt:

10:44:15.334 -> Timer0 0	Timer1 0	 active=-1
10:44:15.836 -> Timer0 0	Timer1 0	 active=-1
10:44:16.338 -> Timer0 0	Timer1 0	 active=-1
10:44:16.825 -> Timer0 0	Timer1 0	 active=-1
10:44:17.142 -> Button pressed 0
10:44:17.342 -> Timer0 200	Timer1 0	 active=0
10:44:17.844 -> Timer0 701	Timer1 0	 active=0
10:44:18.300 -> Timer0 1202	Timer1 0	 active=0
10:44:18.802 -> Timer0 1703	Timer1 0	 active=0
10:44:19.304 -> Timer0 2204	Timer1 0	 active=0
10:44:19.806 -> Timer0 2705	Timer1 0	 active=0
10:44:20.308 -> Timer0 3206	Timer1 0	 active=0
10:44:20.826 -> Timer0 3707	Timer1 0	 active=0
10:44:21.312 -> Timer0 4208	Timer1 0	 active=0
10:44:21.761 -> Button pressed 1
10:44:21.815 -> Timer0 4208	Timer1 46	 active=1
10:44:22.317 -> Timer0 4208	Timer1 547	 active=1
10:44:22.834 -> Timer0 4208	Timer1 1048	 active=1
10:44:23.321 -> Timer0 4208	Timer1 1549	 active=1
10:44:23.824 -> Timer0 4208	Timer1 2050	 active=1
10:44:24.327 -> Timer0 4208	Timer1 2551	 active=1
10:44:24.829 -> Timer0 4208	Timer1 3052	 active=1
10:44:25.331 -> Timer0 4208	Timer1 3553	 active=1
10:44:25.833 -> Timer0 4208	Timer1 4054	 active=1
10:44:26.334 -> Timer0 4208	Timer1 4555	 active=1
10:44:26.784 -> Button pressed 0
10:44:26.837 -> Timer0 4244	Timer1 4555	 active=0
10:44:27.339 -> Timer0 4745	Timer1 4555	 active=0
10:44:27.852 -> Timer0 5246	Timer1 4555	 active=0

edit:
damit man nicht in Versuchung gerät, die Variablen direkt anzusprechen, mal die Timer als Objekte:

// Schachuhr OOP
// https://forum.arduino.cc/t/millis-timer-pausieren-und-wieder-starten/855936
// by noiasca
// 2021-05-01

int8_t activeTimer = -1;                     // stores the active Timer ID

class Timer {
  protected:
    const byte buttonPin;                    // pin to GND starts timer
    uint32_t previousMillis = 0;             // last active millis of this timer
    uint32_t _millis = 0;                    // millis
    const byte id;                           // give each object an ID
    static byte total;                       // keep track of the number of timer objects
    
  public:
    Timer (byte buttonPin): buttonPin(buttonPin), id (total++)
    {}

    void begin()
    {
      pinMode(buttonPin, INPUT_PULLUP);
    }

    void read()  // read button and if different button, activate timer
    {
      if (digitalRead(buttonPin) == LOW && activeTimer != id) activateTimer();
    }

    void activateTimer()                                          // sets timer i to active
    {
      previousMillis = millis();
      Serial.print(F("Button pressed ")); Serial.println(id);
      activeTimer = id;
    }

    uint32_t getMillis()                                          // returns the accumulated millis of one timer
    {
      if (activeTimer == id)                                      // update millis before return
      {
        uint32_t currentMillis = millis();
        _millis += currentMillis - previousMillis;                // add passed time to old millis
        previousMillis = currentMillis;                           // remember the timestamp of the last millis update
      }
      return _millis;
    }

    byte getId()
    {
      return id;
    }
};

byte Timer::total=0;             // declare inside class, but define outside = 0 (leave it 0!)

Timer timer[] {
  {A0},                          // buttonPin
  {A1}
};

void setup() {
  Serial.begin(115200);
  Serial.println(F("Schachuhr"));
  for (auto &i : timer) i.begin();
}

void loop() {
  // read button
  for (auto &i : timer) 
    i.read(); 
  // output
  updateSerial();
}

void updateSerial()                                                 // demo output to serial
{
  static uint32_t previousMillis = 0;
  uint32_t currentMillis = millis();
  if (millis() - previousMillis > 500)
  {
    for (auto &i : timer)
    {
      Serial.print(F("Timer")); Serial.print(i.getId()); Serial.print(" "); Serial.print(i.getMillis()); Serial.print("\t");
    }
    Serial.print(F(" active=")); Serial.println(activeTimer);
    previousMillis = currentMillis;
  }
}
1 Like

@noiasca
Ich würde gerne deinen Sketch ausprobieren, das sieht vielversprechend aus. Jedoch stehe ich komplett auf dem Schlauch wo ich dort meine Buttonpins definieren soll?! Taster1 hängt an D8 und Taster2 an D9.

Nicht nur rauskopieren sondern auch lesen.
Ich habe das sogar kommentiert!
Bei mir hängen die Pins an A0 und A1

@noiasca
keine Sorge, das habe ich schon gemacht :wink: . Ich habe versucht an der entsprechenden Stelle die Pins zu ändern, hat aber einen Fehler ausgespuckt. Dann häng ich die eben nochmal kurz um und versuche nochmal, Danke

Edit: @noiasca, Hab es jetzt grob verstanden und funktionierend umgesetzt. Vielen Dank. Mein Problem mit den Buttons vorhin war dass ich deine A0 und A1 einfach zu "8" und "9" geändert habe, ich hatte die Buttons via pull-up Widerstand an den digitalen Pins, da wurde der serielle Monitor dauerhaft mit fehlerhaft erkannten Button-Presses gefüllt. Ich habe jetzt die Buttons einfach zwischen den analogen Pins und GND, ohne Widerstand. Das funktioniert einwandfrei, das Problem hätte ich vorhin schon besser schildern sollen :slight_smile:

Der weitere Plan ist jetzt die Timerwerte in den EEPROM des Arduino zu speichern, diese beim Start auszulesen und via drittem Button zurückzusetzen. Mal sehen wie das dann läuft :face_with_monocle:

Das wird so einfach nicht klappen. Du wirst auch einen Button zum speichern ins eeprom brauchen denn die Schreibzugriffe ins Eeprom sind begrenzt und du sollst nicht laufend reinschreiben.

Dass das nicht ganz so einfach wird dachte ich mir schon. Mir geht es darum die Werte im Falle eines Ausfalls der Strom/Spannungsquelle gegen Verlust zu sichern.
Was ich mir vorstellen könnte: Eine Abfrage ob X Volt an der Spannungsquelle anliegen, wenn nicht werden die Werte gespeichert. Dafür müsste ich dann natürlich noch einen Kondensator einbinden der noch ausreichend Lange Strom liefert.

Mit einer Diode den Kondensator vom Stromkreis gegen Rückfluss sichern dann geht's.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.