Roll-a-Ball-Game - Autorennen 1v1

Einleitung

Erstes Posting hier und dann gleich ein längeres...
Ich möchte euch kurz ein Projekt zeigen, welches ich in meiner Freizeit für meine beiden Söhne gebastelt habe. Vielleicht kennt der ein oder andere noch die entsprechenden Pferderennspiele auf Jahrmärkten. Habe sie seit geraumer Zeit kaum noch gesehen, früher gab es sie durchaus häufiger.

Für diejenigen die es nicht kennen: Man rollt eine Kugel und versucht dabei die farbigen Löcher zu treffen, damit die Pferde ins Ziel galoppieren.

Beschreibung
Bei meiner Version fahren Autos, statt das Pferde rennen. Die braunen Löcher geben einen Punkt, die gelben zwei, das rote drei und das blaue Loch vier Punkte. Für jeden Punkte bewegt sich das eigene Auto eine Strecke X nach vorne und wer zuerst im Ziel ankommt hat gewonnen. Zusätzlich werden die erzielten Punkte und die Gesamt-Punktzahl auch per LED-Matrix angezeigt. Diese zeigt auch ein paar kleinere simple Animationen und Smileys, etc. Dank Parola-Library sind die Panels mit je zwei Zonen definiert, die gesondert angesprochen werden können (links Gesamtpunkte, rechts aktueller Treffer).

Welches Loch getroffen wurde, wird über insgesamt 4 mittig platzierte optische Sensoren (KY-033) erkannt und die Autos bewegen sich mittels zweier Nema 17-Motoren mit TB6600-Motor-Treibern und zwei Zahnriemen. Der Unterbau ist eine Mischung aus zurechtgesägten Regenrinnen und Rohren aus Plastik.

Ob die Start- oder die Ziellinie erreicht wurde wird über insgesamt 4 Hall-Sensoren ermittelt. Dafür hängen unten an der Aufhängung der Autos ein paar Neodym-Magnete. Außerdem sind noch ein paar LEDs verbaut und zwei Rundumleuchten, die das Erreichen der Ziellinie optisch anzeigt. Die Autos fahren nach Spielende und einer kurzen Pause (für die obligatorische Siegerehrung und Huldigung des stolzen Gewinners) alleine wieder zurück auf die Startposition für das nächste Rennen.

Das ganze Spiel besitzt die Maße 1,20 m x 0.80 m, da eine kleinere Spielfläche irgendwie den Spaß verdorben hätte. Natürlich ist es in der Größe aber nicht mehr so ganz Wohnzimmerkompatibel – aber was interessierte das den angefixten Bastler vorher :smiley:

Die Bahnen sind von der Elektronik her komplett voneinander getrennt, d. h. es sind zwei Arduino Nanos verbaut - einer für die rechte, einer für die linke Bahn. Die Kommunikation zwischen beiden Seiten habe ich über die Wire-Library gelöst, wobei ein Arduino als Master und der andere als Slave agiert, d.h. die Kommunikation findet nur in eine Richtung statt. Damit der Slave auch Infos zurücksenden kann, gibt es noch einen Extra-Pin als Verknüpfung in die andere Richtung… mehr als 0 oder 1 kann der aber nicht übergeben. Es gibt nur einen Sketch, der Status (Master/Slave) des jeweiligen Arduinos wird beim Kompilieren über ein #define festgelegt.

Als Stromquelle dient ein Netzteil mit 60W, 12V und 5A. Am Pluspol habe ich einen Schalter eingebaut, so dass man das ganze bequem ein- und ausschalten kann. Außerdem gibt es noch einen Reset-Button, mit dem das Spiel jederzeit neu gestartet werden kann.

Bilder


Innenleben von der Seite


Innenleben von oben


Kabelwirrwarr auf engstem Raum


Unterbau

Hardware
Hier die Teileliste:

Arduino Nano (x2):
https://smile.amazon.de/gp/product/B078SBBST6

Shield (x2):

https://smile.amazon.de/gp/product/B08LH5TM6C

Tracking Sensoren (x8):

https://smile.amazon.de/gp/product/B07VHP6L71

Hall-Sensoren (x4):

https://smile.amazon.de/gp/product/B07ZZCWLY2

Nema 17-Motoren (x2):

https://smile.amazon.de/gp/product/B07MMQH1HP

Stepper Motor Driver (x2):

Montage-Platten für die Motoren:

