🍀 Discovery-Game im Stadtmuseum Tübingen

Bei dem Spiel sollen Gegenstände, die auf Stationen aufgelegt werden zugeordnete Sound-Dateien abspielen lassen.
[🔗 https://www.stadtmuseum-tuebingen.de/ausstellungen/room-of-memories/]

Bild der Installation

Für ein Spiel werden 4 Stationen mit jeweils 2 RFID-Modulen abgefragt. An jedem Modul ist jeweils ein RGB-Streifen montiert, der zum Einstellen einer Karte bzw. Aufstellen eines Gegenstands motivieren soll.
Die Stationen sind über I2C mit einem Steuerrechner verbunden. Der Steuerrechner steuert den Spielablauf und gibt über ein MP3-Player-Modul Ton aus. Ein Wählscheibentelefon und mehrere Sensoren steuern den Spielablauf.

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.

Alte Version des Projekts: [mezdata.de/mez-entwicklung/100_discovery-game/]

4 Stationen mit jeweils 2 RFID-Modulen

RFID-Modul Steckplatine
RFID-Modul Steckplatine
RFID-Modul Schaltplan
RFID-Modul Schaltplan

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

Software der RFID-Stationen

Bei Revision sollte die neuere Arduino RFID-Lib: [https://github.com/OSSLibraries/Arduino_MFRC522v2] verwendet werden?
Info zu RFID-Tags: NXP MIFARE product and handling of UIDs: [https://www.nxp.com/docs/en/application-note/AN10927.pdf]

GameLib.h
// 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
// 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;
    }
  }
}
Discovery-Game-Rfid.ino
#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);
  }
}

Master Steuerrechner

Master Steckplatine
Master Steckplatine

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

RG45-Buchse zu Station
RG45-Buchse zu Station

Info-Schnipsel I2C

  • 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

Meine Unterrichtsseite zu I2C: 3.7 I2C Schnittstelle

BezeichnungRelaisPinPinBezeichnungWiderstand
TelefonklingelN Relais15352Telefon-GabelN 4,7kΩ VCC
Türmagnet EingangN Relais25150Telefon-KnopfN 4,7kΩ VCC
VerstärkerN Relais34948Telefon-Wählscheibe4,7kΩ VCC
MausklickN Relais44746Taschenl. Ausgabe TasterN 4,7kΩ VCC
Türmagnet AusgangN Relais54544Türen TasterN 4,7kΩ VCC
Spot5 Taschl. FachN Relais64342Taschenl. RückgabeN 4,7kΩ VCC
LED-Streifen am AusgangN Relais74240IR1
Spot6 Hauptlicht EndeN Relais83938IR2
3736IR3
Weitere Belegung Master N steht für negative Logik, also Aktion bei log. 0.

[🔗 https://docs.arduino.cc/hardware/mega-2560/]

RG45-PinKabel-FarbeBezeichnungMaster-Pin
1OR/WSTelfon-Gabel52
2ORTelefon-Wählschebe48
3GN/WSLautsprecherDF-Player
4BLMasseGND
5BL/WSLautsprecherDF-Player
6GNTelefon-Knopf50
7BR/WSKlingelRelais
8BRKlingelRelais
Telefon
Master-PinPWMBeschreibung
2Spot1 (PWM)Hauptlicht
3Spot2 (PWM)Leuchtet auf Gegenstände
4Spot3 (PWM)Leuchtet auf Karten
5Spot5 (PWM)Raumlicht innen
43Spot4 (PWM)Taschenlampenfach blinkt
Lichter

Telefon Verbesserungsvorschlag

Es wird ein umgebautes Telefon verwendet, dies stellt bei Defekten einen deutlichen Aufwand dar, ein Standart-Wählscheibentelefon könnte problemloser beschafft und ausgetauscht werden siehe [Exit-Room Wählscheiben-Telefon mit Arduino Uno und DFPlayer Mini].

Audioausgabe: DFPlayer Mini und Library DFPlayer Mini Fast

Links zum Player: [https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299]

Dateien auf SD-Karte speichern

Dateistruktur: Dateien sollten mit Nummern wie 0001.mp3 gespeichert sein.
Wenn die Dateien im Root- (Haupt-) Verzeichnis abgelegt werden, gibt die Reihenfolge beim Kopieren auf die SD-Karte die Nummerierung vor, unabhängig vom Dateinamen. In Unterverzeichnissen wie „mp3“ wird dagegen die Nummer im Dateinamen beachtet. Hinter den Nummern dürfen durchaus weitere beschreibende Texte stehen, siehe Beispiel rechts.

Codeänderung im Master-Programm

play(nummer) wurde zu playFromMP3Folder(nummer) geändert, es wird Verzeichnis „mp3“ verwendet.
Leerdateien sind nun technisch nicht mehr notwendig.

Weitere Info: [🔗 https://wolles-elektronikkiste.de/dfplayer-mini-ansteuerung-mit-dem-arduino] (Gute Beschreibung der Verzeichnisstruktur der SD-Karte)

Dateistruktur SD-Karte
Dateistruktur SD-Karte

Arduino Library zu Ansteuerung des Players

Ich verwende die Library [🔗 DFPlayerMini Fast].

Abfragen ob der Player gerade spielt

Es gibt zwei Möglichkeiten dafür:

  1. myMP3.isPlaying() wird über die Serielle Schnittstelle gemacht, braucht aber 64ms!
  2. Über den BUSY-Pin am Player, solange er Low ist spielt der Player (gewählt).
Library DFPlayerMini_Fast
Library DFPlayerMini_Fast

Software Discovery-Game-Master

Discovery-Game-Master.ino
//mez 21.12.2024
#include "GameLib.h"
#include <Wire.h>
#include <DFPlayerMini_Fast.h> //https://github.com/PowerBroker2/DFPlayerMini_Fast
#include "rfid_tags.h"  // Zuordnung Tags zu MP3 Stücken

//#define TESTSTATION 1 // Teststation-Betrieb mit einem RFID-Termial0 ohne weitere HW
#define VERSION "1.4 21.12.2024"
#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      //pos Logik
#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_DTool,Z_DToolDo,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. Logik

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);
  #ifndef TESTSTATION
  delay(15000);                    //Versuch gegen IR-Auslösung am Beginn
  #endif
  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(F(VERSION));
  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
  #ifndef TESTSTATION
  byte test = 1;
  for(byte i=0;i<ANZAHL_TERMINALS;i++){  // Test ob alle Terminals antworten
    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;
  #else
  Serial.println(F("Teststation-Betrieb"));
  zustand=Z_DTool;  // Terminal 1 liest oben Tags aus und gibt sie an Discovery-Tool weiter
  #endif
}

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.playFromMP3Folder(lByteVar2);          // spiele es
        }
        else{
          Serial.print(F("Noch kein Stueck hinterlegt: "));
          Serial.println(lByteVar);
        }
      }
      break;
    case Z_DTool: // Terminal 1 liest oben Tags aus und gibt sie an Discovery-Tool weiter
      Serial.println(F("Discovery-Tool Modus, auf grünem Terminal anlernen"));
      alleAusRGB();
      setzeRGB(0,0,Gn,0);
      zustand=Z_DToolDo;
      break;
    case Z_DToolDo:
      leseRfid_DT(0,0);           // Terminal 1 oben lesen und UID ausgeben
      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<ANZAHL_TERMINALS;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<ANZAHL_TERMINALS;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<ANZAHL_TERMINALS;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.playFromMP3Folder(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.playFromMP3Folder(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
#ifdef TESTSTATION  
  long us;
#endif  
  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();             // sende Puffer
  delay(RFID_DELAY);                  // Zeit zum antworten lassen
  Wire.requestFrom(station+8, 8);     // lade 8 Byte von Node in Puffer
  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("");
#ifdef TESTSTATION  
    us=micros();
#endif    
    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) { // hat bis Ende gepasst
        treffer = 1;
        break;
      }
    }
