//mez 7.6.21
#include "GameLib.h"
#include <Wire.h>
#include <DFPlayerMini_Fast.h> //https://github.com/PowerBroker2/DFPlayerMini_Fast
const byte karten[][7] PROGMEM = { // RFID-IDs der Tags
{0x4,0x43,0x42,0x32,0x1E,0x70,0x80},{0x4,0x63,0x42,0x32,0x1E,0x70,0x80},{0x4,0x4B,0x42,0x32,0x1E,0x70,0x80},{0x4,0x4F,0x43,0x32,0x1E,0x70,0x80}, // 1..4
{0x4,0x47,0x42,0x32,0x1E,0x70,0x80},{0x4,0x4F,0x42,0x32,0x1E,0x70,0x80},{0x4,0x53,0x42,0x32,0x1E,0x70,0x80},{0x4,0x57,0x42,0x32,0x1E,0x70,0x80}, // 5..8
{0x4,0x46,0x43,0x32,0x1E,0x70,0x80},{0x4,0x42,0x43,0x32,0x1E,0x70,0x80},{0x4,0x4A,0x43,0x32,0x1E,0x70,0x80},{0x4,0x5B,0x42,0x32,0x1E,0x70,0x80}, // 9..12
{0x4,0x53,0x43,0x32,0x1E,0x70,0x80},{0x4,0x5F,0x42,0x32,0x1E,0x70,0x80},{0x4,0x3E,0x43,0x32,0x1E,0x70,0x80},{0x4,0x55,0xCB,0xBA,0x6F,0x71,0x80}, // 13..16
{0x4,0x52,0xCA,0xBA,0x6F,0x71,0x80},{0x4,0x4E,0xCA,0xBA,0x6F,0x71,0x80},{0x4,0x4B,0xCB,0xBA,0x6F,0x71,0x80},{0x4,0x47,0xCB,0xBA,0x6F,0x71,0x80}, // 17..20
{0xE7,0x81,0xF0,0x19},{0x4,0x3E,0x42,0x32,0x1E,0x70,0x80},{0x4,0x7F,0x43,0x32,0x1E,0x70,0x80},{0x4,0x83,0x43,0x32,0x1E,0x70,0x80}, // 21..24
{0x4,0xD2,0xA3,0x22,0x1E,0x70,0x80},{0x4,0x77,0x43,0x32,0x1E,0x70,0x80},{0x4,0x7B,0x43,0x32,0x1E,0x70,0x80},{0x4,0x67,0x42,0x32,0x1E,0x70,0x80}, // 25..28
{0x4,0xD6,0xA3,0x22,0x1E,0x70,0x80},{0x4,0x67,0x43,0x32,0x1E,0x70,0x80},{0x4,0x33,0xCB,0xBA,0x6F,0x71,0x80},{0x9A,0xDC,0x5C,0xB4}, // 29..32
{0x8C,0x6D,0x4F,0x2E},{0xCA,0xF6,0x68,0xAD},{0x6C,0xF5,0x49,0x31},{0x4,0x5F,0x43,0x32,0x1E,0x70,0x80}, // 33..36
{0x6A,0x19,0x96,0xAE},{0x4A,0xEC,0x82,0xAD},{0xC1,0x91,0x72,0x21},{0x4,0x57,0x43,0x32,0x1E,0x70,0x80}, // 37..40
{0x9C,0xD6,0x48,0x31},{0xC1,0x9A,0x25,0x21},{0xD1,0xC2,0xC5,0x21},{0x4,0x6B,0x43,0x32,0x1E,0x70,0x80}, // 41..44
{0x3A,0xE2,0xA0,0xAD},{0x4,0xE0,0x26,0xD2,0xBA,0x5D,0x80},{0xD1,0xF9,0xB8,0x21},{0x4,0x37,0xCB,0xBA,0x6F,0x71,0x80}, // 45..48
{0x4,0x43,0xCB,0xBA,0x6F,0x71,0x80},{0x4,0x6F,0x43,0x32,0x1E,0x70,0x80},{0x7A,0xF3,0x5D,0xAD},{0xCA,0x2D,0xA8,0xAD}, // 49..52
{0xC3,0x6F,0xAF,0x2},{0xEA,0x67,0x69,0xAD},{0x8A,0xA0,0x7D,0xAD},{0x4,0x3F,0xCB,0xBA,0x6F,0x71,0x80}, // 53..56
{0x4,0x63,0x43,0x32,0x1E,0x70,0x80},{0x4,0x2B,0xCB,0xBA,0x6F,0x71,0x80},{0x4,0x27,0xCB,0xBA,0x6F,0x71,0x80},{0x4,0x23,0xCB,0xBA,0x6F,0x71,0x80}, // 57..60
{0x4,0x20,0xCA,0xBA,0x6F,0x71,0x80},{0x4,0xF0,0xE1,0x8A,0x6F,0x71,0x80},{0x4,0x61,0xCB,0xBA,0x6F,0x71,0x80},{0x4,0x5D,0xCB,0xBA,0x6F,0x71,0x80}, // 61..64
{0x4,0x59,0xCB,0xBA,0x6F,0x71,0x80},{0x4,0xB0,0x6E,0x1,0xD1,0x5,0x3},{0x4,0xB0,0x52,0x1,0x79,0x5,0x3},{0x4,0xB0,0x44,0x1,0x5C,0x5,0x3}, // 65..68
{0x4,0x30,0xCA,0x1,0x22,0x44,0x3},{0x4,0x40,0x62,0x1,0x6,0x45,0x3},{0x4,0xE0,0xA6,0x1,0x6,0x44,0x3},{0x4,0xB0,0x70,0x1,0xC5,0x5,0x3}, // 69..72
{0x4,0xB0,0x30,0x1,0x90,0x5,0x3},{0x4,0x30,0xDD,0x1,0x30,0x45,0x3},{0x4,0xA0,0xF4,0x1,0x10,0x5,0x3},{0x4,0xB0,0x72,0x1,0x58,0x5,0x3} // 73..76
};
const byte mp3stueck[] PROGMEM = {31,32,33,34,35,36,37,38,39,40,21,22,23,24,25,26, // 1..16 Zuordnung zu MP3-Stuecken
27,28,29,30,61,62,62,62,63,63,63,64,64,65,65,66, // 17..32
66,66,66,67,68,68,68,69,70,71,72,73,74,75,75,76, // 33..48
77,78,79,79,79,79,79,80,81,82,83,84,85,86,87,88, // 49..64
89,90,91,92,93,94,95,96,97,98,99,100
};
#define vers "1.2"
#define ANZAHL_TERMINALS 4 // wie viele Terminals angeschlossen sind
#define TASCHENLAMPEN_HAENGEN 1 // Zustand des Sensors in dem die Taschenlampen haengen
#define MAX_MP3_STUECK 100 // Hoechste Nummer auf SD
#define RFID_DELAY 20 // Zeit in ms zwischen Anfrage und Empfang
#define MP3_BUSY 17
#define IR1 40
#define IR2 38
#define IR3 36
#define GABEL 52 //neg Logik
#define T_KNOPF 50 //neg Logik
#define W_SCHEIBE 48
#define TASCHL_AUSGABE 46 //neg Logik
#define TUER_TASTER 44 //neg Logik
#define EXIT_KNOPF 42 //neg Logik
#define HAUPT_LICHT 2 // PWM HauptLicht
#define SPOT2 3 // PWM leuchtet auf Gegenstaende
#define SPOT3 4 // PWM leuchtet auf Karten
#define INNEN_LICHT 5 // PWM Hauptlicht innen
#define T_KLINGEL 53 // Relais1 neg Log
#define TUER_MAGNET_EINGANG 51 // Relais2 neg Log
#define VERSTAERKER 49 // Relais3 neg Log
#define MAUSKLICK 47 // Relais4
#define TUER_MAGNET_AUSGANG 45
#define TASCHENLAMPENFACH_R 43 // Realis Taschenlampenfach Blinker
#define LED_STREIFEN 37 // Lichteffekt am Ausgang
#define ENDE_LICHT 39 // Licht vor Ende
DFPlayerMini_Fast myMP3;
enum zustand_t: byte {Z_nix,Z_kartentest,Z_kartentestDo,Z_reset,Z_checkTuerGabel,Z_checkTerminals,Z_checkTaschenlampen,Z_warteAufMitspieler,Z_warteAufMitspielerDo,Z_zwischenzeitTelefon,
Z_telefonKlingelt,Z_begruessung,Z_wartenWaehlscheibe,Z_wartenAufWahlimpulse,Z_wahlImpuls1,Z_wahlImpuls0,Z_nochNichtsGewaehlt,Z_Spielbeginn,
Z_SpielerLos,Z_wartenGegenstand,Z_Spielteil2,Z_KartenWahl,Z_wartenKarte,Z_SesamOffen,Z_TaschenlampenNehmen,Z_SpielbeginnInnenraum,Z_nachMausklick,Z_vorSpielende,Z_AusgangAuf,Z_Spielende,Z_EndeEnde,Z_test,Z_testDo};
zustand_t zustand = Z_reset;
Zeit zeit1(A0,1,30); // AD-Pin,min,max in Sekunden warten auf Spieler
Zeit zeit2(A1,5,30); // Dauer Telefonläuten
Zeit zeit3(A2,5,30); // Dauer auf Waehlen warten
Zeit zeit4(A3,5,30); // Zeit bis Gegenstand aufgelegt werden soll
Zeit zeit5(A4,30,600); // Zeit von Taschenlampenentnahme (Sensor1) bis Mausklick
Zeit zeit6(A5,5,180); // Zeit von Mausklick bis Innenlicht etwas hochdimmen
Zeit zeit7(A6,5,60); // Zeit Leuchtdauer LED-Streifen am Ausgang
Taster knopf(T_KNOPF,0);
Blinker taschenlampenfach(TASCHENLAMPENFACH_R,1); // Neg Log
void setup() {
pinMode(IR1,INPUT);
pinMode(IR2,INPUT);
pinMode(IR3,INPUT);
pinMode(GABEL,INPUT);
pinMode(W_SCHEIBE,INPUT);
pinMode(TASCHL_AUSGABE,INPUT);
pinMode(TUER_TASTER,INPUT);
pinMode(EXIT_KNOPF,INPUT);
pinMode(HAUPT_LICHT,OUTPUT);
pinMode(SPOT2,OUTPUT);
pinMode(SPOT3,OUTPUT);
pinMode(INNEN_LICHT,OUTPUT);
pinMode(T_KLINGEL,OUTPUT);
digitalWrite(T_KLINGEL,HIGH);
pinMode(TUER_MAGNET_EINGANG,OUTPUT);
digitalWrite(TUER_MAGNET_EINGANG,HIGH);
pinMode(VERSTAERKER,OUTPUT);
digitalWrite(VERSTAERKER,HIGH);
pinMode(MAUSKLICK,OUTPUT);
digitalWrite(MAUSKLICK,HIGH);
pinMode(TUER_MAGNET_AUSGANG,OUTPUT);
digitalWrite(TUER_MAGNET_AUSGANG,HIGH);
pinMode(LED_STREIFEN,OUTPUT);
digitalWrite(LED_STREIFEN,HIGH);
pinMode(ENDE_LICHT,OUTPUT);
digitalWrite(ENDE_LICHT,HIGH);
Serial.begin(115200);
Serial1.begin(9600);
delay(15000); //Versuch gegen IR-Auslösung am Beginn
Wire.begin();
if(!myMP3.begin(Serial1,false)){ // true für debug
Serial.println(F("Unable to begin:"));
}
pinMode(MP3_BUSY,INPUT);
delay(1200); // Terminals Zeit zum Starten geben
Serial.print(F("Discovery-Game Master V "));
Serial.print(vers);
Serial.println(F(" (c) Oliver Mezger"));
Serial.print(F("Anzahl gespeicherter Karten: "));
Serial.println(sizeof(karten)/sizeof(karten[0])); // Anzahl Karten anzeigen
Serial.println(F("Setting volume to 20"));
myMP3.volume(20); // 0..30
byte test = 1;
for(byte i=0;i<ANZAHL_TERMINALS;i++){
if(pruefeHallo(i)){
setzeRGB(i,0,0b000,0);
setzeRGB(i,1,0b000,0);
Serial.println(F(" Station OK"));
delay(300); // nur fuer Effekt
}
else{
Serial.println(F(" Falsche Antwort"));
test = 0;
}
}
while(!test){ // Problem mit einem Terminal
sprecheBisEnde(19); // Ausgabe Meldung Terminal antwortet nicht
zustand=Z_nix;
}
zustand= Z_reset;
}
#define maxTerminals 4 // 4 Terminals sind angeschlossen
void loop() {
static unsigned long lZeitmarke; // fuer Timings
static unsigned char lByteVar; // Universal
static unsigned char lByteVar2; // Universal
static unsigned char hauptLicht=0; // HauptLicht
static unsigned char innenLicht=0; // HauptLicht
static unsigned char lWiederholungen; // Zaehler fuer Wiederholungen
static unsigned char lAnzahlMitspieler;
static unsigned char lAktSpieler;
serialKommandos(); // über seriellen Monitor steuern
switch (zustand){
case Z_nix: break; // Zum Testen
case Z_kartentest:
Serial.println(F("Kartentest, RFID-Tag auf gruenes Terminal auflegen"));
alleAusRGB();
setzeRGB(0,0,Gn,0);
zustand=Z_kartentestDo;
break;
case Z_kartentestDo:
lByteVar = leseRfid(0,0); // Terminal 1 oben lesen
if(lByteVar>0 && lByteVar<255){ // wenn es eine bekannte Karte gibt
Serial.print(F("RFID-Tag No: "));
Serial.print(lByteVar);
lByteVar2=pgm_read_byte(&mp3stueck[lByteVar-1]); // suche das passende Stueck
if(lByteVar2){ // falls es eins gibt
Serial.print(F(" Spiele Stueck: "));
Serial.println(lByteVar2);
myMP3.play(lByteVar2); // spiele es
}
else{
Serial.print(F("Noch kein Stueck hinterlegt: "));
Serial.println(lByteVar);
}
}
break;
case Z_reset: // Akt=10 Reset mit Aufräumen vor jedem Neustart:
Serial.println(F("Reset"));
hauptLicht=255;
analogWrite(HAUPT_LICHT,hauptLicht); // HaupthauptLicht an
analogWrite(SPOT2,0);
analogWrite(SPOT3,0);
digitalWrite(TASCHENLAMPENFACH_R,HIGH);
innenLicht=0;
analogWrite(INNEN_LICHT,innenLicht);
digitalWrite(VERSTAERKER,HIGH); // Verstaerker nach aussen
digitalWrite(ENDE_LICHT,HIGH); // Endelicht aus
alleAusRGB();
zustand=Z_checkTuerGabel;
break;
case Z_checkTuerGabel: // beide Türen und Telefongaben checken
if(digitalRead(TUER_TASTER)){
spreche(58); // Sind beide Türen Zu?
break;
}
if(!digitalRead(GABEL)){ //- falls Hörer nicht aufgelegt: MP3-Datei 56: ("Bitte Telefonhörer auflegen")
spreche(56);
break;
}
zustand=Z_checkTerminals;
break;
case Z_checkTerminals: //- falls per RFID etwas erkennt wird MP3-Datei 57: ("Bitte Gegenstände oder Karten aufräumen" und rot blinken)
lByteVar=0;
for(byte i=0;i<maxTerminals;i++){ // für alle Stationen
resetRfid(i);
delay(RFID_DELAY);
for(byte k=0;k<=1;k++){ // für oben und unten
if(leseRfid(i,k)){ // RFID-Tag erkannt
setzeRGB(i,k,Rt,20); // 200 ms Blinken
lByteVar=1; // merken für Spruch ausgeben
}
else setzeRGB(i,k,Off,0); // kein Tag kein hauptLicht
}
}
if(lByteVar){
sprecheBisEnde(57); // Bitte Gegenstände oder Karten aufräumen
break;
}
zustand=Z_checkTaschenlampen;
taschenlampenfach.setBlinker(500);
break;
case Z_checkTaschenlampen: // liegen die Taschenlampen bereit?
taschenlampenfach.service();
if(digitalRead(TASCHL_AUSGABE)!=TASCHENLAMPEN_HAENGEN){
spreche(60); // Taschenlampenfach am Eingang bestueckt?
}
else{
zustand=Z_warteAufMitspieler;
hauptLicht=255; //anstatt seither 100
analogWrite(HAUPT_LICHT,hauptLicht);
taschenlampenfach.setBlinker(0);
}
break;
case Z_warteAufMitspieler:
for(byte i=0;i<maxTerminals;i++){ // alle RFID-Terminals leuchten oben und unten blau
for(byte k=0;k<=1;k++){
setzeRGB(i,k,Bl,0);
}
}
lWiederholungen = 0;
zustand=Z_warteAufMitspielerDo;
break;
case Z_warteAufMitspielerDo: // Akt20 https://funduino.de/nr-8-bewegungsmelder
if (digitalRead(IR1)||digitalRead(IR2)||digitalRead(IR3)){ //- wenn Bewegungsmelder IR1 oder IR2 oder IR3 anspricht => Akt=30
zustand=Z_zwischenzeitTelefon;
Serial.println(F("Zwischenzeit Telefon"));
alleAusRGB();
Serial.print(F("1."));
zeit1.start(); //- Zeit1 bis Telefonlaeuten beginnt (eingestellt an Poti1, ca. 20 Sekunden)
lZeitmarke=millis(); // Zeit fuer LaufhauptLicht
while(hauptLicht<255){ // vormals 100, Hauptlicht soll maximal bleiben
analogWrite(HAUPT_LICHT,hauptLicht++);
delay(20);
} //
lByteVar=0;
}
if(!digitalRead(GABEL)){ //- wenn Hörer (obwohl nicht läutend) abgenommen wird
zustand=Z_begruessung; // für den Fall, dass kurz nach Läuten-Ende abgenommen wurde
Serial.println(F("Bergruessung"));
}
break;
case Z_zwischenzeitTelefon: // Akt=30: Zwischenzeit bis Telefon läutet
if(millis()-lZeitmarke >300){ // LaufhauptLicht, Stationen blinken
switch(lByteVar){
case 0: setzeRGB(3,0,Off,0); setzeRGB(0,0,Li,3); lByteVar++;break;
case 1: setzeRGB(0,0,Off,0); setzeRGB(1,0,Li,3); lByteVar++;break;
case 2: setzeRGB(1,0,Off,0); setzeRGB(2,0,Li,3); lByteVar++;break;
case 3: setzeRGB(2,0,Off,0); setzeRGB(3,0,Li,3); lByteVar=0;break;
}
lZeitmarke=millis();
}
if(knopf.aktiv(2)){//- wenn ein kurzer Druck auf Knopf1 erfolgt: => Akt=10 (Reset mit Totzeit für Museumsbedienstete, die aus Versehen vom IR erfasst wurden)
zustand = Z_reset;
Serial.println(F("Reset fuer Bedienstete ausgelaoest"));
break;
}
if(!digitalRead(GABEL)){//- wenn Hörer (obwohl nicht läutend) abgenommen wird => Akt=50 (falls Telefon kurz nach Läuten-Ende abgenommen wurde und IR schon weitergeschaltet hat)
zustand=Z_begruessung;
break;
}
if (zeit1.vorbei()){ //- nach Zeit1 => Akt=40
alleAusRGB();
lZeitmarke=millis(); // Zeit zwischen Laeuten
zustand=Z_telefonKlingelt;
Serial.print(F("2."));
zeit2.start(); // Dauer des Telefonlaeutens
lByteVar=0;
}
break;
case Z_telefonKlingelt: // Telefon soll abgenommen werden
if(millis()-lZeitmarke >400){
switch(lByteVar){
case 0: digitalWrite(T_KLINGEL,LOW); lByteVar++; break; // Klingel an
case 1: digitalWrite(T_KLINGEL,HIGH); lByteVar++; break; // Klingel aus
case 2: digitalWrite(T_KLINGEL,LOW); lByteVar++; break; // Klingel an
case 3: digitalWrite(T_KLINGEL,HIGH); lByteVar++; break; // Klingel aus
case 4: lByteVar++; break; // Warten
case 5: lByteVar++; break; // Warten
case 6: lByteVar=0; break; // Warten
}
lZeitmarke=millis();
}
if(!digitalRead(GABEL)||knopf.aktiv(5)){ //- wenn Hörer abgenommen oder Knopf1 kurz (0,5 Sek) gedrückt => Akt=50
zustand=Z_begruessung;
digitalWrite(T_KLINGEL,HIGH); // Klingel aus
break;
}
if(zeit2.vorbei()){ // Dauer des Telefonlaeutens vorbei?
zustand=Z_warteAufMitspieler; //- wenn Zeit2 überschritten Akt=20 (Reset ohne Totzeit)
Serial.println(F("Kein Tel. abgenommen: Warte wieder auf Mitspieler"));
digitalWrite(T_KLINGEL,HIGH); // Klingel aus
}
break;
case Z_begruessung: // Begrüßung per Telefonhörer und per Lautsprecher
spreche(1); //- MP3 Datei1: ("Begrüßung...bitte Spielerzahl 2, 3 oder 4 wählen"), danach Akt=60
zustand=Z_wartenWaehlscheibe;
break;
case Z_wartenWaehlscheibe: // warten auf Teilnehmerzahleingabe per Wählscheibe
Serial.println(F("Warten auf Waehlscheibe"));
Serial.print(F("3."));
alleAusRGB();
zeit3.start(); // Zeit bis warten auf waehlen vorbei
lAnzahlMitspieler=0; // Waehlimpulse zaehlen;
zustand=Z_wartenAufWahlimpulse;
break;
case Z_wartenAufWahlimpulse:
if(digitalRead(W_SCHEIBE)){
zustand=Z_wahlImpuls1;
}
if(zeit3.vorbei()){ //- bis maximal Zeit3 (eingestellt an Poti3, ca. 10 Sekunden) dann Akt=70
zustand=Z_nochNichtsGewaehlt;
Serial.println(F("Noch nichts gewaehlt"));
}
break;
case Z_wahlImpuls1:
delay(20); // Entprellen
if(!digitalRead(W_SCHEIBE)){
zustand=Z_wahlImpuls0;
lAnzahlMitspieler++; // ein Impuls
lZeitmarke=millis(); // Timeout setzen
}
break;
case Z_wahlImpuls0:
delay(20); // Entprellen
if(digitalRead(W_SCHEIBE)){ // Noch ein Impuls
zustand=Z_wahlImpuls1;
break;
}
if(millis()-lZeitmarke >200){ // Timeout, kein weiterer Impuls
Serial.print(F("Gewaehlt: "));
Serial.println(lAnzahlMitspieler);
if(lAnzahlMitspieler < 2){ //- 0 oder 1 gewählt => Akt=80 (zu wenig Mitspieler)
sprecheBisEnde(41); //- MP3 Datei41: ("es müssen mindestens zwei Spieler sein") => Akt=70
zustand=Z_nochNichtsGewaehlt;
}
else if(lAnzahlMitspieler >4){ //- mehr als 4 gewählt => Akt=90 (zu viele Mitspieler)
spreche(42); //- MP3 Datei42: ("es dürfen maximal vier Mitspieler sein!") danach: Akt=60
zustand=Z_wartenWaehlscheibe;
}
else{
zustand = Z_Spielbeginn; //- 2 oder 3 oder 4 gewählt = Spielerzahl
Serial.print(F("Spielbeginn "));
}
}
break;
case Z_nochNichtsGewaehlt: // noch nichts gewählt
if(lWiederholungen > 2){
zustand = Z_warteAufMitspieler; // falls dieser Akt schon mehrfach durchlaufen wurde
break;
}
spreche(2); //- MP3 Datei2: ("...wieviel seid ihr? ...evtl. Mitspieler suchen...") => Akt=60
lWiederholungen++;
zustand= Z_wartenWaehlscheibe;
break;
case Z_Spielbeginn: // Spielbeginn mit der gewählten Mitspielerzahl
switch(lAnzahlMitspieler){ //- MP3 Datei 43/44/ oder 45: ("Willkommen Ihr zwei/ drei/ vier, jetzt kann's losgehen")
case 2: spreche(43); break;
case 3: spreche(44); break;
case 4: spreche(45); break;
default: Serial.print(F("Fehler bei Anzahl Mitspieler: ")); Serial.println(lAnzahlMitspieler); break; // Fehler bei Anzahl Mitspieler
}
for (lByteVar=0;lByteVar<lAnzahlMitspieler;lByteVar++){ //- und je nach Spielerzahl blinken zwei, drei oder vier Terminals oben gelb
setzeRGB(lByteVar,0,Ge,30);
}
for(lByteVar=lAnzahlMitspieler;lByteVar<maxTerminals;lByteVar++){ //die anderen schalten von Dauer-Blau auf Dauer-Rot (was geschieht, wenn ein Terminal erkannt hat und auf einen anderen gelegt wird?)
setzeRGB(lByteVar,0,Rt,0);
setzeRGB(lByteVar,1,Rt,0);
}
for(lByteVar=0;lByteVar<255;lByteVar++){ //- SPOT2 (Regalbeleuchtung) wird hochgedimmt
analogWrite(SPOT2,lByteVar);
delay(15);
}
Serial.println(F("Spieler1 Los!"));
lAktSpieler=0; // Spieler1
alleAusRGB();
for(lByteVar=0;lByteVar<ANZAHL_TERMINALS;lByteVar++){ // damit bereits liegende Dinge erkannt werden koennen
resetRfid(lByteVar);
}
zustand=Z_SpielerLos; //- danach Akt=110
break;
case Z_SpielerLos: // Spieler 1 soll loslegen
setzeRGB(lAktSpieler,0,Ge,50);
spreche(lAktSpieler+4); //- MP3 Datei4: "Spieler 1 soll Gegenstand auf gelb blinkenden Terminal legen"
lWiederholungen=0;
zustand=Z_wartenGegenstand;
break;
case Z_wartenGegenstand: // Warten auf Gegenstand von lAktSpieler
lByteVar = leseRfid(lAktSpieler,0); // oben Lesen
delay(150); // gib dem RFID-Leser Zeit
if(lByteVar>0 && lByteVar<255){ // Gegenstand per RFID erkannt
lByteVar=pgm_read_byte(&mp3stueck[lByteVar-1]); // suche passendes Stück
if(lByteVar>=21 && lByteVar<=40){ // wenn es eine Karte ist
Serial.println(F("Karte ausgeschlossen!"));
break; // ignorieren
}
if(lByteVar){ // Stueck vorhanden?
setzeRGB(lAktSpieler,0,Gn,0); // statt Gelb-Blinken hier nun Gruen
sprecheBisEnde(lByteVar);
lAktSpieler++;
if(lAktSpieler>=lAnzahlMitspieler){ // alle durch
zustand=Z_Spielteil2;
}
else zustand=Z_SpielerLos;
break;
}
}
switch (lWiederholungen){
case 0: // warten bis erster Text zuende, dann Zeitstart
if(digitalRead(MP3_BUSY)){ // wenn Player nicht mehr spielt
Serial.print(F("4."));
zeit4.start(); // Zeit zum Gegenstand auflegen
lWiederholungen++;
}
break;
case 3:
if(zeit4.vorbei()){
spreche(47); // "Ohne Gegenstand muss das Spiel leider enden - 5 4 3 2 1 aus" zuende
lWiederholungen++;
}
break;
case 4:
if(digitalRead(MP3_BUSY)){ // immer noch kein Gegenstand
zustand=Z_reset;
}
break;
default:
if(zeit4.vorbei()) { // wenn nach Zeit4 (einstellbar an Poti4) nichts aufgelegt wurde
spreche(46); // "Bitte Gegenstand auf gelb blinkenden Terminal legen"
lWiederholungen++;
Serial.print(F("4."));
zeit4.start();
}
break;
}
break;
case Z_Spielteil2:
lAktSpieler=0;
Serial.println(F("Spielteil2"));
for(lByteVar=0;lByteVar<255;lByteVar++){
analogWrite(SPOT2,255-lByteVar); //- Spot2 (Regalbeleuchtung) dimmt herunter
analogWrite(SPOT3,lByteVar); //- Spot3 (Beleuchtung der Kompetenzkarten) dimmt hoch
delay(15);
}
zustand=Z_KartenWahl;
break;
case Z_KartenWahl:
setzeRGB(lAktSpieler,1,Ge,50); // Von den genutzten Terminals beginnt nun der erste unten gelb zu blinken
spreche(lAktSpieler+8); // "... Spieler n - jetzt bitte eine Kompetenzkarte wählen und in den gelb blinkenden Schlitz stecken"
lWiederholungen=0;
zustand=Z_wartenKarte;
break;
case Z_wartenKarte:
lByteVar = leseRfid(lAktSpieler,1); // unten Lesen
delay(150); // gib dem RFID-Leser Zeit
if(lByteVar>0 && lByteVar<255){ // Gegenstand per RFID erkannt
lByteVar=pgm_read_byte(&mp3stueck[lByteVar-1]); // suche passendes Stück
if(lByteVar>=61){ // wenn es ein Gegenstand ist
Serial.println(F("Gegenstand ausgeschlossen!"));
break; // ignorieren
}
if(lByteVar){ // Stueck vorhanden?
setzeRGB(lAktSpieler,1,Gn,0); // statt Gelb-Blinken hier nun Dauergelb besser Gruen?
sprecheBisEnde(lByteVar);
lAktSpieler++;
if(lAktSpieler>=lAnzahlMitspieler){ // alle durch
zustand=Z_SesamOffen;
}
else zustand=Z_KartenWahl;
break;
}
}
switch (lWiederholungen){
case 0: // warten bis erster Text zuende, dann Zeitstart
if(digitalRead(MP3_BUSY)){ // wenn Player nicht mehr spielt
Serial.print(F("4."));
zeit4.start(); // Zeit zum Gegenstand auflegen
lWiederholungen++;
}
break;
case 3:
if(zeit4.vorbei()){
spreche(49); // "Ohne Karte muss das Spiel leider enden - 5 4 3 2 1 aus" zuende
lWiederholungen++;
}
break;
case 4:
if(digitalRead(MP3_BUSY)){ // immer noch kein Gegenstand
zustand=Z_reset;
}
break;
default:
if(zeit4.vorbei()) { //- wenn nach Zeit4 (einstellbar an Poti4) nichts aufgelegt wurde
spreche(48); // "Bitte Karte auf gelb blinkenden Terminal legen")
lWiederholungen++;
Serial.print(F("4."));
zeit4.start();
}
break;
}
break;
case Z_SesamOffen:
Serial.println(F("Sesam offen"));
spreche(13); // "Überleitung/ Türknarzen und Begrüßung/ Taschenlampen entnehmen..."
for(lByteVar=0;lByteVar<255;lByteVar++){
analogWrite(HAUPT_LICHT,255-lByteVar); // Hauptlicht dimmt herunter
analogWrite(SPOT3,255-lByteVar); // Kartenbeleuchtung dimmt herunter
delay(20);
}
taschenlampenfach.setBlinker(300); // Spot 4 Blinkfunktion auf Taschenlampenfach an
digitalWrite(TUER_MAGNET_EINGANG,LOW);// Türe springt auf per EMagnet1 per Relais 2
delay(1000);
digitalWrite(TUER_MAGNET_EINGANG,HIGH);
digitalWrite(VERSTAERKER,LOW); // Relais 3 schaltet Verstärker von außen nach Verstärker innen
while(!digitalRead(MP3_BUSY)){ // Ueberleitung zu Ende sprechen lassen
taschenlampenfach.service(); // dabei blinken
}
zustand= Z_TaschenlampenNehmen;
break;
case Z_TaschenlampenNehmen:
taschenlampenfach.service();
if(digitalRead(TASCHL_AUSGABE)!=TASCHENLAMPEN_HAENGEN){ //- wenn Taschenlampen entnommen (Sensor1): Blinkfunktion Taschenlampenfach aus
taschenlampenfach.setBlinker(0);
zustand=Z_SpielbeginnInnenraum;
Serial.println(F("Z_SpielbeginnInnenraum"));
Serial.print(F("5."));
zeit5.start(); // Zeit von Taschenlampenentnahme bis Mausklick
}
else{
spreche(50); //- wenn noch Taschenlampen da: MP3-Datei 50 („Bitte ALLE Taschenlampen mitnehmen)
}
break;
case Z_SpielbeginnInnenraum: // Spielbeginn Innenraum per Klynt-Technik
if(zeit5.vorbei()){ // nach Zeit5 Mausklick per Relais4 zum Starten der Klynt, evtl. Datei
digitalWrite(MAUSKLICK,LOW);
delay(500);
digitalWrite(MAUSKLICK,HIGH);
Serial.print(F("6."));
zeit6.start(); // Zeit von Mausklick bis Innenlicht etwas hochdimmen
zustand=Z_nachMausklick;
Serial.println(F("Z_nachMausklick"));
}
break;
case Z_nachMausklick:
if(zeit6.vorbei()){
spreche(15); //- evtl. MP3 Datei 15 geeheimnisvoll auf Zauberspiegel aufmerksam machen
zustand=Z_vorSpielende;
lZeitmarke=millis(); // Timeout setzen
Serial.println(F("Z_vorSpielende"));
}
break;
case Z_vorSpielende: // Vor Spielende: warten auf EXIT-Knopf
if(millis()-lZeitmarke >1000){ // Nach einer Sekunde
lZeitmarke=millis(); // Timeout setzen
if(innenLicht<=24){ // langsam Innenlicht hochdimmen
analogWrite(INNEN_LICHT,innenLicht++);
}
}
break;
case Z_AusgangAuf:
Serial.println(F("Z_AusgangAuf"));
spreche(16); // evtl. MP3-Datei16 oder Stille
digitalWrite(TUER_MAGNET_AUSGANG,LOW); // und E-Magnet2 per Relais5 öffnet die Ausgangstüre TUER_MAGNET_AUSGANG
delay(1000);
digitalWrite(TUER_MAGNET_AUSGANG,HIGH);
digitalWrite(LED_STREIFEN,LOW); // LED-Streifen an
Serial.print(F("7."));
zeit7.start(); // Zeit solange LED-Streifen leuchetet
while(innenLicht < 255){ // nach Zeit7 wird Hauptlicht maximal hochgedimmt
analogWrite(INNEN_LICHT,innenLicht++);
delay(20);
}
zustand = Z_Spielende;
Serial.println(F("Z_Spielende"));
break;
case Z_Spielende:
if(zeit7.vorbei()){
digitalWrite(LED_STREIFEN,HIGH); // LED-Streifen aus
lZeitmarke=millis(); // Timeout setzen
digitalWrite(ENDE_LICHT,LOW); // Ende Licht an
zustand=Z_EndeEnde;
Serial.println(F("Z_EndeEnde;"));
spreche(17); // mp3-Datei 17 Abschied oder Stille
}
break;
case Z_EndeEnde:
if(millis()-lZeitmarke >150000UL){ //statt 15 früher 30 = 5 Minuten
digitalWrite(ENDE_LICHT,HIGH); // Ende Licht an
zustand=Z_reset;
}
break;
case Z_test:
zeit6.start();
Serial.println(F("Zeit6 gestartet"));
zustand=Z_testDo;
break;
case Z_testDo:
if(zeit6.vorbei()){
Serial.println(F("Zeit6 vorbei"));
zustand=Z_test;
}
break;
}
if(zustand>=Z_SesamOffen && zustand<=Z_vorSpielende && !digitalRead(EXIT_KNOPF)){ // wenn Exit-Knopf gedrueckt
zustand=Z_AusgangAuf;
}
if(zustand>=Z_telefonKlingelt){//Akt>=40 Neustartmöglichkeit
if(knopf.aktiv(20)){ // 2 Sek gedrueckt
Serial.println(F("Neustart durch Knopf"));
zustand=Z_reset; //- wenn Knopf1 lange gedrückt wird Akt=10 (Reset)
}
}
}
void sprecheBisEnde(byte n){ // Nachricht bis zum Ende ausgeben (blockierend)
if(n>MAX_MP3_STUECK){
Serial.print(F("Maximale MP3 Stuecknummer ueberschritten: "));
Serial.println(n);
return;
}
myMP3.play(n);
Serial.print(F("Nachricht: "));
Serial.println(n);
delay(1000);
while(!digitalRead(MP3_BUSY)); // warten bis Ende
}
void spreche(byte n){ // Nachricht ausgeben (nicht blockierend)
static byte letztes = 0;
if(n>MAX_MP3_STUECK){
Serial.print(F("Maximale MP3 Stuecknummer ueberschritten: "));
Serial.println(n);
}
if(digitalRead(MP3_BUSY)||n!=letztes){ // wenn nichts gespielt oder was neues
myMP3.play(n); // spiele
letztes=n;
Serial.print(F("Nachricht: "));
Serial.println(n);
delay(1000);
}
}
void resetRfid(byte station){ // RFID-Leser zurücksetzen um liegengebliebene Karten zu entdecken
Wire.beginTransmission(station+8); // sendet zu Node empfaenger
Wire.write(RFID_reset); // sendet daten
Wire.endTransmission(); // Übermittlungsstop
}
void setzeRGB(byte station, byte p, byte muster, byte dauer){ // p=0: oben dauer*10ms
if(p) muster |= 128; // Bit 7 setzen für unten
Wire.beginTransmission(station+8); // sendet zu Node empfaenger
Wire.write(muster); // sendet daten
Wire.write(dauer); // sendet daten
Wire.endTransmission(); // Übermittlungsstop
}
void alleAusRGB(){
for(byte i=0;i<4;i++){
setzeRGB(i,0,Off,0);
setzeRGB(i,1,Off,0);
}
}
byte pruefeHallo(byte station){ // Station 0..3 Adresse 8..11
Wire.beginTransmission(station+8); // sendet zu Node empfaenger
Wire.write(C_Hallo); // Hallo senden
Wire.endTransmission(); // Übermittlungsstop
Serial.print(F("Hallo Anfrage gesendet an: "));
Serial.print(station);
delay(RFID_DELAY); // Zeit zum antworten lassen
Wire.requestFrom(station+8, 2); // sollte mit "OK" antworten
if(Wire.read()!='O') return 0;
if(Wire.read()!='K') return 0;
return 1; // OK geantwortet
}
byte leseRfid(byte station, byte platz){ // 0..3 platz=0 ist oben
byte l,i,k;
byte id[7]; // Puffer für Karten id
byte treffer=0;
Wire.beginTransmission(station+8); // sendet zu Node empfaenger
if(!platz) Wire.write(RFID_oben); // oben lesen
else Wire.write(RFID_unten); // unten lesen
Wire.endTransmission(); // Übermittlungsstop
delay(RFID_DELAY); // Zeit zum antworten lassen
Wire.requestFrom(station+8, 8); // Fragt Daten von Node empfaenger ab
l = Wire.read(); // erstes Byte gibt Anzahl der Bytes an
if (l==0){ // Keine neues Tag
return 0;
}
else if (l>7){
Serial.print(F("leseRfid: Falsche Byteanzahl: "));
Serial.println(l);
return 0;
}else{
Serial.print(F("Karte mit ID: "));
for (i=0;i<l;i++){ // lies die ID ein
id[i]=Wire.read();
Serial.print(id[i],HEX);
}
Serial.println("");
k=sizeof(karten)/sizeof(karten[0]); // Karte bestimmen
for(k=0;k<sizeof(karten)/sizeof(karten[0]);k++){ // für alle gespeicherten Karten
for(i=0;i<l;i++){ // für alle Bytes
if(id[i]!=pgm_read_byte(&karten[k][i])) break; // hat nicht gepasst
}
if(i==l) {
treffer = 1;
break;
}
}
if (treffer){ // Karte erkannt
return k+1;
}
else{
Serial.print(F("Neue Karte: {"));
for (i=0;i<l-1;i++){ // zwecks Speicherung ausgeben
Serial.print(F("0x"));
Serial.print(id[i],HEX);
Serial.print(F(","));
}
Serial.print(F("0x"));
Serial.print(id[i],HEX);
Serial.println(F("}"));
return 255; // unbekannte Karte
}
}
}
void serialKommandos(){ // über seriellen Monitor steuern
byte key_command;
if (Serial.available()){
key_command=Serial.read();
Serial.print(F("Komando "));
Serial.write(key_command);
Serial.println("");
switch (key_command){
case '?':
Serial.println(F("Liste aller Befehle:"));
Serial.println(F("r Reset"));
Serial.println(F("t Zeittest mit langer Zeit 6"));
Serial.println(F("z alle eingestellten Zeiten ausgeben"));
Serial.println(F("k Kartentest"));
Serial.println(F("1 Z_warteAufMitspieler"));
Serial.println(F("2 Z_begruessung"));
Serial.println(F("3 Z_Spielteil2"));
Serial.println(F("4 Z_SesamOffen"));
break;
case 'r':
zustand=Z_reset;
break;
case 'k':
if(zustand!=Z_kartentestDo){
zustand = Z_kartentest;
}
else{
zustand=Z_reset;
}
break;
case 't':
zustand=Z_test;
break;
case 'z':
Serial.print(F("1."));
zeit1.info();
Serial.print(F("2."));
zeit2.info();
Serial.print(F("3."));
zeit3.info();
Serial.print(F("4."));
zeit4.info();
Serial.print(F("5."));
zeit5.info();
Serial.print(F("6."));
zeit6.info();
Serial.print(F("7."));
zeit7.info();
zustand=Z_nix;
break;
case '1': zustand=Z_warteAufMitspieler; break;
case '2': zustand=Z_begruessung; break;
case '3': zustand=Z_Spielteil2; break;
case '4': zustand=Z_SesamOffen; break;
}
}
}