https://smile.amazon.de/gp/product/B083DQLDC4

Schwingungsdämpfer und Kühlkörper für die Motoren:

LED-Module MAX7219 (x2 mit je 8x32):

https://smile.amazon.de/gp/product/B07HJDV3HN

Netzteil:

https://smile.amazon.de/gp/product/B001W3UYLY

Schraubklemmenleisten:

https://smile.amazon.de/gp/product/B093383MQG

Steckverbinder 12V:

https://smile.amazon.de/gp/product/B06XPVJT1Z

Step-Down Converter:

https://smile.amazon.de/gp/product/B086W79QQ8

Flansch-Kupplungen:

https://smile.amazon.de/gp/product/B0833PTFZM

Zahnriemen & Co (5m - ein Set langt daher für beide Seiten):

https://smile.amazon.de/gp/product/B07PQRJL2W

Straßen-Sticker (der Rest hängt jetzt beim Junior an der Wand):

https://smile.amazon.de/gp/product/B0093JH002

Edelstahlkugeln, je ein Set hohl und eins voll:

https://smile.amazon.de/gp/product/B07PSHKL6C

https://smile.amazon.de/gp/product/B006SFEN68

Dazu natürlich auch noch diverse Holzbalken, Bastelbretter, Schrauben, Kabel, Holzleim, Heißkleber, Metallleisten, Acrylfarben, etc.

Software
Die folgenden Bibliotheken wurden verwendet:
JC_Button.h
MD_MAX72xx.h
MD_Parola.h
SPI.h
Wire.h

Sketch
Geht bestimmt an der ein oder anderen Stelle effizienter und sicher gibt es noch einige mögliche Optimierungen – der Code ist halt über die Zeit immer mehr angewachsen… man kennt das ja. Immerhin hat er bisher noch kein eigenes Bewusstsein entwickelt und versucht Atomraketen zu starten… also alles gut :slight_smile:

//[[----------------------------------------------------------------]]
/* ################################################################ *\
                                                Roll-A-Ball-Game
                                              Created by Agamemnon
  \*  ############################################################### */
//[[----------------------------------------------------------------]]

#include <JC_Button.h>
#include <MD_MAX72xx.h>
#include <MD_Parola.h>
#include <SPI.h>
#include <Wire.h>
#include <AccelStepper.h>

//[[-----------------------     [ON/OFF]     -----------------------]]

#define MOTOR_ENABLE
#define DEBUG_ENABLE
#define MASTER;               //MASTER defined = Bahn oben / rechter Arduino; not defined = SLAVE = Bahn unten / linker Arduino

//[[-----------------------     [PINS]     -----------------------]]

#define COM1            2     //Kommunikation von Slave --> Master
#define BUTTON_RESET    3            //long press für Restart

//KY-033 Sensoren um die Kugeln/Bahnen zu checken
#define SENSOR_4      4
#define SENSOR_3      5
#define SENSOR_2      6
#define SENSOR_1      7

//Max 7219 LED-Module
#define DATA_PIN      8
#define CS_PIN        9
#define CLK_PIN       10

//KY-024 Hall Sensoren um Start-/Ziellinie zu checken
#define SENSOR_START  12
#define SENSOR_FINISH 11

#define LED_WIN         13            //Rundumleuchte für Anzeige Sieger

#ifdef MOTOR_ENABLE                            //TB6600 mit NEMA 17 Schrittmotor
#define MOTOR_ENABLE    A2
#define MOTOR_PULSE     A1
#define MOTOR_DIRECTION A0
#endif

#ifdef DEBUG_ENABLE
#define DEBUG(s, x) { Serial.print(F(s)); Serial.println(x); }
#define DEBUGS(x) Serial.println(F(x))
#else
#define DEBUG(s, x)
#define DEBUGS(x)
#endif

#define LONG_PRESS 1000

//Typ und Konfiguration der LED-Matrix
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4


//[[-----------------------     [KONSTANTEN]     -----------------------]]

//Richtung Motor
const String FORWARD = "0";
const String BACKWARD = "1";

//Mögliche Werte für gameStatus
const int STATUS_INIT = 0;
const int STATUS_ERROR_SENSOR_1 = 1;
const int STATUS_ERROR_SENSOR_2 = 2;
const int STATUS_ERROR_SENSOR_3 = 3;
const int STATUS_ERROR_SENSOR_4 = 4;
const int STATUS_PREPARING = 5;
const int STATUS_START = 6;
const int STATUS_PLAYING = 7;
const int STATUS_WIN = 8;
const int STATUS_LOOSE = 9;
const int STATUS_EPILOG_WIN = 10;
const int STATUS_EPILOG_LOOSE = 11;

