Zeiterfassung mit Wemos D1 und RFID-Modul

Inhalt

Beschreibung der Anleitung

Die Tags (Karten im Scheckkartenformat oder als Schlüsselanhänger) eines RFID-Moduls werden dazu verwendet den Namen einer fiktiven Mitarbeiterin/eines fiktiven Mitarbeiters festzustellen und mit Hilfe der Standardbibliothek time.h die Zeit der Anmeldung oder der Abmeldung zu ermitteln. Diese Daten werden über WiFi an einen Browser übertragen und angezeigt. Alle 60 Sekunden wird die Anzeige automatisch aktualisiert.
Über einen Button kann die Seite auch händisch auf den neuesten Stand gebracht werden.

Ansicht im Browser

Tra­ge unter Datei -> Ein­stel­lun­gen eine zusätz­li­che Board­ver­wal­ter-URL ein:

https://arduino.esp8266.com/stable/package_esp8266com_index.json

 

Wenn das Wemos D1 Mini nicht automatisch erkannt wurde, klicke auf „Wähle ein anderes Board und einen anderen Port“ und suche nach Wemos D1. Je nach Betriebssystem wird der USB-Port eine andere Bezeichnung haben.

Benötigte Bauteile

Das RFID-Kit MFC522

Das RFID-Kit MFC522 besteht aus den sogenannten Tags – Karten im Scheckkartenformat oder als Schlüsselanhänger – und dem Lesegerät. Das Lesegerät generiert einen Code wenn ein Tag vor das Lesegerät gehalten wird.

RFID Pins

Der Schaltplan

Anschlüsse des RFID-Moduls

Benötigte Bibliothek installieren

RFID-Code auslesen

Jeder Tag generiert einen individuellen Code. Damit du die Karten den Mitarbeiter*innen zuordnen kannst, musst du die Codes ermitteln und notieren.

Dazu dient dieses Programm:

 

				
					#include <MFRC522.h>

// Anschlüsse RFID definieren
#define SDA D8
#define RST D3

// RFID-Empfänger benennen
MFRC522 mfrc522(SDA, RST);

// vom RFID ermttelter Wert der Karte als Folge von Dezimalzahlen
String WertDEZ;

void setup() 
{
  Serial.begin(9600);
  delay(500);
  SPI.begin();

  // Initialisierung des RFID-Empfängers
  mfrc522.PCD_Init();
}

void loop() 
{
  // Wert der Karte zurücksetzen
  WertDEZ = "";

  // wenn eine Karte aufgelegt wird startet die Abfrage
  if (mfrc522.PICC_IsNewCardPresent()) 
  {
    mfrc522.PICC_ReadCardSerial();

    // Dezimalwerte der Karte in String schreiben
    for (byte i = 0; i < mfrc522.uid.size; i++) 
    {
      WertDEZ = WertDEZ + String(mfrc522.uid.uidByte[i], DEC) + " ";
    }

    // Wartezeit anpassen, wenn mehrere Werte gelesen werden
    delay(1000);

    // dezimalen Wert im Seriellen Monitor anzeigen
    Serial.println("dezimaler Wert: " + WertDEZ);
  }
}


				
			

Das Programm

Globale Variablen definieren und Bibliothek einbinden

Die Bibliothek time.h sorgt für die Ermittlung der aktuellen Zeit über NTP-Server. Das NTP (Network Time Protocol) ist ein Protokoll zur Synchronisierung von Uhren in Computersystemen. Im Internet existieren öffentlich zugängliche NTP-Server, die sich zur Zeitsynchronisation nutzen lassen. Hier wird ein Pool von Zeitservern verwendet. Das hat den Vorteil, dass bei einem Ausfall eines Zeitservers der nächste in der Liste benutzt wird. Die SSID des Routers und das Passwort musst du anpassen.
Wenn du weitere Mitarbeiter*innen hinzufügen willst, müssen sie im Array MitarbeiterInnen (Zeile 61) notiert werden. Gleichzeitig musst du die Größe der Arrays AnmeldeZeit und AnmeldeStatus (Zeilen 64 und 67) entsprechend erhöhen.

				
					// Bibiotheken hinzufügen
