Scan2Attend
An IoT-powered classroom attendance system that records student check-ins via fingerprint scanning on an ESP32 device, synced to a Laravel backend with offline SD card queuing.
↑ Animated system architecture — ESP32 → Backend → Frontend data flow
// How It Works
Attendance Pipeline
Fingerprint Scan
Student places finger on the AS608 sensor; image is captured and matched
ESP32 Processing
Firmware identifies the fingerprint, timestamps with RTC, and builds JSON payload
Backend Sync
Check-in is POSTed to the Laravel API, or queued to SD if offline
Dashboard View
Attendance records are aggregated and displayed on the Nuxt.js frontend
// Hardware
Component Wiring
ESP32 Dev Module
Main controller
—
AS608 Fingerprint
Biometric input
GPIO 16/17 (UART2)
DS3231 RTC
Timekeeping
GPIO 21/22 (I2C)
LCD 16×2 I2C
User display
GPIO 21/22 (I2C)
MicroSD Module
Offline storage
GPIO 5/23/19/18 (SPI)
Passive Buzzer
Audio feedback
GPIO 25
// Capabilities
Features
// Code Sample
Fingerprint Scan Loop
// 24/7 Continuous Fingerprint Scanning
if (sensorConnected && !enrollRequested) {
uint8_t p = finger.getImage();
if (p == FINGERPRINT_OK) {
p = finger.image2Tz();
p = finger.fingerSearch();
if (p == FINGERPRINT_OK) {
int fpId = finger.fingerID;
// Check cooldown (prevent duplicate scans)
if (fpId == lastScannedID &&
millis() - lastScanTime < SCAN_COOLDOWN) {
return; // Same finger, too fast
}
String scannedAt = getRTCDateTime();
// Build JSON payload
JsonDocument doc;
doc["fingerprint_id"] = fpId;
doc["scanned_at"] = scannedAt;
// POST to Laravel backend
if (backendConnected) {
String response = httpPostJson(checkInURL, payload);
showStudent(displayName, timeStr);
buzzerSuccess();
} else {
// Offline: queue to SD card
queueAttendance(fpId, scannedAt);
showOfflineSaved(displayName);
buzzerOfflineSave();
}
}
}
}// Technical Deep Dive
Challenges & Solutions
Hardware–Software Integration
Problem
Coordinating multiple peripherals (AS608 sensor, DS3231 RTC, LCD, SD card, buzzer) on a single ESP32 without blocking the main scan loop
Solution
Used non-blocking timer patterns and modular header files for each peripheral, allowing the main loop to continuously scan while background tasks run on intervals
Offline Resilience
Problem
Attendance records would be lost when Wi-Fi or the backend server was unreachable
Solution
Implemented JSONL-based queue on the SD card. Records are appended when offline and automatically replayed via /api/attendance/sync when connectivity is restored
Real-time Backend Sync
Problem
Ensuring attendance records arrive in the correct session with accurate timestamps despite network latency
Solution
The ESP32 uses a DS3231 RTC for precise local timestamps, and the Laravel backend resolves the active session server-side based on the scanned_at value
// Architecture
System Overview
ESP32 Firmware
Runs a 24/7 scan loop with non-blocking timers. Manages AS608 sensor, DS3231 RTC, LCD display, SD card queue, and buzzer — all through modular header files.
Laravel Backend
RESTful API handles check-ins, session resolution, enrollment, and attendance sync. Models the university structure with courses, sections, and sessions.
Nuxt.js Dashboard
Admin-facing frontend for viewing attendance records, managing students, enrolling fingerprints, and monitoring device health in real time.
Explore the Source Code
The project is split across two repositories — ESP32 firmware and the web platform.
Enjoyed this project? Consider supporting my work ☕
☕Buy Me a Coffee