//[[-----------------------     [VARIABLEN]     -----------------------]]

#ifdef MASTER
bool resetButtonLongPressed = false;    //Hilfsvariable für long press reset button
bool com1Status;                  //Nur im Master, wird true, wenn der Slave gewonnen hat
#endif
String motorDirection = FORWARD;    //Richtung des Motors
bool startReached;                //Auto hat Start erreicht bei... (LOW/HIGH bei Sensor)
bool startStatus;                 //Aktueller Status des Startsensors
bool endReached;                  //Auto hat Ziel erreicht bei... (LOW/HIGH bei Sensor)
bool endStatus;                   //Aktueller Status des Zielsensors

unsigned int errorCheck = 0;      //Hilfsvariable für Start-Check der Sensoren
unsigned int errorCount = 0;      //Hilfsvariable für Start-Check der Sensoren
unsigned int preparing = 0;       //Hilfsvarable für Rücksetzung auf Startposition

unsigned int motorSteps = 820;    //Steps die das Auto bei 1 Punkt zurücklegen soll
unsigned int stepper = 0;         //Hilfsvariable für stepper motor

unsigned int countHit = 0;        //Welcher Treffer/Sensor soll gezählt werden (1-4)?
unsigned long countHitTime;       //Sicherung für Trefferzählung

unsigned int stateSensor[4];   //Status Sensor 1: 0 = ready, 1 = hit, >=2 = timeout, da Kugel ggf. noch nicht wieder vorbei, >= 100 = Kugel sollte vorbei sein, Sensor wieder freigeben (--> 0)

unsigned int scoreTotal = 0;                          //Gesamtpunktzahl
unsigned long idleCount = 0;  //Zaehlt, wie lange sich die Score nicht geändert hat und löst Animationen aus
unsigned int gameStatus = STATUS_INIT;                       //Aktueller Spielstatus (siehe Konstanten weiter oben)

//[[-----------------------     [KOMPONENTEN]     -----------------------]]

//Buttons
#ifdef MASTER
Button resetButton(BUTTON_RESET);
#endif

//LED
MD_Parola ledDisplay = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

//Accelstepper
AccelStepper accelStepper = AccelStepper(1, MOTOR_PULSE, MOTOR_DIRECTION);

//Custom chars
uint8_t car1[] = { 8, 48, 56, 92, 250, 250, 89, 63, 121 };
uint8_t car2[] = { 8, 122, 58, 92, 252, 248, 88, 48, 48 };
uint8_t smileySad[] = { 8, 60, 118, 243, 191, 191, 243, 118, 60 };
uint8_t smileyHappy[] = { 8, 60, 126, 219, 191, 191, 219, 126, 60 };

//[[-----------------------     [FUNKTIONEN]     -----------------------]]
void readButtonsAndSensors() {

  //Sensor 1
  if (digitalRead(SENSOR_1) == LOW) {
    //Sensor meldet Kontakt
    if (stateSensor[0] == 0) {
        //… und wurde bisher noch nicht registriert
      stateSensor[0] = 1;
      if (countHit == 0) {
        countHit = 1;
        countHitTime = micros();
      }
    }
  } else if (stateSensor[0] >= 2 && stateSensor[0] < 100) {
    //Timeout, da Kugel ggf. noch nicht vorbei
    stateSensor[0]++;
  } else if (stateSensor[0] >= 100) {
    //Timeout beenden - Status wieder auf Startwert setzen
    stateSensor[0] = 0;
  }

  //Sensor 2
  if (digitalRead(SENSOR_2) == LOW) {
    if (stateSensor[1] == 0) {
      stateSensor[1] = 1;
      if (countHit == 0) {
        countHit = 2;
        countHitTime = micros();
      }
    }
  } else if (stateSensor[1] >= 2 && stateSensor[1] < 100) {
    stateSensor[1]++;
  } else if (stateSensor[1] >= 100) {
    stateSensor[1] = 0;
  }

  //Sensor 3
  if (digitalRead(SENSOR_3) == LOW) {
    if (stateSensor[2] == 0) {
      stateSensor[2] = 1;
      if (countHit == 0) {
        countHit = 3;
        countHitTime = micros();
      }
    }
  } else if (stateSensor[2] >= 2 && stateSensor[2] < 100) {
    stateSensor[2]++;
  } else if (stateSensor[2] >= 100) {
    stateSensor[2] = 0;
  }

  //Sensor 4
  if (digitalRead(SENSOR_4) == LOW) {
    if (stateSensor[3] == 0) {
      stateSensor[3] = 1;
      if (countHit == 0) {
        countHit = 4;
        countHitTime = micros();
      }
    }
  } else if (stateSensor[3] >= 2 && stateSensor[3] < 100) {
    stateSensor[3]++;
  } else if (stateSensor[3] >= 100) {
    stateSensor[3] = 0;
  }


#ifdef MASTER    //Beide Abfragen nur bei Master vorhanden
  //[[-----------------------     [Reset-Button]     -----------------------]]
  resetButton.read();
  //[[-----------------------     [COM1]     -----------------------]]
  com1Status = digitalRead(COM1);
#endif

  //[[-----------------------     [Checke Start und Ende]     -----------------------]]
  startStatus = digitalRead(SENSOR_START);
  endStatus = digitalRead(SENSOR_FINISH);
}