#include <ESP8266WebServer.h>
#include <time.h>
#include <MFRC522.h>

// Anschlüsse RFID definieren
#define SDA D8
#define RST D3

// RFID-Empfänger benennen
MFRC522 mfrc522(SDA, RST);

char Router[] = "Router_SSID";
char Passwort[] = "xxxxxxxx";

// vom RFID ermttelter Wert der Karte als Folge von Dezimalzahlen
String WertDEZ;

/*
  NTP-Server aus Pool 
  # define Zeitserver "de.pool.ntp.org"
  https://www.pool.ntp.org/zone/de
  oder z.B. Zeitserver der Physikalisch-technische Bundesanstalt
  # define Zeitserver "ptbtime1.ptb.de"
  wenn eine statische IP-Adresse verwendet kann der Eintrag auch direkt erfolgen
  NTP-Server als IP-Adresse angeben
  192.53.103.108 -> Physikalisch-technische Bundesanstalt
*/
#define Zeitserver "de.pool.ntp.org"

/*
  Liste der Zeitzonen
  https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
  Zeitzone CET = Central European Time -1 -> 1 Stunde zurück
  CEST = Central European Summer Time von
  M3 = März, 5.0 = Sonntag 5. Woche, 02 = 2 Uhr
  bis M10 = Oktober, 5.0 = Sonntag 5. Woche 03 = 3 Uhr
*/
#define Zeitzone "CET-1CEST,M3.5.0/02,M10.5.0/03"

// time_t enthält die Anzahl der Sekunden seit dem 1.1.1970 0 Uhr
time_t aktuelleZeit;

/* 
  Struktur tm wandelt die ermittelte Zeit in ein "lesbares" Format um:
  tm_hour -> Stunde: 0 bis 23
  tm_min -> Minuten: 0 bis 59
  tm_sec -> Sekunden 0 bis 59
  tm_mday -> Tag 1 bis 31
  tm_mon -> Monat: 0 (Januar) bis 11 (Dezember)
  tm_year -> Jahre seit 1900
  tm_yday -> vergangene Tage seit 1. Januar des Jahres
  tm_isdst -> Wert > 0 = Sommerzeit (dst = daylight saving time)
*/
tm Zeit;

WiFiServer Server(80);
WiFiClient Client;

// Namen der Mitarbeiter*innen
String MitarbeiterInnen[3] = { "Klaus Drolshagen", "Eva Hilger", "Claudia Kruse" };

// Anmeldestatus: false = abgemeldet
bool AnmeldeStatus[3] = { false, false, false };

// Zeit der Anmeldung/Abmeldung
String AnmeldeZeit[3];
				
			

Der setup-Teil

				
					void setup() 
{
  Serial.begin(9600);
  delay(500);
  SPI.begin();

  // Initialisierung des RFID-Empfängers
  mfrc522.PCD_Init();

  // Parameter für die zu ermittelnde Zeit
  configTime(Zeitzone, Zeitserver);

  WiFi.begin(Router, Passwort);

  // Verbindung herstellen
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(200);
    Serial.print(".");
  }

  Server.begin();

  // SSID des Routers anzeigen
  Serial.println();
  Serial.print("Verbunden mit ");
  Serial.println(WiFi.SSID());

  // IP des D! anzeigen
  Serial.println(WiFi.localIP());
}
				
			

Im Seriellen Monitor wird die IP-Adresse des Wemos D1 angezeigt.

Diese IP-Adresse musst du in der Adresszeile eines Browsers deiner Wahl eingeben.

Der loop-Teil

