Eine Art Schachuhr mit nur einen Auslöseknopf

Hallo,

meine Countdownkiste ist Dank der tollen Hilfe fertig geworden und wird am 17. zu ihrem ersten Einsatz kommen.
Nun schwebt mir ein zweites Projekt vor. Ich will eine "Buzzersäule" bauen. Kurz, ein 1m langes und 100 mm Duchmesser transparentes Kunststoffrohr mit Multicolor LED Stripes, an dem oben ein sogenannter Grobhandtaster (Buzzerknopf) drauf ist. Die mechanische Hardware habe ich schon so gut wie zusammen.
Das Programm soll folgendermaßen aussehen. Eigentlich wie eine Schachuhr mit nur einem Knopf (Buzzerknopf). Wenn dieser gedrückt wird, läuft eine Zeit in Minuten und Sekunden auf einem Display und z.B. die roten LED´s leuchten. Wird dieser Knopf wieder gedrückt, bleibt die Zeit stehen und eine andere, zweite Zeit fängt an zu zählen und wird auf dem selben oder ein weiteres Display angezeigt. Nun leuchten die gelben LED´s. Wenn eine von beiden Zeiten das Ziel von z.B. eine Stunde erreicht hat, soll eine Tröte los tröten.
Ich hab hier im Forum ein Zeitprogramm gefunden. Doch leider zählen die alle runter. Ich hab ein Programm empfohlen bekommen. Kann man das so modifizieren, das es anstatt runter, hoch zählt? Und die Minuten/Sekundeneingabe kann wegfallen. Dafür ein Reset zum Nullen beider Zeiten. Ich hab hier nur erstmal vobn einen normalen Display auf ein Serialdisplay "umgebaut".

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

/* in der nachfolgenden Zeile wird festgelegt, ob die Schalter
   mit PullUp oder PullDown Widerständen beschaltet sind.
   INPUT_PULLUP aktiviert die internen PullUp-Widerstände des Atmega
   INPUT setzt voraus, dass die Schalter mit externen PullDown-Widerständen beschaltet sind
*/
#define INPUTMODE INPUT_PULLUP  // INPUT_PULLUP oder INPUT setzen
const int minuten_switch = 7;
const int sekunden_switch = 6;
const int start_switch = 8;

int timerLaufzeit = 0;
boolean timerGestartet=false;

void eingabe()
{
  static boolean oldMinSwitch=false; // Statische Variable für letzten Schalterzustand
  static boolean oldSecSwitch=false; // Statische Variable für letzten Schalterzustand
  static boolean oldStartSwitch=false; // Statische Variable für letzten Schalterzustand
  boolean MinSwitch= digitalRead(minuten_switch); // aktueller Schalterzustand
  boolean SecSwitch= digitalRead(sekunden_switch); // aktueller Schalterzustand
  boolean StartSwitch= digitalRead(start_switch); // aktueller Schalterzustand
  if (INPUTMODE==INPUT_PULLUP) // Vertauschte Schaltlogik bei PullUp
  {
    MinSwitch=!MinSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
    SecSwitch=!SecSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
    StartSwitch=!StartSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
  }
  if (MinSwitch==HIGH && oldMinSwitch==LOW) timerLaufzeit+=60; // Zustandsänderung gedrückt?
  oldMinSwitch=MinSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
  if (SecSwitch==HIGH && oldSecSwitch==LOW) timerLaufzeit++; // Zustandsänderung gedrückt?
  oldSecSwitch=SecSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
  if (StartSwitch==HIGH && oldStartSwitch==LOW) timerGestartet=true; // Zustandsänderung gedrückt?
  oldStartSwitch=StartSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
}

void verarbeitung()
{
  static unsigned long lastMillis=0; // statischer Zähler für letzten Stand des millis() Timers
  static unsigned long timedMillis=0;// statischer Zähler für Millisekunden beim Herunterzählen
  unsigned long nowMillis=millis();  // aktueller Stand des millis() Timers
  if (timerGestartet && timerLaufzeit>0) // Zähler läuft und Restzeit vorhanden
  {
    timedMillis+= nowMillis-lastMillis; // Millisekunden hochzählen
    if (timedMillis>=1000)  // Nach jeweils 1000 Millisekunden
    {
      timedMillis=0;  // Millisekundenzähler auf 0 setzen
      timerLaufzeit--; // Eine Sekunde Laufzeit abziehen
      if (timerLaufzeit==0) timerGestartet=false; // Timer anhalten
    }
  }
  lastMillis=nowMillis; // Zählerstand merken, bei dem diese Funktion zuletzt lief
}

void ausgabe()
{
  int minuten=timerLaufzeit/60; // Ganzzahldivision durch 60
  int sekunden=timerLaufzeit%60;// Modulo-Divisionsrecht bei geteilt durch 60
  lcd.setCursor(0, 1);
  if (minuten < 10) lcd.print("0");
  lcd.print(minuten);
  lcd.print(":");
  if (sekunden < 10) lcd.print("0");
  lcd.print(sekunden);
}


void setup() {
  lcd.begin(16, 2);
  pinMode(minuten_switch,INPUTMODE);
  pinMode(sekunden_switch ,INPUTMODE);
  pinMode(start_switch, INPUTMODE);
  lcd.print("TIMER:");
}

void loop() 
{
  eingabe();
  verarbeitung();
  ausgabe();
  delay(5); // Kleines Delay zum softwaremäßigen Entprellen der mechanischen Schalter
}

Gruß Frank

Sieht mir viel zu kompliziert aus.
Du willst doch nur

  • Start bei Knopfdruck
  • Zeit merken bei nächstem Knopfdruck
  • Tröte bei Zeit x

Oder fehlt da was?

Klaus_ww:
Sieht mir viel zu kompliziert aus.
Du willst doch nur

  • Start bei Knopfdruck
  • Zeit merken bei nächstem Knopfdruck
  • Tröte bei Zeit x

Oder fehlt da was?

Zeit einstellen

