MezData-Logo

Discovery-Room

Discovery-Game im Stadtmuseum Tübingen

Discovery-Game

Für ein Spiel sollen 4 Stationen mit jeweils 2 RFID-Modulen abgefragt werden. An den Stationen sind bei den Modulen jeweils ein RGB-Streifen montiert. Die Stationen werden über I2C mit einem Steuerrechner verbunden. Am Steuerrechner ist ausserdem ein Wählscheibentelefon angeschlossen, um die Anzahl der Mitspieler einstellen zu können und ein MP3-Player-Modul zur Ausgabe von Texten. Ferner gibt es diverse Ein- und Ausgänge für die Steuerung des Ablaufs und der Lichter...

Diese Projektdokumentation erhebt nicht den Anspruch, die dargestellte Installation nachbauen zu können, sondern ist für uns Entwickler ein Online-Nachschlagewerk bei Service und für den interessierten Leser eine Inspiration für eigene Projekte.

4 Stationen mit 2 RFID-Modulen

RFID Station
Steckplatinenansicht Modul
Schaltplan
Schaltplan Modul

5V RGB-LED-Streifen mit 3 Segmenten zieht bei 5V ca. 50mA pro Farbe, Reduktion mit Vorwiderständen auf ca. 15mA: Rot 150Ω; Blau&Grün: 120Ω

Erkannte Probleme Nano:

A6 und A7 funktionierten als Digitalpins nicht, PullUps gingen nicht an usw.

Strombedarf der RFID-Reader ca. 30mA ergibt bei allen LED aus 70mA LEDs an 150mA pro Station, Problem mit Kühlung der Nanos bei 12V Vin: (12V-5V)*0,15A = 1,05W. Manche (billig-Nachbau) Nanos gehen auf Reset... Lösungen:

Uno-Info: SCL=A5=D19; SDA=A4=D18

Master Steuerrechner

Master
Master Steckplatine

Am Steuerrechner ist ein MP3-Player-Modul angeschlossen und 4 Stationen über I2C.

RJ45 Buchse
RG45-Buchse zu Station

Info-Schnipsel

Online-Beispiele für I2C sind teilweise fehlerhaft:

Slave-Adresse erst ab Adr. 8 einstellen 0..7 sind reserviert, daher erst ab 8..

Wire.requestFrom(a, b) liest stur von Adresse a Anzahl b Bytes aus. Wenn Station a nichts mehr zu senden hat wird in den angeforderten Bytes 0xFF empfangen. Die IDs der Tags waren verschieden lang, daher habe ich als erstes Byte die Länge gesendet und immer b=8 Bytes gelesen und dann anhand des ersten Bytes entschieden, wieviele Nutzdaten gesendet wurden.

Mega 2560: hat bei Pins 20-21 10kΩ Pull-Ups eingebaut

 

Verhalten des RFID-Lesens verständlich beschreiben, auf interne Zustände der Karten eingehen..

DFPlayerMini_Fast.h: isPlaying() funktioniert nicht mit SoftwareSerial, auch mit Serial1 beim Mega2560 ist die Funktion nicht zuverlässig, musste auf Busy-Pin am Player ausweichen.

Weitere Belegung Master

    GND GND    
Telefonklingel N Relais1 53 52 Telefon-Gabel N 4,7kΩ VCC
Türmagnet Eingang N Relais2 51 50 Telefon-Knopf N 4,7kΩ VCC
Verstärker N Relais3 49 48 Telefon-Wählscheibe 4,7kΩ VCC
Mausklick N Relais4 47 46 Taschenl. Ausgabe Taster N 4,7kΩ VCC
Türmagnet Ausgang N Relais5 45 44 Türen Taster N 4,7kΩ VCC
Spot5 Taschl. Fach N Relais6 43 42 Taschenl. Rückgabe N 4,7kΩ VCC
LED-Streifen am Ausgang N Relais7 41 40 IR1  
Spot6 Hauptlicht Ende N Relais8 39 38 IR2  
    37 36 IR3  
Telefon
1 OR/WS Telfon-Gabel 52
2 OR Telefon-Wählschebe 48
3 GN/WS Lautsprecher DF-Player
4 BL Masse GND
5 BL/WS Lautsprecher DF-Player
6 GN Telefon-Knopf 50
7 BR/WS Klingel Relais
8 BR Klingel Relais
Lichter
2 Spot1 (PWM) Hauptlicht
3 Spot2 (PWM) Leuchtet auf Gegenstände
4 Spot3 (PWM) Leuchtet auf Karten
5 Spot5 (PWM) Raumlicht innen
43 Spot4 Relais Taschenlampenfach blinkt

 

 

Software für Station