Der loop-Teil fragt als Erstes den dezimalen Wert der Karte oder des Schlüsselanhängers ab. Sie werden zur Kontrollen im Seriellen Monitor angezeigt. Für die Abfrage der Mitarbeiterin/des Mitarbeiters musst du die ermittelten Werte in den Zeilen 34, 50 und 60 gegen die ausgelesenen Werte deiner Tags austauschen.

				
					void loop() 
{
  // aktuelle Zeit lesen
  time(&aktuelleZeit);

  // localtime_r -> ermittelte Zeit in die lokale Zeitzone setzen
  localtime_r(&aktuelleZeit, &Zeit);
  
  // Wert der Karte zurücksetzen
  WertDEZ = "";

  // wenn eine Karte aufgelegt wird startet die Abfrage
  if (mfrc522.PICC_IsNewCardPresent()) 
  {
    mfrc522.PICC_ReadCardSerial();

    // Dezimalwerte der Karte in String schreiben
    for (byte i = 0; i < mfrc522.uid.size; i++) 
    {
      WertDEZ = WertDEZ + String(mfrc522.uid.uidByte[i], DEC) + " ";
    }

    // Wartezeit anpassen, wenn mehrere Werte gelesen werden
    delay(1000);

    // dezimalen Wert im Seriellen Monitor anzeigen
    Serial.println("dezimaler Wert: " + WertDEZ);
  }

  // Leerzeichen am Ende entfernen
  WertDEZ.trim();

  // Karten abfragen und Namen der Mitarbeiter*innen und Zeit zuordnen
  if (WertDEZ.compareTo("195 106 18 23") == 0) 
  {
    AnmeldeStatus[0] = !AnmeldeStatus[0];

    // AnmeldeZeit zurücksetzen
    AnmeldeZeit[0] = "";

    // wenn Stunde < 10 -> 0 voranstellen
    if (Zeit.tm_hour < 10) AnmeldeZeit[0] = "0";
    AnmeldeZeit[0] = AnmeldeZeit[0] + Zeit.tm_hour + ":";

    // wenn Minute < 10 -> 0 voranstellen
    if (Zeit.tm_min < 10) AnmeldeZeit[0] = "0";
    AnmeldeZeit[0] = AnmeldeZeit[0] + Zeit.tm_min;
  }

  if (WertDEZ.compareTo("227 77 233 22") == 0) 
  {
    AnmeldeStatus[1] = !AnmeldeStatus[1];
    AnmeldeZeit[1] = "";
    if (Zeit.tm_hour < 10) AnmeldeZeit[1] = "0";
    AnmeldeZeit[1] = AnmeldeZeit[1] + Zeit.tm_hour + ":";
    if (Zeit.tm_min < 10) AnmeldeZeit[1] = "0";
    AnmeldeZeit[1] = AnmeldeZeit[1] + Zeit.tm_min;
  }

  if (WertDEZ.compareTo("131 125 21 23") == 0) 
  {
    AnmeldeStatus[2] = !AnmeldeStatus[2];
    AnmeldeZeit[2] = "";

    if (Zeit.tm_hour < 10) AnmeldeZeit[2] = "0";
    AnmeldeZeit[2] = AnmeldeZeit[2] + Zeit.tm_hour + ":";
    if (Zeit.tm_min < 10) AnmeldeZeit[2] = "0";
    AnmeldeZeit[2] = AnmeldeZeit[2] + Zeit.tm_min;
  }

  Client = Server.available();
  if (Client) 
  {
    // Seite aufbauen wenn SeiteAufbauen true ist
    boolean SeiteAufbauen = true;

    while (Client.connected()) 
    {
      if (Client.available()) 
      {
        char Zeichen = Client.read();
        if (Zeichen == '\n') 
        {
          // wenn SeiteAufbauen den Wert true hat
          if (SeiteAufbauen) 
          {
            // HTTP-Anforderung senden
            Client.println("HTTP/1.1 200 OK");
            Client.println("Content-Type: text/html");

            // Leerzeile zwingend erforderlich
            Client.println();

            /*
              HTML-Seite aufbauen
              die folgenden Anweisungen müssen mit print oder println gesendet werden
              println "verschönert" den Quelltext
              " muss mit \" maskiert werden
            */
            Client.println("<!doctype html>");
            Client.println("<html>");
            Client.println("<body>");

            // alle 60 Sekunden aktualisieren mit meta-Tag
            Client.println("<meta http-equiv=\"refresh\" content=\"60\">");
            Client.println("<h1> Anmeldestatus der Mitarbeiter*innen</h1>");
            Client.println("<hr />");

            // aktuelle Zeit anzeigen
            Client.println("<h2>");

            // tm_mday -> Wochentag anzeigen
            switch (Zeit.tm_wday) 
            {
              case 0:
                Client.print(F("Sonntag"));
                break;
              case 1:
                Client.print(F("Montag"));
                break;
              case 2:
                Client.print(F("Dienstag"));
                break;
              case 3:
                Client.print(F("Mittwoch"));
                break;
              case 4:
                Client.print(F("Donnerstag"));
                break;
              case 5:
                Client.print(F("Freitag"));
                break;
              case 6:
                Client.print(F("Samstag"));
                break;
            }
            Client.print(", ");

            // Tag: führende 0 ergänzen
            if (Zeit.tm_mday < 10) Client.print("0");
            Client.print(Zeit.tm_mday);
            Client.print(".");

            // Monat: führende 0 ergänzen
            if (Zeit.tm_mon < 10) Client.print("0");
            Client.print(Zeit.tm_mon + 1);
            Client.print(".");

            // tm_year + 1900
            Client.print(Zeit.tm_year + 1900);

            // Uhrzeit
            Client.print(" Uhrzeit: ");

            // Stunden
            if (Zeit.tm_hour < 10) Client.print("0");
            Client.print(Zeit.tm_hour);
            Client.print(":");

            // Minuten
            if (Zeit.tm_min < 10) Client.print("0");
            Client.print(Zeit.tm_min);
            Client.print(":");

            // Sekunden
            if (Zeit.tm_sec < 10) Client.print("0");
            Client.print(Zeit.tm_sec);

            Client.println("</h2>");
            Client.println("<hr />");

            // Name und Anmeldestatus der Mitarbeiter*innen anzeigen
            for (int i = 0; i < sizeof(MitarbeiterInnen) / sizeof(MitarbeiterInnen[0]); i++) 
            {
              Client.println(MitarbeiterInnen[i]);
              if (AnmeldeStatus[i] == true) Client.println(": angemeldet seit " + AnmeldeZeit[i] + " Uhr");
              else Client.println(": abgemeldet seit " + AnmeldeZeit[i] + " Uhr");
              Client.println("<br>");
            }
            Client.println("<hr>");

            Client.println("<form>");

            // Button aktualisieren formatieren
            Client.print("<input style=\"font-size:16pt; font-weight:bold;");
            Client.print("background-color:#55A96B;");
            Client.print("display:block; cursor:pointer;\"type=\"button\"");
            Client.println(" onClick=\"location.href='WiFi.localIP()'\" value=\"aktualisieren\">");
            Client.println("</form>");
            Client.println("<hr />");

            // IPs anzeigen
            Client.print(F("<b>Eigene IP: "));
            Client.print(Client.remoteIP());
            Client.print(F("</b>"));
            Client.print(F("<br><b>IP Klient (D1): "));
            Client.print(WiFi.localIP());
            Client.print(F("</b>"));
            Client.println("</b>");

            Client.println("</body>");
            Client.print("</html>");

            // HTTP-Antwort endet mit neuer Zeile
            Client.println();

            // Seite vollständig geladen -> loop verlassen
            break;
          }

          // wenn new line (\n) gesendet wurde -> Seite aufbauen
          if (Zeichen == '\n') SeiteAufbauen = true;

          else if (Zeichen != '\r') SeiteAufbauen = false;
          
          delay(1);
          Client.stop();
        }
      }
    }
  }
}
				
			

Beispiel: Bilder und Beschriftung der Karten

Quelle: https://openclipart.org/detail/20698/smooth-faces

Funduino - Dein Onlineshop für Mikroelektronik

  • Dauerhaft bis zu 10% Rabatt für Schüler, Studenten und Lehrkräfte
  • Mehr als 3.500 Artikel sofort verfügbar!
  • Über 8 Jahre Erfahrung mit Arduino, 3D-Druck und co.
SALE