//[[-----------------------     [Motorsteuerung]     -----------------------]]
void moveCar() {
  accelStepper.runSpeed();
}

void stopCar()
{
#ifdef MOTOR_ENABLE
  DEBUGS("stop");
  accelStepper.stop();
#endif
}

void setDirection(String direction) {
#ifdef MOTOR_ENABLE
  DEBUG("Dir:", direction);

  motorDirection = direction;
  if (motorDirection == FORWARD) {
    accelStepper.setSpeed(500);
  } else {
    accelStepper.setSpeed(-800);
  }
#endif
}

//[[-----------------------     [Neues Spiel starten]     -----------------------]]
void newGame(bool sendToSlave) {
#ifdef MASTER
  //Habe nie herausgefunden warum, aber der Startwert nach dem Einschalten der Hall-Sensoren ist bei beiden Bahnen genau umgedreht und muss daher hier je nach Bahn anders abgefragt werden. 
  startReached = 1;
  endReached = 0;
#else
  startReached = 0;
  endReached = 1;
#endif

  DEBUG("Status Start: ", digitalRead(SENSOR_START));
  DEBUG("Status Ende: ", digitalRead(SENSOR_FINISH));

  //reset all values
  gameStatus = STATUS_PREPARING;
  preparing = 1;
  motorDirection = FORWARD;
  scoreTotal = 0;
  stateSensor[0] = 0;
  stateSensor[1] = 0;
  stateSensor[2] = 0;
  stateSensor[3] = 0;
  countHit = 0;

  //Senden an Slave, dass zurückgesetzt werden soll
#ifdef MASTER
  if (sendToSlave) {
    sendData(1);
  }
#endif
}

//[[-----------------------     [Hilfsfunktion für die LED-Anzeige]     -----------------------]]

void setZoneLed(int8_t z, char * pText, uint16_t speed, uint16_t pause, textEffect_t effectIn, textEffect_t effectOut, uint8_t charSpacing) {
  ledDisplay.setPause(z, pause);
  ledDisplay.setSpeed(z, speed);
  ledDisplay.setCharSpacing(z, charSpacing);
  ledDisplay.displayZoneText(z, pText, ledDisplay.getTextAlignment(), ledDisplay.getSpeed(z), ledDisplay.getPause(z), effectIn , effectOut );
  ledDisplay.displayReset(z);
}

//[[-----------------------     [Kommunikation Master → Slave]     -----------------------]]

#ifdef MASTER
void sendData(byte x) {
  Wire.beginTransmission(4);
  Wire.write(x);
  Wire.endTransmission();
}
#else
void receiveEvent(int howMany)
{
  if (Wire.available() > 0) {
    int x = Wire.read();    // receive byte as an integer
    switch (x) {
      case 1:
        //Reset wurde gedrückt
        newGame(false);
        break;
      case 2:
            //Der Master hat gewonnen
        gameStatus = STATUS_LOOSE;
        break;
      default:
        //
        break;
    }
  }
}
#endif

//[[-----------------------     [SETUP]     -----------------------]]

