Discovery-Game im Stadtmuseum Tübingen
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


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:
- Bessere Nanos verwenden (realisiert)
- Vin auf 9V reduzieren (realisiert)
- RFID-Leser abschalten (per Software oder Reset-Pin) (nicht ausprobiert)
Uno-Info: SCL=A5=D19; SDA=A4=D18
Master Steuerrechner

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

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 |
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 |
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
#include <SPI.h> #include <MFRC522.h> #include <Wire.h> #include "GameLib.h" #define vers "1.0" // SPI-Bus: RFID PIN Numbers : RESET + SDAs #define RST_PIN 2 #define SS_O_PIN 4 #define SS_U_PIN 3 // i2c Bus-Adresse, beim Setup ermitteln #define I2C_ADR_LOW_PIN A2 // A6 und A7 haben nicht funktioniert! #define I2C_ADR_HIGH_PIN A3 // auch nicht D20, D21 byte i2cAdr; // Busnummer des Slave #define NR_OF_READERS 2 // Anzahl der RFID-Reader byte ssPins[] = {SS_O_PIN, SS_U_PIN}; // Select-Pins der Reader // Create an MFRC522 instance : MFRC522 mfrc522[NR_OF_READERS]; RGBStrip rgb_oben(8,9,10); // RGB-Strip RGBStrip rgb_unten(7,5,6); // RGB-Strip rfid_t rfidErg=Rfid_nix; void setup() { Serial.begin(115200); // Initialize serial communications with the PC while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) pinMode(I2C_ADR_LOW_PIN,INPUT_PULLUP); // Adresse durch Jumper setzen pinMode(I2C_ADR_HIGH_PIN,INPUT_PULLUP); i2cAdr = 8 + !digitalRead(I2C_ADR_LOW_PIN)+ (!digitalRead(I2C_ADR_HIGH_PIN)<<1); SPI.begin(); // Init SPI bus rgb_oben.setRGB(Ws,0); rgb_unten.setRGB(Ws,0); Serial.print(F("Discovery-Game RFID-Server V")); Serial.print(vers); Serial.print(F(" (c) Oliver Mezger I2C-Adresse: ")); Serial.println(i2cAdr); readerReset(); Wire.begin(i2cAdr); // I2C-Adresse festlegen Wire.onReceive(receiveEvent); // erstelle ein Empfangen-Ereignis Wire.onRequest(requestEvent); // erstelle ein Anfrage-Ereignis } void receiveEvent(int bytes) { // Komando-Empfang byte x; int y; if (bytes > 1){ // Befehl fuer RGB-Blinker x = Wire.read(); // lies Muster für RGB Bit 7 = 0 oben 1 unten y = Wire.read(); // lies Blinkerperiodendauer Serial.print(F("RGB-Befehl: ")); Serial.print(x,BIN); Serial.print(F(" Dauer ")); Serial.println(y); if(!(x&128)){ //Bit 7 = 0: RGBoben rgb_oben.setRGB((rgb_t)x,y*10); } else{ //Bit 7 = 1: RGBunten rgb_unten.setRGB((rgb_t)x,y*10); } } else { x = Wire.read(); // lies die gesendeten Daten aus Serial.print(F("RFID-Leser-Befehl: ")); Serial.println(x); switch (x){ case RFID_reset: // Leser zuruecksetzen um liegengebliebne Karten zu erkennen readerReset(); break; case RFID_oben: // Karte lesen if (mfrc522[0].PICC_IsNewCardPresent() && mfrc522[0].PICC_ReadCardSerial()) { rfidErg=Rfid_karte_o; mfrc522[0].PICC_HaltA(); mfrc522[0].PCD_StopCrypto1(); Serial.println("Karte oben gelesen"); } else rfidErg=Rfid_nix; break; case RFID_unten: if (mfrc522[1].PICC_IsNewCardPresent() && mfrc522[1].PICC_ReadCardSerial()) { rfidErg=Rfid_karte_u; mfrc522[0].PICC_HaltA(); mfrc522[0].PCD_StopCrypto1(); Serial.println("Karte unten gelesen"); } else rfidErg=Rfid_nix; break; case C_Hallo: // Server Hallo fragen rfidErg=A_Ok; // Bei nächster Datenanfrage "OK" senden break; } } } void requestEvent(){ // Antwort auf Datenanfrage switch (rfidErg){ case Rfid_nix: // Keine neue Karte erkannt //nodePayload[0] = NODE_ADDRESS; Wire.write(0); break; case Rfid_karte_o: Wire.write(mfrc522[0].uid.size); Wire.write(mfrc522[0].uid.uidByte, mfrc522[0].uid.size); break; case Rfid_karte_u: Wire.write(mfrc522[0].uid.size); Wire.write(mfrc522[1].uid.uidByte, mfrc522[1].uid.size); break; case A_Ok: Wire.write("OK"); break; } } void loop() { byte key_command; if(Serial.available()) { key_command=Serial.read(); // get command from serial input Serial.print(F("Kommando ")); Serial.write(key_command); Serial.println(""); switch (key_command){ case 'q': rgb_oben.setRGB(Off,0); rgb_unten.setRGB(Off,0); Serial.println(F("Alle LED aus")); break; case 'r': rgb_unten.setRGB(Rt,0); Serial.println(F("Rot")); break; case 'g': rgb_unten.setRGB(Gn,0); Serial.println(F("Grün")); break; case 'b': rgb_unten.setRGB(Bl,0); Serial.println(F("Blau")); break; case 'l': rgb_unten.setRGB(Li,0); break; //case 'b': rgb_unten.setRGB(Gn,200); break; case 'o': // oberen RFID auslesen if (mfrc522[0].PICC_IsNewCardPresent() && mfrc522[0].PICC_ReadCardSerial()) { dump_byte_array(mfrc522[0].uid.uidByte, mfrc522[0].uid.size); mfrc522[0].PICC_HaltA(); mfrc522[0].PCD_StopCrypto1(); } else Serial.println(F("Keine neue Karte")); break; case 'c': mfrc522[0].PCD_Init(); mfrc522[0].PCD_DumpVersionToSerial(); break; case 'i': dump_byte_array(mfrc522[0].uid.uidByte, mfrc522[0].uid.size); break; } } delay(50); rgb_oben.service(); rgb_unten.service(); } void readerReset(){ for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) { mfrc522[reader].PCD_Init(ssPins[reader], RST_PIN); Serial.print(F("Reader ")); Serial.print(reader); Serial.print(F(": ")); mfrc522[reader].PCD_DumpVersionToSerial(); } } void dump_byte_array(byte * buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } }
// GameLib.h #ifndef GameLib_h #define GameLib_h #endif enum rgb_t : byte {Off=0,Bl=0b001,Gn=0b010,Tk=0b011,Rt=0b100,Li=0b101,Ge=0b110,Ws=0b111}; // Welches RGB-Muster enum command_t: byte {RFID_reset=3,RFID_oben,RFID_unten,C_Hallo}; enum rfid_t : byte {Rfid_nix,Rfid_karte_o,Rfid_karte_u,A_Ok}; class RGBStrip{ public: RGBStrip(byte r_pin,byte g_pin,byte b_pin); // Initialisierung void setRGB(rgb_t l,unsigned int d); // Verhalten einstellen void service(); // periodisch aufgerufen private: byte red_pin; byte green_pin; byte blue_pin; unsigned long myTime; // Startzeitpunkt unsigned int myDelay; // Zeitverzoegerung byte leds; // welche LED blinken sollen byte zyklus; // fuer Blinken void setLed(); // Led an void clearLed(); // alle Led aus }; class Blinker{ public: Blinker(byte b_pin,byte r); // Initialisierung Pinnummer, Ruhezustand void setBlinker(unsigned int d); // Blinkzeit in ms 0 = Blinker aus void service(); // periodisch aufgerufen private: byte blink_pin; byte ruhe; unsigned char zyklus; // Zykluszustand unsigned long myTime; // Startzeitpunkt unsigned int myDelay; // Zeitverzoegerung }; class Zeit{ // Ruft die Potistellung an AD-Pin ab und skaliert die Zeit zwischen min und max public: Zeit(byte pin, unsigned int minZ, unsigned int maxZ); // Zeit in Sekunden void start(); // Zeit starten void start(unsigned int n); // n-fache Zeit byte vorbei(); // 1 falls Zeit um ist void info(); // gibt Attribute auf Serial aus private: byte adPin; unsigned int minZeit; unsigned int maxZeit; unsigned long zeitmarke; }; class Taster{ public: Taster(byte pin,byte aktiv); // lowaktiv=0 byte aktiv(byte dauer); // dauer bis aktiv in 10tel Sekunden private: byte tPin; unsigned long zeitmarke; byte tAktiv; // lowaktiv=0 byte merker; // für Flankendetektion };
// GameLib.cpp #include <Arduino.h> #include "GameLib.h" RGBStrip::RGBStrip(byte r_pin,byte g_pin,byte b_pin){ // Initialisierung red_pin=r_pin; green_pin=g_pin; blue_pin=b_pin; pinMode(red_pin,OUTPUT); pinMode(green_pin,OUTPUT); pinMode(blue_pin,OUTPUT); clearLed(); } void RGBStrip::setRGB(rgb_t l,unsigned int d){ // Verhalten einstellen leds = l&0b111; // nicht beteiligte Bits ausmaskieren zyklus=0; if (l==Off){ myDelay=0; } else{ myDelay=d; myTime=millis(); } } void RGBStrip::service(){ // periodisch aufgerufen für Blinken if(myDelay > 0){ // soll blinken if (millis() - myTime > myDelay) { // schauen, ob die Zeit um ist myTime = millis(); switch (zyklus){ case 0: clearLed(); zyklus++; break; case 1: setLed(); zyklus =0; } } } else if (zyklus == 0){ // kein Blinken nur einmal setzen setLed(); zyklus++; } } void RGBStrip::clearLed(){ digitalWrite(red_pin,HIGH); digitalWrite(green_pin,HIGH); digitalWrite(blue_pin,HIGH); } void RGBStrip::setLed(){ if(leds&0b100) digitalWrite(red_pin,LOW); else digitalWrite(red_pin,HIGH); if(leds&0b010) digitalWrite(green_pin,LOW); else digitalWrite(green_pin,HIGH); if(leds&0b001) digitalWrite(blue_pin,LOW); else digitalWrite(blue_pin,HIGH); } Blinker::Blinker(byte b_pin,byte r){ // Initialisierung blink_pin=b_pin; ruhe=r; pinMode(blink_pin,OUTPUT); digitalWrite(blink_pin,ruhe); } void Blinker::setBlinker(unsigned int d){ // Blinkzeit in ms zyklus=0; myDelay=d; myTime=millis(); digitalWrite(blink_pin,ruhe); } void Blinker::service(){ // periodisch aufgerufen if(myDelay > 0){ // soll blinken if (millis() - myTime > myDelay) { // schauen, ob die Zeit um ist myTime = millis(); switch (zyklus){ case 0: digitalWrite(blink_pin,ruhe); zyklus++; break; case 1: digitalWrite(blink_pin,!ruhe); zyklus =0; } } } } Zeit::Zeit(byte pin, unsigned int minZ, unsigned int maxZ){ // Zeit in Sekunden 0.255 adPin = pin; minZeit = minZ; maxZeit = maxZ; } void Zeit::start(){ // Zeit starten start(1); } void Zeit::start(unsigned int n){ // n-fache Zeit unsigned long z = map(analogRead(adPin),0,1023,minZeit,maxZeit); zeitmarke = millis()+z*1000*n; // ab Zeitmarke ist Zeit um Serial.print(F(" Zeit gesetzt (Sekunden): ")); Serial.println(z); } byte Zeit::vorbei(){ // 1 wenn Zeit vorbei ist return (millis()>zeitmarke); } void Zeit::info(){ unsigned long z = map(analogRead(adPin),0,1023,minZeit,maxZeit); Serial.print(F("Min: ")); Serial.print(minZeit); Serial.print(F(" Max: ")); Serial.print(maxZeit); Serial.print(F(" Wert: ")); Serial.println(z); } Taster::Taster(byte pin,byte aktiv){ // lowaktiv=0 tPin=pin; tAktiv=aktiv; if(!aktiv) pinMode(pin,INPUT_PULLUP); // Pullup an else pinMode(pin,INPUT); } byte Taster::aktiv(byte dauer){ // dauer bis aktiv in 10tel Sekunden if(digitalRead(tPin)!=tAktiv){ // Taster nicht aktiv merker=0; return 0; } else{ if(!merker){ // pos. Flanke merker = 1; zeitmarke=millis(); return 0; } else{ // Taster noch aktiv Dauer checken return millis()-zeitmarke > dauer*100; } } }
Master-Steuerung
//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; } } }