Kontakt

2020 Update: DIY Raspberry Pi Stechuhr mit NFC (Teil 3)

by Alex on 11.03.2020

Zwei Jahre ist es nun her, dass die Raspberry Pi Stechuhr in unserem Dresdner Büro im täglichen Einsatz ist. Im Laufe der Zeit hat sich, danke an Robert für das Feedback, etwas im Setup-Prozess geändert. Das nehmen wir zum Anlass, um unsere Raspberry Pi Stechuhr auf den Stand der Technik in 2020 zu heben.

Zur Erinnerung - die ersten beiden Teile findet ihr hier:

Was hat sich verändert?

Nun, im Grundaufbau der NFC Raspberry Pi Stechuhr hat sich eine kleine, dafür umso mehr entscheidende Stelle geändert. Wir nutzten ein Firefox Add-On, um selbigen per Telnet fernzusteuern. Das war von Anfang an ein Workaround - funktionierte aber tadellos. Bis folgendes passierte:

Ende Januar 2020 entfernte Mozilla alle Firefox-Extensions die Remote-Code ausführten.

Dieser Schritt, aus sicherheitstechnischen Gründen vollkommen nachvollziehbar, führte zwangsläufig zur Löschung des in unserem Setup genutzten Remote-Control Add-Ons. Also hieß es eine stabilere Lösung finden :)

Installation des Raspberry Pi

An der Hardware hat sich für uns seit dem letzten Artikel nichts verändert. Dagegen ändert sich Im Bereich Software sehr viel in zwei Jahren.

Als OS nutzen wir nun Raspbian Buster with Desktop. Dies lässt sich einfach mit dem Apple Pi Backer oder einem anderen Programm dafür auf eine SD-Karte spielen. Die SD-Karte einfach in den Pi einstecken und los geht's.

Zur Konfiguration wird im Terminal folgender Befehl ausgeführt:

  1. sudo raspi-config

Anschließend kann im Tool das SPI (unter Interfacing Options => SPI) und SSH (optional) eingeschaltet werden.

Für die Ausführung des neuen Skripts müssen folgende Befehle zur Installation von Chromium, den Treibern zum Steuern von Chromium und die Steuer-API installiert werden.

  1. sudo apt-get update && sudo apt-get install
  2. sudo apt-get install chromium-browser chromium-chromedriver
  3. pip3 install -U selenium

Zur Verwendung von SPI in Python muss noch die Extension für Python installiert werden. Da dies nicht ohne Weiteres möglich ist, erfolgt die Installation händisch wie folgt: 

  1. cd ~
  2. git clone https://github.com/lthiery/SPI-Py.git
  3. cd SPI-Py
  4. sudo python3 setup.py build && sudo python3 setup.py install

Die Skripte

Aus Kompatibilitätsgründen musste mit dem Wechsel auf Python3 auch das Skript zur Kommunikation mit dem NFC Reader ausgetauscht werden. Hier ist der Link zum neuen Skript. Dabei handelt es sich um einen Fork des alten Skriptes mit kleineren Anpassungen, um es mit Python3 kompatibel zu machen.

