Das vierzehnte Türchen

Heute geht es um RFID, also Radio Frequency Identification. Damit ist es möglich, entsprechende Transponder, oder auch Tag genannt, kontaktlos zu identifizieren. Der Transponder besitzt einen Microchip, aber keine eigene Stromversorgung. Er wird als Karte oder Schlüsselanhänger ausgeführt. Das Lesegerät baut ein hochfrequentes elektromagnetisches Feld auf. Kommt ein Transponder in dieses Feld, entnimmt er diesem Feld Energie für die eigene Stromversorgung. Nachdem der Microchip genug Spannung hat, startet er ein Programm und sendet seine fest kodierte Identifikationsnummer. 

Zur Datenübertragung baut der Transponder kein eigenes elektromagnetisches Feld auf, sondern beeinflusst das Feld des Lesegerätes. Dieses erkennt diese Beeinflussung und kann die Daten empfangen.

Das System im heutigen Angebot arbeitet mit 13.56 MHz. Der Mikrochip in den Karten und Schlüsselanhängern kann nicht nur seine 4-Byte Identifikationsnummer senden. Er hat auch einen Speicher, der 1024 Bytes speichern kann. Diese Daten können vom Lesegerät an die Karte gesendet und von dort wieder gelesen werden. Die Ansteuerung des Lesegeräts verwendet zur Kommunikation mit dem Mikrocontroller den SPI (Serial Peripheral Interface) Bus.

Als Beispielprojekt wollen wir ein einfaches Zutritt-System realisieren. Neben dem RFID-Lesegerät aus dem heutigen Angebot benötigen wir einen Mikrocontroller zur Ansteuerung. Wir verwenden den Nano V3.0 Zusätzlich benötigen wir noch eine RGB-LED oder drei LEDs mit Vorwiderstand zu Anzeige, ein Relais, um zum Beispiel einen Türöffner zu betätigen und einen Taster, um gegebenenfalls die gespeicherten Daten komplett zu löschen.

Die Verdrahtung wird bei diesem Projekt ein wenig umfangreicher, da alleine schon für den SPI-Bus vier Leitungen gebraucht werden.

 


Die SPI Anschlüsse sind am Nano V3.0 D12 für MOSI (Master Out, Slave In), D11 für MISO (Master In, Slave Out), D10 für ChipSelect SDA (Select Data Access) und D13 für den Takt SCK (Serial Clock). Der Anschluss D9 ist mit RST (Reset) verbunden um den Reader zurückzusetzen. Es fehlen noch GND und 3.3V. Der Reader darf nur mit 3.3V betrieben werden!

Das RGB-LED Modul hat eine RGB-LED mit Vorwiderständen, sodass die Anoden direkt mit den Ausgängen des Nano V3.0 verbunden werden können. Die RGB-LED hat eine gemeinsame Kathode, die mit GND verbunden wird. Da besonders die grüne LED viel heller als die Andere ist,  sollen die LEDs mit PWM (Pulse Width Modulation) angesteuert werden.  Nicht alle Anschlüsse des Nano V3.0 sind PWM fähig. Wir verbinden die rote LED mit D3, die grüne LED mit D5 und die blaue LED mit D6. D4 ist nicht für PWM verwendbar.

Das Relais braucht eine Versorgungsspannung von 5V. Wir verbinden GND mit GND und Vcc mit 5V am Nano V3.0. Zur Ansteuerung verwenden wir D7 am Nano Microkontroller.
Mit dem Taster schalten wir eine Verbindung zwischen D8 und GND. Das Programm ist so ausgelegt, dass, wenn beim Starten D8 auf GND liegt, der interne EEPROM des Nano V3.0 gelöscht wird. Dieser Vorgang sollte einmal, bei der ersten Inbetriebnahme durchgeführt werden, um klare Verhältnisse zu erhalten.

Für das Programm benötigen wir die Bibliothek für das Lesegerät MFRC522. Diese können wir über die Bibliotheksverwaltung installieren. Als Suchbegriff geben wir MRFC522 ein.

 

Die weiteren Bibliotheken für den SPI-Bus (SPI.h) und für die Ansteuerung des EEPROMs (EEPROM.h) sind bereits in der Arduino IDE installiert. Wir können also den Sketch eingeben und kompilieren.

Sketch:

#include <SPI.h>        //Bibliothek für SPI-Bus
#include <MFRC522.h>    //Bibliothek für MFRC522 RFID Chip
#include <EEPROM.h>     //Bibliothek für den internen EEPROM

#define SS_PIN 10       //Chipselect Pin
#define RST_PIN 9       //Reset Pin

