DIY Raspberry Pi Stechuhr mit NFC - Teil 2

Manuel Lehner16.02.2018

Die Fortsetzung unseres Neos/Flow Meetups zur Entwicklung einer mit NFC gesteuerten Raspberry Pi Stechuhr lies, im Vergleich zum korrespondierenden Blog-Post, nicht lange auf sich warten. Entschuldigung für die Verzögerung!

Recap Teil 1

In Teil 1 unserer Raspberry-Pi-Bastelaktion trafen wir auf ein unerwartetes Problem:

  • Lesen eigens beschriebener Chips mit dem NFC-Modul RFID RC522

Nach über 4h abendlicher Bastelei waren wir nicht in der Lage dies schnell zu beheben.

Zur Erinnerung und Auffrischung geht es hier zu Teil 1 der Raspberry Pi Stechuhr.

Während des zweiten Bastel-Abends trafen wir auf weitere technische Herausforderungen, die wir fernab solcher Veranstaltungen und parallel zur Entstehung von Teil 2 lösen mussten.

KI generiert: Auf dem Bild sind vier blaue Schlüsselanhänger mit Notizzetteln zu sehen, die auf einem Tisch neben einem Laptop liegen. Die Zettel scheinen Namen zu tragen, die zur Kennzeichnung der Anhänger dienen.

Problem 1: Auslesen eigens beschriebener RFID-Chips mit NFC-Modul RFID RC522

In Teil 1 kamen wir zu dem Schluss, uns nach einer neuen NFC Bibliothek zum Auslesen der Chips umzuschauen, da die ursprünglich verwendete unsere selbstbeschriebenen im NDEF Format nicht lesen konnte.

Nachdem wir einige Nächte darüber geschlafen hatten, fiel uns eine einfachere Lösung ein - die interne Seriennummer der RFID-Chips. 

Jeder RFID-Chip besitzt eine eineindeutige Seriennummer. Diese lesen wir nun aus und ordnen sie innerhalb des Flow Controllers dem jeweiligen Sandstormer zu.

Da es sich bei dieser Zuordnung um keine sicherheitsrelevanten Informationen wie bspw. Passwörter oder SSH-Keys handelt, nutzten wir kurzerhand die settings.yaml

Sandstorm: WorkingTime: people: sandstormer-name: rfidToken: 'ab-cd-ef-91' firstWorkDay: '2015-07-06' workingHoursPerDay: - hours: 6 until: '2015-09-30' - hours: 8 until: 'now' holidaysPerYear: 30
KI generiert: Ein Laptop mit einer programmierten Oberfläche steht auf einem Schreibtisch, während daneben ein Tablet mit LEGO-Teilen umrandet das "Sandstorm"-Logo zeigt. Eine Hand tippt auf der Laptop-Tastatur, während verschiedene elektronische Komponenten und ein Getränk in der Nähe zu sehen sind.

Problem 2: Browser gezielt steuern

Update 2020

Aus sicherheitstechnischen Gründen hat Mozilla sämtliche Extensions entfernt, die Remote-Code ausführen. Dies betrifft auch das von uns genutzte Add-On. Deshalb haben wir diesen Part an die aktuellen Gegebenheiten angepasst. Die aktualisierte Lösung findet ihr hier:

DIY Raspberry Pi Stechuhr mit NFC (Teil 3)

-----------------------------------------------------------------------------------------------------------------------

Analog unserer bisherigen Zeiterfassung soll auch der Raspberry Pi bei Anhalten des individuellen RFID Chips die korrespondierende Zeiterfassungs-Website des Sandstormers öffnen.

Standardmäßig ist es jedoch nicht möglich einzelne Browsertabs zu steuern und sie neue URLs aufrufen zu lassen, wie in unserem Fall nötig. Der Browser kann lediglich beim initialen Start oder Öffnen eines neuen Tabs eine spezifische URL öffnen. Da der Raspberry Pi durch seine weniger potente Hardware jedoch sehr lange brauch, um Firefox zu schließen und wieder zu starten, ist dies keine Option für den operativen Betrieb. Auch das Öffnen dutzender Tabs verlangsamt den Pi sukzessive und macht ihn damit unbrauchbar.

Die Lösung liefert das Addon Remote Control for Firefox. Es erweitert den Raspberry Pi Firefox um die benötigte Funktionalität. Via Telnet schicken wir JS Code an den Pi, die die Seite laden bzw. neuladen lassen.

Der angepasste Python Code sieht nun folgendermaßen aus:

#!/usr/bin/env python # -*- coding: utf8 -*- import RPi.GPIO as GPIO import MFRC522 import signal import datetime import telnetlib class Control(object): def __init__(self, port=32000): self.host = 'localhost' self.port = port def reload(self): tn = telnetlib.Telnet(self.host, self.port) tn.write("reload\n") result = tn.read_until('\n') return result def load(self, url): tn = telnetlib.Telnet(self.host, self.port) tn.write('window.location="%s"' % url) result = tn.read_until('\n') return result continue_reading = True lastUid = [0, 0, 0, 0, 0] timeToDelete = 0 # Capture SIGINT for cleanup when the script is aborted def end_read(signal, frame): global continue_reading continue_reading = False GPIO.cleanup() # Hook the SIGINT signal.signal(signal.SIGINT, end_read) # Create an objecet of the class MFRC522 MIFAREReader = MFRC522.MFRC522() c = Control() # This loop keeps checking for chips. If one is near it will get the UID and authenticate while continue_reading: (status, TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL) # If a card is found if timeToDelete != 0 and timeToDelete < datetime.datetime.now(): timeToDelete = 0 lastUid = [0, 0, 0, 0, 0] c.load('https://www.your-url.de') # Get the UID of the card (status, uid) = MIFAREReader.MFRC522_Anticoll() # If we have the UID, continue if status == MIFAREReader.MI_OK: if lastUid != uid: uidStrings = [] for part in uid: uidStrings.append(str(hex(part)).replace('0x', '')) # we only use the first 4 entries uidStrings.pop(4) uidString = '-'.join(uidStrings) lastUid = uid timeToDelete = datetime.datetime.now() + datetime.timedelta(0, 5) url = "http://www.your-url.de/workingtime/dongle/toggle.html?rfidToken=" + uidString c.load(url)
Wartezeit heißt Zeit für Kicker

Problem 3: Kubernetes

Parallel zum Projekt zogen wir unsere komplette Infrastruktur um. Statt Heroku setzen wir nun auf Kubernetes.

Unser Intranet ist durch einen SSO (Single Sign-on) geschützt, welches bisher keine Ausnahmen für externe Zugriffe außerhalb der bekannten Strukturen erlaubte. Dahingehend mussten wir unsere Nginx Ingress Controller Konfiguration anpassen.

Dabei haben wir einige Stolpersteine mitgenommen. In der Konfiguration wurde "ssl: true" aus der Build-Variablen entfernt. Dies hatte zur Folge, dass der "tls"-Block aus Ingress entfernt wurde und es daher keine SSL-Konfiguration gab.

Der 2. Ingress muss darüber hinaus auch als SSL markiert werden, was den Einsatz von "kubernetes.io/tls-acme" notwendig macht sowie den "tls:"-Block, der sagt, für welche Domains Zertifikate ausgestellt werden sollen.

Erschwert wurde das Gesamtkonstruktut dadurch, dass Kube-Lego deprecated ist und auf Kubernetes 1.9 nicht mehr supported wird. Dahingehend mussten wir alternativ erst auf cert-manager updaten.

KI generiert: Das Bild zeigt jemanden, der mit einem Raspberry Pi und einem angeschlossenen Bildschirm arbeitet. Auf einem Tisch liegen weitere elektronische Geräte und Bauteile.

Problem 4: Skript im Autostart

Last but not least - Der Pi soll ohne weiteres Zutun unsererseits nach einem Neustart von alleine wieder einsatzbereit sein. Dafür wurde Firefox in den Autostart gesetzt und unser eigener System Service frankWalter.service implementiert (s.u.)

Um den System Service ausführen zu können, wird ein neuer Benutzer benötigt. Dabei ist auch darauf zu achten, dass der Nutzer die Rechte hat den Code ausführen zu dürfen.

[Unit] Description=Python Sandstorm time clock After=syslog.target network-online.target [Service] Type=simple User=pi ExecStart=/utl/timeClock.py Restart=on-failure RestartSec=10 KillMode=process [Install] WantedBy=multi-user.target

Raspberry Pi Installation

  1. Kopieren von timeClock.py und MFRC522.py nach /utl/
  2. Kopieren von frankWalter.service nach /etc/systemd/system
  3. Leserechte für timeClock.py für User einrichten
  4. Firefox installieren: sudo apt-get install firefox-esr
    1. Addon Remote Control for Firefox installieren
    2. systemctl daemon-reload
    3. systemctl enable frankWalter
    4. systemctl start frankWalter
  5. Firefox in den Autostart integrieren
  6. Fertig!

Fertig!

Die Zeiterfassung eines jeden Sandstormers lässt sich nun bequem per RFID-Chip starten und stoppen!

Für weitere Fragen stehen wir euch gern zur Verfügung!

Raspberry Pi Zeiterfassung

Dein Besuch auf unserer Website produziert laut der Messung auf websitecarbon.com nur 0,28 g CO₂.