#ifdef TESTSTATION      
    us=micros()-us;
    Serial.print(F("Suchzeit in us: "));
    Serial.println(us);
#endif    
    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
    }
  }  
}

byte leseRfid_DT(byte station, byte platz){ // Für Discovery-Tool: Terminal wird als RFID-Leser verwendet
  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();             // sende Puffer
  delay(RFID_DELAY);                  // Zeit zum antworten lassen
  Wire.requestFrom(station+8, 8);     // lade 8 Byte von Node in Puffer
  l = Wire.read();                    // erstes Byte gibt Anzahl der Bytes an
  if (l==0){                          // Keine neues Tag
    return 0;
  }
  else if (l>7){ // falsche Byteanzahl
    return 0;
  }else{  
    for (i=0;i<l-1;i++){ // lies die ID ein
      k=Wire.read();
      if(k<16)  Serial.print('0'); // zweistelliges Format
      Serial.print(k,HEX);
      Serial.print(':');
    }
    k=Wire.read();
    if(k<16)  Serial.print('0'); // zweistelliges Format
    Serial.println(k,HEX);
  }  
}

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("d Discovery-Tool Modus"));
        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 'd': 
        if(zustand!=Z_DToolDo){
          zustand = Z_DTool;
        }
        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;
    }
  }
}

Verschlissene und verschwundene RFID-Tags ersetzen

Viele RFID-Tags bzw. Gegenstände sind mittlerweile verschlissen bzw. verschwunden. Es werden neue RFID-Tags für die Gegenstände des Spiels benötigt. Ein RFID-Tag löst über seine UID die Wiedergabe einer Sounddatei auf einem DFPlayer mini aus. Die Zuordnung der UID des Tags zu der Nummer eine Sounddatei wird mit zwei Arrays gebildet:

  • Ein Array const byte karten[][7] PROGMEM hat die UIDs aller Tags gespeichert.
  • Ein Array const byte mp3stueck[] PROGMEM speichert die Zuordnung zu den MP3-Dateien
Bisheriger Code für RFID-Tags
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
};

Um neue Tags einpflegen zu können muss der Sketch verändert werden. Diese Lösung ist einfach, wird aber recht unübersichtlich bei der manuellen Wartung.

Lösungsmöglichkeiten
  1. Die UID eines RFID-Tags ist normalerweise nicht änderbar. Es gibt mittlerweile aber Tags mit beschreibbarer UID.
    Die änderbaren Tags müssten beschafft und getestet, ein Gerät zum Beschreiben gewählt, entwickelt werden.
  2. RFID-Tags haben oft beschreibbaren Datenspeicher, in diesem könnte einfach die Nummer des zu spielenden Stücks hinterlegt werden.
    Die Tags müssen beschreiben werden. Eine neue Hardware mit User-Interface wäre notwendig.
    Die Software aller RFID-Stationen müsste angepasst werden. Funktionstest des neuen Verfahrens und der neuen Software braucht wieder Zeit..
  3. Die Verwaltung der Tags und die Erstellung der Arrays wird optimiert. Das Einpflegen neuer Tags wird einfacher.
    Die Firmware der Basis-Station muss dazu jedesmal aktualisiert werden, jemand muss das vor Ort können.
  4. Die Zuordnung Tag-UID -> Stück wird im EEPROM der Basis-Station gespeichert, dazu müsste ein zur Bedienung ein User-Interface entwickelt werden. Mit welchem Endgerät wird dabei kommuniziert? Arduino Terminal..

Discovery_RFID_Tool-Software zur Optimierung der RFID-Tag-Verwaltung (Lösung 3)