#define LED_RED 3       //Anode rote LED
#define LED_GREEN 5     //Anode grüne LED
#define LED_BLUE 6      //Anode blaue LED

#define RELAIS 7        //Ansteuerung Relais

#define ERASE 8         //Taster zum Löschen

//Status Zustände
#define ST_WAIT 0       //Wartezustand alle LED aus
#define ST_MASTER 1     //Warten auf neuen Master LED blau
#define ST_ADD 2        //Unbekannte Karte hinzufügen LED gelb
#define ST_DEL 3        //Bekannte Karte löschen LED cyan
#define ST_ALLOW 4      //Zutritt erlaubt LED grün
#define ST_DENY 5       //Zutritt verweigert LED rot
#define ST_MASTERDEL 6  //Master löschen LED pink

//Instanz für Reader Interface
MFRC522 rfid(SS_PIN, RST_PIN); // Instance of the class

uint32_t cardId;        //letzte gelesene Karten ID
int16_t cardIndex;      //Index der Karte im Speicher
uint32_t masterId;      //ID der Masterkarte
uint32_t lastAction;    //Zeitstempel der letzten Aktion
uint8_t status;         //aktueller Status

//Details des RFID Lesers zur Info anzeigen
void ShowReaderDetails() {
  // Get the MFRC522 software version
  byte v = rfid.PCD_ReadRegister(rfid.VersionReg);
  Serial.print(F("MFRC522 Software Version: 0x"));
  Serial.print(v, HEX);
  if (v == 0x91)
    Serial.print(F(" = v1.0"));
  else if (v == 0x92)
    Serial.print(F(" = v2.0"));
  else
    Serial.print(F(" (unknown),probably a chinese clone?"));
  Serial.println("");
  // When 0x00 or 0xFF is returned, communication probably failed
  if ((v == 0x00) || (v == 0xFF)) {
    Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?"));
    Serial.println(F("SYSTEM HALTED: Check connections."));
    while (true); // do not go further
  }
}

//die Funktion liest die vier Bytes einer Karten ID und gibt
//sie als 32-Bit Integer zurück
uint32_t getId() {
  uint32_t res = 0;
  uint8_t tmp[4];
  // Wurde eine neue Karte gelesen?
  if ( ! rfid.PICC_IsNewCardPresent()) {
    return 0; //nein return 0
  }
  //Karten-Id lesen
  if ( ! rfid.PICC_ReadCardSerial()) {  
    return 0; //nicht erfolgreich return 0
  }
  Serial.println(F("Scanned PICC's UID:"));
  for ( uint8_t i = 0; i < 4; i++) {  //
    //byteorder umdrehen für 32-bit Integer
    tmp[3-i] = rfid.uid.uidByte[i];
  }
  rfid.PICC_HaltA(); // Lesen beenden
  res = *((unsigned long*)tmp);
  Serial.print("Result: ");
  Serial.println(res);
  return res;
}

//Laesst eine bestimmte LED blinken
//led = Pinnummer, cnt = wie oft, val = Helligkeit
void blinkLED(uint8_t led, uint8_t cnt, uint8_t val)  {
  for (uint8_t i = 0; i<cnt; i++) {
    analogWrite(led,val);
    delay(200);
    digitalWrite(led,0);
    delay(200);
  }
}

//Schaltet alle LEDs aus
void allOff() {
    analogWrite(LED_RED,0);
    analogWrite(LED_GREEN,0);
    analogWrite(LED_BLUE,0);
}

//Sucht eine bestimmte ID im EEPROM und gibt den Index
//zurück oder -1 wenn die ID nicht gefunden wurde
int16_t findId(uint32_t id) {
  uint16_t max = (EEPROM.length() - 6) / 4;
  uint16_t i = 0;
  boolean found = false;
  uint32_t data;
  while ((i<max) && !found) {
    EEPROM.get(i*4+6,data);
    found = (data == id);
    if (!found) i++;
  }
  Serial.println(found);
  if (found) return i*4+6; else return -1;
}

//Sucht eine den nächsten freien Platz im EEPROM und gibt den Index
//zurück oder -1 wenn kein Platz mehr ist
int16_t findEmpty() {
  uint16_t max = (EEPROM.length() - 6) / 4;
  uint16_t i = 0;
  boolean found = false;
  uint32_t data;
  while ((i<max) && !found) {
    EEPROM.get(i*4+6,data);
    found = (data == 0);
    if (!found) i++;
  }
  if (found) return i*4+6; else return -1;
}

//Schaltet das Relais ein oder aus
void relais(boolean on) {
  digitalWrite(RELAIS,!on); //wird ein anderes Relais benutzt,
                            //muss eventuell das NOT (!) entfallen
}