void setup() {
#ifdef MASTER
  Wire.begin();                 // join i2c bus (address optional for master)
#else
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
#endif

  Serial.begin(9600);
  DEBUGS("Setup");


  pinMode(SENSOR_1, INPUT);
  pinMode(SENSOR_2, INPUT);
  pinMode(SENSOR_3, INPUT);
  pinMode(SENSOR_4, INPUT);

  /*
    #ifdef MOTOR_ENABLE
    pinMode(MOTOR_ENABLE, OUTPUT);
    pinMode(MOTOR_PULSE, OUTPUT);
    pinMode(MOTOR_DIRECTION, OUTPUT);
    digitalWrite(MOTOR_ENABLE, LOW);
    #endif
  */
  accelStepper.setMaxSpeed(1000);
  accelStepper.setAcceleration(200);

  pinMode(BUTTON_RESET, INPUT);
  pinMode(SENSOR_START, INPUT);
  pinMode(SENSOR_FINISH, INPUT);
#ifdef MASTER
  pinMode(COM1, INPUT);
#else
  pinMode(COM1, OUTPUT);
#endif

  //LED
  pinMode(LED_WIN, OUTPUT);
  ledDisplay.begin(2);
  ledDisplay.setZone(0, 0, 1);
  ledDisplay.setZone(1, 2, 3);
  ledDisplay.setIntensity(0);
  ledDisplay.setTextAlignment(PA_CENTER);
  ledDisplay.setPause(3000);
  ledDisplay.setSpeed(80);
  ledDisplay.addChar('$', car1);
  ledDisplay.addChar('&', car2);
  ledDisplay.addChar('%', smileySad);
  ledDisplay.addChar('@', smileyHappy);
  ledDisplay.displayClear();

  delay(1);
  newGame(false);
}

