Tastenbetätigungen während einer bestimmten Zeit auslesen, LED Blinken

Hallo Zusammen!

Seit zwei Wochen arbeite ich von der Schule aus mit einem Arduino Uno R3 SMD. Bisher habe ich vier ziemlich einfache Aufgaben lösen müssen, jetzt, bei der Aufgabe 5, stehe ich seit ungefähr vier Stunden etwas auf dem Schlauch.
Da ich betreffend Arduino programmieren eigentlich überhaupt keine Vorkenntnisse habe, weiss ich jetzt wirklich nicht weiter, wie ich diese Aufgabe umsetzen muss...

Hier Mal die Aufgabe:
"Schreiben Sie ein Programm mit folgender Funktion:
Wenn man den Taster 1 einmal drückt, blinkt die Diode 1 einmal.
Wenn man den Taster 1 zweimal (innerhalb zwei Sekunden) drückt, blinkt die Diode 1 zweimal. Usw..."

Kann mir da eventuell jemand weiterhelfen? Ich habe auch online schon nach Lösungen gesucht, aber entweder diese funktionieren bei mir überhaupt nicht, oder ich verstehe kein Wort...

Hier noch mein "Code", falls dieser überhaupt zu etwas nütze sein könnte:

int ledpin=13;
int buttonpinone=2;
int counterone=1;
int countertwo=1;
bool markerone;
bool markertwo;
unsigned long mytimer=0;
unsigned long mytimeout=3000;
void setup() {
  pinMode (ledpin,OUTPUT);
  pinMode (buttonpinone,INPUT);
}
void loop() {
  mytimer=millis();
  countertwo=1;
  while(millis()< mytimer + mytimeout){
    if(digitalRead(buttonpinone)==HIGH){
      countertwo+1;
    }
  }
  for(counterone=1;counterone<countertwo;counterone=counterone+1){
  digitalWrite(ledpin,HIGH);
  delay(500);
  digitalWrite(ledpin,LOW);
  delay(500);
  }
 }

Danke schonmal im Voraus für eure Hilfe!

Da zählt der Zähler munter hoch solange der Taster gedrückt ist.
Du mußt die Zusandsänderung erkennen.
Siehe:

oder

https://toptechboy.com/arduino-tutorial-28-using-a-pushbutton-as-a-toggle-switch/

Grüße Uwe

Sind das mechanische Taster? Sind die entprellt?

Nicht entprellte mechanische Taster kannst Du per Software entprellen. Wenn Du Bibliotheken verwenden darfst, dann beispielsweise bounce2. Da gibt es passende Methoden, um eine Flanke auszuwerten.

Hallo Uwe,
Danke erstmal für deine schnelle Antwort! Ich habe den Code jetzt mal nach diesem Prinzip angepasst;

int ledpin=13;
int buttonpinone=2;
int counterone=1;
int countertwo=1;
bool markerone;
bool markertwo;
unsigned long mytimer=0;
unsigned long mytimeout=3000;
void setup() {
  pinMode (ledpin,OUTPUT);
  pinMode (buttonpinone,INPUT);
}
void loop() {
  mytimer=millis();
  countertwo=1;
  while(millis()< mytimer + mytimeout){
    if(digitalRead(buttonpinone)==HIGH){
      countertwo+1;
      digitalWrite(ledpin,HIGH);
    } else {
      digitalWrite(ledpin,LOW);
    }
    }
  for(counterone=1;counterone<countertwo;counterone+1){
  digitalWrite(ledpin,HIGH);
  delay(500);
  digitalWrite(ledpin,LOW);
  delay(500);
  }
 }

Hat leider noch nicht funktioniert, aber ich habe langsam das Gefühlt das vielleicht das mit der Zeit nicht ganz funktioniert, und das hochzählen und Blinken irgendwie auch nicht, es hört einfach nie auf zu blinken, obwohl die Bedingung längst nicht mehr gegeben ist... Hast du da noch eine Idee dazu?

Die Taster sind entprellt, ja!

Lass dir doch mal den Inhalt von
countertwo auf dem SerialMonitor ausgeben.

Das finde ich bedenklich, weil das ziemlich genau nach 49 Tagen auf die Nase fällt.

Dazu:

E:\Programme\arduino\portable\sketchbook\sketch_aug20e\sketch_aug20e.ino: In function 'void loop()':
E:\Programme\arduino\portable\sketchbook\sketch_aug20e\sketch_aug20e.ino:19:17: warning: statement has no effect [-Wunused-value]
   19 |       countertwo+1;
      |       ~~~~~~~~~~^~
E:\Programme\arduino\portable\sketchbook\sketch_aug20e\sketch_aug20e.ino:25:54: warning: for increment expression has no effect [-Wunused-value]
   25 |     for(counterone=1;counterone<countertwo;counterone+1){
      |                                            ~~~~~~~~~~^~

Vorab: Ich schreibe sonst gerne Codeschnipsel, aber möchte das hier mal vermeiden, weil es ja doch eher nach einer Leistungsaufgabe aussieht und Lehrkräfte erfahrungsgemäß hier auch mitlesen....

Ja, das ist immer gut.
Es zeigt zum Teil auch, auf welcher Basis Hinweise gegeben werden können.
Ich hätte Dir zum Beispiel angeboten aus einem anderen Projekt was ich betreut habe, für dich die Funktion readSwitch() zu übernehmen.

Aber zu allererst: Wie hast Du Deinen Taster angeschlossen?
Wenn Du INPUT benutzt und auf HIGH prüfst, musst Du sicher stellen, das beim loslassen der Taste sofort LOW auf dem PIN anliegt.
Ist die Schaltung so ausgelegt, oder liegt der taster nur nach +5V?

Ich würde die Aufgabe anders lösen.
Das mytimeout ist nicht dafür ausgelegt, das Du erkennst, ob innerhalb 1 Sekunde ein Tastendruck erfolgte.
Das muss also als erstes auf 1 Sekunde gesetzt werden.

Dann brauchst Du nur einen Counter. Der wird mit jedem Tastendruck - so er in der Zeit erfolgte - aufgezählt.

Das while()-Konstrukt macht Dir mehr Sorgen als Nutzen, da Du erst nach dem timeout da raus kommst. Zudem ist loop() ja schon eine Schleife.

Was jetzt noch fehlt ist das jeweilige setzen und auswerten der Zeiten und Tastendrücke.
Für letztere musst Du noch merken, ob die Taste gedrückt oder losgelassen wurde, weil Du sonst ewig aufaddierst.

mytimer setzt Du das erste mal am Ende von setup().

Anstelle des Schlüsselwortes while benutzt Du if. Dann wird mit jedem erkannten Tastendruck der Counter aufaddiert und mytimer wieder neu mit millis() gesetzt.

Wenn innerhalb des Ablaufs bis mytimeout wieder die Taste gedrückt wird, wird das wieder angestossen.

Läuft die eine Sekunde aus, ohne tastendruck, erfolgt die Ausgabe durch blinken auf der led.
Das lässt sich realisieren mit einer Schleife, die den counter mit jedem Durchlauf wieder rückwärts auf null stellt.

Ist counter 0 kannst wieder von vorn anfangen....

Du läßt while immer laufen, was den Ablauf aber blockiert. Eigentlich sollte die Zeit aber erst beim Drücken starten, da stimmt also die Reihenfolge nicht.

Weil Du die Bedingung am Ende des Blinkens nicht löscht.

Basierend auf Deinem Programm, nur etwas umgestellt:

int ledpin = 13;
int buttonpinone = 2;
int countertwo = 0;
unsigned long mytimer = 0;
unsigned long mytimeout = 2000;

void setup() {
  pinMode (ledpin, OUTPUT);
  pinMode (buttonpinone, INPUT_PULLUP);
}

void loop() {
  if (digitalRead(buttonpinone) == HIGH && countertwo == 0) {
    mytimer = millis();
    countertwo++;
    delay (100);  // für nicht entprellte Tasten
  }
  if (digitalRead(buttonpinone) == LOW && countertwo == 1) {
    countertwo++;
    delay (100);  // für nicht entprellte Tasten
    if (countertwo == 2) {
      while (millis() < mytimer + mytimeout) {
        if (digitalRead(buttonpinone) == HIGH) {
          countertwo++;
          break;
        }
      }
      digitalWrite(ledpin, HIGH);
      delay(500);
      digitalWrite(ledpin, LOW);
      delay(500);
      if (countertwo > 2) {
        digitalWrite(ledpin, HIGH);
        delay(500);
        digitalWrite(ledpin, LOW);
        delay(500);
      }
      countertwo = 0;
    }
  }
}

Mit blockierendem delay und while ist dieses Programm böse :japanese_ogre:, aber es tut, was es soll.

Als Lehrer würde ich mit der Zusatzaufgabe, eine zweite LED blinken zu lassen, das blockierende Kartenhaus zusammenstürzen lassen. Aber glücklicherweise bin ich kein Lehrer :joy:

Nein, ich sehe nichts davon. Du triggerst nicht auf die Zustandsänderung.

Was macht

countertwo+1;

Grüße Uwe

Erstmal Danke für deine Antwort!
Fragen:
Was kann die Funktion readSwitch genau?
Inwiefern ist mytimeout nicht dafür ausgelegt, zu erkennen ob innerhalb einer Sekunde ein Tastendruck erfolgte?
Und wie ist das mit der if-Schleife statt der while-Schleife gemeint? da funktioniert nämlich dann bei mir gar nix mehr...

Zu deiner Frage: Ich habe meinen Taster direkt an +5V angeschlossen, wie kann man das sonst anders lösen?

Sorry, ich bin wie erwähnt kompletter Anfänger.
Ähm bei countertwo+1 soll immer wenn der Taster gedrückt wurde der countertwo um eins hochgezählt werden!? Also in der Theorie, nur funktioniert das Ganze halt eben nicht so wie gewollt...

also
countertwo ++;
oder
countertwo+=1;

Grüße Uwe

Ich habe das Ganze jetzt mal so umgeschrieben, dass die zeit erst mit dem Tastendruck startet:

int ledpin=13;
int buttonpinone=2;
int counterone=0;
int countertwo=0;
bool merker;
unsigned long mytimer=0;
unsigned long mytimeout=2000;
void setup() {
  pinMode (ledpin,OUTPUT);
  pinMode (buttonpinone,INPUT);
}
void loop() {
  if (digitalRead(buttonpinone) == HIGH && (countertwo == 0)) {
    mytimer = millis();
    countertwo++;
    } 
  if (countertwo == 1) {
      while(millis() < mytimer + mytimeout) {
        if (digitalRead(buttonpinone) == HIGH) {
          countertwo++;
        } 
       }
       for(counterone=1;counterone<countertwo;counterone++){
          digitalWrite(ledpin,HIGH);
          delay(500);
          digitalWrite(ledpin,LOW);
          delay(500);
        }
       }
      countertwo=0;
    }

Das mit der Bedingung am Ende des Blinkens löschen ist mir noch etwas unklar, das hat auch bei mir gar nicht funktioniert... Auch nicht mit einer Codeumschreibung nach deinem Beispiel (habe ich jetzt wieder abgeändert)

Die liest einen Taster auf den Zustand gedrueckt / nicht gedrueckt aus und mit jedem druecken des Tasters innerhalb einer vorgegebenen Zeit (da sinds 400ms) startet der timer wieder neu. (bei Dir mytimer wird neu gesetzt)
Ist der timer (bei Dir myTimeout) abgelaufen, wird die Zahl der Tastendruecke ausgewertet.

Wenn Du mytimeout auf 3000ms setzt, dann kannst Du zwar feststellen, wieviele Auslöser Du in 3 Sekunden bekommst, kannst aber nicht sagen, ob die in einer Sekunde regelmässig kamen oder innerhalb von 3 ms.
Und wenn Du in 2100ms drei Auslöser bekommst, kommst Du aus Deiner while-Schleife nicht vor 3000ms zu Deiner Auswertung.

Das mit der if-Bedingung (if-schleife) ist eigentlich nicht so schwer.
Schau mal in das Beispiel: Datei: Beispiele 02. Digital - blinkWithoutDelay.

Ich werd mal sehen, ob ich die Funktion so umschreiben kann, das es nicht gleich 100% deine Lösung ist Du aber mit weiterarbeiten kannst...

Den Taster zwischen dem Pin und GND anschliessen.
Und dann den internen PULLUP aktivieren.
Das macht dieser Code hier.

Ich habe extra kein blinken drin, nur die Ausgabe auf dem seriellen Monitor.

const byte buttonpin = 2;

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  pinMode (buttonpin, INPUT_PULLUP);
}