//Zeigt die Farben des aktuellen Status auf den LEDs an
void statusColorOn() {
  Serial.print("Status ");
  Serial.println(status);
  allOff();
  switch (status) {
    //nur blau
    case ST_MASTER: analogWrite(LED_BLUE,255); break;
    //grün und rot = gelb
    case ST_ADD: analogWrite(LED_GREEN,30); analogWrite(LED_RED,255); break;
    //blau und grün = cyan
    case ST_DEL: analogWrite(LED_BLUE,255); analogWrite(LED_GREEN,70); break;
    //nur grün
    case ST_ALLOW: analogWrite(LED_GREEN,50); break;
    //nur rot
    case ST_DENY: analogWrite(LED_RED,255); break;
    //blau und rot = magenta
    case ST_MASTERDEL: analogWrite(LED_BLUE,255);analogWrite(LED_RED,255); break;
  }
}

//Eine Karte wurde gelesen und soll ausgewertet werden
void gotCard(uint32_t id){
  switch (status) {
    //Wartezustand Masterkarte öffnet sorfort die Tür
    //wurde eine andere Karte im Speicher gefunden, kann sie mit der Masterkarte gelöscht werden
    //wurde eine andere Karte nicht im Speicher gefunden, kann sie mit der Masterkarte hinzugefügt werden
    case ST_WAIT: if (id==masterId)  {
                    status = ST_ALLOW;
                    relais(true);
                  } else {
                    cardIndex = findId(id);
                    cardId = id;
                    if (cardIndex>=0) {
                      Serial.print("ID gefunden Index = ");
                      Serial.println(cardIndex);
                      status = ST_DEL;
                    } else {
                      Serial.println("ID nicht gefunden");
                      status = ST_ADD;
                    }
                  }
                  break;
    //Zustand ist warten auf eine neue Masterkarte. gelesene ID = MasterID
    case ST_MASTER: masterId = id;
                  EEPROM.write(1,197);
                  EEPROM.put(2,masterId);
                  Serial.print("Master ID = ");
                  Serial.println(masterId);
                  status = ST_WAIT;
                  break;
    //wurde die MasterId gelesen wird eine Karte die nicht im Speicher war gespeichert
    //danach wird die Tür geöffnet
    case ST_ADD: if (id == masterId) {
                   int16_t ix = findEmpty();
                   if (ix>=0) {
                   Serial.print("Addiere ID ");
                   Serial.print(cardId);
                   Serial.print(" Index = ");
                   Serial.println(ix);
                    EEPROM.put(ix,cardId);
                    status = ST_ALLOW;
                    relais(true);
                   }
                 } else {
                    status = ST_DENY;
                    relais(false);
                 }
                 break;
    //wurde die MasterId gelesen wird eine Karte die im Speicher war gelöscht
    //danach wird der Zutritt verweigert
    case ST_DEL: if ((id == masterId) && (cardIndex >= 0)) {
                   Serial.print("Lösche ID ");
                   Serial.println(cardIndex);
                   EEPROM.put(cardIndex,long(0));
                   status = ST_DENY;
                   relais(false);
                 } else {
                   status = ST_ALLOW;
                   relais(true);
                 }
                 break;
    //wurde die MaterID gelesen, wird die MasterID gelöscht
    //es kann eine andere Karte als Master gespeichert werden
    case ST_MASTERDEL: if (id == masterId) {
                   EEPROM.put(2,long(0));
                   EEPROM.write(1,0);
                   Serial.println("Lösche Master");
                   status = ST_MASTER;
                   relais(false);
                 }
                 break;
    }
  statusColorOn();
  lastAction = millis();               
}

//3 Sekunden seit der letzten Aktion sind vergangen
void handleTimeout() {
  switch (status) {
    //ungültige Karte, Zutritt verweigern
    case ST_ADD:  status = ST_DENY;
         relais(false);
         break;
    //gültige Karte, Zutritt erlauben
    case ST_DEL:  status = ST_ALLOW;
         relais(true);
         break;
    //sonst immer Zutritt verweigern
    default: status = ST_WAIT;
         relais(false);
  }
  statusColorOn();
  lastAction = millis();
}

