• Hallo Zusammen, Aufgrund der aktuellen Situation setzten wir die Möglichkeit aus, sich mit Gmail zu registrieren. Wir bitten um Verständnis Das RCMP Team

Eigenbau LCD-Anzeige für Tipp-Kick oder ähnliche Spiele

froetz

Mitglied
Jemand im Bastelthread meinte das sollte auch hier her...
(original hier: https://www.rc-modellbau-portal.de/...-gebastelt-allgemein-ohne-rc.4757/post-183816)

Tipp-Kick-Dash

20200228


in letzter Zeit ist beim Junior Tipp-Kick wieder hoch im Kurs. Um auch mal wieder was mit ihm zu basteln, haben wir uns für eine Tipp-Kick-Anzeige entschieden. Darauf sollen die Tore und die verbleibende Spielzeit zu sehen sein. Also haben wir in der Teilekiste gekramt und einen Arduino Micro gefunden. Dazu noch ein schönes 16x2 LC-Display, das blau hinterleuchtet ist. Kleinkram wie Taster, Kabel und Widerstände hat man ja eh immer zu Hause. Ein erster Softwareprototyp stand recht fix, der zeigte aber erst mal nur eine statische Anzeige um zu sehen, wie das später dann überhaupt wirkt. Beim schreiben des eigentlichen Programms hatte mein Gehirn einen Fehler und ich hab knapp eine Stunde gebraucht bis ich aus "||" endlich "&&" gemacht hab. Ich hab die zwei Pipes irgendwie die ganze Zeit als "UND" gelesen. Auch nach zigfachem gegenlesen und nachprüfen nix falsches gesehen. Irgendwann kam dann das berühmte "Ou mann wie dumm..." und siehe da, macht man es richtig funktioniert es plötzlich.
Aufgebaut hab ich das Teil erst mal auf einem Steckbrett:



Schaltplan:


Und da bewegte Bilder immer toll sind, hier ein Video:


Die LEDs und der Piepser, der den An- und Abpfiff macht kamen erst während des Programmierens dazu. vor dem Anpfiff und nach dem Abpfiff ist eine Änderung der Punkte nicht mehr möglich. passiert während dem Spiel eine "Falscheingabe" kann man einfach weiter auf die entsprechende Taste drücken und korrigieren, da nach 9 Punkten automatisch wieder auf 0 zurückgesetzt wird.
Ein Gehäuse dafür hat der Kurze dann mal mit Bleistift gezeichnet und ich hab es dann mit ihm in Sketchup digitalisiert. Das Gehäuse wird derzeit noch etwas verfeinert und dann gedruckt.


Hier ist das fertige Teil:

von innen:


und komplett:


Im Spoiler ist der komplette Code - ich hoffe ausreichend gut kommentiert.
tk_lcd1.ino
[noae]
Code:
// Dashboard for TIPP-KICK with timer

// select the required libraries

//#include <Adafruit_SSD1331.h>
//#include <PID_v1.h>
//#include <Bounce2.h>
//#include <Stepper.h>
//#include <WiFi.h>
//#include <WiFiClient.h>
//#include <WiFiServer.h>
//#include <WiFiUdp.h>
//#include <ESP8266WiFi.h>
#include <LiquidCrystal.h>
//#include <max6675.h>
//#include <Servo.h>
//#include <OneWire.h>
//#include <DallasTemperature.h>


//    _____            _                 _   _
//   |  __ \          | |               | | (_)
//   | |  | | ___  ___| | __ _ _ __ __ _| |_ _  ___  _ __  ___
//   | |  | |/ _ \/ __| |/ _` | '__/ _` | __| |/ _ \| '_ \/ __|
//   | |__| |  __/ (__| | (_| | | | (_| | |_| | (_) | | | \__ \
//   |_____/ \___|\___|_|\__,_|_|  \__,_|\__|_|\___/|_| |_|___/
//
//

//---------------------------------------------------------------------
// these variables are meant to be used for calling functions by time
// in the "loop", because not every function has to be called in every
// loop of the programm.

const int tasks = 9;
unsigned long millis_current;
unsigned long millis_old_task[tasks];
bool task[tasks];
int task_time[] = {5, 10, 25, 100, 250, 500, 1000, 5000, 10000, 60000};

//---------------------------------------------------------------------

const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

int ScoreYellow = 0;
int ScoreRed = 0;
long GameTime = 305000;


int Buzzer = 7;
int ButtonRed = 10;
int ButtonYellow = 9;
int ButtonReset = 8;


int LEDGreen = A4;
int LEDRed = A5;



// Buttons
bool ButtonRedState = 0;
bool ButtonYellowState = 0;
bool ButtonRedStateOld = 0;
bool ButtonYellowStateOld = 0;
bool ButtonResetState = 0;
bool ButtonResetStateOld = 0;

//
bool GameReady = 0;
bool GameActive = 0;
bool GameActiveOld = 0;


long TimeGameEndMillis = 0;
int TimeLeft = 0;
int TimeLeftMinutes = 0;
int TimeLeftSeconds = 0;

//---------------------------------------------------------------------

//     _____      _
//    / ____|    | |
//   | (___   ___| |_ _   _ _ __
//    \___ \ / _ \ __| | | | '_ \
//    ____) |  __/ |_| |_| | |_) |
//   |_____/ \___|\__|\__,_| .__/
//                         | |
//                         |_|


void setup() {
  pinMode(ButtonRed, INPUT);
  pinMode(ButtonYellow, INPUT);
  pinMode(LEDGreen, OUTPUT);
  pinMode(LEDRed, OUTPUT);
  pinMode(Buzzer, OUTPUT);
  analogWrite(LEDGreen, 0);
  analogWrite(LEDRed, 0);

  Serial.begin(115200);             // initialize serial communications at 115200bps

  //---------------------------------------------------------------------

  // The "Splashscreen"

  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Tipp-Kick-Dash  ");
  lcd.setCursor(0, 1);
  lcd.print("       froetz.de");
  delay(2000);
  lcd.clear();
  delay(100);
  lcd.setCursor(0, 1);
  lcd.print("  Press Start!  ");
  TimeLeft = ((GameTime / 1000) + 5);

  digitalWrite(Buzzer, LOW);
  analogWrite(LEDRed, 150);

  //---------------------------------------------------------------------
}


//    _
//   | |
//   | |     ___   ___  _ __
//   | |    / _ \ / _ \| '_ \
//   | |___| (_) | (_) | |_) |
//   |______\___/ \___/| .__/
//                     | |
//                     |_|

void loop() {
  // generate slopes for tasks
  millis_current = millis();
  for (int i = 0; i < tasks; i++) {
    if (millis_current - millis_old_task[i] > task_time[i]) {
      millis_old_task[i] = millis_current;
      task[i] = 1;
    }
  }

  // Buttonstates and old buttonstates to detect changes
  GameActiveOld = GameActive;
  ButtonRedStateOld = ButtonRedState;
  ButtonYellowStateOld = ButtonYellowState;
  ButtonResetStateOld = ButtonResetState;

  // read the actual states
  ButtonRedState = digitalRead(ButtonRed);
  ButtonYellowState = digitalRead(ButtonYellow);
  ButtonResetState = digitalRead(ButtonReset);



// Check the buttons and count scores or reset the game

  if (GameReady && ButtonYellowState == HIGH && ButtonYellowStateOld == LOW && (TimeLeft <= (GameTime / 1000) - 5)) {
    delay(75);
    CountYellow();
    delay(75);
  }



  if (GameReady && ButtonRedState == HIGH && ButtonRedStateOld == LOW && (TimeLeft <= (GameTime / 1000) - 5)) {
    delay(75);
    CountRed();
    delay(75);
  }


  if (ButtonResetState == LOW && ButtonResetStateOld == HIGH ) {
    delay(50);
    Reset();
    delay(75);
  }


  if (task[0] == 1) {
    // 5ms calls
    //Serial.println("5ms");
  }

  if (task[1] == 1) {
    // 10ms calls
    //Serial.println("10ms");
  }

  if (task[2] == 1) {
    // 25ms calls
    //Serial.println("25ms");
  }

  if (task[3] == 1) {
    // 100ms calls
    //Serial.println("100ms");
    if (GameReady) {
      CalculateDisplayTime();
      DisplayOutputGame();
    }
  }

  if (task[4] == 1) {
    // 250ms calls
    //Serial.println("250ms");
SerialOutput();
    //start the game
    if (GameReady && (TimeLeft <= (GameTime / 1000) - 5)) {
      analogWrite(LEDGreen, 140);
      analogWrite(LEDRed, 0);
      GameActive = HIGH;
    }
    // it time is up, then gameover
    if (TimeLeft <= 0) {
      GameOver();
      DisplayOutputGameOver();
    }


    // kick-off
    if (GameActive == HIGH && GameActiveOld == LOW) {
      KickOff();
    
    }

    // final whistle
    if (GameActive == LOW && GameActiveOld == HIGH) {
      finalwhistle();
    }
  }

  if (task[5] == 1) {
    // 500ms calls
    //Serial.println("500ms");

    // Update the remaining time
    if (GameReady) {
      UpdateTime();
    }
  }

  if (task[6] == 1) {
    // 1s calls
    //Serial.println("1s");

  }

  if (task[7] == 1) {
    // 5s calls
    //Serial.println("5s");
  }

  if (task[8] == 1) {
    // 10s calls
    //Serial.println("10s");
  }

  if (task[9] == 1) {
    // 1m calls
    //Serial.println("1m");
  }


  // reset slopes for the tasks
  for (int i = 0; i < tasks; i++) {
    task[i] = 0;
  }



// DEBUGGING
  //Serial.println("######DEBUG########");
  //Serial.print("ButtonResetState: ");
  //Serial.println(ButtonResetState);
  //Serial.print("ButtonResetStateOld: ");
  //Serial.println(ButtonResetStateOld);
  //
  //Serial.print("ButtonRedState: ");
  //Serial.println(ButtonRedState);
  //Serial.print("ButtonRedStateOld: ");
  //Serial.println(ButtonRedStateOld);
  //
  //Serial.print("ButtonYellowState: ");
  //Serial.println(ButtonYellowState);
  //Serial.print("ButtonYellowStateOld: ");
  //Serial.println(ButtonYellowStateOld);
  //
  //Serial.print("ScoreRed: ");
  //Serial.println(ScoreRed);
  //Serial.print("ScoreYellow: ");
  //Serial.println(ScoreYellow);
  //  Serial.print("TimeLeft: ");
  //  Serial.println(TimeLeft);
  //  Serial.print("Gametime: ");
  //  Serial.println(GameTime);
  //  Serial.print("TimeLeftMinutes: ");
  //  Serial.println(TimeLeftMinutes);
  //  Serial.print("TimeLeftSeconds: ");
  //  Serial.println(TimeLeftSeconds);
}
[/noae]

functions.ino
[noae]
Code:
//    ______                _   _
//   |  ____|              | | (_)
//   | |__ _   _ _ __   ___| |_ _  ___  _ __  ___
//   |  __| | | | '_ \ / __| __| |/ _ \| '_ \/ __|
//   | |  | |_| | | | | (__| |_| | (_) | | | \__ \
//   |_|   \__,_|_| |_|\___|\__|_|\___/|_| |_|___/
//
//


void SerialOutput() {
  Serial.println("Start serial communication");
  Serial.print("Remainung time: ");
  Serial.println(TimeLeft);
  Serial.print("Red: ");
  Serial.println(ScoreRed);
  Serial.print("Yellow: ");
  Serial.println(ScoreYellow);
  Serial.println("End serial communication");
}

void CountRed() {
  //Serial.println("CountRed start");
  ScoreRed++;
  tone(Buzzer, 60);
  delay(20);
  noTone(Buzzer);
  if (ScoreRed > 9) {
    ScoreRed = 0;
  }
}

void CountYellow() {
  //Serial.println("CountYellow start");
  ScoreYellow++;
  tone(Buzzer, 60);
  delay(20);
  noTone(Buzzer);
  if (ScoreYellow > 9) {
    ScoreYellow = 0;
  }
}

void DisplayOutputGame() {
  lcd.setCursor(0, 0);
  lcd.print("Rot : Gelb  ");

  if (GameReady && (TimeLeft > (GameTime / 1000 - 5))) {
    //show the "Get Ready" signal when game is about to start
    lcd.setCursor(12, 0);
    lcd.print("Get");
    lcd.setCursor(11, 1);
    lcd.print("Ready");
  }

  if (GameReady && (TimeLeft <= (GameTime / 1000) - 5)) {
    //show the time while game is running
    lcd.print(TimeLeftMinutes);
    lcd.print(":");

    if (TimeLeftSeconds > 9) {
      lcd.print(TimeLeftSeconds);
    }
    if (TimeLeftSeconds <= 9) {
      lcd.setCursor (14, 0);
      lcd.print("0");
      lcd.print(TimeLeftSeconds);
    }
  }
  lcd.setCursor(1, 1);
  lcd.print(ScoreRed);
  lcd.setCursor(4, 1);
  lcd.print(":");
  lcd.setCursor(7, 1);
  lcd.print(ScoreYellow);
}

void DisplayOutputGameOver() {
  lcd.setCursor(0, 0);
  lcd.print("Rot : Gelb Press");
  lcd.setCursor(1, 1);
  lcd.print(ScoreRed);
  lcd.setCursor(4, 1);
  lcd.print(":");
  lcd.setCursor(7, 1);
  lcd.print(ScoreYellow);
  lcd.setCursor(11, 1);
  lcd.print("Start");
}


void UpdateTime() {
  TimeLeft = ((TimeGameEndMillis - millis()) / 1000);
}

void GameOver() {
  analogWrite(LEDGreen, 0);
  analogWrite(LEDRed, 150);
  GameReady = LOW;
  GameActive = LOW;

}

void Reset() {
  lcd.clear();
  ScoreYellow = 0;
  ScoreRed = 0;
  TimeGameEndMillis = (millis() + GameTime); //set the timestamp for the end of the game
  analogWrite(LEDGreen, 0);
  analogWrite(LEDRed, 150);
  GameReady = HIGH;
  GameActive = LOW; //needed for ingame-resets, without, there will be no kick-off-whistle after an ingame-reset
  TimeLeft = ((GameTime / 1000) + 5);

}

void KickOff() {
  tone(Buzzer, 50);
  delay(200);
  noTone(Buzzer);
  lcd.clear();
}


void finalwhistle() {
  tone(Buzzer, 50);
  delay(250);
  noTone(Buzzer);
  delay(100);
  tone(Buzzer, 50);
  delay(250);
  noTone(Buzzer);
  delay(100);
  tone(Buzzer, 50);
  delay(250);
  tone(Buzzer, 75);
  delay(200);
  noTone(Buzzer);
}


void CalculateDisplayTime() {
  // display the remaining time in minutes:seconds
  TimeLeftMinutes = (TimeLeft / 60);
  TimeLeftSeconds = (TimeLeft - TimeLeftMinutes * 60);
}
[/noae]


Fertig!

Wer nachbauen möchte:
 
Zuletzt bearbeitet:
Top Bottom