void loop()
{
  readSwitch();
}

void readSwitch()
{
  const uint32_t timeOut = 1000;
  static uint32_t startTime = 0;
  static bool lastState = HIGH;
  static byte counter = 0;
  if (digitalRead(buttonpin) == LOW)
  {
    if (lastState)
    {
      startTime = millis();
      lastState = LOW;
      counter++;
    }
  }
  else
  {
    lastState = HIGH;
  }
  if (counter != 0 && millis() - startTime > timeOut)
  {
    switch (counter)
    {
      case 1:
        Serial.print(F("Ein"));
        break;
      case 2:
        Serial.print(F("Zwei"));
        break;
      case 3:
        Serial.print(F("Drei"));
        break;
      case 4:
        Serial.print(F("Vier"));
        break;
      case 5:
        Serial.print(F("Fünf"));
        break;
      default:
        Serial.print(counter);
        break;
    }
    Serial.println(F(" mal Taste gedrückt"));
    counter = 0;
  }
}

Ich habe das Ganze Mal nach diversen Vorschlägen umgeschrieben, hier mal der Code:

int ledpin=13;
int buttonpinone=2;
bool merker;
void setup() {
  pinMode (ledpin,OUTPUT);
  pinMode (buttonpinone,INPUT_PULLUP);
}
void loop() {
  readSwitch();
}
void readSwitch(){
  const uint32_t timeOut = 1000;
  static uint32_t startTime = 0;
  static bool lastState = HIGH;
  static byte counter = 0;
  if (digitalRead(buttonpinone) == LOW){
    if (lastState){
      startTime = millis();
      lastState = LOW;
      counter++;
    }
  } else {
    lastState = HIGH;
    }
  if (counter != 0 && millis() - startTime > timeOut){
    digitalWrite(ledpin,HIGH);
    delay(500);
    digitalWrite(ledpin,LOW);
    delay(500);
  }
  counter = 0;
}