//Setup
void setup() {
  Serial.begin(9600);
  //Pins als Output oder Input definieren
  pinMode(LED_RED,OUTPUT);
  pinMode(LED_GREEN,OUTPUT);
  pinMode(LED_BLUE,OUTPUT);
  pinMode(RELAIS,OUTPUT);
  pinMode(ERASE,INPUT_PULLUP);
  //Relais abschalten
  relais(false);
  //LEDs der Reihe nach zum Test blinken lassen
  blinkLED(LED_RED,3,255);
  blinkLED(LED_GREEN,3,50);
  blinkLED(LED_BLUE,3,255);
  //SPI und RFID Leser starten
  SPI.begin();
  rfid.PCD_Init();
  //Startmeldung und Info zum Reader ausgeben
  Serial.println("Adventblog 13.12.2020");
  ShowReaderDetails();
  //Falls die Taste gedrückt wurde, den EEPROM loschen
  if (digitalRead(ERASE) == 0) {
    Serial.print("Lösche EEPROM" );
    Serial.print(EEPROM.length());
    Serial.println(" Bytes");
    for (int i = 0 ; i < EEPROM.length() ; i++) {
      EEPROM.write(i, 0);
    }
  }
  //die ersten 16 gespeicherten Karten anzeigen
  for (uint16_t i = 0; i<16; i++) {
    EEPROM.get(i*4+6,cardId);
    Serial.print(i*4+6);
    Serial.print(" = ");
    Serial.println(cardId);
  }

  //haben wir eine Master Card ?
  if (EEPROM.read(1)==197) {
    Serial.println("Wir haben eine Master Id");
    EEPROM.get(2,masterId);
    status = ST_MASTERDEL; //das Löschen der Masterkarte ermöglichen
    statusColorOn();
  } else {
    Serial.println("Wir haben keine Master Id");
    masterId = 0;
    status = ST_MASTER;  //auf die ID einer Karte warten und
                         //diese als Master speichern
    statusColorOn();
  }
  //Zeitstempel zurücksetzen
  lastAction = millis();
}

void loop() {
  uint32_t tmpId;
  tmpId = getId();  //KartenId abfragen
  //Wurde eine Karte gelesen, so wird die Nummer ausgewertet
  if (tmpId > 0) gotCard(tmpId);
  //Sind 3 Sekunden abgelaufen wir dein Timeout bearbeitet
  //Ausnahme der Wartezustand und das Warten auf eine Masterkarte
  //bei diesen Zuständen gibt es kein Timeout
  if ((status != ST_WAIT) && (status != ST_MASTER) && ((millis() - lastAction) > 3000)) handleTimeout();
}

Download Sketch


Funktionsweise

Beim ersten Start sollte die Löschtaste gedrückt werden, damit der EEPROM gelöscht wird. Beim Starten blinken die drei LEDs zur Kontrolle je dreimal. Wurde noch keine Masterkarte gespeichert, zeigt die LED Blau. Es kann jetzt eine Masterkarte gesetzt werden, indem diese Karte vor das Lesegerät gehalten wird. Wurde die Karte gelesen, wechselt die LED auf Grün und das Relais zieht an. Die gelesene ID wird als Master gespeichert. Nach drei Sekunden erlischt die LED und das Relais fällt ab.

War beim Starten schon eine Masterkarte gespeichert wird diese verwendet. Die LED zeigt Magenta. In diesem Zustand kann die Masterkarte gelöscht werden, indem man sie vor das Lesegerät hält. War das Löschen erfolgreich, wechselt die LED auf Blau und eine neue Masterkarte kann eingelesen werden.

Im Wartezustand ist die LED aus. Wird eine Karte gelesen, die noch nicht gespeichert wurde, geht die LED auf gelb. Jetzt hat man drei Sekunden Zeit, um mit der Masterkarte die unbekannte Karte gültig zu machen. Ist diese Aktion erfolgreich, wechselt die LED auf Grün und das Relais zieht an. Wird die unbekannte Karte nicht mit der Masterkarte bestätigt, geht die LED auf Rot und das Relais zieht nicht an.

Wird eine Karte gelesen, die bereits gespeichert wurde, geht die LED auf Cyan. Jetzt hat man drei Sekunden Zeit, um mit der Masterkarte die gespeicherte Karte zu löschen. Ist diese Aktion erfolgreich, wechselt die LED auf Rot und das Relais zieht nicht an. Wird die gespeicherte Karte nicht mit der Masterkarte gelöscht, geht die LED auf Grün und das Relais zieht an.

Nach drei Sekunden geht die grüne LED wieder aus und das Relais fällt ab. Dasselbe passiert mit der roten LED.

Viel Spaß beim Nachbau.

Specials

Einen Kommentar hinterlassen

Alle Kommentare werden vor der Veröffentlichung moderiert

Aanbevolen blog berichten

  1. Installeer ESP32 nu van de raad van bestuur
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP Programmeren via Wi-Fi