874 RFID Hands-On Labs and Assessment
874.1 Learning Objectives
By the end of this chapter, you will be able to:
- Build Access Control Systems: Create complete RFID door lock systems with LCD display, LEDs, and logging
- Develop Inventory Dashboards: Implement web-based inventory management with real-time RFID scanning
- Apply Security Knowledge: Evaluate RFID system security and select appropriate tag types
- Select Frequency Bands: Choose LF, HF, or UHF based on application requirements
- Integrate Transit Systems: Understand NFC payment integration for public transportation
- Troubleshoot RFID Issues: Diagnose tag detection problems, interference, and read failures
What is this chapter? Complete hands-on projects and knowledge assessment for RFID systems.
Before starting labs: 1. Review RFID Hardware Integration 2. Gather required hardware components 3. Set up development environment (Arduino IDE or Python)
Hardware for Labs:
| Lab | Hardware Required | Estimated Cost |
|---|---|---|
| Lab 1 | ESP32 + RC522 + LCD + LEDs | $15-20 |
| Lab 2 | Raspberry Pi + RC522 | $40-50 |
Assessment Focus: - RFID frequency selection - Security vulnerabilities - Payment system design
874.2 Prerequisites
Before diving into this chapter, you should be familiar with:
- RFID Hardware Integration: Arduino and Python programming for RFID readers
- RFID Industry Applications: Real-world deployment scenarios
- RFID Security and Privacy: Understanding vulnerabilities and secure tag types
874.3 Lab 1: ESP32 RFID Access Control System
Build a complete access control system with LCD display, buzzer feedback, and access logging.
Hardware Required: - ESP32 Development Board - RC522 RFID Reader Module (13.56 MHz) - I2C LCD Display (16x2 or 20x4) - Buzzer - Green and Red LEDs - RFID cards/tags - Resistors (220Ω for LEDs, 1kΩ for buzzer)
Wiring:
| Component | ESP32 Pin |
|---|---|
| RC522 SDA | GPIO 5 |
| RC522 SCK | GPIO 18 |
| RC522 MOSI | GPIO 23 |
| RC522 MISO | GPIO 19 |
| RC522 RST | GPIO 4 |
| RC522 3.3V | 3.3V |
| RC522 GND | GND |
| LCD SDA | GPIO 21 |
| LCD SCL | GPIO 22 |
| Green LED | GPIO 25 (via 220Ω resistor) |
| Red LED | GPIO 26 (via 220Ω resistor) |
| Buzzer | GPIO 27 (via 1kΩ resistor) |
Complete Code:
#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Pin definitions
#define SS_PIN 5
#define RST_PIN 4
#define GREEN_LED 25
#define RED_LED 26
#define BUZZER 27
// Initialize components
MFRC522 rfid(SS_PIN, RST_PIN);
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 cols, 2 rows
// Authorized UIDs (add your card UIDs here)
String authorizedUIDs[] = {
"04521AB2", // Employee card 1
"04A1B2C3D4E5F6", // Employee card 2
"D3A512C4" // Admin card
};
int numAuthorized = sizeof(authorizedUIDs) / sizeof(authorizedUIDs[0]);
// Access log structure
struct AccessLog {
String uid;
String timestamp;
bool granted;
};
AccessLog accessLog[100]; // Store last 100 access attempts
int logIndex = 0;
void setup() {
Serial.begin(115200);
// Initialize SPI and RFID
SPI.begin();
rfid.PCD_Init();
// Initialize LCD
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("RFID Access");
lcd.setCursor(0, 1);
lcd.print("Control Ready");
// Initialize LEDs and buzzer
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(BUZZER, OUTPUT);
// Test sequence
digitalWrite(GREEN_LED, HIGH);
delay(200);
digitalWrite(GREEN_LED, LOW);
digitalWrite(RED_LED, HIGH);
delay(200);
digitalWrite(RED_LED, LOW);
tone(BUZZER, 1000, 100);
Serial.println("\n=== RFID Access Control System ===");
Serial.println("System initialized. Waiting for cards...\n");
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Scan Your Card");
}
void loop() {
// Look for new cards
if (!rfid.PICC_IsNewCardPresent())
return;
// Select one of the cards
if (!rfid.PICC_ReadCardSerial())
return;
// Read card UID
String uid = "";
for (byte i = 0; i < rfid.uid.size; i++) {
uid += String(rfid.uid.uidByte[i] < 0x10 ? "0" : "");
uid += String(rfid.uid.uidByte[i], HEX);
}
uid.toUpperCase();
// Check authorization
bool authorized = false;
for (int i = 0; i < numAuthorized; i++) {
if (uid.equals(authorizedUIDs[i])) {
authorized = true;
break;
}
}
// Display result and log access
if (authorized) {
grantAccess(uid);
} else {
denyAccess(uid);
}
// Halt card and stop encryption
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
// Return to ready state after 3 seconds
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Scan Your Card");
digitalWrite(GREEN_LED, LOW);
digitalWrite(RED_LED, LOW);
}
void grantAccess(String uid) {
Serial.println("✓ ACCESS GRANTED");
Serial.println(" UID: " + uid);
Serial.println(" Time: " + getTimestamp());
// LCD display
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ACCESS GRANTED");
lcd.setCursor(0, 1);
lcd.print("Welcome!");
// Green LED and success beep
digitalWrite(GREEN_LED, HIGH);
tone(BUZZER, 2000, 100);
delay(100);
tone(BUZZER, 2500, 100);
// Log access
logAccess(uid, true);
}
void denyAccess(String uid) {
Serial.println("✗ ACCESS DENIED");
Serial.println(" UID: " + uid);
Serial.println(" Time: " + getTimestamp());
Serial.println(" Reason: Unauthorized card");
// LCD display
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ACCESS DENIED");
lcd.setCursor(0, 1);
lcd.print("Unauthorized!");
// Red LED and error beep
digitalWrite(RED_LED, HIGH);
tone(BUZZER, 500, 200);
delay(250);
tone(BUZZER, 500, 200);
delay(250);
tone(BUZZER, 500, 200);
// Log access attempt
logAccess(uid, false);
}
void logAccess(String uid, bool granted) {
accessLog[logIndex].uid = uid;
accessLog[logIndex].timestamp = getTimestamp();
accessLog[logIndex].granted = granted;
logIndex = (logIndex + 1) % 100; // Circular buffer
}
String getTimestamp() {
// In real system, use RTC module for accurate timestamps
// For now, return milliseconds since boot
unsigned long ms = millis();
unsigned long seconds = ms / 1000;
unsigned long minutes = seconds / 60;
unsigned long hours = minutes / 60;
return String(hours % 24) + ":" +
String(minutes % 60) + ":" +
String(seconds % 60);
}
void printAccessLog() {
// Call this function via Serial command or button press
Serial.println("\n=== ACCESS LOG (Last 10 Entries) ===");
int start = max(0, logIndex - 10);
for (int i = start; i < logIndex; i++) {
Serial.print(accessLog[i].timestamp);
Serial.print(" | ");
Serial.print(accessLog[i].uid);
Serial.print(" | ");
Serial.println(accessLog[i].granted ? "GRANTED" : "DENIED");
}
Serial.println();
}Expected Output (Serial Monitor):
=== RFID Access Control System ===
System initialized. Waiting for cards...
✓ ACCESS GRANTED
UID: 04521AB2
Time: 0:5:12
✗ ACCESS DENIED
UID: F7A9B2C1
Time: 0:5:45
Reason: Unauthorized card
✓ ACCESS GRANTED
UID: E2801160600002A1
Time: 0:6:03
Expected Output (LCD Display):
[When ready]
Scan Your Card
[On authorized card]
ACCESS GRANTED
Welcome!
[On unauthorized card]
ACCESS DENIED
Unauthorized!
874.4 Lab 2: Python RFID Inventory Dashboard with Real-Time Monitoring
Build a complete inventory management dashboard with web interface using Flask.
Requirements:
pip install Flask mfrc522 RPi.GPIO sqlite3Web Dashboard Template (templates/dashboard.html):
<!DOCTYPE html>
<html>
<head>
<title>RFID Inventory Dashboard</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; background: #f5f5f5; padding: 20px; }
.container { max-width: 1400px; margin: 0 auto; }
h1 { color: #333; margin-bottom: 20px; }
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.stat-card h3 { color: #666; font-size: 14px; margin-bottom: 10px; }
.stat-card .value { font-size: 32px; font-weight: bold; color: #4CAF50; }
.sections {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.section h2 { margin-bottom: 15px; color: #333; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; }
th { background: #f9f9f9; font-weight: 600; color: #666; }
.live-scan {
background: #E3F2FD;
padding: 15px;
border-radius: 8px;
border-left: 4px solid #2196F3;
margin-bottom: 20px;
}
.live-scan.active {
background: #C8E6C9;
border-left-color: #4CAF50;
}
.timestamp { color: #999; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<h1>RFID Inventory Dashboard</h1>
<div class="live-scan" id="liveScan">
<strong>Last Scan:</strong> <span id="lastScanInfo">Waiting for tags...</span>
</div>
<div class="stats">
<div class="stat-card">
<h3>Total Items</h3>
<div class="value" id="totalItems">0</div>
</div>
<div class="stat-card">
<h3>Scans Today</h3>
<div class="value" id="scansToday">0</div>
</div>
<div class="stat-card">
<h3>Categories</h3>
<div class="value" id="categories">0</div>
</div>
</div>
<div class="sections">
<div class="section">
<h2>Inventory Items</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Category</th>
<th>Last Seen</th>
</tr>
</thead>
<tbody id="itemsTable">
<tr><td colspan="3">Loading...</td></tr>
</tbody>
</table>
</div>
<div class="section">
<h2>Recent Activity</h2>
<table>
<thead>
<tr>
<th>Item</th>
<th>Location</th>
<th>Time</th>
</tr>
</thead>
<tbody id="activityTable">
<tr><td colspan="3">Loading...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
// Update statistics
function updateStats() {
fetch('/api/statistics')
.then(r => r.json())
.then(data => {
document.getElementById('totalItems').textContent = data.total_items;
document.getElementById('scansToday').textContent = data.scans_today;
document.getElementById('categories').textContent =
Object.keys(data.by_category).length;
});
}
// Update items table
function updateItems() {
fetch('/api/items')
.then(r => r.json())
.then(items => {
const tbody = document.getElementById('itemsTable');
tbody.innerHTML = items.map(item => `
<tr>
<td><strong>${item.name}</strong><br>
<span class="timestamp">${item.rfid_uid}</span></td>
<td>${item.category}</td>
<td class="timestamp">${item.last_seen || 'Never'}</td>
</tr>
`).join('');
});
}
// Update recent scans
function updateActivity() {
fetch('/api/recent_scans')
.then(r => r.json())
.then(scans => {
const tbody = document.getElementById('activityTable');
tbody.innerHTML = scans.slice(0, 10).map(scan => `
<tr>
<td>${scan.item_name}</td>
<td>${scan.location}</td>
<td class="timestamp">${new Date(scan.timestamp).toLocaleTimeString()}</td>
</tr>
`).join('');
});
}
// Check for new scans (live updates)
let lastScanUID = null;
function checkLastScan() {
fetch('/api/last_scan')
.then(r => r.json())
.then(data => {
if (data.uid && data.uid !== lastScanUID) {
lastScanUID = data.uid;
const liveScan = document.getElementById('liveScan');
const scanInfo = document.getElementById('lastScanInfo');
liveScan.classList.add('active');
scanInfo.textContent = `${data.name} (${data.uid}) - ${new Date(data.timestamp).toLocaleTimeString()}`;
// Remove highlight after 3 seconds
setTimeout(() => liveScan.classList.remove('active'), 3000);
// Refresh data
updateStats();
updateItems();
updateActivity();
}
});
}
// Initial load
updateStats();
updateItems();
updateActivity();
// Periodic updates
setInterval(checkLastScan, 1000); // Check for new scans every second
setInterval(updateStats, 5000); // Update stats every 5 seconds
setInterval(updateItems, 10000); // Update items every 10 seconds
setInterval(updateActivity, 5000); // Update activity every 5 seconds
</script>
</body>
</html>Running the Dashboard:
python3 rfid_dashboard.pyExpected Output:
Starting RFID Inventory Dashboard...
RFID scanner thread started...
Access dashboard at: http://localhost:5000
* Running on http://0.0.0.0:5000
Tag detected: 1234567890
Item: Arduino Uno R3
Tag detected: 9876543210
Item: Raspberry Pi 4
874.5 Knowledge Check
Test your understanding of RFID technology and applications.
Deep Dives: - RFID Fundamentals and Standards - Operating principles and ISO standards - RFID Security and Privacy - Cryptographic authentication and privacy protocols - NFC Architecture - Near-field communication as HF RFID extension
Comparisons: - NFC vs RFID - When to use NFC versus traditional RFID - Technology Comparison - RFID in context of IoT technology stack
Learning: - Quizzes Hub - Test your RFID knowledge - Knowledge Gaps - Identify areas for deeper study
874.6 Summary
This chapter provided hands-on labs and assessment for RFID systems:
- Lab 1: ESP32 Access Control: Complete door lock system with RC522, LCD display, LED indicators, buzzer feedback, and access logging using circular buffer
- Lab 2: Python Dashboard: Flask-based web interface with real-time RFID scanning, inventory tracking, and periodic AJAX updates
- Security Assessment: MIFARE Classic Crypto1 vulnerability (broken 2008) vs DESFire EV3 AES-128 security
- Frequency Selection: LF for tissue penetration (pet microchips), HF/NFC for payments (<10cm range), UHF for inventory (1-12m range)
- Transit Systems: NFC is universal for payments (Apple Pay, Google Pay, EMV) - UHF’s long range causes accidental charges
874.7 What’s Next
Continue your RFID learning with RFID Security and Privacy for deeper coverage of cryptographic authentication, privacy protocols, and secure deployment practices.