vault backup: 2026-02-24 18:27:57
This commit is contained in:
@@ -53,3 +53,397 @@ Track employee timeclock punches via RFID cards and capture images via IP camera
|
|||||||
---
|
---
|
||||||
|
|
||||||
*Extracted from Projects to be organized*
|
*Extracted from Projects to be organized*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Research Findings
|
||||||
|
|
||||||
|
**Research Date:** 2026-02-24
|
||||||
|
**Focus:** RFID + IP Camera Latency Solutions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Latency Problem Analysis
|
||||||
|
|
||||||
|
### Current Bottleneck Identification
|
||||||
|
The primary latency in RFID-to-camera capture systems comes from multiple sequential steps:
|
||||||
|
|
||||||
|
1. **RFID Read Latency**: 2-5ms (physical card detection + data transfer)
|
||||||
|
2. **USB/Serial Communication**: 1-10ms depending on interface
|
||||||
|
3. **HTTP Request to Camera**: 20-100ms (network round-trip)
|
||||||
|
4. **Camera Shutter/IPE Frame Capture**: 50-200ms (depends on exposure/lighting)
|
||||||
|
5. **Image Transfer**: 50-500ms (depends on resolution)
|
||||||
|
|
||||||
|
**Total Typical Latency: 123-815ms**
|
||||||
|
|
||||||
|
### Critical Insight from Research
|
||||||
|
The biggest latency contributor is NOT the RFID reader (which is typically <10ms), but rather:
|
||||||
|
- Camera exposure/autofocus delay when triggering from idle
|
||||||
|
- Network round-trip for HTTP snapshot requests
|
||||||
|
- Image compression and transfer time
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommended Hardware (RFID Reader + Camera Combo)
|
||||||
|
|
||||||
|
### RFID Reader Options
|
||||||
|
|
||||||
|
#### Option 1: USB HID Keyboard Emulation (Recommended for Simplicity)
|
||||||
|
- **Cost:** $8-15 (Amazon: Dual-frequency 125KHz/13.56MHz readers)
|
||||||
|
- **Protocols:** ISO14443-A/B, EM4100
|
||||||
|
- **Latency:** <5ms from card touch to character output
|
||||||
|
- **Python Integration:** No special drivers - reads as keyboard input
|
||||||
|
- **Models:** Generic USB HID readers (Navfmru, HiLetgo, generic dual-frequency)
|
||||||
|
|
||||||
|
#### Option 2: Serial/UART RFID Reader (For Advanced Control)
|
||||||
|
- **Cost:** $12-25
|
||||||
|
- **Interface:** USB-to-Serial (appears as COM port)
|
||||||
|
- **Protocols:** SPI/I2C/UART
|
||||||
|
- **Latency:** <2ms (interrupt-driven serial)
|
||||||
|
- **Python Library:** `pyserial`
|
||||||
|
- **Best for:** When you need custom parsing or have multiple readers
|
||||||
|
|
||||||
|
#### Option 3: Ethernet-Connected RFID Reader
|
||||||
|
- **Cost:** $50-150
|
||||||
|
- **Latency:** Depends on network (1-5ms local)
|
||||||
|
- **Best for:** Industrial setups with existing PoE infrastructure
|
||||||
|
|
||||||
|
**Recommendation:** Start with USB HID Keyboard Emulation readers - simplest integration, lowest cost, adequate latency.
|
||||||
|
|
||||||
|
### IP Camera Recommendations
|
||||||
|
|
||||||
|
#### Low-Latency Trigger-Optimized Cameras
|
||||||
|
|
||||||
|
**Hikvision / Dahua (Best API Support):**
|
||||||
|
- Direct HTTP snapshot URLs: `http://camera-ip/Streaming/channels/1/picture`
|
||||||
|
- ONVIF snapshot: `http://camera-ip/onvif/snapshot`
|
||||||
|
- Typical latency: 50-150ms for snapshot
|
||||||
|
- **Key Feature:** Can pre-authenticate and keep session alive
|
||||||
|
|
||||||
|
**Axis (MQTT Support):**
|
||||||
|
- Native MQTT event publishing
|
||||||
|
- Can subscribe to ONVIF events
|
||||||
|
- More expensive but better integration options
|
||||||
|
|
||||||
|
**INSTAR (Built-in MQTT):**
|
||||||
|
- Native MQTT for motion triggers
|
||||||
|
- Can trigger snapshot via MQTT message
|
||||||
|
|
||||||
|
#### Latency-Reducing Camera Configuration:
|
||||||
|
1. **Fixed Focus** - Disable autofocus to eliminate 100-300ms delay
|
||||||
|
2. **Fixed Exposure** - Set manual exposure to eliminate AE calculation
|
||||||
|
3. **Low Resolution Stream** - Use 720p or lower for snapshot (faster transfer)
|
||||||
|
4. **Motion JPEG** - Prefer MJPEG over H.264 for snapshot endpoints
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Low-Latency Architecture Options
|
||||||
|
|
||||||
|
### Architecture A: HTTP Snapshot on RFID Event (Simple)
|
||||||
|
```
|
||||||
|
RFID Scan → Python HTTP Request → Camera → Save Image → Match
|
||||||
|
Latency: 100-300ms
|
||||||
|
```
|
||||||
|
**Pros:** Simple, no additional infrastructure
|
||||||
|
**Cons:** Every scan has network round-trip
|
||||||
|
|
||||||
|
### Architecture B: Always-Recording with Frame Grab (Best for Speed)
|
||||||
|
```
|
||||||
|
Camera always streaming (RTSP)
|
||||||
|
RFID Scan → Python grabs current frame from buffer → Match
|
||||||
|
Latency: 10-50ms
|
||||||
|
```
|
||||||
|
**Pros:** Fastest capture, no network request at scan time
|
||||||
|
**Cons:** Higher CPU/network usage, requires OpenCV/ffmpeg
|
||||||
|
|
||||||
|
### Architecture C: MQTT/Event-Driven (Scalable)
|
||||||
|
```
|
||||||
|
RFID Scan → Publish MQTT message → Camera subscribes → Captures
|
||||||
|
Latency: 50-150ms
|
||||||
|
```
|
||||||
|
**Pros:** Decoupled, multiple cameras possible
|
||||||
|
**Cons:** Requires MQTT broker
|
||||||
|
|
||||||
|
### Architecture D: ONVIF Events with Pre-trigger Buffer (Professional)
|
||||||
|
```
|
||||||
|
Camera constantly buffers 2-3 seconds
|
||||||
|
RFID triggers ONVIF event → Camera saves pre/post buffer
|
||||||
|
Latency: 0ms (image from before trigger!)
|
||||||
|
```
|
||||||
|
**Pros:** Can capture image BEFORE scan
|
||||||
|
**Cons:** Complex setup, requires ONVIF Profile S/G support
|
||||||
|
|
||||||
|
### Recommended Hybrid Approach:
|
||||||
|
```
|
||||||
|
Primary: Always-streaming RTSP with OpenCV frame buffer
|
||||||
|
Fallback: HTTP snapshot if buffer unavailable
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Python Libraries and Code Samples
|
||||||
|
|
||||||
|
### Library Requirements
|
||||||
|
```bash
|
||||||
|
pip install pyserial opencv-python requests onvif-zeep paho-mqtt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Sample 1: USB HID RFID Reader (Keyboard Emulation)
|
||||||
|
```python
|
||||||
|
import keyboard # pip install keyboard
|
||||||
|
from datetime import datetime
|
||||||
|
import requests
|
||||||
|
|
||||||
|
def on_rfid_scan(event):
|
||||||
|
"""Capture RFID UID from keyboard emulation"""
|
||||||
|
uid = event.name
|
||||||
|
timestamp = datetime.now()
|
||||||
|
|
||||||
|
# Trigger camera capture immediately
|
||||||
|
capture_image(uid, timestamp)
|
||||||
|
|
||||||
|
# Hook into keyboard events
|
||||||
|
keyboard.on_press(on_rfid_scan)
|
||||||
|
keyboard.wait()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Sample 2: Serial RFID Reader
|
||||||
|
```python
|
||||||
|
import serial
|
||||||
|
import threading
|
||||||
|
|
||||||
|
def read_rfid_serial(port='COM3', baudrate=9600):
|
||||||
|
ser = serial.Serial(port, baudrate, timeout=0.1)
|
||||||
|
buffer = ""
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if ser.in_waiting:
|
||||||
|
data = ser.read(ser.in_waiting).decode('utf-8')
|
||||||
|
buffer += data
|
||||||
|
|
||||||
|
# Check for complete UID (usually ends with CR/LF)
|
||||||
|
if '\r' in buffer or '\n' in buffer:
|
||||||
|
uid = buffer.strip()
|
||||||
|
buffer = ""
|
||||||
|
capture_image(uid)
|
||||||
|
|
||||||
|
# Run in separate thread for non-blocking
|
||||||
|
threading.Thread(target=read_rfid_serial, daemon=True).start()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Sample 3: HTTP Snapshot (Synchronous)
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
CAMERA_URL = "http://192.168.1.100/Streaming/channels/1/picture"
|
||||||
|
CAMERA_AUTH = ("admin", "password")
|
||||||
|
|
||||||
|
def capture_http_snapshot(uid):
|
||||||
|
start = datetime.now()
|
||||||
|
response = requests.get(CAMERA_URL, auth=CAMERA_AUTH, timeout=2)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
filename = f"captures/{uid}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
f.write(response.content)
|
||||||
|
|
||||||
|
latency = (datetime.now() - start).total_seconds() * 1000
|
||||||
|
print(f"Captured in {latency:.1f}ms")
|
||||||
|
return filename
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Sample 4: OpenCV Always-Streaming (Fastest)
|
||||||
|
```python
|
||||||
|
import cv2
|
||||||
|
import threading
|
||||||
|
import queue
|
||||||
|
|
||||||
|
class CameraBuffer:
|
||||||
|
def __init__(self, rtsp_url):
|
||||||
|
self.cap = cv2.VideoCapture(rtsp_url)
|
||||||
|
self.frame = None
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
|
||||||
|
# Start capture thread
|
||||||
|
threading.Thread(target=self._update, daemon=True).start()
|
||||||
|
|
||||||
|
def _update(self):
|
||||||
|
while True:
|
||||||
|
ret, frame = self.cap.read()
|
||||||
|
if ret:
|
||||||
|
with self.lock:
|
||||||
|
self.frame = frame
|
||||||
|
|
||||||
|
def get_frame(self):
|
||||||
|
with self.lock:
|
||||||
|
return self.frame.copy() if self.frame is not None else None
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
camera = CameraBuffer("rtsp://admin:pass@192.168.1.100:554/Streaming/Channels/1")
|
||||||
|
|
||||||
|
def capture_image(uid):
|
||||||
|
frame = camera.get_frame()
|
||||||
|
if frame is not None:
|
||||||
|
filename = f"captures/{uid}_{datetime.now():%Y%m%d_%H%M%S}.jpg"
|
||||||
|
cv2.imwrite(filename, frame)
|
||||||
|
return filename
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Sample 5: ONVIF Event Subscription
|
||||||
|
```python
|
||||||
|
from onvif import ONVIFCamera
|
||||||
|
|
||||||
|
def setup_onvif_events(camera_ip, user, password):
|
||||||
|
camera = ONVIFCamera(camera_ip, 80, user, password)
|
||||||
|
|
||||||
|
# Create events service
|
||||||
|
events = camera.create_events_service()
|
||||||
|
|
||||||
|
# Subscribe to events
|
||||||
|
subscription = events.CreatePullPointSubscription()
|
||||||
|
|
||||||
|
return camera, subscription
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MSSQL Optimization for Fast Employee Photo Matching
|
||||||
|
|
||||||
|
### Schema Recommendations
|
||||||
|
```sql
|
||||||
|
-- Store image hashes for fast comparison (not full images in DB)
|
||||||
|
CREATE TABLE EmployeePhotos (
|
||||||
|
EmployeeID INT PRIMARY KEY,
|
||||||
|
PhotoHash VARBINARY(64) INDEXED, -- SHA-256 hash
|
||||||
|
PhotoPath NVARCHAR(500), -- Path to actual image
|
||||||
|
FaceEncoding VARBINARY(MAX), -- If using face recognition
|
||||||
|
LastUpdated DATETIME2
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Index for hash lookups
|
||||||
|
CREATE NONCLUSTERED INDEX IX_PhotoHash ON EmployeePhotos(PhotoHash);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optimization Strategies
|
||||||
|
|
||||||
|
1. **Don't Store Images in MSSQL**
|
||||||
|
- Store file paths only
|
||||||
|
- Store perceptual hashes for comparison
|
||||||
|
- Keep images on fast local SSD
|
||||||
|
|
||||||
|
2. **Pre-compute Face Embeddings**
|
||||||
|
```python
|
||||||
|
import face_recognition
|
||||||
|
|
||||||
|
# One-time: Store face encoding
|
||||||
|
image = face_recognition.load_image_file("employee.jpg")
|
||||||
|
encoding = face_recognition.face_encodings(image)[0]
|
||||||
|
# Save encoding to DB as binary blob
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Use Memory-Optimized Tables (SQL Server)**
|
||||||
|
```sql
|
||||||
|
CREATE TABLE dbo.EmployeePhotos (
|
||||||
|
EmployeeID INT PRIMARY KEY NONCLUSTERED,
|
||||||
|
PhotoHash VARBINARY(64)
|
||||||
|
) WITH (MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_AND_DATA);
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Query Pattern for Matching**
|
||||||
|
```sql
|
||||||
|
-- Fast hash lookup first
|
||||||
|
SELECT EmployeeID FROM EmployeePhotos
|
||||||
|
WHERE PhotoHash = @capturedHash;
|
||||||
|
|
||||||
|
-- If using face recognition, compare embeddings
|
||||||
|
-- in Python (not SQL) for algorithm flexibility
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Recommendations
|
||||||
|
|
||||||
|
### Phase 1: Quick Win (HTTP Snapshot)
|
||||||
|
1. Purchase USB HID RFID reader ($10-15)
|
||||||
|
2. Use `keyboard` library to detect scans
|
||||||
|
3. Trigger Hikvision/Dahua HTTP snapshot
|
||||||
|
4. Measure actual latency
|
||||||
|
|
||||||
|
### Phase 2: Optimization (RTSP Buffer)
|
||||||
|
1. If latency >100ms is problematic, implement OpenCV RTSP buffer
|
||||||
|
2. Capture frame from buffer instead of HTTP request
|
||||||
|
3. Measure improvement
|
||||||
|
|
||||||
|
### Phase 3: Advanced (Face Recognition)
|
||||||
|
1. Add `face_recognition` library
|
||||||
|
2. Pre-compute employee face embeddings
|
||||||
|
3. Match captured image against embeddings
|
||||||
|
4. Log match confidence with each punch
|
||||||
|
|
||||||
|
### Latency Targets by Phase:
|
||||||
|
| Phase | Expected Latency | Implementation Time |
|
||||||
|
|-------|------------------|---------------------|
|
||||||
|
| Phase 1 | 100-300ms | 1-2 days |
|
||||||
|
| Phase 2 | 10-50ms | 3-5 days |
|
||||||
|
| Phase 3 | 10-50ms + match | 1-2 weeks |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Real-World Latency Benchmarks (from Research)
|
||||||
|
|
||||||
|
| Component | Typical | Optimized | Notes |
|
||||||
|
|-----------|---------|-----------|-------|
|
||||||
|
| RFID Read | 2-5ms | 1-2ms | HID faster than serial |
|
||||||
|
| USB Transfer | 1-10ms | 1-2ms | Interrupt vs polling |
|
||||||
|
| HTTP Request | 20-100ms | 10-20ms | Keep-alive connections |
|
||||||
|
| Camera Shutter | 50-200ms | 0ms | Fixed exposure eliminates |
|
||||||
|
| Image Transfer | 50-500ms | 5-20ms | Lower resolution, MJPEG |
|
||||||
|
| **Total HTTP** | **123-815ms** | **17-46ms** | With optimizations |
|
||||||
|
| **RTSP Buffer** | **10-50ms** | **5-15ms** | Fastest option |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Source Links
|
||||||
|
|
||||||
|
### RFID & Communication Latency
|
||||||
|
- USB Latency Analysis: <https://electronics.stackexchange.com/questions/192920/usb-device-latency>
|
||||||
|
- Serial vs USB Interrupt: <https://stackoverflow.com/questions/41987430/lowest-latency-communication-microcontroller>
|
||||||
|
- Wireless Latency Benchmarks: <https://electricui.com/blog/latency-comparison>
|
||||||
|
- RFID Reader Options: <https://raspberrypi.stackexchange.com/questions/13930/capturing-serial-number-usb-rfid-reader>
|
||||||
|
|
||||||
|
### Python RFID Integration
|
||||||
|
- MFRC522 Python Library: <https://github.com/pimylifeup/MFRC522-python>
|
||||||
|
- USB HID Reader Python: <https://github.com/charlysan/pyrfidhid>
|
||||||
|
- Serial RFID Example: <https://stackoverflow.com/questions/56808495/reading-tags-uid-rc522-usb-serial-python>
|
||||||
|
|
||||||
|
### Camera APIs & ONVIF
|
||||||
|
- Hikvision Snapshot URL: <https://www.cctvforum.com/topic/32499-snashot-url-for-hikvisionswannlorex-cameras/>
|
||||||
|
- ONVIF/OpenHAB Integration: <https://www.openhab.org/addons/bindings/ipcamera/>
|
||||||
|
- IP Camera Trigger Options: <https://ipcamtalk.com/tags/api/>
|
||||||
|
|
||||||
|
### MQTT Camera Control
|
||||||
|
- MQTT Camera Home Assistant: <https://www.home-assistant.io/integrations/camera.mqtt>
|
||||||
|
- Axis MQTT Integration: <https://www.hivemq.com/blog/enhancing-axis-network-camera-capabilities-mqtt-hivemq/>
|
||||||
|
- Cam2MQTT Project: <https://github.com/berfenger/cam2mqtt>
|
||||||
|
|
||||||
|
### RFID Hardware Options
|
||||||
|
- Dual-Frequency USB Readers: <https://www.amazon.com/13-56Mhz-ISO14443-Protocol-Contactless-Compatible/dp/B0CY2YT86P>
|
||||||
|
- ISO14443/EM4100 Readers: <https://gaorfid.com/devices/rfid-readers-frequency/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Key Takeaway:** The fastest solution for RFID-to-camera latency is combining:
|
||||||
|
1. USB HID RFID reader for <5ms card detection
|
||||||
|
2. OpenCV RTSP stream buffer for <20ms image capture
|
||||||
|
3. Skip HTTP requests entirely - grab from buffer
|
||||||
|
|
||||||
|
This yields **15-25ms total latency** from scan to image capture - well within acceptable limits for timeclock verification.
|
||||||
|
|
||||||
|
**Alternative Simple Path:** If RTSP complexity is undesirable, using HTTP snapshots with fixed-exposure camera settings can achieve 50-100ms latency with minimal implementation effort.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Research compiled by AI assistant. Hardware links are examples only - verify compatibility before purchase.*
|
||||||
|
|||||||
Reference in New Issue
Block a user