🔐 IoT-enabled biometric door lock powered by ESP32-S3 and Microsoft Azure
A personal IoT project (university assignment) featuring multiple authentication methods (fingerprint or PIN), cloud telemetry, and intelligent power management. Built on ESP32-S3 with real-time Azure IoT Hub integration.
- 🖐️ Biometric Authentication - Optical fingerprint sensor (JM-101B) with 2-stage enrollment
- 🔢 PIN Authentication - Secure password system (up to 64 digits) stored in NVS flash
- 🚨 Auto-Lockdown - 30-second security lockdown after 3 failed authentication attempts
- 🔄 Password Management - On-device password change with double verification
- 📡 Azure IoT Hub - Real-time device-to-cloud telemetry
- 🔌 Auto-Provisioning - Device Provisioning Service (DPS) with symmetric key auth
- 📊 Live Monitoring - Integration with Azure IoT Central dashboards
- 🔔 Event Streaming - 10 telemetry events (door status, security alerts, system events)
- 🔐 Secure Communication - MQTT over TLS 1.2 with Azure CA certificates
- 💤 Deep Sleep Mode - Ultra-low power consumption after 30s inactivity
- ⏰ RTC Wake-Up - Wake from keypad, fingerprint touch, or buttons via RTC GPIO
- 🔌 Smart Power Control - PNP transistor-switched peripherals (on-demand activation)
- 🔋 Battery Optimized - Intelligent module power gating for extended operation
- 🔊 Voice Feedback - I2S-based audio prompts in Korean (extensible to other languages)
- 🎹 Matrix Keypad - 3x4 tactile keypad with debouncing
- 🚪 Auto-Lock - Automatic door lock after 5 seconds
- ⏱️ Activity Timeout - 30-second inactivity timer
- 🔴 Emergency Access - Physical button override for manual control
| Component | Model/Spec | Purpose |
|---|---|---|
| 🧠 Microcontroller | ESP32-S3-N16R8 (16MB Flash, 8MB PSRAM) | Main controller with WiFi |
| 🖐️ Fingerprint Reader | JM-101B (UART) | Biometric authentication |
| 🎹 Keypad | 3x4 Matrix Keypad | PIN entry |
| 🔊 Audio Amplifier | MAX98357A (I2S) | Voice feedback |
| ⚙️ Motor Driver | L298N Dual H-Bridge | Lock/unlock mechanism |
| 🔩 DC Motor | Geared DC Motor | Physical lock actuator |
| ⚡ PNP Transistors | ≥350mA current rating × 3 | Peripheral power control |
| 🔘 Buttons | Tactile push buttons × 3 | Reset, Door Control, Enroll |
- 👁️ PIR Sensor - Motion detection for auto-wake (GPIO 0)
- 🔆 LED Indicators - Visual status feedback
- ☁️ Azure IoT Hub - Device messaging and telemetry
- 🔄 Device Provisioning Service (DPS) - Automated device registration
- 📊 Azure IoT Central (Optional) - Dashboard and monitoring
- 📱 Notification Hubs - Mobile push notifications
- 🔥 Firebase Cloud Messaging - Cross-platform alerts
- Create a DPS instance in Azure Portal
- Create an enrollment group with Symmetric Key attestation
- Copy the following values to
main/config.h:#define AZURE_IOT_DPS_ID_SCOPE "0neXXXXXXXX" #define AZURE_IOT_DPS_SYMMETRIC_KEY "your-primary-key-here"
- Create an IoT Central application
- Create a device template with the following telemetry schema:
{ "status": "integer", "desc": "string" } - Copy the model ID to config.h:
#define AZURE_IOT_DPS_MODEL_ID "dtmi:yourcompany:SmartLock;1"
📚 Authentication Guide: Device authentication concepts in IoT Central
GPIO 42- Power Control (PNP Transistor)GPIO 18- UART TXGPIO 17- UART RXGPIO 9- Touch Sensor RXGPIO 10- Touch Sensor Power
- Columns:
GPIO 4, 5, 6 - Rows:
GPIO 11, 12, 13, 14
GPIO 40- Power Control (PNP Transistor)GPIO 2- BCLK (Bit Clock)GPIO 1- WS (Word Select)GPIO 41- DOUT (Data Out)
GPIO 7- Power Control (PNP Transistor)GPIO 39- ENA/IN1GPIO 38- ENA/IN2
GPIO 8- Reset Button (Factory Reset)GPIO 15- Door Switch (Manual Override)GPIO 16- Enroll Button (Fingerprint Enrollment)
GPIO 33-37 (Reserved for PSRAM)
- ✅ ESP-IDF v5.0 or later
- ✅ Azure account with IoT Hub + DPS
- ✅ WiFi network (2.4GHz)
-
Clone the repository
git clone https://github.com/player-alex/sls.git cd sls -
Configure settings in
main/config.h:// Device Identity #define DEV_ID "your-device-id" // WiFi Credentials #define WIFI_AP_SSID "your-wifi-ssid" #define WIFI_AP_PASSWORD "your-wifi-password" // Azure Credentials (see Azure Services Setup) #define AZURE_IOT_DPS_ID_SCOPE "your-id-scope" #define AZURE_IOT_DPS_SYMMETRIC_KEY "your-symmetric-key" #define AZURE_IOT_DPS_MODEL_ID "your-model-id"
-
Build and flash
idf.py build idf.py -p COM3 flash monitor # Replace COM3 with your port
- 🔌 Device powers on
- 📶 Connects to WiFi
- ⏰ Syncs time via SNTP
- ☁️ Provisions with Azure DPS
- 🔗 Connects to IoT Hub
- ✅ Ready for operation!
Default Password: 0000
- Enter your password on the keypad (e.g.,
1234) - Press
*to verify - ✅ Door unlocks if password is correct
- 🔊 Audio: "열렸습니다" (Opened)
- Touch the fingerprint sensor
- Place your enrolled finger
- ✅ Door unlocks if fingerprint matches
- 🔊 Audio: "열렸습니다" (Opened)
- Auto-Lock: Door automatically locks 5 seconds after opening
- Manual Lock: Press Door Switch button (GPIO 15)
- Enter current password
- Press
#(enters password change mode) - 🔊 System beeps 2 times
- Enter new password + press
* - 🔊 System beeps 3 times
- Re-enter new password + press
* - ✅ Password changed
- 🔊 Audio: "등록되었습니다" (Enrolled)
- Press Enroll Button (GPIO 16)
- 🔊 System beeps 3 times (enrollment mode)
- Place finger on sensor → Remove → Place again
- ✅ Fingerprint enrolled
- 🔊 Audio: "등록되었습니다" (Enrolled) or "등록에 실패했습니다" (Failed)
- Press and hold Reset Button (GPIO 8)
- All passwords and fingerprints erased
- System restarts with default password
0000
- Triggered after 3 failed authentication attempts (password or fingerprint)
- System locks for 30 seconds
- 🚨 Security siren plays
- 📡 Telemetry alert sent to Azure
- All authentication blocked during lockdown
TTS (Text-to-Speech) → MP3 → WAV → wav2code → Embedded C Arrays
| File | Korean | English | Usage |
|---|---|---|---|
audio_data_opened.h |
열렸습니다 | Opened | Door unlocked |
audio_data_closed.h |
닫혔습니다 | Closed | Door locked |
audio_data_enrolled.h |
등록되었습니다 | Enrolled | Fingerprint/password enrolled |
audio_data_enrollment_failed.h |
등록에 실패했습니다 | Enrollment Failed | Enrollment error |
audio_data_repeat_again.h |
다시 입력해주세요 | Please try again | Authentication failed |
audio_data_beep.h |
🔔 | Beep | Key press feedback |
audio_data_siren.h |
🚨 | Siren | Security lockdown |
- 🎵 Sound effects from Pixabay
- 🎙️ Voice prompts: Custom TTS generation
- Generate MP3 using TTS service
- Convert to WAV (16-bit PCM, mono)
- Use
wav2codetool to create C array - Add header file to
main/audio/data/ - Register in
metadata.cpp
| Code | Event Name | Description | Trigger |
|---|---|---|---|
| 1 | Opened |
Door unlocked | Successful authentication |
| 2 | Closed |
Door locked | Auto-lock or manual lock |
| 3 | PasswordMismatch |
Wrong password entered | Failed password attempt |
| 4 | FingerprintMismatch |
Unknown fingerprint | Failed fingerprint scan |
| 5 | LockdownCausePasswordMismatch |
Security lockdown | 3 password failures |
| 6 | LockdownCauseFingerprintMismatch |
Security lockdown | 3 fingerprint failures |
| 7 | PasswordChanged |
Password updated | Successful password change |
| 8 | StartFingerprintEnrollment |
Enrollment started | Enroll button pressed |
| 9 | FingerprintEnrolled |
Fingerprint added | Successful enrollment |
| 10 | FingerprintEnrollmentFailed |
Enrollment error | Failed enrollment |
| 11 | NotEnoughBattery |
Low battery warning | Battery threshold (reserved) |
| 12 | SystemBooted |
Device started | Power-on or reset |
{
"status": 1,
"desc": "Door opened via fingerprint"
}- Protocol: MQTT over TLS 1.2
- QoS: 1 (At least once delivery)
- Port: 8883
- Telemetry Interval: 3 seconds
- Queue Size: 100 messages
- Retry Logic: 5 attempts with 500ms interval
┌─────────────────────────────────────────────────────────────┐
│ ESP32-S3 FreeRTOS │
├─────────────────────────────────────────────────────────────┤
│ 🎯 Main Application (app_main.cpp) │
│ ├── Task Manager (8 concurrent tasks) │
│ ├── State Machine (Door/System Status) │
│ └── Event Handlers (Buttons/Keypad/Fingerprint) │
├─────────────────────────────────────────────────────────────┤
│ 📦 Core Modules │
│ ├── 🔷 Azure IoT Integration (DPS, IoT Hub, Telemetry) │
│ ├── 🖐️ Fingerprint Auth (JM-101B Driver) │
│ ├── 🔊 I2S Audio Controller (MAX98357A) │
│ ├── 🎹 Keypad Scanner (3x4 Matrix) │
│ ├── ⚙️ Motor Controller (L298N) │
│ └── 📶 WiFi Station Manager │
├─────────────────────────────────────────────────────────────┤
│ 🛠️ Helper/Utility Layer │
│ ├── 💾 NVS Storage (Password persistence) │
│ ├── ⏰ SNTP Time Sync (TLS requirement) │
│ └── 🔄 Cancellation Tokens (Async control) │
├─────────────────────────────────────────────────────────────┤
│ 🔧 ESP-IDF HAL (GPIO, UART, I2S, WiFi, NVS, RTC) │
└─────────────────────────────────────────────────────────────┘
| Task | Priority | Stack | Interval | Purpose |
|---|---|---|---|---|
tsk_scan_btns |
0 | 8KB | 100ms | Button scanning |
tsk_scan_keys |
0 | 8KB | 10ms | Keypad matrix scan |
tsk_scan_fp |
0 | 8KB | 250ms | Fingerprint touch detection |
tsk_upd_status |
0 | 8KB | 10ms | Status monitor & timers |
tsk_init_sys |
0 | 8KB | Once | WiFi + time sync |
tsk_init_azure |
0 | 8KB | Once | Azure provisioning |
tsk_send_tels |
0 | 8KB | 3s | Telemetry dispatcher |
tsk_az_loop |
0 | 8KB | 3s | MQTT process loop |
None→Closed→Opened→Closed(cycle)
None(Normal operation)PasswordChangeMode(Awaiting new password)PasswordChanged(Confirmation)FingerprintEnrollmentMode(Active enrollment)RequestFingerprintEnrollmentMode(Enrollment pending)
- WiFi enabled for cloud connectivity
- Fingerprint reader: Power-gated (on-demand)
- Audio amplifier: Power-gated (on-demand)
- Motor driver: Power-gated (on-demand)
- Activity timer: 30-second countdown
- Trigger: 30 seconds of inactivity
- Power: <10µA typical consumption
- Wake Sources (RTC GPIO):
- 🎹 Any keypad key press (GPIO 11-14)
- 🖐️ Fingerprint touch sensor (GPIO 9)
- 🔘 Any button press (GPIO 8, 15, 16)
- 👁️ PIR motion sensor (GPIO 0) (optional)
- Set keypad columns HIGH
- Enable GPIO wake-up (high-level trigger)
- Enable fingerprint touch sensor
- Hold all RTC GPIO states
- Enter
esp_deep_sleep_start()
- RTC GPIO interrupt triggers wake
- Boot from deep sleep (retain RTC memory)
- Re-initialize WiFi and Azure connection
- Resume normal operation
Problem: MQTT errors may occur when calling subscribe functions
Status: Root cause unidentified
Note: Cloud-to-device messaging not currently implemented
Problem: UART deadlock if fingerprint reader disconnects during transmission
Status: Known hardware limitation
Mitigation: Cancellation token system partially handles this
Problem: Delay-based timing causes inconsistent lock/unlock
Recommendation: Use hardware timer for precise motor control
Current: vTaskDelay(1000ms) for motor operation
Problem: Noticeable delay when waking from deep sleep
Solution: Implement light sleep mode with fine-grained task control
Risk: Requires careful task cancellation to prevent crashes
Problem: Tokens created via create_linked_token() are never freed
Location: cancellationtokensource.cpp line 38
Impact: Memory leak (~48 bytes per token)
Problem: Most resources use manual C-style management
Status: Only UART and I2S use RAII destructors
Impact: Semaphores and event groups may leak on error paths
- 🌙 Light Sleep Mode - Reduce wake-up latency with light sleep
- 🔄 Executable Task Refactoring - Fine-grained control for sleep modes
- 📱 Mobile Notifications - Integrate Azure Notification Hubs + Firebase
- ⏱️ Precise Motor Control - Hardware timer-based lock mechanism
- 🔍 Debug Azure Subscribe - Investigate MQTT subscription errors
- 🌐 Multi-Language Audio - Add English/Spanish/Chinese voice prompts
- 🔋 Battery Monitoring - Implement low-battery telemetry (Code 11)
- 🏠 Home Assistant Integration - MQTT bridge for smart home
sls/
├── main/
│ ├── app_main.cpp # Main application logic
│ ├── config.h # System configuration
│ ├── azure/ # Azure IoT integration
│ │ ├── dev_provisioning.cpp # DPS registration
│ │ ├── iot_hub_provisioning.cpp # IoT Hub connection
│ │ ├── iot_hub_action.cpp # Telemetry sender
│ │ └── network_helper.cpp # TLS + SNTP
│ ├── fingerprint/ # Biometric authentication
│ │ ├── reader.cpp # JM-101B driver
│ │ └── helper.cpp # Enrollment/search logic
│ ├── modules/ # Hardware modules
│ │ ├── keypad.cpp # Matrix keypad scanner
│ │ └── i2s_controller.cpp # Audio playback
│ ├── audio/data/ # Embedded audio files
│ │ └── metadata.cpp # Audio registry
│ ├── helper/ # Utilities
│ │ ├── system.cpp # SNTP time sync
│ │ └── nvs.cpp # Storage operations
│ └── wifi/ # Network connectivity
│ └── station.cpp # WiFi manager
├── components/
│ └── azure-iot-esp32/ # ESP32 Azure SDK port
├── libs/
│ └── azure-iot-middleware-freertos/ # Azure SDK
└── CMakeLists.txt # Build configuration
# Set up ESP-IDF environment
. $HOME/esp/esp-idf/export.sh # Linux/macOS
# OR
%USERPROFILE%\esp\esp-idf\export.bat # Windows
# Configure project
idf.py menuconfig
# Build
idf.py build
# Flash + Monitor
idf.py -p /dev/ttyUSB0 flash monitor# Enable verbose logging in menuconfig
Component config → Log output → Verbose
# Monitor specific tags
idf.py monitor -p COM3 | grep "app_main"- C++23 features enabled (gnu++2b)
- STL containers used alongside C-style arrays
- Limited RAII implementation (UART, I2S)
- Lambda-based task definitions
- Manual resource management for most peripherals
This project is provided as-is for educational and development purposes.
Academic Project - Created as a university assignment by @player-alex
Q: Device won't connect to Azure A: Check WiFi credentials, verify SNTP time sync, confirm DPS credentials
Q: Fingerprint reader not responding A: Check UART connections (GPIO 17/18), verify power transistor (GPIO 42)
Q: Audio not playing
A: Verify I2S connections, check power transistor (GPIO 40), test with audio_data_beep.h
Q: Motor not working A: Check L298N connections (GPIO 38/39), verify power supply, test transistor (GPIO 7)
🎓 Academic Project - Created for university coursework
🔬 Prototype Status - Suitable for learning and experimentation