//[[-----------------------     [LOOP]     -----------------------]]
void loop() {
  switch (gameStatus) {

    case STATUS_PREPARING:
      readButtonsAndSensors();

#ifdef MOTOR_ENABLE
      //Startvorgang wird durchgeführt --> Zurückfahren
      if (preparing == 1) {
        setDirection(BACKWARD);
        DEBUGS("Auto > Start");
        digitalWrite(LED_WIN, HIGH);
        setZoneLed(0, ">>>", 100, 2000, PA_PRINT, PA_SCROLL_RIGHT, 1);
        setZoneLed(1, "$&", 100, 2000, PA_PRINT, PA_SCROLL_RIGHT, 0);
        preparing++;
      } else if (startStatus != startReached) {
        moveCar();
        preparing++;
      } else {
        //Startpunkt erreicht, stoppen
        preparing = 0;
        motorDirection = FORWARD;
        stopCar();
        DEBUGS("Start done");
        gameStatus = STATUS_START;
        digitalWrite(LED_WIN, LOW);
      }
#else
      preparing = 0;
      motorDirection = FORWARD;
      DEBUGS("skip start");
      gameStatus = STATUS_START;
#endif
      break;

    case STATUS_PLAYING:
      readButtonsAndSensors();

      //Prüfen, ob Punkte erzielt wurden
      int score = 0;
      if (stateSensor[0] == 1) {
        DEBUGS("Sensor 1");
        stateSensor[0] = 2;
        score = countHit;
        countHit = 0;
      } else if (stateSensor[1] == 1) {
        DEBUGS("Sensor 2");
        stateSensor[1] = 2;
      } else if (stateSensor[2] == 1) {
        DEBUGS("Sensor 3");
        stateSensor[2] = 2;
      } else if (stateSensor[3] == 1) {
        DEBUGS("Sensor 4");
        stateSensor[3] = 2;
      } else if (countHit > 0 && micros() - countHitTime > 1000000) {
        //Timeout - Sensor 1 hat nicht ausgelöst? Ergebnis trotzdem zählen!
        DEBUG("Timeout: ", countHit);
        stateSensor[0] = 2;
        score = countHit;
        countHit = 0;
      }

      if (score > 0) {
        switch (score) {
          case 1:
            setZoneLed(0, "+1", 80, 200, PA_SCROLL_LEFT, PA_SCROLL_LEFT, 1);

            break;
          case 2:
            setZoneLed(0, "+2", 80, 200, PA_SCROLL_LEFT, PA_SCROLL_LEFT, 1);
            break;
          case 3:
            setZoneLed(0, "+3", 80, 200, PA_SCROLL_LEFT, PA_SCROLL_LEFT, 1);
            break;
          case 4:
            setZoneLed(0, "+4", 80, 200, PA_SCROLL_LEFT, PA_SCROLL_LEFT, 1);
            break;
          default:
            break;
        }
        //Alte Punktzahl nach 1s animiert ausblenden
        String s = String(scoreTotal);
        char c[20];
        s.toCharArray(c, sizeof(c));
        setZoneLed(1, c, 80, 1000, PA_PRINT, PA_SCROLL_DOWN, 1);

        //Gesamtpunktzahl erhöhen...
        scoreTotal += score;

        //... und Autostrecke die gefahren werden soll erhöhen
        stepper += (motorSteps * score);
        setDirection(FORWARD);
      }

      if (endStatus != endReached && stepper > 0) {
        //Wenn Zielpunkt noch nicht erreicht: Autos bewegen
        moveCar();
        stepper--;
      } else if (endStatus == endReached) {
        //Zielline erreicht - gewonnen!
        stopCar();
        gameStatus = STATUS_WIN;
        DEBUGS("Ziel!");
        digitalWrite(LED_WIN, HIGH);
#ifdef MASTER
           //Slave über Sieg informieren
        sendData(2);
      } else if (com1Status) {
        //Slave hat gewonnen - und wir verloren
        gameStatus = STATUS_LOOSE;
#else
            //Master über Sieg informieren
        digitalWrite(COM1, HIGH);
#endif
      }
      break;
    case STATUS_ERROR_SENSOR_1:
    case STATUS_ERROR_SENSOR_2:
    case STATUS_ERROR_SENSOR_3:
    case STATUS_ERROR_SENSOR_4:
             //Sensorfehler bei Start → Abbruch
      return;
      break;

    case STATUS_INIT:
      //Erster Start - Check der Sensoren
      errorCheck++;
      if (stateSensor[0] == 1 || stateSensor[1] == 1 || stateSensor[2] == 1 || stateSensor[3] == 1) {
        //Mindestens einer der Sensoren reagiert nicht (oder hat Kontakt mit Kugel o.ä.)
        errorCount++;
        DEBUGS("Sensor error");
      }

      if (errorCount >= 10) {
        //Wenn bei jedem Durchlauf ein Fehler aufgetreten ist --> Abbruch mit Meldung
        if (stateSensor[0] == 1) {
          gameStatus = STATUS_ERROR_SENSOR_1;
          DEBUGS("Fehler Sensor 1");
        }
        if (stateSensor[1] == 1) {
          gameStatus = STATUS_ERROR_SENSOR_2;
          DEBUGS("Fehler Sensor 2");
        }
        if (stateSensor[2] == 1) {
          gameStatus = STATUS_ERROR_SENSOR_3;
          DEBUGS("Fehler Sensor 3");
        }
        if (stateSensor[3] == 1) {
          gameStatus = STATUS_ERROR_SENSOR_4;
          DEBUGS("Fehler Sensor 4");
        }
        stateSensor[0] = 0;
        stateSensor[1] = 0;
        stateSensor[2] = 0;
        stateSensor[3] = 0;
      } else if (errorCheck >= 10) {
        //Nach 10 Durchläufen ohne Fehler Check beenden
        gameStatus = STATUS_PREPARING;
        stateSensor[0] = 0;
        stateSensor[1] = 0;
        stateSensor[2] = 0;
        stateSensor[3] = 0;
        DEBUGS("ok... go");
      }
      return;
      break;

    case STATUS_WIN:
      //Spiel vorbei, Sensoren 
      readButtonsAndSensors();
      break;

    default:
      break;
  }
 

  if (ledDisplay.displayAnimate()) {
    switch (gameStatus) {
      case STATUS_PLAYING:
        if (ledDisplay.getZoneStatus(1)) {
          String s = String(scoreTotal);
          char c[20];
          s.toCharArray(c, sizeof(c));
          setZoneLed(1, c, 40, 999999, PA_SCROLL_DOWN, PA_PRINT, 1);
        }
        break;

      case STATUS_PREPARING:
        if (ledDisplay.getZoneStatus(0)) {
          setZoneLed(0, ">>>", 100, 2000, PA_PRINT, PA_SCROLL_RIGHT, 1);
        }
        if (ledDisplay.getZoneStatus(1)) {
          setZoneLed(1, "$&", 100, 2000, PA_PRINT, PA_SCROLL_RIGHT, 0);
        }
        break;

      case STATUS_WIN:
        setZoneLed(0, "@", 80, 5000, PA_OPENING, PA_CLOSING, 1);
        gameStatus = STATUS_EPILOG_WIN;
        break;

      case STATUS_LOOSE:
        setZoneLed(0, "%", 80, 5000, PA_OPENING, PA_CLOSING, 0);
        gameStatus = STATUS_EPILOG_LOOSE;
        break;
      case STATUS_EPILOG_WIN:
        if (ledDisplay.getZoneStatus(0)) {
          digitalWrite(LED_WIN, LOW);
#ifndef MASTER
          digitalWrite(COM1, LOW);
#endif
          newGame(false);
        }
        break;
      case STATUS_EPILOG_LOOSE:
        if (ledDisplay.getZoneStatus(0)) {
          newGame(false);
        }
        break;
      case STATUS_ERROR_SENSOR_1:
      case STATUS_ERROR_SENSOR_2:
      case STATUS_ERROR_SENSOR_3:
      case STATUS_ERROR_SENSOR_4:
        setZoneLed(0, "%", 80, 5000, PA_FADE, PA_FADE, 1);
        setZoneLed(1, "E" + gameStatus, 80, 5000, PA_FADE, PA_FADE, 1);
        break;
      case STATUS_START:
        //if (ledDisplay.getZoneStatus(0) && ledDisplay.getZoneStatus(1)) {
          setZoneLed(1, "$&", 80, 3000, PA_SCROLL_UP, PA_SCROLL_LEFT, 0);
          setZoneLed(0, "GO!", 80, 3000, PA_SCROLL_DOWN, PA_SCROLL_RIGHT, 1);
          gameStatus = STATUS_PLAYING;
          break;
        //}
    }
  }

#ifdef MASTER
  if (resetButtonLongPressed && resetButton.wasReleased()) {
    resetButtonLongPressed = false;
    newGame(true);
  } else if (resetButton.pressedFor(LONG_PRESS)) {
    resetButtonLongPressed = true;
    setZoneLed(0, "RESET!", 80, 10, PA_SCROLL_LEFT , PA_SCROLL_LEFT , 1);
  }
#endif
}