Statt wie bisher den Browser per Telnet fernzusteuern, nutzen wir nun eine API um den Browser direkt zu steuern. Dies hat den Vorteil dass es deutlich sicherer ist und (hoffentlich) länger unterstützt wird.

  1. #!/usr/bin/env python
  2. # -*- coding: utf8 -*-
  3.  
  4. import RPi.GPIO as GPIO
  5. import MFRC522
  6. import signal
  7. import datetime
  8. import platform
  9. import sys
  10. import time
  11. import threading
  12.  
  13. from selenium import webdriver
  14.  
  15.  
  16. def main():
  17. # start browser, might take a while
  18. browser = initBrowser()
  19. continue_reading = True
  20.  
  21. last_uid = [0, 0, 0, 0, 0]
  22. time_to_delete = 0
  23.  
  24.  
  25. # Capture SIGINT for cleanup when the script is aborted
  26. def end_read(signal, frame):
  27. global continue_reading
  28. continue_reading = False
  29. GPIO.cleanup()
  30.  
  31. if browser is not None:
  32. browser.quit()
  33.  
  34. sys.exit(0)
  35.  
  36.  
  37. # Hook the SIGINT and SIGTERM
  38. signal.signal(signal.SIGINT, end_read)
  39. signal.signal(signal.SIGTERM, end_read)
  40.  
  41. # Create an objecet of the class MFRC522
  42. MIFAREReader = MFRC522.MFRC522()
  43.  
  44. # This loop keeps checking for chips. If one is near it will get the UID and authenticate
  45. while continue_reading:
  46. (status, TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
  47.  
  48. # If a card is found
  49. if time_to_delete != 0 and time_to_delete < datetime.datetime.now():
  50. time_to_delete = 0
  51. last_uid = [0, 0, 0, 0, 0]
  52. navigate(browser, 'https://sandstorm.de')
  53.  
  54. # Get the UID of the card
  55. (status, uid) = MIFAREReader.MFRC522_Anticoll()
  56.  
  57. # If we have the UID, continue
  58. if status == MIFAREReader.MI_OK:
  59. if last_uid != uid:
  60. uid_strings = []
  61. for part in uid:
  62. uid_strings.append(str(hex(part)).replace('0x', ''))
  63. # we only use the first 4 entries
  64. uid_strings.pop(4)
  65. uid_string = '-'.join(uid_strings)
  66.  
  67. last_uid = uid
  68. time_to_delete = datetime.datetime.now() + datetime.timedelta(0, 5)
  69.  
  70. url = "https://www.your-url.de/workingtime/dongle/toggle.html?rfidToken=" + uid_string
  71. navigate(browser, url)
  72.  
  73.  
  74. # start the browser in fullscreen and open the default page
  75. def initBrowser():
  76. options = webdriver.ChromeOptions()
  77. options.add_argument('--kiosk')
  78.  
  79. browser = webdriver.Chrome(options=options)
  80. global_browser = browser
  81. browser.get('https://sandstorm.rocks/')
  82. return browser
  83.  
  84. # small funktion to change current page in browser
  85. def navigate(browser, url):
  86. if browser is None:
  87. print('navigation not available')
  88. else:
  89. browser.get(url)
  90.  
  91. if __name__ == '__main__':
  92. main()
  93.  

Die beiden Skripte befinden sich bei uns im Ordner `/utl` und lassen sich nach Anpassung des folgenden Services bei Bedarf ohne Probleme auch in einem anderen Ordner ablegen.

Achtung: der ausführende Nutzer muss die notwendigen Rechte haben, um die Skripte ausführen zu dürfen.

  1. cd /utl
  2. chmod +x timeClock.py
  3. chmod +x MFRC522.py

Autostart

Wie bisher, überlassen wir das automatische Starten des Skripts dem systemd von Rasbpian. Eine Änderung ist allerdings, dass dies nun pro Nutzer und nicht mehr systemweit konfiguriert wird. Die Konfiguration des Dienstes sieht wie folgt aus:

  1. [Unit]
  2. Description=Python Sandstorm time clock
  3. After=network.target
  4. PartOf=graphical-session.target
  5.  
  6. [Service]
  7. Type=simple
  8. ExecStart=python3 /utl/timeClock.py
  9. Restart=on-failure
  10. RestartSec=10
  11. KillMode=process
  12.  
  13. [Install]
  14. WantedBy=default.target

Diese Datei heißt bei uns `frankWalter.service` und befindet sich im Ordner `~/.config/systemd/user`

Um das Ganze nun zu starten, muss nur der Service gestartet werden:

  1. systemctl --user enable frankWalter.service

Geschafft!

Wir hoffen sehr, dass euch das Update gefällt! Für weitere Fragen stehen wir euch gern zur Verfügung!