Quellcode [Discovery-Game-Rfid/Discovery-Game-Rfid.ino]
  1. #include <SPI.h>
  2. #include <MFRC522.h>
  3. #include <Wire.h>
  4. #include "GameLib.h"
  5. #define vers "1.0"
  6.  
  7. // SPI-Bus: RFID PIN Numbers : RESET + SDAs
  8. #define RST_PIN 2
  9. #define SS_O_PIN 4
  10. #define SS_U_PIN 3
  11. // i2c Bus-Adresse, beim Setup ermitteln
  12. #define I2C_ADR_LOW_PIN A2 // A6 und A7 haben nicht funktioniert!
  13. #define I2C_ADR_HIGH_PIN A3 // auch nicht D20, D21
  14. byte i2cAdr; // Busnummer des Slave
  15.  
  16. #define NR_OF_READERS 2 // Anzahl der RFID-Reader
  17.  
  18. byte ssPins[] = {SS_O_PIN, SS_U_PIN}; // Select-Pins der Reader
  19. // Create an MFRC522 instance :
  20. MFRC522 mfrc522[NR_OF_READERS];
  21. RGBStrip rgb_oben(8,9,10); // RGB-Strip
  22. RGBStrip rgb_unten(7,5,6); // RGB-Strip
  23. rfid_t rfidErg=Rfid_nix;
  24. void setup() {
  25. Serial.begin(115200); // Initialize serial communications with the PC
  26. while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
  27. pinMode(I2C_ADR_LOW_PIN,INPUT_PULLUP); // Adresse durch Jumper setzen
  28. pinMode(I2C_ADR_HIGH_PIN,INPUT_PULLUP);
  29. i2cAdr = 8 + !digitalRead(I2C_ADR_LOW_PIN)+ (!digitalRead(I2C_ADR_HIGH_PIN)<<1);
  30. SPI.begin(); // Init SPI bus
  31. rgb_oben.setRGB(Ws,0);
  32. rgb_unten.setRGB(Ws,0);
  33. Serial.print(F("Discovery-Game RFID-Server V"));
  34. Serial.print(vers);
  35. Serial.print(F(" (c) Oliver Mezger I2C-Adresse: "));
  36. Serial.println(i2cAdr);
  37. readerReset();
  38. Wire.begin(i2cAdr); // I2C-Adresse festlegen
  39. Wire.onReceive(receiveEvent); // erstelle ein Empfangen-Ereignis
  40. Wire.onRequest(requestEvent); // erstelle ein Anfrage-Ereignis
  41. }
  42.  
  43. void receiveEvent(int bytes) { // Komando-Empfang
  44. byte x;
  45. int y;
  46. if (bytes > 1){ // Befehl fuer RGB-Blinker
  47. x = Wire.read(); // lies Muster für RGB Bit 7 = 0 oben 1 unten
  48. y = Wire.read(); // lies Blinkerperiodendauer
  49. Serial.print(F("RGB-Befehl: "));
  50. Serial.print(x,BIN);
  51. Serial.print(F(" Dauer "));
  52. Serial.println(y);
  53. if(!(x&128)){ //Bit 7 = 0: RGBoben
  54. rgb_oben.setRGB((rgb_t)x,y*10);
  55. }
  56. else{ //Bit 7 = 1: RGBunten
  57. rgb_unten.setRGB((rgb_t)x,y*10);
  58. }
  59. }
  60. else {
  61. x = Wire.read(); // lies die gesendeten Daten aus
  62. Serial.print(F("RFID-Leser-Befehl: "));
  63. Serial.println(x);
  64. switch (x){
  65. case RFID_reset: // Leser zuruecksetzen um liegengebliebne Karten zu erkennen
  66. readerReset();
  67. break;
  68. case RFID_oben: // Karte lesen
  69. if (mfrc522[0].PICC_IsNewCardPresent() && mfrc522[0].PICC_ReadCardSerial()) {
  70. rfidErg=Rfid_karte_o;
  71. mfrc522[0].PICC_HaltA();
  72. mfrc522[0].PCD_StopCrypto1();
  73. Serial.println("Karte oben gelesen");
  74. }
  75. else rfidErg=Rfid_nix;
  76. break;
  77. case RFID_unten:
  78. if (mfrc522[1].PICC_IsNewCardPresent() && mfrc522[1].PICC_ReadCardSerial()) {
  79. rfidErg=Rfid_karte_u;
  80. mfrc522[0].PICC_HaltA();
  81. mfrc522[0].PCD_StopCrypto1();
  82. Serial.println("Karte unten gelesen");
  83. }
  84. else rfidErg=Rfid_nix;
  85. break;
  86. case C_Hallo: // Server Hallo fragen
  87. rfidErg=A_Ok; // Bei nächster Datenanfrage "OK" senden
  88. break;
  89. }
  90. }
  91. }
  92. void requestEvent(){ // Antwort auf Datenanfrage
  93. switch (rfidErg){
  94. case Rfid_nix: // Keine neue Karte erkannt
  95. //nodePayload[0] = NODE_ADDRESS;
  96. Wire.write(0);
  97. break;
  98. case Rfid_karte_o:
  99. Wire.write(mfrc522[0].uid.size);
  100. Wire.write(mfrc522[0].uid.uidByte, mfrc522[0].uid.size);
  101. break;
  102. case Rfid_karte_u:
  103. Wire.write(mfrc522[0].uid.size);
  104. Wire.write(mfrc522[1].uid.uidByte, mfrc522[1].uid.size);
  105. break;
  106. case A_Ok:
  107. Wire.write("OK");
  108. break;
  109. }
  110. }
  111.  
  112. void loop() {
  113. byte key_command;
  114. if(Serial.available()) {
  115. key_command=Serial.read(); // get command from serial input
  116. Serial.print(F("Kommando "));
  117. Serial.write(key_command);
  118. Serial.println("");
  119. switch (key_command){
  120. case 'q':
  121. rgb_oben.setRGB(Off,0);
  122. rgb_unten.setRGB(Off,0);
  123. Serial.println(F("Alle LED aus"));
  124. break;
  125. case 'r': rgb_unten.setRGB(Rt,0); Serial.println(F("Rot")); break;
  126. case 'g': rgb_unten.setRGB(Gn,0); Serial.println(F("Grün")); break;
  127. case 'b': rgb_unten.setRGB(Bl,0); Serial.println(F("Blau")); break;
  128. case 'l': rgb_unten.setRGB(Li,0); break;
  129. //case 'b': rgb_unten.setRGB(Gn,200); break;
  130. case 'o': // oberen RFID auslesen
  131. if (mfrc522[0].PICC_IsNewCardPresent() && mfrc522[0].PICC_ReadCardSerial()) {
  132. dump_byte_array(mfrc522[0].uid.uidByte, mfrc522[0].uid.size);
  133. mfrc522[0].PICC_HaltA();
  134. mfrc522[0].PCD_StopCrypto1();
  135. }
  136. else Serial.println(F("Keine neue Karte"));
  137. break;
  138. case 'c': mfrc522[0].PCD_Init(); mfrc522[0].PCD_DumpVersionToSerial(); break;
  139. case 'i': dump_byte_array(mfrc522[0].uid.uidByte, mfrc522[0].uid.size); break;
  140. }
  141. }
  142. delay(50);
  143. rgb_oben.service();
  144. rgb_unten.service();
  145. }
  146.  
  147. void readerReset(){
  148. for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) {
  149. mfrc522[reader].PCD_Init(ssPins[reader], RST_PIN);
  150. Serial.print(F("Reader "));
  151. Serial.print(reader);
  152. Serial.print(F(": "));
  153. mfrc522[reader].PCD_DumpVersionToSerial();
  154. }
  155. }
  156.  
  157. void dump_byte_array(byte * buffer, byte bufferSize) {
  158. for (byte i = 0; i < bufferSize; i++) {
  159. Serial.print(buffer[i] < 0x10 ? " 0" : " ");
  160. Serial.print(buffer[i], HEX);
  161. }
  162. }
  163.  
Quellcode [Discovery-Game-Master/GameLib.h]
  1. // GameLib.h
  2. #ifndef GameLib_h
  3. #define GameLib_h
  4. #endif
  5.  
  6. enum rgb_t : byte {Off=0,Bl=0b001,Gn=0b010,Tk=0b011,Rt=0b100,Li=0b101,Ge=0b110,Ws=0b111}; // Welches RGB-Muster
  7. enum command_t: byte {RFID_reset=3,RFID_oben,RFID_unten,C_Hallo};
  8. enum rfid_t : byte {Rfid_nix,Rfid_karte_o,Rfid_karte_u,A_Ok};
  9.  
  10. class RGBStrip{
  11. public:
  12. RGBStrip(byte r_pin,byte g_pin,byte b_pin); // Initialisierung
  13. void setRGB(rgb_t l,unsigned int d); // Verhalten einstellen
  14. void service(); // periodisch aufgerufen
  15. private:
  16. byte red_pin;
  17. byte green_pin;
  18. byte blue_pin;
  19. unsigned long myTime; // Startzeitpunkt
  20. unsigned int myDelay; // Zeitverzoegerung
  21. byte leds; // welche LED blinken sollen
  22. byte zyklus; // fuer Blinken
  23. void setLed(); // Led an
  24. void clearLed(); // alle Led aus
  25. };
  26.  
  27. class Blinker{
  28. public:
  29. Blinker(byte b_pin,byte r); // Initialisierung Pinnummer, Ruhezustand
  30. void setBlinker(unsigned int d); // Blinkzeit in ms 0 = Blinker aus
  31. void service(); // periodisch aufgerufen
  32. private:
  33. byte blink_pin;
  34. byte ruhe;
  35. unsigned char zyklus; // Zykluszustand
  36. unsigned long myTime; // Startzeitpunkt
  37. unsigned int myDelay; // Zeitverzoegerung
  38. };
  39.  
  40. class Zeit{ // Ruft die Potistellung an AD-Pin ab und skaliert die Zeit zwischen min und max
  41. public:
  42. Zeit(byte pin, unsigned int minZ, unsigned int maxZ); // Zeit in Sekunden
  43. void start(); // Zeit starten
  44. void start(unsigned int n); // n-fache Zeit
  45. byte vorbei(); // 1 falls Zeit um ist
  46. void info(); // gibt Attribute auf Serial aus
  47. private:
  48. byte adPin;
  49. unsigned int minZeit;
  50. unsigned int maxZeit;
  51. unsigned long zeitmarke;
  52. };
  53.  
  54. class Taster{
  55. public:
  56. Taster(byte pin,byte aktiv); // lowaktiv=0
  57. byte aktiv(byte dauer); // dauer bis aktiv in 10tel Sekunden
  58. private:
  59. byte tPin;
  60. unsigned long zeitmarke;
  61. byte tAktiv; // lowaktiv=0
  62. byte merker; // für Flankendetektion
  63. };
  64.  
