Eieruhr Countdown unterbrechen

Guten Abend miteinander,

Ich habe ein kleines Problem mit meinem Eieruhrprogramm. Ich wähle mit einem Taster meine Zeit aus (1-10min) und starte dann mit einem zweiten Knopf. Mit diesem zweiten Knopf möchte ich den begonnenen Countdown wieder stoppen, jedoch habe ich keine Ahnung wie ich das machen soll :frowning: Ich werd aus dem Internet einfach nicht schlau, jedes mal wenn ich auf einer Webseite lande findet sich dort einfach nicht die Lösung für mein Problem.

Hier mein Code, vielleicht findet ja jemand eine Möglichkeit, bzw. meinen Fehler. Ich versuche hier mein “void Countdown” Programm mithilfe einer Variablen (f) zu unterbrechen. Ich habe mir eigentlich vorgestellt, dass sobald f=2 ist, der Countdown stoppt. (While (f == 1))

#include <Wire.h> 
#include "Adafruit_LEDBackpack.h"  
#include "Adafruit_GFX.h"

Adafruit_7segment matrix = Adafruit_7segment();  
int x = 0;            
int y = 0;
int f = 0;

void Countdown (int y) {
 while (f == 1) {
  if (y == 42) {
    matrix.print(0000, HEX);
    matrix.drawColon(true);
    matrix.writeDisplay();
  }
  if (y == 1) {
   matrix.print( 100, DEC);
   matrix.drawColon(true);
   matrix.blinkRate(0);
   matrix.writeDisplay();
   delay (1000);
  uint16_t blinkcounter = 0;
  boolean drawDots = false;
  for (uint16_t counter =   59; counter >     0; counter --) {
    matrix.writeDigitNum(0, (counter / 1000), drawDots);
    matrix.writeDigitNum(1, (counter / 100) % 10, drawDots);
    matrix.drawColon(true);
    matrix.writeDigitNum(3, (counter / 10) % 10, drawDots);
    matrix.writeDigitNum(4, counter % 10, drawDots);
    matrix.writeDisplay();
    delay(1000); }
  }
  if (y == 2) {
   matrix.print( 200, DEC);
   matrix.drawColon(true);
   matrix.blinkRate(0);
   matrix.writeDisplay();
   delay (1000);
  uint16_t blinkcounter = 0;
  boolean drawDots = false;
  for (uint16_t counter =  159; counter >  100; counter --) {
    matrix.writeDigitNum(0, (counter / 1000), drawDots);
    matrix.writeDigitNum(1, (counter / 100) % 10, drawDots);
    matrix.drawColon(true);
    matrix.writeDigitNum(3, (counter / 10) % 10, drawDots);
    matrix.writeDigitNum(4, counter % 10, drawDots);
    matrix.writeDisplay();
    delay(1000); }
  for (uint16_t counter =   59; counter >    0; counter --) {
    matrix.writeDigitNum(0, (counter / 1000), drawDots);
    matrix.writeDigitNum(1, (counter / 100) % 10, drawDots);
    matrix.drawColon(true);
    matrix.writeDigitNum(3, (counter / 10) % 10, drawDots);
    matrix.writeDigitNum(4, counter % 10, drawDots);
    matrix.writeDisplay();
    delay(1000); }
  } 

//USW.

void ShowTime (int x) { //subroutine to show the mins before start
  if (x == 1) {           //1 min
    matrix.print( 100, DEC);
    matrix.drawColon(true);
    matrix.blinkRate(2);
    matrix.writeDisplay();
    y = 1;
  }
  if (x == 2) {           //2 min
    matrix.print( 200, DEC);
    matrix.drawColon(true);
    matrix.blinkRate(2);
    matrix.writeDisplay();
    y = 2;
  }
  if (x == 3) {           //3 min
    matrix.print( 300, DEC);
    matrix.drawColon(true);
    matrix.blinkRate(2);
    matrix.writeDisplay();
    y = 3;
  }
  if (x == 4) {           //4 min
    matrix.print( 400, DEC);
    matrix.drawColon(true);
    matrix.blinkRate(2);
    matrix.writeDisplay();
    y = 4;
  }
  if (x == 5) {           //5 min
    matrix.print( 500, DEC);
    matrix.drawColon(true);
    matrix.blinkRate(2);
    matrix.writeDisplay();
    y = 5;
  }

//USW. (bis 10)

void setup() {
#ifndef __AVR_ATtiny85__ //backpack address
  Serial.begin(9600);
#endif
  matrix.begin(0x70); // 0x70 = l2c address of the 7-segment display
pinMode (10, INPUT); //set-time button
matrix.print(1000, DEC);
matrix.drawColon(true);
matrix.writeDisplay();
}

void loop() {
if (digitalRead (10)==HIGH) {
  x=x+1;
  ShowTime(x);
  delay(1000);
    if (x==11) {
    x=0;
     }
  }
if (f == 0) {
   if (digitalRead (9)==HIGH) {
     f = 1;
     delay(50);
     Countdown(y);
  }
if (f == 1) {
  if (digitalRead (9)==HIGH) {
     f = 2;
     delay(50);
     Countdown(42); //ist einfach nur die anzeige 00:00 
     delay(1000);
     f = 0;
 
  
  }
} 
}
}

Die Lösung steckt im BlinkWithoutDelay-Tutorial.

Ich habe mir das jetzt mal angeschaut und wo steckt da eine Lösung für mein Problem? Ich möchte ja nicht die Zeit messen, die vergangen ist sondern das Programm sofort unterbrechen/durch ein anderes ersetzen, sobald ich einen bestimmten Taster drücke. (Sobald PIN 9 High ist)

Das Problem ist dass du ein for-Schleife hast und delay() verwendest. Das ist falsch.

Du musst ständig loop() durchlaufen lassen damit du sofort auf Ereignisse reagierst. Und die Verzögerung macht man eben mit millis(). Wenn eine bestimmte Zeit abgelaufen ist, macht man den nächsten Schritt. Aber zwischen den Schritten darfst du nicht warten, sondern ständig in loop() den Taster abfragen.

Solange du aber in den delays gefangen bist, funktioniert deine Tasterabfrage nicht. Deshalb müssen die zuerst mal weg.

Der Delay ist nötig, damit meine Adafruit-Anzeige im Sekundentakt runterläuft, wenn er weg wäre läuft sie sofort auf 0. Das möchte ich natürlich nicht. Gibt es nicht noch einen anderen Weg? Es muss doch irgendwie einen stop if f = 2 command geben oder ähnliches. :disappointed_relieved:

Oh Mann ich versteh das mit dem millis einfach nicht... Gibt wohl doch eine 6. :disappointed_relieved:

Kyosuke42: Gibt wohl doch eine 6. :disappointed_relieved:

?

Verinnerliche dir millis() Funktion. Das ist gehört zum Grundwissen bei Arduino! Wenn du es so machst, wie Serenfly schreibst, läuft es nicht sofort auf 0 runter.

static unsigned long lastMillis;

if(millis() - lastMillis >= 1000)
{
lastMillis = millis();

// Wird jede Sekunde einmal aufgefürt.
}

Ich habe keine Ahnung wie ich das einbauen soll… Eine LED blinken lassen? Das ist ja einfach aber wie soll ich damit meine 7-segment Anzeige leuchten lassen? Ich muss jetzt irgendwie meinen gesamten Code umschreiben nur für eine dämliche Stop-Taste… Ich sitzt daran jetzt schon seit 3 Stunden ich verlier langsam die Geduld. Keine Ahnung was ich jetzt noch machen soll.

Das ist das Problem. Du versucht einfach wild darauf los zu programmieren. Ohne die Basics geht sowas nicht, und das dauert Zeit.

Das meinte ich nicht, ich habe nicht einfach versucht "wild darauf los zu programmieren". Ich kann die Basics, aber das mit dem millis hab ich für meinen Code einfach nicht gebraucht. Bis der Stop Button daher kam und plötzlich sagt mir jemand ich muss meinen kompletten Code neu schreiben... Dass ich jetzt ein wenig aufgeschmissen bin kann ja wohl jeder nachvollziehen.

Kyosuke42: Ich muss jetzt irgendwie meinen gesamten Code umschreiben nur für eine dämliche Stop-Taste...

Du solltest allgemein lernen Programme vernünftig zu strukturieren. Das brauchst du immer wieder. Es ist also besser das gleich zu lernen.

Hier ist es nicht möglich Dinge scheinbar "gleichzeitig" ablaufen zu lassen. Das braucht man aber bei zig Anwendungen. z.B. Sensoren auslesen, etwas regeln oder steuern und gleichzeitig auf verschiedene Eingaben (Serial und Taster) reagieren.

Man machst das dann direkt hintereinander und verzögert bestimmte Dinge mit millis() sofern nötig. Verschiedene Aufgaben lagert man in eigene Funktionen aus, aber die müssen nach loop() zurückkehren damit dort etwas anderes erledigt werden kann. Das kann man dann so machen, dass jede Funktion in jedem Durchlauf von loop() aufgerufen wird und die Funktionen bestimmen jede für sich ob es Zeit ist etwas zu tun. Zusätzliche Verriegelungen, so dass bestimmte Dinge in unter bestimmten Zuständen aufgerufen werden sind da natürlich auch möglich.

void loop() {
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= 1000)
{
previousMillis = currentMillis;
}

Soweit bin ich bis jetzt gekommen, kann mir jemand sagen wie ich jetzt mein void Countdown in den loop einbauen kann? In der Schule wurde uns sowas nie gesagt... deswegen wirke ich hier grade so stümperhaft. Es gab nur ein millis Beispiel mit einer LED... aber nicht mit Unterprogrammen zu einer Adafruit 7-Segment-Anzeige.

Was lernt ihr dann in der Schule überhaupt? Wie seit ihr vorgegangen? Ich sehe überhaupt keinen Ansatz, dass du ansatzweise deinen Code strukturierst.

Mit der millis() Abfrage von dir hast du doch schon einen wichtigen Teil. Die Abfrage wird jede Sekunde aufgerufen. Du kannst somit auch jede Sekunde den Wert um 1 verringern.
In der Abfrage kannst du auch die neue Ausgabe einbauen.

Einen fertigen Code würde hier einfach nichts bringen. Es fehlen die Basics. Entweder das Playground durcharbeiten, oder ein ordentliches Buch für den Arduino besorgen mit guten Beschreibungen (Erik Bartmann zum Beispiel)

Ich habe mein Programm nun komplett in loop geschrieben, allerdings scheint es millis() nicht als 1000ms anzuerkennen, es läuft immer noch sofort durch. Nur komischerweise startet sich das Programm immer wieder neu, solange ich den Startknopf gedrückt habe. Da es eh nichts bringt lass ich den stop knopf einfach weg, wird schon irgendwie klappen.

In dieser Abfrage kannst du dann jede Sekunde was machen. z.B. runterzählen. Da kann man dann auch zusätzliche Abfragen auf Statusvariablen einbauen um so zu steuern ob überhaupt etwas gemacht werden soll.

z.B.:

bool runFunc1;

void loop()
{
    if(....)
        runFunc1 = true;
    else
        runFunc1 = false;

    if(func1() == true)
    {
    }

    func2();
}

bool func1()
{
    if(runFunc1 == false)
       return false;

    static unsigned long previousMillis;

    if(millis() - previousMillis > 1000)
    {
        previousMillis = millis();

        if(...)
          return true;
    }

    return false;
}

void func2()
{
    static unsigned long previousMillis;

    if(millis() - previousMillis > 2000)
    {
       previousMillis = millis();
    }
}

Die Funktionen werden jeden loop() Durchlauf aufgerufen. func1() macht alle 1 Sekunde was, aber nur wenn es mit runFunc1 freigegeben wurde. Das kann z.B. durch die Abfrage eines Tasters geschehen, aber es könnte auch das Auslesen eines Sensors sein.
Die Kontrolle ob func1() aufgerufen wird könnte man auch in loop() machen. Wenn das aber komplexer und mit mehr Bedingungen ist, ist das aber auch besser in der Funktion aufgehoben. Die einfache Version wäre das:

if(runFunc1)
   func1();

func2() macht alle 2 Sekunden etwas.

Außerdem meldet func1() wenn in der Funktion ein Ereignis eingetreten ist. Darauf kann man dann in loop() reagieren. Das brauchst du hier vielleicht nicht, aber in anderen Anwendung kann es nötig sein. Ich habe es mal eingefügt um zu zeigen wie man zwischen den Funktionen kommunizieren kann. Man kann auch Parameter an die Funktionen übergeben, oder mit globalen Variablen arbeiten.

Vielen Dank! Das ist doch mal was! Damit kann ich endlich was anfangen... vielen, vielen Dank :) Hast meinen Tag/meine Nacht gerettet.

Hmmm kann mir jemand erklären wie ich in eine for-Schleife anstatt den delay millis verwenden kann? Bei mir läuft das immer sofort durch. Das dumme ist ich kann meine for-Schleife leider nicht ersetzen, weil man für die Adafruit 7-Segment Anzeige für einen Countdown eine for-Schleife braucht. Sonst müsste man jede einzelne Zahl anzeigen lassen.

EDIT: Nach stundenlanger Recherche hab ich herausgefunden, dass es wohl nicht möglich ist eine for-Schleife ohne delay zu betreiben. Kann mir jemand sagen wie ich den Effekt der folgenden for-Schleife in einer if-Schleife ausdrücken kann?

[...]
uint16_t blinkcounter = 0;                                                     //Befehl um das Display blinken zu lassen
boolean drawDots = false;                                                     //Befehl um Punkte anzeigen zu lassen
for (uint16_t counter = 1111; counter >     0; counter --) {     // zählt herunter von 1111 bis 1
    matrix.writeDigitNum(0, (counter / 1000), drawDots);         //1. Ziffer
    matrix.writeDigitNum(1, (counter / 100) % 10, drawDots);  //2. Ziffer
    matrix.drawColon(true);                                                    //Doppelpunkt (an)
    matrix.writeDigitNum(3, (counter / 10) % 10, drawDots);    //3. Ziffer
    matrix.writeDigitNum(4, counter % 10, drawDots);             //4. Ziffer
    matrix.writeDisplay();                                                       //Alles anzeigen lassen
    delay(1000);                                                                    //delay von einer Sekunde 
}                                                                  
[...]

Nehm anstatt der for Schleife eine if Schleife!

Ja, das habe ich jetzt leider auch bemerken müssen... Wenn es jetzt eine "normale" for Schleife wäre würde ich das Umwandeln noch hinbekommen aber mit diesem ganzen 7-Segment-Extracode steh ich total auf dem Schlauch. :(