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
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
//[[----------------------------------------------------------------]]
/* ################################################################ *\
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
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