Quellcode [Discovery-Game-Master/GameLib.cpp]
  1. // GameLib.cpp
  2. #include <Arduino.h>
  3. #include "GameLib.h"
  4.  
  5. RGBStrip::RGBStrip(byte r_pin,byte g_pin,byte b_pin){ // Initialisierung
  6. red_pin=r_pin;
  7. green_pin=g_pin;
  8. blue_pin=b_pin;
  9. pinMode(red_pin,OUTPUT);
  10. pinMode(green_pin,OUTPUT);
  11. pinMode(blue_pin,OUTPUT);
  12. clearLed();
  13. }
  14.  
  15. void RGBStrip::setRGB(rgb_t l,unsigned int d){ // Verhalten einstellen
  16. leds = l&0b111; // nicht beteiligte Bits ausmaskieren
  17. zyklus=0;
  18. if (l==Off){
  19. myDelay=0;
  20. }
  21. else{
  22. myDelay=d;
  23. myTime=millis();
  24. }
  25. }
  26.  
  27. void RGBStrip::service(){ // periodisch aufgerufen für Blinken
  28. if(myDelay > 0){ // soll blinken
  29. if (millis() - myTime > myDelay) { // schauen, ob die Zeit um ist
  30. myTime = millis();
  31. switch (zyklus){
  32. case 0:
  33. clearLed();
  34. zyklus++;
  35. break;
  36. case 1:
  37. setLed();
  38. zyklus =0;
  39. }
  40. }
  41. }
  42. else if (zyklus == 0){ // kein Blinken nur einmal setzen
  43. setLed();
  44. zyklus++;
  45. }
  46. }
  47.  
  48. void RGBStrip::clearLed(){
  49. digitalWrite(red_pin,HIGH);
  50. digitalWrite(green_pin,HIGH);
  51. digitalWrite(blue_pin,HIGH);
  52. }
  53.  
  54. void RGBStrip::setLed(){
  55. if(leds&0b100) digitalWrite(red_pin,LOW); else digitalWrite(red_pin,HIGH);
  56. if(leds&0b010) digitalWrite(green_pin,LOW); else digitalWrite(green_pin,HIGH);
  57. if(leds&0b001) digitalWrite(blue_pin,LOW); else digitalWrite(blue_pin,HIGH);
  58. }
  59.  
  60. Blinker::Blinker(byte b_pin,byte r){ // Initialisierung
  61. blink_pin=b_pin;
  62. ruhe=r;
  63. pinMode(blink_pin,OUTPUT);
  64. digitalWrite(blink_pin,ruhe);
  65. }
  66.  
  67. void Blinker::setBlinker(unsigned int d){ // Blinkzeit in ms
  68. zyklus=0;
  69. myDelay=d;
  70. myTime=millis();
  71. digitalWrite(blink_pin,ruhe);
  72. }
  73.  
  74. void Blinker::service(){ // periodisch aufgerufen
  75. if(myDelay > 0){ // soll blinken
  76. if (millis() - myTime > myDelay) { // schauen, ob die Zeit um ist
  77. myTime = millis();
  78. switch (zyklus){
  79. case 0:
  80. digitalWrite(blink_pin,ruhe);
  81. zyklus++;
  82. break;
  83. case 1:
  84. digitalWrite(blink_pin,!ruhe);
  85. zyklus =0;
  86. }
  87. }
  88. }
  89. }
  90.  
  91. Zeit::Zeit(byte pin, unsigned int minZ, unsigned int maxZ){ // Zeit in Sekunden 0.255
  92. adPin = pin;
  93. minZeit = minZ;
  94. maxZeit = maxZ;
  95. }
  96.  
  97. void Zeit::start(){ // Zeit starten
  98. start(1);
  99. }
  100. void Zeit::start(unsigned int n){ // n-fache Zeit
  101. unsigned long z = map(analogRead(adPin),0,1023,minZeit,maxZeit);
  102. zeitmarke = millis()+z*1000*n; // ab Zeitmarke ist Zeit um
  103. Serial.print(F(" Zeit gesetzt (Sekunden): "));
  104. Serial.println(z);
  105. }
  106.  
  107. byte Zeit::vorbei(){ // 1 wenn Zeit vorbei ist
  108. return (millis()>zeitmarke);
  109. }
  110.  
  111. void Zeit::info(){
  112. unsigned long z = map(analogRead(adPin),0,1023,minZeit,maxZeit);
  113. Serial.print(F("Min: "));
  114. Serial.print(minZeit);
  115. Serial.print(F(" Max: "));
  116. Serial.print(maxZeit);
  117. Serial.print(F(" Wert: "));
  118. Serial.println(z);
  119. }
  120.  
  121. Taster::Taster(byte pin,byte aktiv){ // lowaktiv=0
  122. tPin=pin;
  123. tAktiv=aktiv;
  124. if(!aktiv) pinMode(pin,INPUT_PULLUP); // Pullup an
  125. else pinMode(pin,INPUT);
  126. }
  127.  
  128. byte Taster::aktiv(byte dauer){ // dauer bis aktiv in 10tel Sekunden
  129. if(digitalRead(tPin)!=tAktiv){ // Taster nicht aktiv
  130. merker=0;
  131. return 0;
  132. }
  133. else{
  134. if(!merker){ // pos. Flanke
  135. merker = 1;
  136. zeitmarke=millis();
  137. return 0;
  138. }
  139. else{ // Taster noch aktiv Dauer checken
  140. return millis()-zeitmarke > dauer*100;
  141. }
  142. }
  143. }
  144.  

Master-Steuerung