Nicht umgesetzte Ziele und ToDos

Sound
Eigentlich war MP3-Sound mit einem DFPlayer Mini geplant, aber mangels freier Pins, vollem Speicher und mangelnder Motivation dafür noch einen dritten Arduino zu implementieren, ist der Plan erstmal auf Eis gelegt.

Ampel
Für den Start war eine Ready-Steady-Go-Ampel angedacht:
https://smile.amazon.de/gp/product/B086V33MST
Mangels Zeit ist das aber derzeit noch hinten angestellt.

Aufstellung und Transport
Derzeit liegt das Spiel einfach auf dem Boden oder dem Tisch. In der finalen Version möchte ich aber noch die einklappbaren Füße eines Klapptischs unten montieren. Außerdem soll ein Tragegriff an die Seite.

Seitenverkleidung und generelle Optik
Die Seiten sind derzeit noch offen - das soll so nicht bleiben. Auch möchte ich gerne die Macken, Löcher und Schrauben noch kaschieren.

Kabelwirrwarr bereinigen
Erklärt sich wohl von selbst beim Anblick der Bilder :slight_smile:

Abschluss
Hat Spaß gemacht, war aber teilweise anstrengender als gedacht... insbesondere bei meinen rudimentären handwerklichen Fähigkeiten. Hinterher ist man immer schlauer und ich denke, mit dem heutigen Wissen hätte ich wohl vieles anders gemacht. Ich hoffe, dem ein oder anderen hat es Spaß gemacht die Beschreibung zu lesen und vielleicht bringt es ja auch andere Bastler noch auf nette Ideen. Meine Kinder und ihre Freunde haben jedenfalls viel Spaß mit dem Spiel.

Liebe Grüße
Wizball/Thomas

Edit: Bilder und Links nachgepflegt, nach Erreichung der entsprechenden Forenstufe

3 Likes

JA!
Nicht schlecht - das hat was.

Vielleicht möchtest Du das hier eintragen: Zeigt her eure geilen Projekte!
Dann bleibt der auch weiter offen :wink:

Na dann. Was man nicht alles für die Kinder macht :wink:

1 Like

Danke! Mir hat es viel Spaß gemacht :+1:t2: :slightly_smiling_face:

1 Like

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