Meine erste Erfahrung mit [🔗 Processing] ein Programm zu entwickeln.
Als GUI Builder Tool habe ich [🔗 http://www.lagers.org.uk/g4ptool/index.html] verwendet. Ästhetisch bin ich nicht glücklich, der Code ist Quick & Dirty, aber es erfüllt seinen Zweck…

Ziel ist es, den Gegenständen wieder neue RFID-Tags zuweisen zu können und dabei den Überblick zu behalten.
Vorhanden sind auch RFID-Tags zu Fähigkeits-Karten die MP3-Ausgabe auslösen.
Aus RFID-UID folgt Gegenstand-ID folgt MP3-Nr.

Aufteilung der mp3-Dateien im Lieferzustand:
0001-0020 Ansagen zum Ablauf
0021-0040 Kommentare zu den Fähigkeits-Karten (siehe Liste)
0041-0060 Ansagen zum Ablauf
0061-0100 Kommentare bzw. Geräusche passen zu den Gegenständen

Mein Lösungsweg sind einfache CSV-Dateien, die mit einer Tabellenkalkulation bearbeitet und kundenfreundlich visualisiert werden können und eine Software, die uns leicht neue RFID-Tags einfügen lässt und eine C++ Datei rfid_tags.h erzeugt, in der die zwei Arrays für die Umsetzung enthalten sind. Vor Ort kann damit die Firmware aktualisiert werden. Ist eine Bastel- (Maker)- Lösung.

Hinweis: Sortieren und Löschen von Tags geschieht über die CSV-Dateien mit Tabellenkalkulation.

🛠️ Gruseliger Quellcode 🥴

Tabelle mit Gegenständen gegenstand.csv ist vorgegeben und wird beim Programmstart gelesen, diese Datei wird vom Programm nicht verändert. Info gibt hier die gewünschte Anzahl der Tags vor.

G-IDBezeichnungMP3InfoAnzahlRFID-Tags
G01✞ Adidas Samba ✞610
G02✞ Action Man ✞620
G03Barbie639
G04Harry Potter649
G05Mosaik DDR Comic656
G06VHS Pippi Langstrumpf6610
G07Fußball WM 2006675
G08Zigarettenschachteln689
G09Pokal695
G10Lego705
G11Märklin Metall715
G12GNTM Tasche725
G13Homer Simpson735
G14Singles Mappe747
G15Nintendo DS758
G16Nokia Handy767
G17Führerschein778
G18Inline Skates785
G19BRAVOS7913
G20YuGiOh Herz806
G21Tasse Atomkraft816
G22Frei1826
G23Frei2836
G24Frei3846
G25Frei4856

Tabelle mit RFID-Tags rfid_mp3.csv wird gelesen und kann mit neuen Tags ergänzt werden. Bei jedem Dateien Speichern wird eine Kopie mit Datum und Uhrzeit zusätzlich gespeichert.

UIDMP3Bezeichnung
04:4A:43:32:1E:70:8021mutig
04:5B:42:32:1E:70:8022Humor
04:53:43:32:1E:70:8023durchsetzen
04:5F:42:32:1E:70:8024gut reden
04:3E:43:32:1E:70:8025kritisch denken
04:55:CB:BA:6F:71:8026
04:52:CA:BA:6F:71:8027
04:4E:CA:BA:6F:71:8028
04:4B:CB:BA:6F:71:8029
04:47:CB:BA:6F:71:8030
04:43:42:32:1E:70:8031Verantwortung
04:63:42:32:1E:70:8032A helfen
04:4B:42:32:1E:70:8033gelassen
04:4F:43:32:1E:70:8034begeistern
04:47:42:32:1E:70:8035Spontan
04:4F:42:32:1E:70:8036Strategisch
04:53:42:32:1E:70:8037praktisch
04:57:42:32:1E:70:8038kreativ
04:46:43:32:1E:70:8039geduldig
04:42:43:32:1E:70:8040zuhören
04:D2:A3:22:1E:70:8063G03-25
04:77:43:32:1E:70:8063G03-26
04:7B:43:32:1E:70:8063G03-27
04:23:CB:BA:6F:71:8063G03-84
53:40:97:29:02:67:0063G03-01
53:51:A2:03:02:40:0063G03-02
53:59:E6:29:02:80:0063G03-03
53:09:F3:03:02:A0:0063G03-04
53:D9:D2:09:02:77:0063G03-05
04:67:42:32:1E:70:8064G04-28
04:D6:A3:22:1E:70:8064G04-29
04:20:CA:BA:6F:71:8064G04-85
1D:80:CB:D4:09:10:8064G04-01
1D:7F:CB:D4:09:10:8064G04-02
1D:7B:CB:D4:09:10:8064G04-03
1D:7C:CB:D4:09:10:8064G04-04
1D:7E:CB:D4:09:10:8064G04-05
1D:7D:CB:D4:09:10:8064G04-06
04:67:43:32:1E:70:8065G05-30
04:33:CB:BA:6F:71:8065G05-31
1D:79:CB:D4:09:10:8065G05-01
1D:7A:CB:D4:09:10:8065G05-02
1D:F1:CE:D4:09:10:8065G05-03
1D:F0:CE:D4:09:10:8065G05-04
9A:DC:5C:B466G06-32
8C:6D:4F:2E66G06-33
CA:F6:68:AD66G06-34
6C:F5:49:3166G06-35
1D:F5:CE:D4:09:10:8066G06-01
1D:F2:CE:D4:09:10:8066G06-02
1D:F4:CE:D4:09:10:8066G06-03
1D:F3:CE:D4:09:10:8066G06-04
1D:F6:CE:D4:09:10:8066G06-05
1D:F7:CE:D4:09:10:8066G06-06
04:5F:43:32:1E:70:8067G07-36
1D:F8:CE:D4:09:10:8067G07-01
1D:FB:CE:D4:09:10:8067G07-02
1D:FA:CE:D4:09:10:8067G07-03
1D:F9:CE:D4:09:10:8067G07-04
6A:19:96:AE68G08-37
4A:EC:82:AD68G08-38
C1:91:72:2168G08-39
1D:65:CE:D4:09:10:8068G08-01
1D:64:CE:D4:09:10:8068G08-02
1D:63:CE:D4:09:10:8068G08-03
1D:62:CE:D4:09:10:8068G08-04
1D:FD:CE:D4:09:10:8068G08-05
1D:FC:CE:D4:09:10:8068G08-06
04:57:43:32:1E:70:8069G09-40
1D:69:CE:D4:09:10:8069G09-01
1D:6A:CE:D4:09:10:8069G09-02
1D:66:CE:D4:09:10:8069G09-03
1D:68:CE:D4:09:10:8069G09-04
9C:D6:48:3170G10-41
1D:85:CB:D4:09:10:8070G10-01
1D:6E:CE:D4:09:10:8070G10-02
1D:84:CB:D4:09:10:8070G10-03
1D:67:CE:D4:09:10:8070G10-04
C1:9A:25:2171G11-42
1D:81:CB:D4:09:10:8071G11-01
1D:6B:CE:D4:09:10:8071G11-02
1D:FE:CE:D4:09:10:8071G11-03
1D:61:CE:D4:09:10:8071G11-04
D1:C2:C5:2172G12-43
1D:83:CB:D4:09:10:8072G12-01
1D:6D:CE:D4:09:10:8072G12-02
1D:82:CB:D4:09:10:8072G12-03
1D:6C:CE:D4:09:10:8072G12-04
04:6B:43:32:1E:70:8073G13-44
1D:88:CB:D4:09:10:8073G13-01
1D:72:CE:D4:09:10:8073G13-02
1D:87:CB:D4:09:10:8073G13-03
1D:71:CE:D4:09:10:8073G13-04
3A:E2:A0:AD74G14-45
1D:6F:CE:D4:09:10:8074G14-01
1D:70:CE:D4:09:10:8074G14-02
1D:86:CB:D4:09:10:8074G14-03
1D:73:CE:D4:09:10:8074G14-04
1D:89:CB:D4:09:10:8074G14-05
1D:8A:CB:D4:09:10:8074G14-06
04:E0:26:D2:BA:5D:8075G15-46
D1:F9:B8:2175G15-47
1D:14:CB:D4:09:10:8075G15-01
1D:77:CE:D4:09:10:8075G15-02
1D:12:CB:D4:09:10:8075G15-03
1D:15:CB:D4:09:10:8075G15-04
1D:75:CE:D4:09:10:8075G15-05
1D:78:CE:D4:09:10:8075G15-06
04:37:CB:BA:6F:71:8076G16-76
04:27:CB:BA:6F:71:8076G16-83
1D:7A:CE:D4:09:10:8076G16-01
1D:17:CB:D4:09:10:8076G16-02
1D:79:CE:D4:09:10:8076G16-03
1D:13:CB:D4:09:10:8076G16-04
1D:74:CE:D4:09:10:8076G16-05
04:43:CB:BA:6F:71:8077G17-77
04:F0:E1:8A:6F:71:8077G17-86
1D:7D:CE:D4:09:10:8077G17-01
1D:7E:CE:D4:09:10:8077G17-02
1D:1A:CB:D4:09:10:8077G17-03
1D:19:CB:D4:09:10:8077G17-04
1D:76:CE:D4:09:10:8077G17-05
1D:7C:CE:D4:09:10:8077G17-06
04:6F:43:32:1E:70:8078G18-50
1D:7B:CE:D4:09:10:8078G18-01
1D:18:CB:D4:09:10:8078G18-02
1D:16:CB:D4:09:10:8078G18-03
1D:1B:CB:D4:09:10:8078G18-04
7A:F3:5D:AD79G19-51
CA:2D:A8:AD79G19-52
C3:6F:AF:0279G19-53
EA:67:69:AD79G19-54
8A:A0:7D:AD79G19-55
1D:80:CE:D4:09:10:8079G19-01
1D:1C:CB:D4:09:10:8079G19-02
1D:1D:CB:D4:09:10:8079G19-03
1D:20:CB:D4:09:10:8079G19-04
1D:E6:CD:D4:09:10:8079G19-05
1D:7F:CE:D4:09:10:8079G19-06
1D:E5:CD:D4:09:10:8079G19-07
1D:1F:CB:D4:09:10:8079G19-08
04:3F:CB:BA:6F:71:8080G20-80
FF:0F:FA:B0:20:00:0080G20-01
FF:0F:80:74:21:00:0080G20-02
FF:0F:5A:9F:20:00:0080G20-03
FF:0F:8C:74:21:00:0080G20-04
FF:0F:58:9B:21:00:0080G20-05
04:63:43:32:1E:70:8081G21-81
FF:0F:39:9B:21:00:0081G21-01
FF:0F:66:9F:20:00:0081G21-02
FF:0F:22:9B:21:00:0081G21-03
FF:0F:74:9F:20:00:0081G21-04
FF:0F:5F:C4:20:00:0081G21-05
FF:0F:4E:9B:21:00:0082G22-01
FF:0F:98:74:21:00:0082G22-02
FF:0F:7C:9F:20:00:0082G22-03
FF:0F:73:C4:20:00:0082G22-04
FF:0F:56:CD:21:00:0082G22-05
FF:0F:44:9B:21:00:0082G22-06
FF:0F:B7:BD:21:00:0083G23-01
FF:0F:CD:AD:21:00:0083G23-02
FF:0F:6D:9F:20:00:0083G23-03
FF:0F:60:9F:20:00:0083G23-04
FF:0F:69:C4:20:00:0083G23-05
FF:0F:2E:9B:21:00:0083G23-06
FF:0F:6F:9A:21:00:0084G24-01
FF:0F:79:87:21:00:0084G24-02
FF:0F:66:9A:21:00:0084G24-03
FF:0F:5C:9A:21:00:0084G24-04
FF:0F:7D:4E:21:00:0084G24-05
FF:0F:72:4E:21:00:0084G24-06
FF:0F:A7:87:21:00:0085G25-01
FF:0F:AD:BD:21:00:0085G25-02
FF:0F:C1:AD:21:00:0085G25-03
FF:0F:80:9A:21:00:0085G25-04
FF:0F:8D:87:21:00:0085G25-05
FF:0F:78:9A:21:00:0085G25-06
04:61:CB:BA:6F:71:8087
04:5D:CB:BA:6F:71:8088
04:59:CB:BA:6F:71:8089
04:B0:6E:01:D1:05:0390
04:B0:52:01:79:05:0391
04:B0:44:01:5C:05:0392
04:30:CA:01:22:44:0393
04:40:62:01:06:45:0394
04:E0:A6:01:06:44:0395
04:B0:70:01:C5:05:0396
04:B0:30:01:90:05:0397
04:30:DD:01:30:45:0398
04:A0:F4:01:10:05:0399
04:B0:72:01:58:05:03100

Weitere erzeugte Dateien beim Dateien Speichern

Eine C++ Datei rfid_tags.h mit den Tags zum Einbinden.
// RFID Tags 16.12.2024 Anzahl: 191
const byte karten[][7] PROGMEM = {  // RFID-UIDs der Tags
{0x04,0x4A,0x43,0x32,0x1E,0x70,0x80}, //   1. 21
{0x04,0x5B,0x42,0x32,0x1E,0x70,0x80}, //   2. 22
{0x04,0x53,0x43,0x32,0x1E,0x70,0x80}, //   3. 23
{0x04,0x5F,0x42,0x32,0x1E,0x70,0x80}, //   4. 24
{0x04,0x3E,0x43,0x32,0x1E,0x70,0x80}, //   5. 25
{0x04,0x55,0xCB,0xBA,0x6F,0x71,0x80}, //   6. 26
{0x04,0x52,0xCA,0xBA,0x6F,0x71,0x80}, //   7. 27
{0x04,0x4E,0xCA,0xBA,0x6F,0x71,0x80}, //   8. 28
{0x04,0x4B,0xCB,0xBA,0x6F,0x71,0x80}, //   9. 29
{0x04,0x47,0xCB,0xBA,0x6F,0x71,0x80}, //  10. 30
{0x04,0x43,0x42,0x32,0x1E,0x70,0x80}, //  11. 31
{0x04,0x63,0x42,0x32,0x1E,0x70,0x80}, //  12. 32
{0x04,0x4B,0x42,0x32,0x1E,0x70,0x80}, //  13. 33
{0x04,0x4F,0x43,0x32,0x1E,0x70,0x80}, //  14. 34
{0x04,0x47,0x42,0x32,0x1E,0x70,0x80}, //  15. 35
{0x04,0x4F,0x42,0x32,0x1E,0x70,0x80}, //  16. 36
{0x04,0x53,0x42,0x32,0x1E,0x70,0x80}, //  17. 37
{0x04,0x57,0x42,0x32,0x1E,0x70,0x80}, //  18. 38
{0x04,0x46,0x43,0x32,0x1E,0x70,0x80}, //  19. 39
{0x04,0x42,0x43,0x32,0x1E,0x70,0x80}, //  20. 40
{0x04,0xD2,0xA3,0x22,0x1E,0x70,0x80}, //  21. 63
{0x04,0x77,0x43,0x32,0x1E,0x70,0x80}, //  22. 63
{0x04,0x7B,0x43,0x32,0x1E,0x70,0x80}, //  23. 63
{0x04,0x23,0xCB,0xBA,0x6F,0x71,0x80}, //  24. 63
{0x53,0x40,0x97,0x29,0x02,0x67,0x00}, //  25. 63
{0x53,0x51,0xA2,0x03,0x02,0x40,0x00}, //  26. 63
{0x53,0x59,0xE6,0x29,0x02,0x80,0x00}, //  27. 63
{0x53,0x09,0xF3,0x03,0x02,0xA0,0x00}, //  28. 63
{0x53,0xD9,0xD2,0x09,0x02,0x77,0x00}, //  29. 63
{0x04,0x67,0x42,0x32,0x1E,0x70,0x80}, //  30. 64
{0x04,0xD6,0xA3,0x22,0x1E,0x70,0x80}, //  31. 64
{0x04,0x20,0xCA,0xBA,0x6F,0x71,0x80}, //  32. 64
{0x1D,0x80,0xCB,0xD4,0x09,0x10,0x80}, //  33. 64
{0x1D,0x7F,0xCB,0xD4,0x09,0x10,0x80}, //  34. 64
{0x1D,0x7B,0xCB,0xD4,0x09,0x10,0x80}, //  35. 64
{0x1D,0x7C,0xCB,0xD4,0x09,0x10,0x80}, //  36. 64
{0x1D,0x7E,0xCB,0xD4,0x09,0x10,0x80}, //  37. 64
{0x1D,0x7D,0xCB,0xD4,0x09,0x10,0x80}, //  38. 64
{0x04,0x67,0x43,0x32,0x1E,0x70,0x80}, //  39. 65
{0x04,0x33,0xCB,0xBA,0x6F,0x71,0x80}, //  40. 65
{0x1D,0x79,0xCB,0xD4,0x09,0x10,0x80}, //  41. 65
{0x1D,0x7A,0xCB,0xD4,0x09,0x10,0x80}, //  42. 65
{0x1D,0xF1,0xCE,0xD4,0x09,0x10,0x80}, //  43. 65
{0x1D,0xF0,0xCE,0xD4,0x09,0x10,0x80}, //  44. 65
{0x9A,0xDC,0x5C,0xB4}               , //  45. 66
{0x8C,0x6D,0x4F,0x2E}               , //  46. 66
{0xCA,0xF6,0x68,0xAD}               , //  47. 66
{0x6C,0xF5,0x49,0x31}               , //  48. 66
{0x1D,0xF5,0xCE,0xD4,0x09,0x10,0x80}, //  49. 66
{0x1D,0xF2,0xCE,0xD4,0x09,0x10,0x80}, //  50. 66
{0x1D,0xF4,0xCE,0xD4,0x09,0x10,0x80}, //  51. 66
{0x1D,0xF3,0xCE,0xD4,0x09,0x10,0x80}, //  52. 66
{0x1D,0xF6,0xCE,0xD4,0x09,0x10,0x80}, //  53. 66
{0x1D,0xF7,0xCE,0xD4,0x09,0x10,0x80}, //  54. 66
{0x04,0x5F,0x43,0x32,0x1E,0x70,0x80}, //  55. 67
{0x1D,0xF8,0xCE,0xD4,0x09,0x10,0x80}, //  56. 67
{0x1D,0xFB,0xCE,0xD4,0x09,0x10,0x80}, //  57. 67
{0x1D,0xFA,0xCE,0xD4,0x09,0x10,0x80}, //  58. 67
{0x1D,0xF9,0xCE,0xD4,0x09,0x10,0x80}, //  59. 67
{0x6A,0x19,0x96,0xAE}               , //  60. 68
{0x4A,0xEC,0x82,0xAD}               , //  61. 68
{0xC1,0x91,0x72,0x21}               , //  62. 68
{0x1D,0x65,0xCE,0xD4,0x09,0x10,0x80}, //  63. 68
{0x1D,0x64,0xCE,0xD4,0x09,0x10,0x80}, //  64. 68
{0x1D,0x63,0xCE,0xD4,0x09,0x10,0x80}, //  65. 68
{0x1D,0x62,0xCE,0xD4,0x09,0x10,0x80}, //  66. 68
{0x1D,0xFD,0xCE,0xD4,0x09,0x10,0x80}, //  67. 68
{0x1D,0xFC,0xCE,0xD4,0x09,0x10,0x80}, //  68. 68
{0x04,0x57,0x43,0x32,0x1E,0x70,0x80}, //  69. 69
{0x1D,0x69,0xCE,0xD4,0x09,0x10,0x80}, //  70. 69
{0x1D,0x6A,0xCE,0xD4,0x09,0x10,0x80}, //  71. 69
{0x1D,0x66,0xCE,0xD4,0x09,0x10,0x80}, //  72. 69
{0x1D,0x68,0xCE,0xD4,0x09,0x10,0x80}, //  73. 69
{0x9C,0xD6,0x48,0x31}               , //  74. 70
{0x1D,0x85,0xCB,0xD4,0x09,0x10,0x80}, //  75. 70
{0x1D,0x6E,0xCE,0xD4,0x09,0x10,0x80}, //  76. 70
{0x1D,0x84,0xCB,0xD4,0x09,0x10,0x80}, //  77. 70
{0x1D,0x67,0xCE,0xD4,0x09,0x10,0x80}, //  78. 70
{0xC1,0x9A,0x25,0x21}               , //  79. 71
{0x1D,0x81,0xCB,0xD4,0x09,0x10,0x80}, //  80. 71
{0x1D,0x6B,0xCE,0xD4,0x09,0x10,0x80}, //  81. 71
{0x1D,0xFE,0xCE,0xD4,0x09,0x10,0x80}, //  82. 71
{0x1D,0x61,0xCE,0xD4,0x09,0x10,0x80}, //  83. 71
{0xD1,0xC2,0xC5,0x21}               , //  84. 72
{0x1D,0x83,0xCB,0xD4,0x09,0x10,0x80}, //  85. 72
{0x1D,0x6D,0xCE,0xD4,0x09,0x10,0x80}, //  86. 72
{0x1D,0x82,0xCB,0xD4,0x09,0x10,0x80}, //  87. 72
{0x1D,0x6C,0xCE,0xD4,0x09,0x10,0x80}, //  88. 72
{0x04,0x6B,0x43,0x32,0x1E,0x70,0x80}, //  89. 73
{0x1D,0x88,0xCB,0xD4,0x09,0x10,0x80}, //  90. 73
{0x1D,0x72,0xCE,0xD4,0x09,0x10,0x80}, //  91. 73
{0x1D,0x87,0xCB,0xD4,0x09,0x10,0x80}, //  92. 73
{0x1D,0x71,0xCE,0xD4,0x09,0x10,0x80}, //  93. 73
{0x3A,0xE2,0xA0,0xAD}               , //  94. 74
{0x1D,0x6F,0xCE,0xD4,0x09,0x10,0x80}, //  95. 74
{0x1D,0x70,0xCE,0xD4,0x09,0x10,0x80}, //  96. 74
{0x1D,0x86,0xCB,0xD4,0x09,0x10,0x80}, //  97. 74
{0x1D,0x73,0xCE,0xD4,0x09,0x10,0x80}, //  98. 74
{0x1D,0x89,0xCB,0xD4,0x09,0x10,0x80}, //  99. 74
{0x1D,0x8A,0xCB,0xD4,0x09,0x10,0x80}, // 100. 74
{0x04,0xE0,0x26,0xD2,0xBA,0x5D,0x80}, // 101. 75
{0xD1,0xF9,0xB8,0x21}               , // 102. 75
{0x1D,0x14,0xCB,0xD4,0x09,0x10,0x80}, // 103. 75
{0x1D,0x77,0xCE,0xD4,0x09,0x10,0x80}, // 104. 75
{0x1D,0x12,0xCB,0xD4,0x09,0x10,0x80}, // 105. 75
{0x1D,0x15,0xCB,0xD4,0x09,0x10,0x80}, // 106. 75
{0x1D,0x75,0xCE,0xD4,0x09,0x10,0x80}, // 107. 75
{0x1D,0x78,0xCE,0xD4,0x09,0x10,0x80}, // 108. 75
{0x04,0x37,0xCB,0xBA,0x6F,0x71,0x80}, // 109. 76
{0x04,0x27,0xCB,0xBA,0x6F,0x71,0x80}, // 110. 76
{0x1D,0x7A,0xCE,0xD4,0x09,0x10,0x80}, // 111. 76
{0x1D,0x17,0xCB,0xD4,0x09,0x10,0x80}, // 112. 76
{0x1D,0x79,0xCE,0xD4,0x09,0x10,0x80}, // 113. 76
{0x1D,0x13,0xCB,0xD4,0x09,0x10,0x80}, // 114. 76
{0x1D,0x74,0xCE,0xD4,0x09,0x10,0x80}, // 115. 76
{0x04,0x43,0xCB,0xBA,0x6F,0x71,0x80}, // 116. 77
{0x04,0xF0,0xE1,0x8A,0x6F,0x71,0x80}, // 117. 77
{0x1D,0x7D,0xCE,0xD4,0x09,0x10,0x80}, // 118. 77
{0x1D,0x7E,0xCE,0xD4,0x09,0x10,0x80}, // 119. 77
{0x1D,0x1A,0xCB,0xD4,0x09,0x10,0x80}, // 120. 77
{0x1D,0x19,0xCB,0xD4,0x09,0x10,0x80}, // 121. 77
{0x1D,0x76,0xCE,0xD4,0x09,0x10,0x80}, // 122. 77
{0x1D,0x7C,0xCE,0xD4,0x09,0x10,0x80}, // 123. 77
{0x04,0x6F,0x43,0x32,0x1E,0x70,0x80}, // 124. 78
{0x1D,0x7B,0xCE,0xD4,0x09,0x10,0x80}, // 125. 78
{0x1D,0x18,0xCB,0xD4,0x09,0x10,0x80}, // 126. 78
{0x1D,0x16,0xCB,0xD4,0x09,0x10,0x80}, // 127. 78
{0x1D,0x1B,0xCB,0xD4,0x09,0x10,0x80}, // 128. 78
{0x7A,0xF3,0x5D,0xAD}               , // 129. 79
{0xCA,0x2D,0xA8,0xAD}               , // 130. 79
{0xC3,0x6F,0xAF,0x02}               , // 131. 79
{0xEA,0x67,0x69,0xAD}               , // 132. 79
{0x8A,0xA0,0x7D,0xAD}               , // 133. 79
{0x1D,0x80,0xCE,0xD4,0x09,0x10,0x80}, // 134. 79
{0x1D,0x1C,0xCB,0xD4,0x09,0x10,0x80}, // 135. 79
{0x1D,0x1D,0xCB,0xD4,0x09,0x10,0x80}, // 136. 79
{0x1D,0x20,0xCB,0xD4,0x09,0x10,0x80}, // 137. 79
{0x1D,0xE6,0xCD,0xD4,0x09,0x10,0x80}, // 138. 79
{0x1D,0x7F,0xCE,0xD4,0x09,0x10,0x80}, // 139. 79
{0x1D,0xE5,0xCD,0xD4,0x09,0x10,0x80}, // 140. 79
{0x1D,0x1F,0xCB,0xD4,0x09,0x10,0x80}, // 141. 79
{0x04,0x3F,0xCB,0xBA,0x6F,0x71,0x80}, // 142. 80
{0xFF,0x0F,0xFA,0xB0,0x20,0x00,0x00}, // 143. 80
{0xFF,0x0F,0x80,0x74,0x21,0x00,0x00}, // 144. 80
{0xFF,0x0F,0x5A,0x9F,0x20,0x00,0x00}, // 145. 80
{0xFF,0x0F,0x8C,0x74,0x21,0x00,0x00}, // 146. 80
{0xFF,0x0F,0x58,0x9B,0x21,0x00,0x00}, // 147. 80
{0x04,0x63,0x43,0x32,0x1E,0x70,0x80}, // 148. 81
{0xFF,0x0F,0x39,0x9B,0x21,0x00,0x00}, // 149. 81
{0xFF,0x0F,0x66,0x9F,0x20,0x00,0x00}, // 150. 81
{0xFF,0x0F,0x22,0x9B,0x21,0x00,0x00}, // 151. 81
{0xFF,0x0F,0x74,0x9F,0x20,0x00,0x00}, // 152. 81
{0xFF,0x0F,0x5F,0xC4,0x20,0x00,0x00}, // 153. 81
{0xFF,0x0F,0x4E,0x9B,0x21,0x00,0x00}, // 154. 82
{0xFF,0x0F,0x98,0x74,0x21,0x00,0x00}, // 155. 82
{0xFF,0x0F,0x7C,0x9F,0x20,0x00,0x00}, // 156. 82
{0xFF,0x0F,0x73,0xC4,0x20,0x00,0x00}, // 157. 82
{0xFF,0x0F,0x56,0xCD,0x21,0x00,0x00}, // 158. 82
{0xFF,0x0F,0x44,0x9B,0x21,0x00,0x00}, // 159. 82
{0xFF,0x0F,0xB7,0xBD,0x21,0x00,0x00}, // 160. 83
{0xFF,0x0F,0xCD,0xAD,0x21,0x00,0x00}, // 161. 83
{0xFF,0x0F,0x6D,0x9F,0x20,0x00,0x00}, // 162. 83
{0xFF,0x0F,0x60,0x9F,0x20,0x00,0x00}, // 163. 83
{0xFF,0x0F,0x69,0xC4,0x20,0x00,0x00}, // 164. 83
{0xFF,0x0F,0x2E,0x9B,0x21,0x00,0x00}, // 165. 83
{0xFF,0x0F,0x6F,0x9A,0x21,0x00,0x00}, // 166. 84
{0xFF,0x0F,0x79,0x87,0x21,0x00,0x00}, // 167. 84
{0xFF,0x0F,0x66,0x9A,0x21,0x00,0x00}, // 168. 84
{0xFF,0x0F,0x5C,0x9A,0x21,0x00,0x00}, // 169. 84
{0xFF,0x0F,0x7D,0x4E,0x21,0x00,0x00}, // 170. 84
{0xFF,0x0F,0x72,0x4E,0x21,0x00,0x00}, // 171. 84
{0xFF,0x0F,0xA7,0x87,0x21,0x00,0x00}, // 172. 85
{0xFF,0x0F,0xAD,0xBD,0x21,0x00,0x00}, // 173. 85
{0xFF,0x0F,0xC1,0xAD,0x21,0x00,0x00}, // 174. 85
{0xFF,0x0F,0x80,0x9A,0x21,0x00,0x00}, // 175. 85
{0xFF,0x0F,0x8D,0x87,0x21,0x00,0x00}, // 176. 85
{0xFF,0x0F,0x78,0x9A,0x21,0x00,0x00}, // 177. 85
{0x04,0x61,0xCB,0xBA,0x6F,0x71,0x80}, // 178. 87
{0x04,0x5D,0xCB,0xBA,0x6F,0x71,0x80}, // 179. 88
{0x04,0x59,0xCB,0xBA,0x6F,0x71,0x80}, // 180. 89
{0x04,0xB0,0x6E,0x01,0xD1,0x05,0x03}, // 181. 90
{0x04,0xB0,0x52,0x01,0x79,0x05,0x03}, // 182. 91
{0x04,0xB0,0x44,0x01,0x5C,0x05,0x03}, // 183. 92
{0x04,0x30,0xCA,0x01,0x22,0x44,0x03}, // 184. 93
{0x04,0x40,0x62,0x01,0x06,0x45,0x03}, // 185. 94
{0x04,0xE0,0xA6,0x01,0x06,0x44,0x03}, // 186. 95
{0x04,0xB0,0x70,0x01,0xC5,0x05,0x03}, // 187. 96
{0x04,0xB0,0x30,0x01,0x90,0x05,0x03}, // 188. 97
{0x04,0x30,0xDD,0x01,0x30,0x45,0x03}, // 189. 98
{0x04,0xA0,0xF4,0x01,0x10,0x05,0x03}, // 190. 99
{0x04,0xB0,0x72,0x01,0x58,0x05,0x03}  // 191. 100
};

const byte mp3stueck[] PROGMEM = { // Tag-Index-> MP3-Nr
21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,
63,63,63,63,63,63,63,63,63,64,
64,64,64,64,64,64,64,64,65,65,
65,65,65,65,66,66,66,66,66,66,
66,66,66,66,67,67,67,67,67,68,
68,68,68,68,68,68,68,68,69,69,
69,69,69,70,70,70,70,70,71,71,
71,71,71,72,72,72,72,72,73,73,
73,73,73,74,74,74,74,74,74,74,
75,75,75,75,75,75,75,75,76,76,
76,76,76,76,76,77,77,77,77,77,
77,77,77,78,78,78,78,78,79,79,
79,79,79,79,79,79,79,79,79,79,
79,80,80,80,80,80,80,81,81,81,
81,81,81,82,82,82,82,82,82,83,
83,83,83,83,83,84,84,84,84,84,
84,85,85,85,85,85,85,87,88,89,
90,91,92,93,94,95,96,97,98,99,
100
};


Eine Datei GegenstaendeTags.csv mit einer Übersicht aller Gegenstände mit ihren RFID-Tags. Die neuen Tags werden von uns mit der Gegenstands-ID (G-ID) und der TagNr z.B. G03-01 für ein neues Barbie-Tag beschriftet. Die internen Tag-Nummern der alten Tags wurden bei behalten. Für 4 neue Gegenstände sind Audioplätze und jeweils 6 Tags reserviert.

Viele neue RFID-Tags
Viele neue RFID-Tags und 3 SD-Kärtchen

Anleitung für KI Makerspace zum Einspielen der neuen Firmware

Arduino Umgebung, [🔗 DFPlayerMini Fast] Lib laden. Falls mit Seriellem Monitor gearbeitet wird, dann 115200 Baud einstellen. RFID-Kartentest-Modus mit k starten siehe im Source-Code ganz unten..
Wichtig, sollte wie hier auskommentiert sein : //#define TESTSTATION 1 // Teststation-Betrieb mit einem RFID-Termial0 ohne weitere HW
Firmware für Master als ZIP-Datei:

Andere Audio-Sprachen auswählen

Wie kann in den Spielablauf eine Umschaltung auf eine weitere Ausgabesprache eingebracht werden?
Im Zustand Z_warteAufMitspielerDo wird entweder durch einen der Bewegungsmelder zum Telefonklingeln und dann, oder direkt durch das Abnehmen der Telefongabel zum Zustand Z_begruessung verzweigt, in dem der Begrüßungstext ausgegeben wird. Zu diesem Zeitpunkt sollte die Sprache eingestellt, oder eine Umstellung der Sprache noch möglich sein. Lösungsideen:

  1. Durch einen RFID-Tag wird vom Bedienpersonal die Sprache eingestellt, dazu muss an passender Stelle ein Zustand zum Einlesen des Tags eingebaut werden.
  2. Beim Abnehmen des Telefonhörers kann mit der Wählscheibe eine andere Sprache eingestellt werden. Auch hier muss der Ablauf entsprechend geändert werden.
  3. Neben dem Telefon ist eine Box mit einem Sprachumschalter installiert, vor jeder Sprachausgabe wird dieser Schalter abgefragt und die passende Sprachdatei ausgegeben. Eine Umschaltung der Sprache sollte auch während eine Wiedergabe läuft möglich sein, mit Neustart des Stückes in anderer Sprache. Ein Vorteil ist die Möglichkeit für gemischte Gruppen mitten in der Vorstellung die Spräche jederzeit ändern zu können. Ein Nachteil ist der Aufwand die Hardware ergänzen zu müssen.
    Box könnte mir I2C angebunden werden..

Ein Kommentar

Kommentare sind geschlossen.