Update 34 files

- /nfc/README.md
- /nfc/clean_old_logs.sh
- /nfc/clockin.py
- /nfc/emptycards.py
- /nfc/sync_clocks.sh
- /piservices/Dockerfile
- /piservices/Portainer-lamp-stack.env
- /piservices/Portainer-lamp-stack.yaml
- /piservices/README.md
- /piservices/avahi-alias.service
- /piservices/clockpy.service
- /piservices/emptycards.service
- /piservices/nfc-sync.service
- /piservices/test.sh
- /www/404
- /www/get-table-data.php
- /www/habiwan copy.png
- /www/habiwan.png
- /www/index.php
- /www/log_swipe.php
- /www/manage_data.php
- /www/manifest.json
- /www/names.csv
- /www/network_audit.csv
- /www/network-tracker.html
- /www/nfc_debug.log
- /www/scanner.html
- /www/staff_names.php
- /www/times.csv
- /www/check_update.php
- /www/dev.php
- /www/favicon.png
- /www/JsBarcode.all.min.js
- /www/README.md
This commit is contained in:
jpd committed 2026-07-24 14:37:45 +01:00
1 parent 10824ab851
commit a201d5d7ac
34 files changed
+9453

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
Use this folder as an example of what should be in your working "NFC" python folder on your pi.
This was the folder that was generated after all the compliling of the NFC manufacturer software.
(broadly explained in the main README.md...)
This folder, in my example also has a few shell scripts that I run via services...
in my example I mounted a "NAS" and a "www" folder on the pi's /etc/fstab so the scripts therein can copy
names.csv and times.csv on those locations with simple cp commands...
You could say that IF you want to implement your own version and want to do it my way, this a PREREQUISITE then:
mount on the pi's /etc/fstab 2 targets:
- //192.168.X.X/NASFOLDER /media/NAS cifs credentials=/root/.NASCREDS,uid=1000,gid=1000,noauto,x-systemd.automount,x-systemd.mount-timeout=30 0 0
- YOURUVMUSER@192.168.X.X:/home/YOURUVMUSER/docker/docker-compose-lamp/www /media/uvm fuse.sshfs noauto,x-systemd.automount,_netdev,allow_other,IdentityFile=/root/.ssh/id_rsa,StrictHostKeyChecking=accept-new,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 0 0
For the SMB CIFS I added the NASCREDS file with username and password in the RPi /root/ folder (root runs these scripts in services! that's why... room for improvement though)
For the SSHFS UVM www folder mount on the pi, first apt install sshfs then you got to exchange keys and have them in ../html/www/.ssh (I know is not optimal, I am working on hardening see TODO.md)
I also wanted to keep my samba mount for local dev on the php code an the UMV machine but not as guest so I had to run smbpasswd command on the UVM (UbuntuVM).
If you also want to keep the /etc/samba/smb.conf on the UVM you still want to keep samba with password secured:
[www]
path = /home/YOURUVMUSER/docker/docker-compose-lamp/www
browseable = yes
writable = yes
read only = no
public = no
guest ok = no
valid users = YOURUVMUSER
force user = YOURUVMUSER
create mask = 0664
directory mask = 0775
- mount it manually once on the pi, it will ask for password then it should be using the ssh keys... this should definitely be improved for production.
The scripts on the pi run as a service by root, so they can copy names.csv and times.csv to the "NAS" and "UVM"
In my example I used a "NAS" wannabe... horrible consumer grade WD MyCloud for backups and where the Excel got it's data from
and "UVM" which was a decomissioned now "upclycled" consumer-grade PC that I installed pve on...
If you want to use this like me, the rationale was to have the pi as lean as possible only processing the nfc card "scans" and copying the data over to
the "webapp - UbuntuVM - PVE machine" and to the "NAS" for backups / disaster recovery... The processing goes to Excel clients or php / LAMP on the "PC"...
UPDATE:
I modified the main python so that it uses mysql so most of the above is not needed anymore byt feel free to keep the csvs as raw hardcopies... I modified the php pages to use mysql too and the Docker compose to a Portainer stack that could be used on a Pi too... so the csv file syncin no more needed... anymore
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
# added sudo crontab to the pi to trim old data at 2 am:
# 0 2 * * * /bin/bash /home/YOURPIUSER/nfc/clean_old_logs.sh >/dev/null 2>&1
TIMES_FILE="/home/YOURPIUSER/nfc/times.csv"
# 1. Get the date 6 months ago (for testing)
LOG_CUTOFF=$(date -d "6 months ago" +%Y-%m-%d)
# 2. Filter using universally compatible literal matching
awk -v cutoff="$LOG_CUTOFF" '
{
if (match($0, /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/)) {
date_part = substr($0, RSTART, RLENGTH)
if (date_part < cutoff) {
next
}
}
print $0
}' "$TIMES_FILE" > "${TIMES_FILE}.tmp"
# 3. Safely replace the file
mv "${TIMES_FILE}.tmp" "$TIMES_FILE"
# 4. Force the sync script to grab the new file inode
systemctl restart nfc-sync.service
+134
View File
@@ -0,0 +1,134 @@
"""
This example shows connecting to the PN532 with I2C (requires clock
stretching support), SPI, or UART. SPI is best, it uses the most pins but
is the most reliable and universally supported.
After initialization, try waving various 13.56MHz RFID cards over it!
Modified and enhanced by F.Javier "habiwan" Puig Diaz in May/June 2025
Remember to adjust the paths!
It writes the raw times.csv file which is then used with names.csv later during processing.
This example is production ready and has been working since June 2025.
We were using an excel file that lists the date swiped and name of employee,
but now I developed a php webapp with the same functionality and nicer look.
Copyleft by F.Javier Puig Diaz, July 2026
UPDATE: mysql for lamp usage (pip3 install mysql-connector-python first of course...)
"""
import RPi.GPIO as GPIO
import vcgencmd
import mysql.connector
from datetime import datetime
import time # Imported time module directly for our debounce math
from pn532 import *
# --- DATABASE CONFIGURATION ---
db_config = {
'host': 'db', # Or your Pi's IP/localhost
'user': 'root',
'password': 'rootpassword',
'database': 'lampapp'
}
# --- DEBOUNCE CONFIGURATION ---
last_swipe_times = {}
DEBOUNCE_SECONDS = 60
def get_cpu_temp():
"""Fetches the CPU temperature using vcgencmd."""
try:
# Standard approach for vcgencmd on Raspberry Pi
temp = vcgencmd.measure_temp()
# Returns a string like "temp=56.0'C", we need to extract the float
temp_value = float(temp.replace("temp=", "").replace("'C", ""))
return temp_value
except:
return 0.0 # Fallback if reading fails
def ensure_tables_exist(cursor):
"""Creates tables if they are missing."""
# Create 'names' table
cursor.execute("""
CREATE TABLE IF NOT EXISTS names (
id INT AUTO_INCREMENT PRIMARY KEY,
CardID VARCHAR(12) NOT NULL UNIQUE,
name VARCHAR(100) DEFAULT NULL
)
""")
# Create 'times' table
cursor.execute("""
CREATE TABLE IF NOT EXISTS times (
UID VARCHAR(12) NOT NULL,
temp FLOAT,
timestamp DATETIME
)
""")
def log_to_db(uid_str, timestamp):
try:
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor()
# --- NEW: Ensure structure exists before inserting ---
ensure_tables_exist(cursor)
cpu_temp = get_cpu_temp()
sql = "INSERT INTO times (UID, temp, timestamp) VALUES (%s, %s, %s)"
cursor.execute(sql, (uid_str, cpu_temp, timestamp))
conn.commit()
cursor.close()
conn.close()
except mysql.connector.Error as err:
print(f"Database error: {err}")
if __name__ == '__main__':
try:
pn532 = PN532_I2C(debug=False, reset=20, req=16)
ic, ver, rev, support = pn532.get_firmware_version()
print('Found PN532 with firmware version: {0}.{1}'.format(ver, rev))
pn532.SAM_configuration()
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
buzzer = 23
GPIO.setup(buzzer, GPIO.OUT)
# Boot beep
GPIO.output(buzzer, GPIO.HIGH)
time.sleep(0.1)
GPIO.output(buzzer, GPIO.LOW)
print('Waiting for RFID/NFC card...')
while True:
uid = pn532.read_passive_target(timeout=0.5)
if uid is None:
continue
# Convert list of hex to a clean string immediately
uid_str = " ".join([hex(i) for i in uid])
current_epoch = time.time()
# --- Per-Card Debounce Logic ---
if uid_str in last_swipe_times:
time_since_last = current_epoch - last_swipe_times[uid_str]
if time_since_last < DEBOUNCE_SECONDS:
# Ignore the card if it was swiped less than 60 seconds ago
continue
# Record the new swipe time for this specific card
last_swipe_times[uid_str] = current_epoch
# -------------------------------
# Log to DB
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
log_to_db(uid_str, current_time)
# Buzzer feedback
GPIO.output(buzzer, GPIO.HIGH)
time.sleep(0.1)
GPIO.output(buzzer, GPIO.LOW)
except Exception as e:
print(f"System error: {e}")
finally:
GPIO.cleanup()
+72
View File
@@ -0,0 +1,72 @@
"""
This example shows connecting to the PN532 with I2C (requires clock
stretching support), SPI, or UART. SPI is best, it uses the most pins but
is the most reliable and universally supported.
After initialization, try waving various 13.56MHz RFID cards over it!
Modified and enhanced by F.Javier "habiwan" Puig Diaz in May/June 2025
This optional set of script/service is to get an excel file file written with the codes of empty cards you swipe (whevever you get new cards)
For example you could get an excel file written with the codes and matching names so a number can be written on empty cards
uses the production names.csv that I got from prescanning 100 cards and getting their unique ID on column 1 and _UNASSIGNEDXX on column 2
so that matching names to their unique ID is simple. I suggest writing with a pen a number to a label on those new empty cards...
This uses the same production names.csv file, new names like _UNASSIGNEDXX should be column 2 column 1 the unique ID stripped as shown in the get-table-data.php example:
Look for /** 2. PARSE AND MAP TIMESTAMPS (times.csv) **/ and see what $combinedData = []; does...
... or use an EXCEL with the formulae shown in the main README.md ....
GOAL is to see the results after swiping a card and writing a little number in the corner with the unassigned card number for future employees / temps...
This way the empty cards that all look the same will have the labelled number matched to their unique ID for later processing...
Remember to adjust the paths!
"""
import RPi.GPIO as GPIO # used for nfc and buzzer
import vcgencmd # added for CPU temperature readings
from datetime import datetime # added for timestamps
from time import sleep # added for buzzer
from pathlib import Path # added to 'touch' the csv if it does not exist yet or gets deleted
from pn532 import *
Path('/home/YOURPIUSER/nfc/emptycards.csv').touch()
if __name__ == '__main__':
try:
pn532 = PN532_I2C(debug=False, reset=20, req=16)
ic, ver, rev, support = pn532.get_firmware_version()
print('Found PN532 with firmware version: {0}.{1}'.format(ver, rev))
# Configure PN532 to communicate with MiFare cards
pn532.SAM_configuration()
#Disable warnings (optional)
GPIO.setwarnings(False)
#Select GPIO mode
GPIO.setmode(GPIO.BCM)
#Set buzzer - pin 23 as output
buzzer=23
GPIO.setup(buzzer,GPIO.OUT)
# debug Beep on boot
GPIO.output(buzzer,GPIO.HIGH)
sleep(0.01)
GPIO.output(buzzer,GPIO.LOW)
print('Waiting for RFID/NFC card...')
while True:
# Check if a card is available to read
uid = pn532.read_passive_target(timeout=0.5)
print('', end="")
# Try again if no card is available.
if uid is None:
continue
temp = vcgencmd.measure_temp()
s = ["UID: ", [hex(i) for i in uid], temp, str(datetime.now())]
with open("/home/YOURPIUSER/nfc/emptycards.csv", "a") as f:
f.writelines(str(s))
f.write("\n")
print(s) # debug
GPIO.output(buzzer,GPIO.HIGH)
#print("Beep") # debug
sleep(0.1) # Beep delay in seconds
GPIO.output(buzzer,GPIO.LOW)
sleep(1.7) # debounce
f.close()
except Exception as e:
print(e)
finally:
GPIO.cleanup()
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# Paths to the files we are watching
TIMES_FILE="/home/YOURPIUSER/nfc/times.csv"
NAMES_FILE="/home/YOURPIUSER/nfc/names.csv"
echo "Watching $TIMES_FILE and $NAMES_FILE for changes..."
# Loop indefinitely
while inotifywait -e modify "$TIMES_FILE" "$NAMES_FILE"; do
echo "Change detected! Copying files..."
# copy commands that happen after any swipe (or NFC-card name change):
cp /home/YOURPIUSER/nfc/*.csv /media/NAS/PiClock/nfc/ # make sure you mount these 2 on your pi's /etc/fstab
cp /home/YOURPIUSER/nfc/*.csv /media/uvm/ # see nfc/pifolder.md for more info
echo "Sync complete."
done