Quellcode [Discovery-Game-Master/Discovery-Game-Master.ino]
  1. //mez 7.6.21
  2. #include "GameLib.h"
  3. #include <Wire.h>
  4. #include <DFPlayerMini_Fast.h> //https://github.com/PowerBroker2/DFPlayerMini_Fast
  5.  
  6. const byte karten[][7] PROGMEM = { // RFID-IDs der Tags
  7. {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
  8. {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
  9. {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
  10. {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
  11. {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
  12. {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
  13. {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
  14. {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
  15. {0x8C,0x6D,0x4F,0x2E},{0xCA,0xF6,0x68,0xAD},{0x6C,0xF5,0x49,0x31},{0x4,0x5F,0x43,0x32,0x1E,0x70,0x80}, // 33..36
  16. {0x6A,0x19,0x96,0xAE},{0x4A,0xEC,0x82,0xAD},{0xC1,0x91,0x72,0x21},{0x4,0x57,0x43,0x32,0x1E,0x70,0x80}, // 37..40
  17. {0x9C,0xD6,0x48,0x31},{0xC1,0x9A,0x25,0x21},{0xD1,0xC2,0xC5,0x21},{0x4,0x6B,0x43,0x32,0x1E,0x70,0x80}, // 41..44
  18. {0x3A,0xE2,0xA0,0xAD},{0x4,0xE0,0x26,0xD2,0xBA,0x5D,0x80},{0xD1,0xF9,0xB8,0x21},{0x4,0x37,0xCB,0xBA,0x6F,0x71,0x80}, // 45..48
  19. {0x4,0x43,0xCB,0xBA,0x6F,0x71,0x80},{0x4,0x6F,0x43,0x32,0x1E,0x70,0x80},{0x7A,0xF3,0x5D,0xAD},{0xCA,0x2D,0xA8,0xAD}, // 49..52
  20. {0xC3,0x6F,0xAF,0x2},{0xEA,0x67,0x69,0xAD},{0x8A,0xA0,0x7D,0xAD},{0x4,0x3F,0xCB,0xBA,0x6F,0x71,0x80}, // 53..56
  21. {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
  22. {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
  23. {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
  24. {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
  25. {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
  26. };
  27.  
  28. 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
  29. 27,28,29,30,61,62,62,62,63,63,63,64,64,65,65,66, // 17..32
  30. 66,66,66,67,68,68,68,69,70,71,72,73,74,75,75,76, // 33..48
  31. 77,78,79,79,79,79,79,80,81,82,83,84,85,86,87,88, // 49..64
  32. 89,90,91,92,93,94,95,96,97,98,99,100
  33. };
  34. #define vers "1.2"
  35. #define ANZAHL_TERMINALS 4 // wie viele Terminals angeschlossen sind
  36. #define TASCHENLAMPEN_HAENGEN 1 // Zustand des Sensors in dem die Taschenlampen haengen
  37. #define MAX_MP3_STUECK 100 // Hoechste Nummer auf SD
  38. #define RFID_DELAY 20 // Zeit in ms zwischen Anfrage und Empfang
  39. #define MP3_BUSY 17
  40. #define IR1 40
  41. #define IR2 38
  42. #define IR3 36
  43. #define GABEL 52 //neg Logik
  44. #define T_KNOPF 50 //neg Logik
  45. #define W_SCHEIBE 48
  46. #define TASCHL_AUSGABE 46 //neg Logik
  47. #define TUER_TASTER 44 //neg Logik
  48. #define EXIT_KNOPF 42 //neg Logik
  49. #define HAUPT_LICHT 2 // PWM HauptLicht
  50. #define SPOT2 3 // PWM leuchtet auf Gegenstaende
  51. #define SPOT3 4 // PWM leuchtet auf Karten
  52. #define INNEN_LICHT 5 // PWM Hauptlicht innen
  53. #define T_KLINGEL 53 // Relais1 neg Log
  54. #define TUER_MAGNET_EINGANG 51 // Relais2 neg Log
  55. #define VERSTAERKER 49 // Relais3 neg Log
  56. #define MAUSKLICK 47 // Relais4
  57. #define TUER_MAGNET_AUSGANG 45
  58. #define TASCHENLAMPENFACH_R 43 // Realis Taschenlampenfach Blinker
  59. #define LED_STREIFEN 37 // Lichteffekt am Ausgang
  60. #define ENDE_LICHT 39 // Licht vor Ende
  61.  
  62. DFPlayerMini_Fast myMP3;
  63. enum zustand_t: byte {Z_nix,Z_kartentest,Z_kartentestDo,Z_reset,Z_checkTuerGabel,Z_checkTerminals,Z_checkTaschenlampen,Z_warteAufMitspieler,Z_warteAufMitspielerDo,Z_zwischenzeitTelefon,
  64. Z_telefonKlingelt,Z_begruessung,Z_wartenWaehlscheibe,Z_wartenAufWahlimpulse,Z_wahlImpuls1,Z_wahlImpuls0,Z_nochNichtsGewaehlt,Z_Spielbeginn,
  65. 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};
  66. zustand_t zustand = Z_reset;
  67. Zeit zeit1(A0,1,30); // AD-Pin,min,max in Sekunden warten auf Spieler
  68. Zeit zeit2(A1,5,30); // Dauer Telefonläuten
  69. Zeit zeit3(A2,5,30); // Dauer auf Waehlen warten
  70. Zeit zeit4(A3,5,30); // Zeit bis Gegenstand aufgelegt werden soll
  71. Zeit zeit5(A4,30,600); // Zeit von Taschenlampenentnahme (Sensor1) bis Mausklick
  72. Zeit zeit6(A5,5,180); // Zeit von Mausklick bis Innenlicht etwas hochdimmen
  73. Zeit zeit7(A6,5,60); // Zeit Leuchtdauer LED-Streifen am Ausgang
  74. Taster knopf(T_KNOPF,0);
  75. Blinker taschenlampenfach(TASCHENLAMPENFACH_R,1); // Neg Log
  76.  
  77. void setup() {
  78. pinMode(IR1,INPUT);
  79. pinMode(IR2,INPUT);
  80. pinMode(IR3,INPUT);
  81. pinMode(GABEL,INPUT);
  82. pinMode(W_SCHEIBE,INPUT);
  83. pinMode(TASCHL_AUSGABE,INPUT);
  84. pinMode(TUER_TASTER,INPUT);
  85. pinMode(EXIT_KNOPF,INPUT);
  86. pinMode(HAUPT_LICHT,OUTPUT);
  87. pinMode(SPOT2,OUTPUT);
  88. pinMode(SPOT3,OUTPUT);
  89. pinMode(INNEN_LICHT,OUTPUT);
  90. pinMode(T_KLINGEL,OUTPUT);
  91. digitalWrite(T_KLINGEL,HIGH);
  92. pinMode(TUER_MAGNET_EINGANG,OUTPUT);
  93. digitalWrite(TUER_MAGNET_EINGANG,HIGH);
  94. pinMode(VERSTAERKER,OUTPUT);
  95. digitalWrite(VERSTAERKER,HIGH);
  96. pinMode(MAUSKLICK,OUTPUT);
  97. digitalWrite(MAUSKLICK,HIGH);
  98. pinMode(TUER_MAGNET_AUSGANG,OUTPUT);
  99. digitalWrite(TUER_MAGNET_AUSGANG,HIGH);
  100. pinMode(LED_STREIFEN,OUTPUT);
  101. digitalWrite(LED_STREIFEN,HIGH);
  102. pinMode(ENDE_LICHT,OUTPUT);
  103. digitalWrite(ENDE_LICHT,HIGH);
  104. Serial.begin(115200);
  105. Serial1.begin(9600);
  106. delay(15000); //Versuch gegen IR-Auslösung am Beginn
  107. Wire.begin();
  108. if(!myMP3.begin(Serial1,false)){ // true für debug
  109. Serial.println(F("Unable to begin:"));
  110. }
  111. pinMode(MP3_BUSY,INPUT);
  112. delay(1200); // Terminals Zeit zum Starten geben
  113. Serial.print(F("Discovery-Game Master V "));
  114. Serial.print(vers);
  115. Serial.println(F(" (c) Oliver Mezger"));
  116. Serial.print(F("Anzahl gespeicherter Karten: "));
  117. Serial.println(sizeof(karten)/sizeof(karten[0])); // Anzahl Karten anzeigen
  118. Serial.println(F("Setting volume to 20"));
  119. myMP3.volume(20); // 0..30
  120. byte test = 1;
  121. for(byte i=0;i<ANZAHL_TERMINALS;i++){
  122. if(pruefeHallo(i)){
  123. setzeRGB(i,0,0b000,0);
  124. setzeRGB(i,1,0b000,0);
  125. Serial.println(F(" Station OK"));
  126. delay(300); // nur fuer Effekt
  127. }
  128. else{
  129. Serial.println(F(" Falsche Antwort"));
  130. test = 0;
  131. }
  132. }
  133. while(!test){ // Problem mit einem Terminal
  134. sprecheBisEnde(19); // Ausgabe Meldung Terminal antwortet nicht
  135. zustand=Z_nix;
  136. }
  137. zustand= Z_reset;
  138. }
  139. #define maxTerminals 4 // 4 Terminals sind angeschlossen
  140.  
  141. void loop() {
  142. static unsigned long lZeitmarke; // fuer Timings
  143. static unsigned char lByteVar; // Universal
  144. static unsigned char lByteVar2; // Universal
  145. static unsigned char hauptLicht=0; // HauptLicht
  146. static unsigned char innenLicht=0; // HauptLicht
  147. static unsigned char lWiederholungen; // Zaehler fuer Wiederholungen
  148. static unsigned char lAnzahlMitspieler;
  149. static unsigned char lAktSpieler;
  150. serialKommandos(); // über seriellen Monitor steuern
  151. switch (zustand){
  152. case Z_nix: break; // Zum Testen
  153. case Z_kartentest:
  154. Serial.println(F("Kartentest, RFID-Tag auf gruenes Terminal auflegen"));
  155. alleAusRGB();
  156. setzeRGB(0,0,Gn,0);
  157. zustand=Z_kartentestDo;
  158. break;
  159. case Z_kartentestDo:
  160. lByteVar = leseRfid(0,0); // Terminal 1 oben lesen
  161. if(lByteVar>0 && lByteVar<255){ // wenn es eine bekannte Karte gibt
  162. Serial.print(F("RFID-Tag No: "));
  163. Serial.print(lByteVar);
  164. lByteVar2=pgm_read_byte(&mp3stueck[lByteVar-1]); // suche das passende Stueck
  165. if(lByteVar2){ // falls es eins gibt
  166. Serial.print(F(" Spiele Stueck: "));
  167. Serial.println(lByteVar2);
  168. myMP3.play(lByteVar2); // spiele es
  169. }
  170. else{
  171. Serial.print(F("Noch kein Stueck hinterlegt: "));
  172. Serial.println(lByteVar);
  173. }
  174. }
  175. break;
  176. case Z_reset: // Akt=10 Reset mit Aufräumen vor jedem Neustart:
  177. Serial.println(F("Reset"));
  178. hauptLicht=255;
  179. analogWrite(HAUPT_LICHT,hauptLicht); // HaupthauptLicht an
  180. analogWrite(SPOT2,0);
  181. analogWrite(SPOT3,0);
  182. digitalWrite(TASCHENLAMPENFACH_R,HIGH);
  183. innenLicht=0;
  184. analogWrite(INNEN_LICHT,innenLicht);
  185. digitalWrite(VERSTAERKER,HIGH); // Verstaerker nach aussen
  186. digitalWrite(ENDE_LICHT,HIGH); // Endelicht aus
  187. alleAusRGB();
  188. zustand=Z_checkTuerGabel;
  189. break;
  190. case Z_checkTuerGabel: // beide Türen und Telefongaben checken
  191. if(digitalRead(TUER_TASTER)){
  192. spreche(58); // Sind beide Türen Zu?
  193. break;
  194. }
  195. if(!digitalRead(GABEL)){ //- falls Hörer nicht aufgelegt: MP3-Datei 56: ("Bitte Telefonhörer auflegen")
  196. spreche(56);
  197. break;
  198. }
  199. zustand=Z_checkTerminals;
  200. break;
  201. case Z_checkTerminals: //- falls per RFID etwas erkennt wird MP3-Datei 57: ("Bitte Gegenstände oder Karten aufräumen" und rot blinken)
  202. lByteVar=0;
  203. for(byte i=0;i<maxTerminals;i++){ // für alle Stationen
  204. resetRfid(i);
  205. delay(RFID_DELAY);
  206. for(byte k=0;k<=1;k++){ // für oben und unten
  207. if(leseRfid(i,k)){ // RFID-Tag erkannt
  208. setzeRGB(i,k,Rt,20); // 200 ms Blinken
  209. lByteVar=1; // merken für Spruch ausgeben
  210. }
  211. else setzeRGB(i,k,Off,0); // kein Tag kein hauptLicht
  212. }
  213. }
  214. if(lByteVar){
  215. sprecheBisEnde(57); // Bitte Gegenstände oder Karten aufräumen
  216. break;
  217. }
  218. zustand=Z_checkTaschenlampen;
  219. taschenlampenfach.setBlinker(500);
  220. break;
  221. case Z_checkTaschenlampen: // liegen die Taschenlampen bereit?
  222. taschenlampenfach.service();
  223. if(digitalRead(TASCHL_AUSGABE)!=TASCHENLAMPEN_HAENGEN){
  224. spreche(60); // Taschenlampenfach am Eingang bestueckt?
  225. }
  226. else{
  227. zustand=Z_warteAufMitspieler;
  228. hauptLicht=255; //anstatt seither 100
  229. analogWrite(HAUPT_LICHT,hauptLicht);
  230. taschenlampenfach.setBlinker(0);
  231. }
  232. break;
  233. case Z_warteAufMitspieler:
  234. for(byte i=0;i<maxTerminals;i++){ // alle RFID-Terminals leuchten oben und unten blau
  235. for(byte k=0;k<=1;k++){
  236. setzeRGB(i,k,Bl,0);
  237. }
  238. }
  239. lWiederholungen = 0;
  240. zustand=Z_warteAufMitspielerDo;
  241. break;
  242. case Z_warteAufMitspielerDo: // Akt20 https://funduino.de/nr-8-bewegungsmelder
  243. if (digitalRead(IR1)||digitalRead(IR2)||digitalRead(IR3)){ //- wenn Bewegungsmelder IR1 oder IR2 oder IR3 anspricht => Akt=30
  244. zustand=Z_zwischenzeitTelefon;
  245. Serial.println(F("Zwischenzeit Telefon"));
  246. alleAusRGB();
  247. Serial.print(F("1."));
  248. zeit1.start(); //- Zeit1 bis Telefonlaeuten beginnt (eingestellt an Poti1, ca. 20 Sekunden)
  249. lZeitmarke=millis(); // Zeit fuer LaufhauptLicht
  250. while(hauptLicht<255){ // vormals 100, Hauptlicht soll maximal bleiben
  251. analogWrite(HAUPT_LICHT,hauptLicht++);
  252. delay(20);
  253. } //
  254. lByteVar=0;
  255. }
  256. if(!digitalRead(GABEL)){ //- wenn Hörer (obwohl nicht läutend) abgenommen wird
  257. zustand=Z_begruessung; // für den Fall, dass kurz nach Läuten-Ende abgenommen wurde
  258. Serial.println(F("Bergruessung"));
  259. }
  260. break;
  261. case Z_zwischenzeitTelefon: // Akt=30: Zwischenzeit bis Telefon läutet
  262. if(millis()-lZeitmarke >300){ // LaufhauptLicht, Stationen blinken
  263. switch(lByteVar){
  264. case 0: setzeRGB(3,0,Off,0); setzeRGB(0,0,Li,3); lByteVar++;break;
  265. case 1: setzeRGB(0,0,Off,0); setzeRGB(1,0,Li,3); lByteVar++;break;
  266. case 2: setzeRGB(1,0,Off,0); setzeRGB(2,0,Li,3); lByteVar++;break;
  267. case 3: setzeRGB(2,0,Off,0); setzeRGB(3,0,Li,3); lByteVar=0;break;
  268. }
  269. lZeitmarke=millis();
  270. }
  271. 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)
  272. zustand = Z_reset;
  273. Serial.println(F("Reset fuer Bedienstete ausgelaoest"));
  274. break;
  275. }
  276. 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)
  277. zustand=Z_begruessung;
  278. break;
  279. }
  280. if (zeit1.vorbei()){ //- nach Zeit1 => Akt=40
  281. alleAusRGB();
  282. lZeitmarke=millis(); // Zeit zwischen Laeuten
  283. zustand=Z_telefonKlingelt;
  284. Serial.print(F("2."));
  285. zeit2.start(); // Dauer des Telefonlaeutens
  286. lByteVar=0;
  287. }
  288. break;
  289. case Z_telefonKlingelt: // Telefon soll abgenommen werden
  290. if(millis()-lZeitmarke >400){
  291. switch(lByteVar){
  292. case 0: digitalWrite(T_KLINGEL,LOW); lByteVar++; break; // Klingel an
  293. case 1: digitalWrite(T_KLINGEL,HIGH); lByteVar++; break; // Klingel aus
  294. case 2: digitalWrite(T_KLINGEL,LOW); lByteVar++; break; // Klingel an
  295. case 3: digitalWrite(T_KLINGEL,HIGH); lByteVar++; break; // Klingel aus
  296. case 4: lByteVar++; break; // Warten
  297. case 5: lByteVar++; break; // Warten
  298. case 6: lByteVar=0; break; // Warten
  299. }
  300. lZeitmarke=millis();
  301. }
  302. if(!digitalRead(GABEL)||knopf.aktiv(5)){ //- wenn Hörer abgenommen oder Knopf1 kurz (0,5 Sek) gedrückt => Akt=50
  303. zustand=Z_begruessung;
  304. digitalWrite(T_KLINGEL,HIGH); // Klingel aus
  305. break;
  306. }
  307. if(zeit2.vorbei()){ // Dauer des Telefonlaeutens vorbei?
  308. zustand=Z_warteAufMitspieler; //- wenn Zeit2 überschritten Akt=20 (Reset ohne Totzeit)
  309. Serial.println(F("Kein Tel. abgenommen: Warte wieder auf Mitspieler"));
  310. digitalWrite(T_KLINGEL,HIGH); // Klingel aus
  311. }
  312. break;
  313. case Z_begruessung: // Begrüßung per Telefonhörer und per Lautsprecher
  314. spreche(1); //- MP3 Datei1: ("Begrüßung...bitte Spielerzahl 2, 3 oder 4 wählen"), danach Akt=60
  315. zustand=Z_wartenWaehlscheibe;
  316. break;
  317. case Z_wartenWaehlscheibe: // warten auf Teilnehmerzahleingabe per Wählscheibe
  318. Serial.println(F("Warten auf Waehlscheibe"));
  319. Serial.print(F("3."));
  320. alleAusRGB();
  321. zeit3.start(); // Zeit bis warten auf waehlen vorbei
  322. lAnzahlMitspieler=0; // Waehlimpulse zaehlen;
  323. zustand=Z_wartenAufWahlimpulse;
  324. break;
  325. case Z_wartenAufWahlimpulse:
  326. if(digitalRead(W_SCHEIBE)){
  327. zustand=Z_wahlImpuls1;
  328. }
  329. if(zeit3.vorbei()){ //- bis maximal Zeit3 (eingestellt an Poti3, ca. 10 Sekunden) dann Akt=70
  330. zustand=Z_nochNichtsGewaehlt;
  331. Serial.println(F("Noch nichts gewaehlt"));
  332. }
  333. break;
  334. case Z_wahlImpuls1:
  335. delay(20); // Entprellen
  336. if(!digitalRead(W_SCHEIBE)){
  337. zustand=Z_wahlImpuls0;
  338. lAnzahlMitspieler++; // ein Impuls
  339. lZeitmarke=millis(); // Timeout setzen
  340. }
  341. break;
  342. case Z_wahlImpuls0:
  343. delay(20); // Entprellen
  344. if(digitalRead(W_SCHEIBE)){ // Noch ein Impuls
  345. zustand=Z_wahlImpuls1;
  346. break;
  347. }
  348. if(millis()-lZeitmarke >200){ // Timeout, kein weiterer Impuls
  349. Serial.print(F("Gewaehlt: "));
  350. Serial.println(lAnzahlMitspieler);
  351. if(lAnzahlMitspieler < 2){ //- 0 oder 1 gewählt => Akt=80 (zu wenig Mitspieler)
  352. sprecheBisEnde(41); //- MP3 Datei41: ("es müssen mindestens zwei Spieler sein") => Akt=70
  353. zustand=Z_nochNichtsGewaehlt;
  354. }
  355. else if(lAnzahlMitspieler >4){ //- mehr als 4 gewählt => Akt=90 (zu viele Mitspieler)
  356. spreche(42); //- MP3 Datei42: ("es dürfen maximal vier Mitspieler sein!") danach: Akt=60
  357. zustand=Z_wartenWaehlscheibe;
  358. }
  359. else{
  360. zustand = Z_Spielbeginn; //- 2 oder 3 oder 4 gewählt = Spielerzahl
  361. Serial.print(F("Spielbeginn "));
  362. }
  363. }
  364. break;
  365. case Z_nochNichtsGewaehlt: // noch nichts gewählt
  366. if(lWiederholungen > 2){
  367. zustand = Z_warteAufMitspieler; // falls dieser Akt schon mehrfach durchlaufen wurde
  368. break;
  369. }
  370. spreche(2); //- MP3 Datei2: ("...wieviel seid ihr? ...evtl. Mitspieler suchen...") => Akt=60
  371. lWiederholungen++;
  372. zustand= Z_wartenWaehlscheibe;
  373. break;
  374. case Z_Spielbeginn: // Spielbeginn mit der gewählten Mitspielerzahl
  375. switch(lAnzahlMitspieler){ //- MP3 Datei 43/44/ oder 45: ("Willkommen Ihr zwei/ drei/ vier, jetzt kann's losgehen")
  376. case 2: spreche(43); break;
  377. case 3: spreche(44); break;
  378. case 4: spreche(45); break;
  379. default: Serial.print(F("Fehler bei Anzahl Mitspieler: ")); Serial.println(lAnzahlMitspieler); break; // Fehler bei Anzahl Mitspieler
  380. }
  381. for (lByteVar=0;lByteVar<lAnzahlMitspieler;lByteVar++){ //- und je nach Spielerzahl blinken zwei, drei oder vier Terminals oben gelb
  382. setzeRGB(lByteVar,0,Ge,30);
  383. }
  384. 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?)
  385. setzeRGB(lByteVar,0,Rt,0);
  386. setzeRGB(lByteVar,1,Rt,0);
  387. }
  388. for(lByteVar=0;lByteVar<255;lByteVar++){ //- SPOT2 (Regalbeleuchtung) wird hochgedimmt
  389. analogWrite(SPOT2,lByteVar);
  390. delay(15);
  391. }
  392. Serial.println(F("Spieler1 Los!"));
  393. lAktSpieler=0; // Spieler1
  394. alleAusRGB();
  395. for(lByteVar=0;lByteVar<ANZAHL_TERMINALS;lByteVar++){ // damit bereits liegende Dinge erkannt werden koennen
  396. resetRfid(lByteVar);
  397. }
  398. zustand=Z_SpielerLos; //- danach Akt=110
  399. break;
  400. case Z_SpielerLos: // Spieler 1 soll loslegen
  401. setzeRGB(lAktSpieler,0,Ge,50);
  402. spreche(lAktSpieler+4); //- MP3 Datei4: "Spieler 1 soll Gegenstand auf gelb blinkenden Terminal legen"
  403. lWiederholungen=0;
  404. zustand=Z_wartenGegenstand;
  405. break;
  406. case Z_wartenGegenstand: // Warten auf Gegenstand von lAktSpieler
  407. lByteVar = leseRfid(lAktSpieler,0); // oben Lesen
  408. delay(150); // gib dem RFID-Leser Zeit
  409. if(lByteVar>0 && lByteVar<255){ // Gegenstand per RFID erkannt
  410. lByteVar=pgm_read_byte(&mp3stueck[lByteVar-1]); // suche passendes Stück
  411. if(lByteVar>=21 && lByteVar<=40){ // wenn es eine Karte ist
  412. Serial.println(F("Karte ausgeschlossen!"));
  413. break; // ignorieren
  414. }
  415. if(lByteVar){ // Stueck vorhanden?
  416. setzeRGB(lAktSpieler,0,Gn,0); // statt Gelb-Blinken hier nun Gruen
  417. sprecheBisEnde(lByteVar);
  418. lAktSpieler++;
  419. if(lAktSpieler>=lAnzahlMitspieler){ // alle durch
  420. zustand=Z_Spielteil2;
  421. }
  422. else zustand=Z_SpielerLos;
  423. break;
  424. }
  425. }
  426. switch (lWiederholungen){
  427. case 0: // warten bis erster Text zuende, dann Zeitstart
  428. if(digitalRead(MP3_BUSY)){ // wenn Player nicht mehr spielt
  429. Serial.print(F("4."));
  430. zeit4.start(); // Zeit zum Gegenstand auflegen
  431. lWiederholungen++;
  432. }
  433. break;
  434. case 3:
  435. if(zeit4.vorbei()){
  436. spreche(47); // "Ohne Gegenstand muss das Spiel leider enden - 5 4 3 2 1 aus" zuende
  437. lWiederholungen++;
  438. }
  439. break;
  440. case 4:
  441. if(digitalRead(MP3_BUSY)){ // immer noch kein Gegenstand
  442. zustand=Z_reset;
  443. }
  444. break;
  445. default:
  446. if(zeit4.vorbei()) { // wenn nach Zeit4 (einstellbar an Poti4) nichts aufgelegt wurde
  447. spreche(46); // "Bitte Gegenstand auf gelb blinkenden Terminal legen"
  448. lWiederholungen++;
  449. Serial.print(F("4."));
  450. zeit4.start();
  451. }
  452. break;
  453. }
  454. break;
  455. case Z_Spielteil2:
  456. lAktSpieler=0;
  457. Serial.println(F("Spielteil2"));
  458. for(lByteVar=0;lByteVar<255;lByteVar++){
  459. analogWrite(SPOT2,255-lByteVar); //- Spot2 (Regalbeleuchtung) dimmt herunter
  460. analogWrite(SPOT3,lByteVar); //- Spot3 (Beleuchtung der Kompetenzkarten) dimmt hoch
  461. delay(15);
  462. }
  463. zustand=Z_KartenWahl;
  464. break;
  465. case Z_KartenWahl:
  466. setzeRGB(lAktSpieler,1,Ge,50); // Von den genutzten Terminals beginnt nun der erste unten gelb zu blinken
  467. spreche(lAktSpieler+8); // "... Spieler n - jetzt bitte eine Kompetenzkarte wählen und in den gelb blinkenden Schlitz stecken"
  468. lWiederholungen=0;
  469. zustand=Z_wartenKarte;
  470. break;
  471. case Z_wartenKarte:
  472. lByteVar = leseRfid(lAktSpieler,1); // unten Lesen
  473. delay(150); // gib dem RFID-Leser Zeit
  474. if(lByteVar>0 && lByteVar<255){ // Gegenstand per RFID erkannt
  475. lByteVar=pgm_read_byte(&mp3stueck[lByteVar-1]); // suche passendes Stück
  476. if(lByteVar>=61){ // wenn es ein Gegenstand ist
  477. Serial.println(F("Gegenstand ausgeschlossen!"));
  478. break; // ignorieren
  479. }
  480. if(lByteVar){ // Stueck vorhanden?
  481. setzeRGB(lAktSpieler,1,Gn,0); // statt Gelb-Blinken hier nun Dauergelb besser Gruen?
  482. sprecheBisEnde(lByteVar);
  483. lAktSpieler++;
  484. if(lAktSpieler>=lAnzahlMitspieler){ // alle durch
  485. zustand=Z_SesamOffen;
  486. }
  487. else zustand=Z_KartenWahl;
  488. break;
  489. }
  490. }
  491. switch (lWiederholungen){
  492. case 0: // warten bis erster Text zuende, dann Zeitstart
  493. if(digitalRead(MP3_BUSY)){ // wenn Player nicht mehr spielt
  494. Serial.print(F("4."));
  495. zeit4.start(); // Zeit zum Gegenstand auflegen
  496. lWiederholungen++;
  497. }
  498. break;
  499. case 3:
  500. if(zeit4.vorbei()){
  501. spreche(49); // "Ohne Karte muss das Spiel leider enden - 5 4 3 2 1 aus" zuende
  502. lWiederholungen++;
  503. }
  504. break;
  505. case 4:
  506. if(digitalRead(MP3_BUSY)){ // immer noch kein Gegenstand
  507. zustand=Z_reset;
  508. }
  509. break;
  510. default:
  511. if(zeit4.vorbei()) { //- wenn nach Zeit4 (einstellbar an Poti4) nichts aufgelegt wurde
  512. spreche(48); // "Bitte Karte auf gelb blinkenden Terminal legen")
  513. lWiederholungen++;
  514. Serial.print(F("4."));
  515. zeit4.start();
  516. }
  517. break;
  518. }
  519. break;
  520. case Z_SesamOffen:
  521. Serial.println(F("Sesam offen"));
  522. spreche(13); // "Überleitung/ Türknarzen und Begrüßung/ Taschenlampen entnehmen..."
  523. for(lByteVar=0;lByteVar<255;lByteVar++){
  524. analogWrite(HAUPT_LICHT,255-lByteVar); // Hauptlicht dimmt herunter
  525. analogWrite(SPOT3,255-lByteVar); // Kartenbeleuchtung dimmt herunter
  526. delay(20);
  527. }
  528. taschenlampenfach.setBlinker(300); // Spot 4 Blinkfunktion auf Taschenlampenfach an
  529. digitalWrite(TUER_MAGNET_EINGANG,LOW);// Türe springt auf per EMagnet1 per Relais 2
  530. delay(1000);
  531. digitalWrite(TUER_MAGNET_EINGANG,HIGH);
  532. digitalWrite(VERSTAERKER,LOW); // Relais 3 schaltet Verstärker von außen nach Verstärker innen
  533. while(!digitalRead(MP3_BUSY)){ // Ueberleitung zu Ende sprechen lassen
  534. taschenlampenfach.service(); // dabei blinken
  535. }
  536. zustand= Z_TaschenlampenNehmen;
  537. break;
  538. case Z_TaschenlampenNehmen:
  539. taschenlampenfach.service();
  540. if(digitalRead(TASCHL_AUSGABE)!=TASCHENLAMPEN_HAENGEN){ //- wenn Taschenlampen entnommen (Sensor1): Blinkfunktion Taschenlampenfach aus
  541. taschenlampenfach.setBlinker(0);
  542. zustand=Z_SpielbeginnInnenraum;
  543. Serial.println(F("Z_SpielbeginnInnenraum"));
  544. Serial.print(F("5."));
  545. zeit5.start(); // Zeit von Taschenlampenentnahme bis Mausklick
  546. }
  547. else{
  548. spreche(50); //- wenn noch Taschenlampen da: MP3-Datei 50 („Bitte ALLE Taschenlampen mitnehmen)
  549. }
  550. break;
  551. case Z_SpielbeginnInnenraum: // Spielbeginn Innenraum per Klynt-Technik
  552. if(zeit5.vorbei()){ // nach Zeit5 Mausklick per Relais4 zum Starten der Klynt, evtl. Datei
  553. digitalWrite(MAUSKLICK,LOW);
  554. delay(500);
  555. digitalWrite(MAUSKLICK,HIGH);
  556. Serial.print(F("6."));
  557. zeit6.start(); // Zeit von Mausklick bis Innenlicht etwas hochdimmen
  558. zustand=Z_nachMausklick;
  559. Serial.println(F("Z_nachMausklick"));
  560. }
  561. break;
  562. case Z_nachMausklick:
  563. if(zeit6.vorbei()){
  564. spreche(15); //- evtl. MP3 Datei 15 geeheimnisvoll auf Zauberspiegel aufmerksam machen
  565. zustand=Z_vorSpielende;
  566. lZeitmarke=millis(); // Timeout setzen
  567. Serial.println(F("Z_vorSpielende"));
  568. }
  569. break;
  570. case Z_vorSpielende: // Vor Spielende: warten auf EXIT-Knopf
  571. if(millis()-lZeitmarke >1000){ // Nach einer Sekunde
  572. lZeitmarke=millis(); // Timeout setzen
  573. if(innenLicht<=24){ // langsam Innenlicht hochdimmen
  574. analogWrite(INNEN_LICHT,innenLicht++);
  575. }
  576. }
  577. break;
  578. case Z_AusgangAuf:
  579. Serial.println(F("Z_AusgangAuf"));
  580. spreche(16); // evtl. MP3-Datei16 oder Stille
  581. digitalWrite(TUER_MAGNET_AUSGANG,LOW); // und E-Magnet2 per Relais5 öffnet die Ausgangstüre TUER_MAGNET_AUSGANG
  582. delay(1000);
  583. digitalWrite(TUER_MAGNET_AUSGANG,HIGH);
  584. digitalWrite(LED_STREIFEN,LOW); // LED-Streifen an
  585. Serial.print(F("7."));
  586. zeit7.start(); // Zeit solange LED-Streifen leuchetet
  587. while(innenLicht < 255){ // nach Zeit7 wird Hauptlicht maximal hochgedimmt
  588. analogWrite(INNEN_LICHT,innenLicht++);
  589. delay(20);
  590. }
  591. zustand = Z_Spielende;
  592. Serial.println(F("Z_Spielende"));
  593. break;
  594. case Z_Spielende:
  595. if(zeit7.vorbei()){
  596. digitalWrite(LED_STREIFEN,HIGH); // LED-Streifen aus
  597. lZeitmarke=millis(); // Timeout setzen
  598. digitalWrite(ENDE_LICHT,LOW); // Ende Licht an
  599. zustand=Z_EndeEnde;
  600. Serial.println(F("Z_EndeEnde;"));
  601. spreche(17); // mp3-Datei 17 Abschied oder Stille
  602. }
  603. break;
  604. case Z_EndeEnde:
  605. if(millis()-lZeitmarke >150000UL){ //statt 15 früher 30 = 5 Minuten
  606. digitalWrite(ENDE_LICHT,HIGH); // Ende Licht an
  607. zustand=Z_reset;
  608. }
  609. break;
  610. case Z_test:
  611. zeit6.start();
  612. Serial.println(F("Zeit6 gestartet"));
  613. zustand=Z_testDo;
  614. break;
  615. case Z_testDo:
  616. if(zeit6.vorbei()){
  617. Serial.println(F("Zeit6 vorbei"));
  618. zustand=Z_test;
  619. }
  620. break;
  621. }
  622. if(zustand>=Z_SesamOffen && zustand<=Z_vorSpielende && !digitalRead(EXIT_KNOPF)){ // wenn Exit-Knopf gedrueckt
  623. zustand=Z_AusgangAuf;
  624. }
  625. if(zustand>=Z_telefonKlingelt){//Akt>=40 Neustartmöglichkeit
  626. if(knopf.aktiv(20)){ // 2 Sek gedrueckt
  627. Serial.println(F("Neustart durch Knopf"));
  628. zustand=Z_reset; //- wenn Knopf1 lange gedrückt wird Akt=10 (Reset)
  629. }
  630. }
  631. }
  632.  
  633. void sprecheBisEnde(byte n){ // Nachricht bis zum Ende ausgeben (blockierend)
  634. if(n>MAX_MP3_STUECK){
  635. Serial.print(F("Maximale MP3 Stuecknummer ueberschritten: "));
  636. Serial.println(n);
  637. return;
  638. }
  639. myMP3.play(n);
  640. Serial.print(F("Nachricht: "));
  641. Serial.println(n);
  642. delay(1000);
  643. while(!digitalRead(MP3_BUSY)); // warten bis Ende
  644. }
  645.  
  646. void spreche(byte n){ // Nachricht ausgeben (nicht blockierend)
  647. static byte letztes = 0;
  648. if(n>MAX_MP3_STUECK){
  649. Serial.print(F("Maximale MP3 Stuecknummer ueberschritten: "));
  650. Serial.println(n);
  651. }
  652. if(digitalRead(MP3_BUSY)||n!=letztes){ // wenn nichts gespielt oder was neues
  653. myMP3.play(n); // spiele
  654. letztes=n;
  655. Serial.print(F("Nachricht: "));
  656. Serial.println(n);
  657. delay(1000);
  658. }
  659. }
  660.  
  661. void resetRfid(byte station){ // RFID-Leser zurücksetzen um liegengebliebene Karten zu entdecken
  662. Wire.beginTransmission(station+8); // sendet zu Node empfaenger
  663. Wire.write(RFID_reset); // sendet daten
  664. Wire.endTransmission(); // Übermittlungsstop
  665. }
  666.  
  667. void setzeRGB(byte station, byte p, byte muster, byte dauer){ // p=0: oben dauer*10ms
  668. if(p) muster |= 128; // Bit 7 setzen für unten
  669. Wire.beginTransmission(station+8); // sendet zu Node empfaenger
  670. Wire.write(muster); // sendet daten
  671. Wire.write(dauer); // sendet daten
  672. Wire.endTransmission(); // Übermittlungsstop
  673. }
  674.  
  675. void alleAusRGB(){
  676. for(byte i=0;i<4;i++){
  677. setzeRGB(i,0,Off,0);
  678. setzeRGB(i,1,Off,0);
  679. }
  680. }
  681. byte pruefeHallo(byte station){ // Station 0..3 Adresse 8..11
  682. Wire.beginTransmission(station+8); // sendet zu Node empfaenger
  683. Wire.write(C_Hallo); // Hallo senden
  684. Wire.endTransmission(); // Übermittlungsstop
  685. Serial.print(F("Hallo Anfrage gesendet an: "));
  686. Serial.print(station);
  687. delay(RFID_DELAY); // Zeit zum antworten lassen
  688. Wire.requestFrom(station+8, 2); // sollte mit "OK" antworten
  689. if(Wire.read()!='O') return 0;
  690. if(Wire.read()!='K') return 0;
  691. return 1; // OK geantwortet
  692. }
  693.  
  694. byte leseRfid(byte station, byte platz){ // 0..3 platz=0 ist oben
  695. byte l,i,k;
  696. byte id[7]; // Puffer für Karten id
  697. byte treffer=0;
  698. Wire.beginTransmission(station+8); // sendet zu Node empfaenger
  699. if(!platz) Wire.write(RFID_oben); // oben lesen
  700. else Wire.write(RFID_unten); // unten lesen
  701. Wire.endTransmission(); // Übermittlungsstop
  702. delay(RFID_DELAY); // Zeit zum antworten lassen
  703. Wire.requestFrom(station+8, 8); // Fragt Daten von Node empfaenger ab
  704. l = Wire.read(); // erstes Byte gibt Anzahl der Bytes an
  705. if (l==0){ // Keine neues Tag
  706. return 0;
  707. }
  708. else if (l>7){
  709. Serial.print(F("leseRfid: Falsche Byteanzahl: "));
  710. Serial.println(l);
  711. return 0;
  712. }else{
  713. Serial.print(F("Karte mit ID: "));
  714. for (i=0;i<l;i++){ // lies die ID ein
  715. id[i]=Wire.read();
  716. Serial.print(id[i],HEX);
  717. }
  718. Serial.println("");
  719. k=sizeof(karten)/sizeof(karten[0]); // Karte bestimmen
  720. for(k=0;k<sizeof(karten)/sizeof(karten[0]);k++){ // für alle gespeicherten Karten
  721. for(i=0;i<l;i++){ // für alle Bytes
  722. if(id[i]!=pgm_read_byte(&karten[k][i])) break; // hat nicht gepasst
  723. }
  724. if(i==l) {
  725. treffer = 1;
  726. break;
  727. }
  728. }
  729. if (treffer){ // Karte erkannt
  730. return k+1;
  731. }
  732. else{
  733. Serial.print(F("Neue Karte: {"));
  734. for (i=0;i<l-1;i++){ // zwecks Speicherung ausgeben
  735. Serial.print(F("0x"));
  736. Serial.print(id[i],HEX);
  737. Serial.print(F(","));
  738. }
  739. Serial.print(F("0x"));
  740. Serial.print(id[i],HEX);
  741. Serial.println(F("}"));
  742. return 255; // unbekannte Karte
  743. }
  744. }
  745. }
  746. void serialKommandos(){ // über seriellen Monitor steuern
  747. byte key_command;
  748. if (Serial.available()){
  749. key_command=Serial.read();
  750. Serial.print(F("Komando "));
  751. Serial.write(key_command);
  752. Serial.println("");
  753. switch (key_command){
  754. case '?':
  755. Serial.println(F("Liste aller Befehle:"));
  756. Serial.println(F("r Reset"));
  757. Serial.println(F("t Zeittest mit langer Zeit 6"));
  758. Serial.println(F("z alle eingestellten Zeiten ausgeben"));
  759. Serial.println(F("k Kartentest"));
  760. Serial.println(F("1 Z_warteAufMitspieler"));
  761. Serial.println(F("2 Z_begruessung"));
  762. Serial.println(F("3 Z_Spielteil2"));
  763. Serial.println(F("4 Z_SesamOffen"));
  764. break;
  765. case 'r':
  766. zustand=Z_reset;
  767. break;
  768. case 'k':
  769. if(zustand!=Z_kartentestDo){
  770. zustand = Z_kartentest;
  771. }
  772. else{
  773. zustand=Z_reset;
  774. }
  775. break;
  776. case 't':
  777. zustand=Z_test;
  778. break;
  779. case 'z':
  780. Serial.print(F("1."));
  781. zeit1.info();
  782. Serial.print(F("2."));
  783. zeit2.info();
  784. Serial.print(F("3."));
  785. zeit3.info();
  786. Serial.print(F("4."));
  787. zeit4.info();
  788. Serial.print(F("5."));
  789. zeit5.info();
  790. Serial.print(F("6."));
  791. zeit6.info();
  792. Serial.print(F("7."));
  793. zeit7.info();
  794. zustand=Z_nix;
  795. break;
  796. case '1': zustand=Z_warteAufMitspieler; break;
  797. case '2': zustand=Z_begruessung; break;
  798. case '3': zustand=Z_Spielteil2; break;
  799. case '4': zustand=Z_SesamOffen; break;
  800. }
  801. }
  802. }
  803.