Leider leuchtet jetzt absolut gar nichts mehr. Da ich mit der Funktion ReadSwitch() nicht so vertraut bin, bin ich mir jetzt nicht sicher, ob ich diese richtig umgesetzt habe.

Kein Problem. Dir ist ein Flüchtigkeitsfehler während dem umschreiben passiert.

  if (counter != 0 && millis() - startTime > timeOut){
    digitalWrite(ledpin,HIGH);
    delay(500);
    digitalWrite(ledpin,LOW);
    delay(500);
  }
  counter = 0;

Das zurücksetzen des Counter erfolgt ausserhalb des Funktionsblocks zur Anzeige.
Damit wird der Counter bei jedem Umlauf gelöscht.

Wenn Du das wie folgt setzt:

  if (counter != 0 && millis() - startTime > timeOut){
    digitalWrite(ledpin,HIGH);
    delay(500);
    digitalWrite(ledpin,LOW);
    delay(500);
    counter = 0;
  }

wird der counter erst gelöscht wenn die LED an und aus gegangen ist.

Da Du den counter aber mit mehr als 1 füttern kannst, solltest Du schon mal vorsorglich den counter nicht direkt auf 0 setzen, sondern herunterzählen lassen:

  if (counter != 0 && millis() - startTime > timeOut){
    digitalWrite(ledpin,HIGH);
    delay(500);
    digitalWrite(ledpin,LOW);
    delay(500);
    counter--; // zieht 1 ab
  }

Also ich habe den Code jetzt nochmals angepasst und es hat alles FUNKTIONIERT!!!
Ganz herzlichen Dank an alle, die mir geholfen haben, allein wäre ich da nie drauf gekommen!

Naja, Du bist noch nicht ganz fertig...
Momentan bist Du während des blinken gefangen.
In der einen Sekunde die die LED blinkt, kannst Du nichts anderes machen.
Das gehört definitiv noch geändert. Schau dazu in das Beispiel 02. Digital - BlinkWithoutDelay

Und: Während des Blinken kannst Du derzeit den counter wieder aufaddieren.
Wenn Du erst abwarten musst, das die Blinksequenz durchgelaufen ist, musst Du mit einer Variablen die Taste sperren.