Start (Zeit Einstelltasten deaktivieren)
zwischen Knopfdruck 1 und 2 die Zeit in Variable ZeitA addireren
zwischen Knopfdruck 2 und 3 die Zeit in Variable ZeitB addireren
zwischen Knopfdruck 3 und 4 die Zeit in Variable ZeitA addireren
ecc
Bei erreichen eines Limits für zeit A oder Zeit B Sound un optisches Signal.
Knopfdruck reset ( Zeit A und Zeit B auf null stellen; Zeit Einstelltasten aktivieren)
Start ecc

Mir scheint Du kommst etwas durcheinander mit lokalen und Globelen Variablen.

void eingabe()
{
  static boolean oldMinSwitch=false; // Statische Variable für letzten Schalterzustand
...

setzt oldMinSwitch immer auf false.

Gleiches gilt für
static unsigned long lastMillis=0 und andere Variablen.

int timerLaufzeit = 0; läuft nach ca 9 Stunden über.

Grüße Uwe

Die Initialisierung von lokalen static Variablen wird nur einmal am Anfang gemacht. Die Verhalten sich genauso wie globale Variablen, aber haben lokalen Scope. Die Zuweisung bei jedem Aufruf der Funktion findet woanders statt.

Man muss globale und static Variablen aber auch nicht explizit auf 0/false initialisieren. Das macht der Compiler automatisch. Nur lokale nicht-static Variablen müssen unbedingt initialisiert werden.

Nitri:
Ich hab hier im Forum ein Zeitprogramm gefunden. Doch leider zählen die alle runter. Ich hab ein Programm empfohlen bekommen. Kann man das so modifizieren, das es anstatt runter, hoch zählt? Und die Minuten/Sekundeneingabe kann wegfallen. Dafür ein Reset zum Nullen beider Zeiten. Ich hab hier nur erstmal vobn einen normalen Display auf ein Serialdisplay "umgebaut".

Der Sketch wurde von mir hier als Kurzzeitmesser/Countdown-Timer mit vorwählbarer Laufzeit in Minuten und Sekunden gepostet:
http://forum.arduino.cc/index.php?topic=231846.msg1672443#msg1672443

Natürlich läßt sich so ein beispielhaftes Programm mit den Funktionen "eingabe()", "verarbeitung()" und "ausgabe()" auch umprogrammieren, indem man die Funktionen so verändert wie man es braucht.

Z.B. kann man die Einstellmöglichkeit für Minuten und Sekunden weglassen und die Timerlaufzeit als MAXTIME Konstante im Sketch deklarieren. Man kann auch anstelle eines Countdown-Timers einen Countup-Timer machen, indem man die Sekunden hoch- statt runterzählt. Und last but not least kann man das Runterzählen der Sekunden auch auf zwei Zählvariablen verteilen: Mal wird die eine Variable runtergezählt und mal die andere, abhängig davon, wie oft ein Button gedrückt wurde.

Ich habe da mal was vorbereitet und auch noch eine "GameOver" Funktion eingebaut, die nach Zeitablauf für einen Spieler eine abschließende Anzeige macht und danach in eine Endlosschleife geht, bis zum nächsten Reset.

Der Reset-Button des Arduino-Boards setzt alles wieder zurück und startet das Programm neu.

Wenn die Ausgabe nicht nur auf LCD erfolgen soll, sondern auch mit Licht- und Soundeffekten aufgepeppt werden soll, wäre die Funktion "ausgabe" (und ggf. "gameOver")im Programm die Stelle, an der es eingebaut werden müßte.

#define MAXTIME 3600     // Vorgabezeit in Sekunden
#define START_SWITCH 16  // Pin an dem der Button angeschlossen ist
#define PLAYERYELLOW 0   // symbolische Konstante für gelben Spieler
#define PLAYERBLUE 1     // symbolische Konstante für blauen Spieler

#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

/* in der nachfolgenden Zeile wird festgelegt, ob die Schalter
   mit PullUp oder PullDown Widerständen beschaltet sind.
   INPUT_PULLUP aktiviert die internen PullUp-Widerstände des Atmega
   INPUT setzt voraus, dass die Schalter mit externen PullDown-Widerständen beschaltet sind
*/
#define INPUTMODE INPUT_PULLUP  // INPUT_PULLUP oder INPUT setzen

int timerYellow = 0;
int timerBlue = 0;
int playerMove= PLAYERYELLOW; // Gelb fängt an
boolean timerGestartet=false;
int winnerIs=-1;

void eingabe()
{
  static boolean oldStartSwitch=false; // Statische Variable für letzten Schalterzustand
  boolean StartSwitch= digitalRead(START_SWITCH); // aktueller Schalterzustand
  if (INPUTMODE==INPUT_PULLUP) // Vertauschte Schaltlogik bei PullUp
  {
    StartSwitch=!StartSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
  }
  if (StartSwitch==HIGH && oldStartSwitch==LOW) // Zustandsänderung gedrückt?
  {
    if (timerGestartet==false) timerGestartet=true; 
    else if (playerMove==PLAYERYELLOW) playerMove=PLAYERBLUE;
    else playerMove=PLAYERYELLOW;
  }
  oldStartSwitch=StartSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
}

void verarbeitung()
{
  static unsigned long lastMillis=0; // statischer Zähler für letzten Stand des millis() Timers
  static unsigned long timedMillis=0;// statischer Zähler für Millisekunden beim Herunterzählen
  unsigned long nowMillis=millis();  // aktueller Stand des millis() Timers
  if (timerGestartet==true && winnerIs==-1) // Zähler läuft noch, Gewinner steht noch nicht fest
  {
    timedMillis+= nowMillis-lastMillis; // Millisekunden hochzählen
    if (timedMillis>=1000)  // Nach jeweils 1000 Millisekunden
    {
      timedMillis=0;  // Millisekundenzähler auf 0 setzen
      if (playerMove==PLAYERYELLOW) 
      {
        timerYellow++; // Eine Sekunde Laufzeit addieren bei gelb
        if (timerYellow>=MAXTIME) winnerIs=PLAYERBLUE;
      }  
      else 
      {
        timerBlue++;  // Eine Sekunde Laufzeit addieren bei blau
        if (timerBlue>=MAXTIME) winnerIs=PLAYERYELLOW;
      }
    }
  }
  lastMillis=nowMillis; // Zählerstand merken, bei dem diese Funktion zuletzt lief
}

void printTimer(int timerLaufzeit)
{
  int minuten=timerLaufzeit/60; // Ganzzahldivision durch 60
  int sekunden=timerLaufzeit%60;// Modulo-Divisionsrecht bei geteilt durch 60
  if (minuten < 10) lcd.print("0");
  lcd.print(minuten);
  lcd.print(":");
  if (sekunden < 10) lcd.print("0");
  lcd.print(sekunden);
}

void ausgabe()
{
  lcd.setCursor(0,0);
  lcd.print("Gelb: ");
  printTimer(timerYellow);
  lcd.setCursor(0,1);
  lcd.print("Blau: ");
  printTimer(timerBlue);
}

void gameOver()
{
  if (winnerIs == PLAYERYELLOW)
  {
    lcd.setCursor(0,0);
    lcd.print("* GELB GEWINNT *");
  }
  else
  {
    lcd.setCursor(0,1);
    lcd.print("* BLAU GEWINNT *");
  }
  while(1); // Endlosschleife nach Spielende bis zum Reset
}

void setup() {
  lcd.begin(16, 2);
  pinMode(START_SWITCH, INPUTMODE);
}

void loop() 
{
  eingabe();
  verarbeitung();
  ausgabe();
  if (winnerIs>=0) gameOver();
  delay(5); // Kleines Delay zum softwaremäßigen Entprellen der mechanischen Schalter
}

Aufgrund der Formatierung im Zeitformat 00:00 ist die LCD-Anzeige momentan für Laufzeiten bis 99 Minuten und 59 Sekunden pro Spieler ausgelegt (entsprechend einer MAXTIME von 5999 Sekunden).

Vielen Dank für den Sketch. Der funktioniert super. Allerdings hatte ich den Pin für den Button von 16 auf 6 geschrieben. Und die Farbe, die zuerst die Endzeit erreicht hat, wäre die Gewinnerfarbe.
Nun habe ich versucht, entsprechend der LCD Anzeige die LED Stripes zu schalten. Doch irgendwie bekomme ich das nach stundenlangen probieren nicht hin. Ich hab versucht den Status in der Ausgabe auszuwerten und ensprechenden Pin am Board zu schalten. aber irgendwie ist da der Wurm drinn.
Quasi soll, wenn die Zeit für Gelb läuft, die gelben Stripes leuchten und die blauen Stripes aus sein. Wenn die blaue Zeit läuft, entsprechend Blau an und Gelb aus.
Wo steckt da der Fehler?

#define MAXTIME 3600     // Vorgabezeit in Sekunden
#define START_SWITCH 6  // Pin an dem der Button angeschlossen ist
#define PLAYERYELLOW 0   // symbolische Konstante für gelben Spieler
#define PLAYERBLUE 1     // symbolische Konstante für blauen Spieler

#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

/* in der nachfolgenden Zeile wird festgelegt, ob die Schalter
   mit PullUp oder PullDown Widerständen beschaltet sind.
   INPUT_PULLUP aktiviert die internen PullUp-Widerstände des Atmega
   INPUT setzt voraus, dass die Schalter mit externen PullDown-Widerständen beschaltet sind
*/
#define INPUTMODE INPUT_PULLUP  // INPUT_PULLUP oder INPUT setzen
int led1 = 7; //Pin 7 ist für gelbes LED stripe definiert
int led2 = 8; //Pin 8 ist für blaues LED stripe definiert
int timerYellow = 0;
int timerBlue = 0;
int playerMove= PLAYERYELLOW; // Gelb fängt an
boolean timerGestartet=false;
int winnerIs=-1;

void eingabe()
{
  static boolean oldStartSwitch=false; // Statische Variable für letzten Schalterzustand
  boolean StartSwitch= digitalRead(START_SWITCH); // aktueller Schalterzustand
  if (INPUTMODE==INPUT_PULLUP) // Vertauschte Schaltlogik bei PullUp
  {
    StartSwitch=!StartSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
  }
  if (StartSwitch==HIGH && oldStartSwitch==LOW) // Zustandsänderung gedrückt?
  {
    if (timerGestartet==false) timerGestartet=true; 
    else if (playerMove==PLAYERYELLOW) playerMove=PLAYERBLUE;
    else playerMove=PLAYERYELLOW;
  }
  oldStartSwitch=StartSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
}

void verarbeitung()
{
  static unsigned long lastMillis=0; // statischer Zähler für letzten Stand des millis() Timers
  static unsigned long timedMillis=0;// statischer Zähler für Millisekunden beim Herunterzählen
  unsigned long nowMillis=millis();  // aktueller Stand des millis() Timers
  if (timerGestartet==true && winnerIs==-1) // Zähler läuft noch, Gewinner steht noch nicht fest
  {
    timedMillis+= nowMillis-lastMillis; // Millisekunden hochzählen
    if (timedMillis>=1000)  // Nach jeweils 1000 Millisekunden
    {
      timedMillis=0;  // Millisekundenzähler auf 0 setzen
      if (playerMove==PLAYERYELLOW) 
      {
        timerYellow++; // Eine Sekunde Laufzeit addieren bei gelb
        if (timerBlue>=MAXTIME) winnerIs=PLAYERBLUE;
      }  
      else 
      {
        timerBlue++;  // Eine Sekunde Laufzeit addieren bei blau
        if (timerYellow>=MAXTIME) winnerIs=PLAYERYELLOW;
      }
    }
  }
  lastMillis=nowMillis; // Zählerstand merken, bei dem diese Funktion zuletzt lief
}

void printTimer(int timerLaufzeit)
{
  int minuten=timerLaufzeit/60; // Ganzzahldivision durch 60
  int sekunden=timerLaufzeit%60;// Modulo-Divisionsrecht bei geteilt durch 60
  if (minuten < 10) lcd.print("0");
  lcd.print(minuten);
  lcd.print(":");
  if (sekunden < 10) lcd.print("0");
  lcd.print(sekunden);
}

void ausgabe()
{
  lcd.setCursor(0,0);
  lcd.print("Gelb: ");
  printTimer(timerYellow);
  if (timerYellow == digitalWrite(led1, HIGH); //Schaltet den gelben LED Stripe ein
  if (timerYellow == digitalWrite(led2,LOW); //Schaltet den blauen LED Stripe aus
  lcd.setCursor(0,1);
  lcd.print("Blau: ");
  printTimer(timerBlue);
  if (timerBlue == digitalWrite(led1,LOW); //Schaltet den gelben LED Stripe aus
  if (timerBlue == digitalWrite(led2,HIGH); //Schaltet den blauen LED Stripe ein
}

void gameOver()
{
  if (winnerIs == PLAYERYELLOW)
  {
    lcd.setCursor(0,0);
    lcd.print("* GELB GEWINNT *");
  }
  else
  {
    lcd.setCursor(0,1);
    lcd.print("* BLAU GEWINNT *");
  }
  while(1); // Endlosschleife nach Spielende bis zum Reset
}

void setup() {
  pinMode(led1, OUTPUT); //LED1 als Digitalausgang definiert
  pinMode(led2; OUTPUT); //LED2 als Digitalausgang definiert
  lcd.begin(16, 2);
  pinMode(START_SWITCH, INPUTMODE);
}

void loop() 
{
  eingabe();
  verarbeitung();
  ausgabe();
  if (winnerIs>=0) gameOver();
  delay(5); // Kleines Delay zum softwaremäßigen Entprellen der mechanischen Schalter
}

Gruß Frank

Nitri:
Wo steckt da der Fehler?

In der Logik, mit der Du versuchst, die LEDs zu schalten.

Versuch's mal damit:

void ausgabe()
{
  lcd.setCursor(0,0);
  lcd.print("Gelb: ");
  printTimer(timerYellow);
  lcd.setCursor(0,1);
  lcd.print("Blau: ");
  printTimer(timerBlue);
  switch (playerMove) // in playerMove steht drin, wer mit dem Zug dran ist, davon abhängig wird geschaltet
  {
    case PLAYERYELLOW: // Gelb ist am Zug
      digitalWrite(led1, HIGH); //Schaltet den gelben LED Stripe ein
      digitalWrite(led2,LOW); //Schaltet den blauen LED Stripe aus
      break;
    case PLAYERBLUE: // Blau ist am Zug
      digitalWrite(led1,LOW); //Schaltet den gelben LED Stripe aus
      digitalWrite(led2,HIGH); //Schaltet den blauen LED Stripe ein
      break;
    default: // Spiel hat noch nicht begonnen, alle LEDs aus
      digitalWrite(led1,LOW); //Schaltet den gelben LED Stripe aus
      digitalWrite(led2,LOW); //Schaltet den blauen LED Stripe aus
  }
}

Oder umgekehrt, ich bin mir über die genaue Logik bei Deinem Spiel nicht im Klaren.

Oder umgekehrt, ich bin mir über die genaue Logik bei Deinem Spiel nicht im Klaren.

Na ganz einfach :slight_smile: Die Farbe, die zuerst eine Stunde an Zeit voll hat, hat gewonnen. ^^
Mein Code war nah drann, oder? Ich hab deinen Code eingefügt, und die Funktion überflogen. Aber wenn die beiden Zeiten auf null stehen, sollten da nicht beide LED Typen auf aus stehen? Zumindest ist das im Code zu erkennen, das beide auf aus sein sollten. Eine, ich glaub, die gelbe ist an. und dann läuft die Zeit über eine Stunde hinaus, trotzdem ja Maxtime 3600 Sekunden festgelegt ist.
Und dann hab ich ein schier unlösbares Problem festgestellt. Bei Knopfdruck startet immer zuerst gelb. Wenn nun aber ein blauer Spieler es schafft zum Knopf zu kommen? Hmm, ich hab bisher echt noch keine Theorie gefunden, dieses Problem zu lösen.
Vielleicht mit zwei kleine Startknöpfe, jeweils für Gelb und Blau. Wenn dann das Spiel gestartet ist, das dann nur noch der Buzzerknopf aktiv ist?

Nitri:
Na ganz einfach :slight_smile: Die Farbe, die zuerst eine Stunde an Zeit voll hat, hat gewonnen. ^^

Ach so, eine Mannschaft soll "gute Zeit" für sich selbst einsammeln.
Ich war der Meinung, man sollte im Gegenteil den Gegner "schlechte Zeit" sammeln lassen, im Sinne von "Deine Uhr läuft ab", und wenn die Uhr des Gegners abgelaufen ist, hat er verloren.

Nitri:
Ich hab deinen Code eingefügt, und die Funktion überflogen. Aber wenn die beiden Zeiten auf null stehen, sollten da nicht beide LED Typen auf aus stehen? Zumindest ist das im Code zu erkennen, das beide auf aus sein sollten.

Ja, so ist das beabsichtigt. Im Zweifelsfalls mit Serial.begin(9600) die serielle Schnittstelle aktivieren und zum Debuggen an relevanten Stellen mal ein paar Debug-Meldungen ausgeben lassen.

Nitri:
Eine, ich glaub, die gelbe ist an. und dann läuft die Zeit über eine Stunde hinaus, trotzdem ja Maxtime 3600 Sekunden festgelegt ist.

Dann wird bei Deiner Programmlogik offenbar die gameOver() Funktion nicht korrekt aufgerufen. Ich hatte in gameOver() am Ende eigentlich eine Endlosschleife eingebaut, so dass nach gameOver() nichts mehr weiter im Sketch passiert, also insbesondere auch keine Zeit mehr gezählt wird.

Ich habe den von mir geposteten Sketch allerdings auch nur mit Laufzeiten von 30 und 60 Sekunden getestet und nicht mit 3600 Sekunden.

Nitri:
Und dann hab ich ein schier unlösbares Problem festgestellt. Bei Knopfdruck startet immer zuerst gelb. Wenn nun aber ein blauer Spieler es schafft zum Knopf zu kommen? Hmm, ich hab bisher echt noch keine Theorie gefunden, dieses Problem zu lösen.

Der erste Spieler, der den Knopf drückt, setzt das Spiel im Gang. Wenn das nicht "Gelb" ist, muß er eben ZWEIMAL NACHEINANDER auf den Knopf drücken. Dann zählt es für Blau.

Edit/Nachtrag wg. geschalteter gelber LEDs vor Spielbeginn:
Die gelben LEDs werden geschaltet, weil schon vor Spielbeginn anfänglich der Spieler auf gelb gesetzt wird:

int playerMove= PLAYERYELLOW; // Gelb fängt an

Das müßte vielmehr auf "kein Spieler" gesetzt werden, also:

int playerMove=-1; // noch ist kein Spieler am Zug

Und erst mit Spielbeginn, beim ersten Drücken des Tasters, darf dann der gelbe Spieler gesetzt werden:

  if (timerGestartet==false)
  {
    timerGestartet=true; 
    playerMove= PLAYERYELLOW; 
  }
  else if (playerMove==PLAYERYELLOW) playerMove=PLAYERBLUE;
  else playerMove=PLAYERYELLOW;

Nur dann wird der gelbe Spieler (und in der Folge die gelben LEDs) erst dann gesetzt, wenn der Timer wirklich gestartet wurde.

Das mit den LED Ausgängen haut jetzt hin. Aber leider wird kein Gewinner festgelegt, wenn entsprechend die MAXTIME von (zum test) 60 Sekunden erreicht ist. Die Zeit läuft weiter und es wird erst der Gewinnder festgelegt, wenn nach den 60 Sekunden der Knopf gedrückt wird. Dann steht da, das z.B. * Gelb gewinnt *.
Ich kann den Sketch ändern wie ich will. Es ist immer wieder das selbe. :~

Nitri:
Ich kann den Sketch ändern wie ich will. Es ist immer wieder das selbe. :~

Poste den geänderten Sketch hier ins Forum.!

Ich kann es nicht raten, was Du geändert hast, dass die gameOver() Funktion nicht mehr rein nach Zeitablauf aufgerufen wird, sondern erst nach Zeitablauf und dann beim nächsten Tastendruck.

Ich hab jedesmal, wenn ich was geändert habe, und es nicht funktionierte, den Originalzustand wieder hergestellt. Nur die Maxtime habe ich auf 30 sekunden gelassen um nicht eine Stunde zu warten :slight_smile:

#define MAXTIME 30     // Vorgabezeit in Sekunden
#define START_SWITCH 6  // Pin an dem der Button angeschlossen ist
#define PLAYERYELLOW 0   // symbolische Konstante für gelben Spieler
#define PLAYERBLUE 1     // symbolische Konstante für blauen Spieler

#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

/* in der nachfolgenden Zeile wird festgelegt, ob die Schalter
   mit PullUp oder PullDown Widerständen beschaltet sind.
   INPUT_PULLUP aktiviert die internen PullUp-Widerstände des Atmega
   INPUT setzt voraus, dass die Schalter mit externen PullDown-Widerständen beschaltet sind
*/
#define INPUTMODE INPUT_PULLUP  // INPUT_PULLUP oder INPUT setzen
int led1 = 7; //Pin 7 ist für gelbes LED stripe definiert
int led2 = 8; //Pin 8 ist für blaues LED stripe definiert
int timerYellow = 0;
int timerBlue = 0;
//int playerMove= PLAYERYELLOW; // Gelb fängt an
int playerMove=-1; // noch ist kein Spieler am Zug
boolean timerGestartet=false;
int winnerIs=-1;

void eingabe()
{
  static boolean oldStartSwitch=false; // Statische Variable für letzten Schalterzustand
  boolean StartSwitch= digitalRead(START_SWITCH); // aktueller Schalterzustand
  if (INPUTMODE==INPUT_PULLUP) // Vertauschte Schaltlogik bei PullUp
  {
    StartSwitch=!StartSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
  }
  if (StartSwitch==HIGH && oldStartSwitch==LOW) // Zustandsänderung gedrückt?
  {
    if (timerGestartet==false)
   {
    timerGestartet=true; 
    playerMove=PLAYERYELLOW;
   }
    else if (playerMove==PLAYERYELLOW) playerMove=PLAYERBLUE;
    else playerMove=PLAYERYELLOW;
  }
  oldStartSwitch=StartSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
}

void verarbeitung()
{
  static unsigned long lastMillis=0; // statischer Zähler für letzten Stand des millis() Timers
  static unsigned long timedMillis=0;// statischer Zähler für Millisekunden beim Herunterzählen
  unsigned long nowMillis=millis();  // aktueller Stand des millis() Timers
  if (timerGestartet==true && winnerIs==-1) // Zähler läuft noch, Gewinner steht noch nicht fest
  {
    timedMillis+= nowMillis-lastMillis; // Millisekunden hochzählen
    if (timedMillis>=1000)  // Nach jeweils 1000 Millisekunden
    {
      timedMillis=0;  // Millisekundenzähler auf 0 setzen
      if (playerMove==PLAYERYELLOW) 
      {
        timerYellow++; // Eine Sekunde Laufzeit addieren bei gelb
        if (timerBlue>=MAXTIME) winnerIs=PLAYERBLUE;
      }  
      else 
      {
        timerBlue++;  // Eine Sekunde Laufzeit addieren bei blau
        if (timerYellow>=MAXTIME) winnerIs=PLAYERYELLOW;
      }
    }
  }
  lastMillis=nowMillis; // Zählerstand merken, bei dem diese Funktion zuletzt lief
}

void printTimer(int timerLaufzeit)
{
  int minuten=timerLaufzeit/60; // Ganzzahldivision durch 60
  int sekunden=timerLaufzeit%60;// Modulo-Divisionsrecht bei geteilt durch 60
  if (minuten < 10) lcd.print("0");
  lcd.print(minuten);
  lcd.print(":");
  if (sekunden < 10) lcd.print("0");
  lcd.print(sekunden);
}

void ausgabe()
{
  lcd.setCursor(0,0);
  lcd.print("Gelb: ");
  printTimer(timerYellow);
  lcd.setCursor(0,1);
  lcd.print("Blau: ");
  printTimer(timerBlue);
  switch (playerMove) // in playerMove steht drin, wer mit dem Zug dran ist, davon abhängig wird geschaltet
  {
    case PLAYERYELLOW: // Gelb ist am Zug
      digitalWrite(led1, HIGH); //Schaltet den gelben LED Stripe ein
      digitalWrite(led2,LOW); //Schaltet den blauen LED Stripe aus
      break;
    case PLAYERBLUE: // Blau ist am Zug
      digitalWrite(led1,LOW); //Schaltet den gelben LED Stripe aus
      digitalWrite(led2,HIGH); //Schaltet den blauen LED Stripe ein
      break;
    default: // Spiel hat noch nicht begonnen, alle LEDs aus
      digitalWrite(led1,LOW); //Schaltet den gelben LED Stripe aus
      digitalWrite(led2,LOW); //Schaltet den blauen LED Stripe aus
  }
}


void gameOver()
{
  if (winnerIs == PLAYERYELLOW)
  {
    lcd.setCursor(0,0);
    lcd.print("* GELB GEWINNT *");
  }
  else
  {
    lcd.setCursor(0,1);
    lcd.print("* BLAU GEWINNT *");
  }
  while(1); // Endlosschleife nach Spielende bis zum Reset
}

void setup() {
  pinMode(led1, OUTPUT); //LED1 als Digitalausgang definiert
  pinMode(led2, OUTPUT); //LED2 als Digitalausgang definiert
  lcd.begin(16, 2);
  pinMode(START_SWITCH, INPUTMODE);
}

void loop() 
{
  eingabe();
  verarbeitung();
  ausgabe();
  if (winnerIs>=0) gameOver();
  delay(5); // Kleines Delay zum softwaremäßigen Entprellen der mechanischen Schalter
}

Ich hab in void verarbeitung() ne Menge versucht. Entweder ging gar nichts. Oder es wurde falsch gezählt. Ist aber wieder in Originalzustand.

Irgendwas ist Dir da beim Umstellen der Programmlogik von "den Gegner schlechte Zeit ansammeln lassen" auf "selbst gute Zeit einsammeln" schiefgegangen. Und möglicherweise hast Du die Bedeutung der Variablen playerMove falsch verstanden.

Für die Programmlogik gilt:
"playerMove" steht für den Spieler, der "dran ist ist sich zu bewegen und den nächsten Zug zu machen".

Das bedeutet für die geänderte Programmlogik ("gute Zeit sammeln"):
Wenn playerMove mal angenommen PLAYERBLUE ist, dann muss als nächstes der blaue Spieler den Button drücken und gleichzeitig zählt währenddessen die (gute) Zeit für den gelben Spieler. Und umgekehrt.

Ich habe Dir die Logik mal korrigiert, einen entsprechenden Kommentar bei playerMove eingefügt und am Ende auch die GameOver-Anzeige nochmal etwas geändert, so dass man in der Schlussanzeige sehen kann, mit welchem Zeitvorsprung die Gewinnermannschaft gesiegt hat.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

#define MAXTIME 30     // Vorgabezeit in Sekunden
#define START_SWITCH 6  // Pin an dem der Button angeschlossen ist
#define PLAYERYELLOW 0   // symbolische Konstante für gelben Spieler
#define PLAYERBLUE 1     // symbolische Konstante für blauen Spieler


/* in der nachfolgenden Zeile wird festgelegt, ob die Schalter
   mit PullUp oder PullDown Widerständen beschaltet sind.
   INPUT_PULLUP aktiviert die internen PullUp-Widerstände des Atmega
   INPUT setzt voraus, dass die Schalter mit externen PullDown-Widerständen beschaltet sind
*/
#define INPUTMODE INPUT_PULLUP  // INPUT_PULLUP oder INPUT setzen
int led1 = 7; //Pin 7 ist für gelbes LED stripe definiert
int led2 = 8; //Pin 8 ist für blaues LED stripe definiert
int timerYellow = 0;
int timerBlue = 0;
int playerMove=-1; // noch ist kein Spieler am Zug
// playerMove steht für den Spieler, der den nächsten Spielzug durchführen muss
// Falls playerMove==PLAYERYELLOW, dann muss der gelbe Spieler den Button drücken 
// und bis dahin zählt die Zeit für blau und umgekehrt
boolean timerGestartet=false;
int winnerIs=-1;

void eingabe()
{
  static boolean oldStartSwitch=false; // Statische Variable für letzten Schalterzustand
  boolean StartSwitch= digitalRead(START_SWITCH); // aktueller Schalterzustand
  if (INPUTMODE==INPUT_PULLUP) // Vertauschte Schaltlogik bei PullUp
  {
    StartSwitch=!StartSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
  }
  if (StartSwitch==HIGH && oldStartSwitch==LOW) // Zustandsänderung gedrückt?
  {
    if (timerGestartet==false)
   {
    timerGestartet=true; 
    playerMove=PLAYERBLUE; // Der Spieler, der als nächstes den Schalter drücken soll
   }
    else if (playerMove==PLAYERYELLOW) playerMove=PLAYERBLUE;
    else playerMove=PLAYERYELLOW;
  }
  oldStartSwitch=StartSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
}

void verarbeitung()
{
  static unsigned long lastMillis=0; // statischer Zähler für letzten Stand des millis() Timers
  static unsigned long timedMillis=0;// statischer Zähler für Millisekunden beim Herunterzählen
  unsigned long nowMillis=millis();  // aktueller Stand des millis() Timers
  if (timerGestartet==true && winnerIs==-1) // Zähler läuft noch, Gewinner steht noch nicht fest
  {
    timedMillis+= nowMillis-lastMillis; // Millisekunden hochzählen
    if (timedMillis>=1000)  // Nach jeweils 1000 Millisekunden
    {
      timedMillis=0;  // Millisekundenzähler auf 0 setzen
      if (playerMove==PLAYERBLUE) 
      {
        timerYellow++; // Eine Sekunde Laufzeit addieren bei gelb
        if (timerYellow>=MAXTIME) winnerIs=PLAYERYELLOW;
      }  
      else 
      {
        timerBlue++;  // Eine Sekunde Laufzeit addieren bei blau
        if (timerBlue>=MAXTIME) winnerIs=PLAYERBLUE;
      }
    }
  }
  lastMillis=nowMillis; // Zählerstand merken, bei dem diese Funktion zuletzt lief
}

void printTimer(int timerLaufzeit)
{
  int minuten=timerLaufzeit/60; // Ganzzahldivision durch 60
  int sekunden=timerLaufzeit%60;// Modulo-Divisionsrecht bei geteilt durch 60
  if (minuten < 10) lcd.print("0");
  lcd.print(minuten);
  lcd.print(":");
  if (sekunden < 10) lcd.print("0");
  lcd.print(sekunden);
}

void ausgabe()
{
  lcd.setCursor(0,0);
  lcd.print("Gelb: ");
  printTimer(timerYellow);
  lcd.setCursor(0,1);
  lcd.print("Blau: ");
  printTimer(timerBlue);
  switch (playerMove) // in playerMove steht drin, wer mit dem Zug dran ist, davon abhängig wird geschaltet
  {
    case PLAYERYELLOW: // Gelb ist am Zug
      digitalWrite(led1,LOW); //Schaltet den gelben LED Stripe aus
      digitalWrite(led2,HIGH); //Schaltet den blauen LED Stripe ein
      break;
    case PLAYERBLUE: // Blau ist am Zug
      digitalWrite(led1, HIGH); //Schaltet den gelben LED Stripe ein
      digitalWrite(led2,LOW); //Schaltet den blauen LED Stripe aus
      break;
    default: // Spiel hat noch nicht begonnen, alle LEDs aus
      digitalWrite(led1,LOW); //Schaltet den gelben LED Stripe aus
      digitalWrite(led2,LOW); //Schaltet den blauen LED Stripe aus
  }
}


void gameOver()
{
  lcd.setCursor(0,0);
  if (winnerIs == PLAYERYELLOW)
  {
    lcd.print("* GELB GEWINNT *");
    lcd.setCursor(0,1);
    printTimer(timerYellow);
    lcd.print(" zu ");
    printTimer(timerBlue);
  }
  else
  {
    lcd.print("* BLAU GEWINNT *");
    lcd.setCursor(0,1);
    printTimer(timerBlue);
    lcd.print(" zu ");
    printTimer(timerYellow);
  }
  while(1); // Endlosschleife nach Spielende bis zum Reset
}

void setup() {
  pinMode(led1, OUTPUT); //LED1 als Digitalausgang definiert
  pinMode(led2, OUTPUT); //LED2 als Digitalausgang definiert
  lcd.begin(16, 2);
  pinMode(START_SWITCH, INPUTMODE);
}

void loop() 
{
  eingabe();
  verarbeitung();
  ausgabe();
  if (winnerIs>=0) gameOver();
  delay(5); // Kleines Delay zum softwaremäßigen Entprellen der mechanischen Schalter
}

Die LED-Anzeige ist so geschaltet, dass die LEDs der Farbe angezeigt werden, für die die Zeit zählt. Also nicht die Farbe der Mannschaft, die am Zug ist, den Button zu drücken. Aber das kannst Du ggf. leicht an der Stelle im Sketch ändern, wo die LEDs geschaltet werden.

Vielen Dank fürs korrigieren. Das funktioniert super :slight_smile: Nun wollte ich ein paar Feinheiten mit einbauen und nix klappt. Ich wollte im Sekundentakt ein Piepton erzeugt wird. Entweder ist es bei mir ein Dauerton und der Timer startet einfach von allein. Oder ich hör gar nichts. Genauso, wenn ein Gewinner fest steht, soll ein laute Tröte los gehen oder ein Sprachmodul mit z.B. "Game over, Blau gewinnt" gestartet werden. Damit hab ich noch gar nicht angefangen. Ich hab versucht aus der Paintball"bombe" da was zu kopieren. Aber damit klappts erst recht nicht. Hier mal die Tone versuche.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Tone.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

#define MAXTIME 30     // Vorgabezeit in Sekunden
#define START_SWITCH 6  // Pin an dem der Button angeschlossen ist
#define PLAYERYELLOW 0   // symbolische Konstante für gelben Spieler
#define PLAYERBLUE 1     // symbolische Konstante für blauen Spieler


Tone tone1; 

/* in der nachfolgenden Zeile wird festgelegt, ob die Schalter
   mit PullUp oder PullDown Widerständen beschaltet sind.
   INPUT_PULLUP aktiviert die internen PullUp-Widerstände des Atmega
   INPUT setzt voraus, dass die Schalter mit externen PullDown-Widerständen beschaltet sind
*/
#define INPUTMODE INPUT_PULLUP  // INPUT_PULLUP oder INPUT setzen
int led1 = 7; //Pin 7 ist für gelbes LED stripe definiert
int led2 = 8; //Pin 8 ist für blaues LED stripe definiert
int timerYellow = 0;
int timerBlue = 0;
int playerMove=-1; // noch ist kein Spieler am Zug
// playerMove steht für den Spieler, der den nächsten Spielzug durchführen muss
// Falls playerMove==PLAYERYELLOW, dann muss der gelbe Spieler den Button drücken 
// und bis dahin zählt die Zeit für blau und umgekehrt
boolean timerGestartet=false;
int winnerIs=-1;

void eingabe()
{
  static boolean oldStartSwitch=false; // Statische Variable für letzten Schalterzustand
  boolean StartSwitch= digitalRead(START_SWITCH); // aktueller Schalterzustand
  if (INPUTMODE==INPUT_PULLUP) // Vertauschte Schaltlogik bei PullUp
  {
    StartSwitch=!StartSwitch; // ausgelesenen Zustand vertauschen wg. PullUp
  }
  if (StartSwitch==HIGH && oldStartSwitch==LOW) // Zustandsänderung gedrückt?
  {
    if (timerGestartet==false)
   {
    timerGestartet=true; 
    playerMove=PLAYERBLUE; // Der Spieler, der als nächstes den Schalter drücken soll
   }
    else if (playerMove==PLAYERYELLOW) playerMove=PLAYERBLUE;
    else playerMove=PLAYERYELLOW;
  }
  oldStartSwitch=StartSwitch; // aktueller Schalterzustand wird zum alten Schalterzustand
}

void verarbeitung()
{
  static unsigned long lastMillis=0; // statischer Zähler für letzten Stand des millis() Timers
  static unsigned long timedMillis=0;// statischer Zähler für Millisekunden beim Herunterzählen
  unsigned long nowMillis=millis();  // aktueller Stand des millis() Timers
  if (timerGestartet==true && winnerIs==-1) // Zähler läuft noch, Gewinner steht noch nicht fest
    {
    timedMillis+= nowMillis-lastMillis; // Millisekunden hochzählen
    if (timedMillis>=1000)  // Nach jeweils 1000 Millisekunden
    {
      timedMillis=0;  // Millisekundenzähler auf 0 setzen
      if (playerMove==PLAYERBLUE) 
      {
        timerYellow++; // Eine Sekunde Laufzeit addieren bei gelb
        if (timerYellow>=MAXTIME) winnerIs=PLAYERYELLOW;
      }  
      else 
      {
        timerBlue++;  // Eine Sekunde Laufzeit addieren bei blau
        if (timerBlue>=MAXTIME) winnerIs=PLAYERBLUE;
      }
    }
  }
  lastMillis=nowMillis; // Zählerstand merken, bei dem diese Funktion zuletzt lief
}

void printTimer(int timerLaufzeit)
{
  int minuten=timerLaufzeit/60; // Ganzzahldivision durch 60
  int sekunden=timerLaufzeit%60;// Modulo-Divisionsrecht bei geteilt durch 60
  if (minuten < 10) lcd.print("0");
  lcd.print(minuten);
  lcd.print(":");
  if (sekunden < 10) lcd.print("0");
  lcd.print(sekunden);
}

void ausgabe()
{
  lcd.setCursor(0,0);
  lcd.print("Gelb: ");
  printTimer(timerYellow);
  lcd.setCursor(0,1);
  lcd.print("Blau: ");
  printTimer(timerBlue);
  switch (playerMove) // in playerMove steht drin, wer mit dem Zug dran ist, davon abhängig wird geschaltet
  {
    case PLAYERYELLOW: // Gelb ist am Zug
      digitalWrite(led1,LOW); //Schaltet den gelben LED Stripe aus
      digitalWrite(led2,HIGH); //Schaltet den blauen LED Stripe ein
      break;
    case PLAYERBLUE: // Blau ist am Zug
      digitalWrite(led1, HIGH); //Schaltet den gelben LED Stripe ein
      digitalWrite(led2,LOW); //Schaltet den blauen LED Stripe aus
      break;
    default: // Spiel hat noch nicht begonnen, alle LEDs aus
      digitalWrite(led1,LOW); //Schaltet den gelben LED Stripe aus
      digitalWrite(led2,LOW); //Schaltet den blauen LED Stripe aus
  }
}


void gameOver()
{
  lcd.setCursor(0,0);
  if (winnerIs == PLAYERYELLOW)
  {
    lcd.print("* GELB GEWINNT *");
    lcd.setCursor(0,1);
    printTimer(timerYellow);
    lcd.print(" zu ");
    printTimer(timerBlue);
  }
  else
  {
    lcd.print("* BLAU GEWINNT *");
    lcd.setCursor(0,2);
    printTimer(timerBlue);
    lcd.print(" zu ");
    printTimer(timerYellow);
  }
  while(1); // Endlosschleife nach Spielende bis zum Reset
}

void setup() {
  pinMode(led1, OUTPUT); //LED1 als Digitalausgang definiert
  pinMode(led2, OUTPUT); //LED2 als Digitalausgang definiert
  lcd.begin(16, 2);
  pinMode(START_SWITCH, INPUTMODE);
  tone1.begin(9); //Digitalpin für Tonausgabe definiert
}

void loop() 
{
  eingabe();
  verarbeitung();
  ausgabe();
  if (winnerIs>=0) gameOver();
  delay(5); // Kleines Delay zum softwaremäßigen Entprellen der mechanischen Schalter
}

Ich hab versucht in diesem Bereich den Ton irgendwie mit ein zu binden. Ich hab gehofft, wenn immer eine sekunde voll ist, das ein piepton an meinem Lautsprecher zu hören ist. Funktioniert es nicht mit "tone1.play(NOTE_A2, 90);" Es gab meist ein Dauerton und die Zeit spielte verrückt.

{
  int minuten=timerLaufzeit/60; // Ganzzahldivision durch 60
  int sekunden=timerLaufzeit%60;// Modulo-Divisionsrecht bei geteilt durch 60
  if (minuten < 10) lcd.print("0");
  lcd.print(minuten);
  lcd.print(":");
  if (sekunden < 10) lcd.print("0");
  lcd.print(sekunden);
}

Hab es hin bekommen :slight_smile: Endlich hab ich im Sekundentakt meinen Piepton und beim Gameover ein fünf Sekunden Dauerton. Ich werd das aber noch ändern. Denn der Sekundenton soll relativ normal hörbar sein und der Gameoverton muss sehr laut zu hören sein. Ich hab mir gedacht, den Sekundenton auf einem Digitaloutpin zu legen und den Gameoverton auf einem anderen Digitaloutpin zu legen. Diesen werd ich dann verstärken. Sollte doch gehen, oder? ^^