Chapter 220: Access Control Systems Integration
Chapter Objectives
By the end of this chapter, you will be able to:
- Understand the architecture of a typical access control system.
- Explain the role of readers, controllers, and locking mechanisms.
- Describe common credential technologies like RFID and NFC.
- Understand the Wiegand protocol, the industry standard for reader-controller communication.
- Implement a standalone ESP32-based RFID access control system.
- Manage a list of authorized users within an embedded application.
- Integrate an access control system with other building automation functions.
- Troubleshoot common issues in hardware wiring and software logic for access control.
Introduction
Beyond controlling comfort and energy, smart buildings must also be secure. Access control—the selective restriction of access to a place or resource—is a cornerstone of modern building security and management. From simple office door locks to complex, multi-site enterprise systems, controlling who can go where, and when, is a fundamental requirement.
Traditionally, access control has been the domain of specialized, often expensive, proprietary hardware. However, the power and connectivity of microcontrollers like the ESP32 open up a world of possibilities for creating custom, cost-effective, and highly flexible access control solutions.
In this chapter, we will demystify the components of access control systems. We will focus on the most common technology in the field: RFID card readers. We will build a complete, functional access control node using an ESP32 that can read RFID cards, validate credentials against an authorized list, and actuate an electric door strike. This project will serve as a practical foundation for integrating intelligent security into any building automation project.
Theory
Access Control System Architecture
A physical access control system is composed of three primary components:
- The Reader: This is the device at the door that reads a user’s credential. This could be a keypad, a fingerprint scanner, or, most commonly, a card reader (RFID/NFC). Its job is simply to read the credential data and pass it to the controller.
- The Controller: This is the brain of the system. It receives the credential data from the reader, compares it against a database of authorized users, and decides whether to grant or deny access. If access is granted, it sends a signal to unlock the door. The ESP32 will serve as our controller.
- The Locking Mechanism: This is the physical hardware that secures the door, such as an electric strike (which allows the door latch to swing open) or a magnetic lock (which holds the door closed with strong electromagnetic force). The controller energizes or de-energizes this lock via a relay.
Credential Technologies: RFID
Radio-Frequency Identification (RFID) is a technology that uses electromagnetic fields to automatically identify and track tags attached to objects. In access control, the “tag” is the user’s key card or fob.
- How it works: The reader continuously emits a low-power radio field. When an RFID card (which is a passive device with no battery) enters this field, the card’s internal antenna is energized. This powers up the microchip on the card, which then transmits its unique ID number back to the reader.
- Frequencies: Common RFID frequencies for access control are 125 kHz (Low Frequency, LF) and 13.56 MHz (High Frequency, HF). HF systems, often based on NFC (Near-Field Communication) standards like Mifare, are more secure and can store more data. We will use a 13.56 MHz system in our example.
The Wiegand Protocol
How does the reader talk to the controller? While modern readers might use encrypted protocols like OSDP, the long-standing and incredibly common standard is the Wiegand protocol. It’s not a protocol in the sense of TCP/IP, but rather a simple electrical signaling standard.
- Physical Layer: It uses three wires: Ground, DATA0 (or D0), and DATA1 (or D1).
- Signaling: To send a binary
0
, the reader briefly pulls the DATA0 line low. To send a binary1
, it pulls the DATA1 line low. The controller simply listens for these pulses on two separate input pins and reassembles the bits. - Data Format: The most common format is 26-bit Wiegand, which includes a facility code (to identify a company or site) and a unique card ID number, along with parity bits for basic error checking.
While we won’t implement the low-level Wiegand pulse timing in our main example, it’s crucial to know that an ESP32 can be programmed to act as a Wiegand controller by using two GPIO pins with interrupts. For our practical example, we will connect a reader module directly to the ESP32 using a more common peripheral interface, SPI.
Practical Example: ESP32 RFID Access Controller
We will build a complete, standalone access controller using an ESP32 and a popular MFRC522 RFID reader module. This system will read card UIDs and grant access if the UID is on a pre-defined “allow list.”
Hardware Requirements
- ESP32 Development Board.
- MFRC522 RFID Reader Module: A common and inexpensive 13.56 MHz reader that communicates over SPI. It usually comes with a blank RFID card and a fob.
- 1-Channel Relay Module: To simulate actuating an electric door strike.
- LEDs (optional): One green and one red LED for visual feedback (Access Granted / Access Denied).
- Jumper Wires and a Breadboard.
Wiring (ESP32 -> MFRC522 via SPI):
- 3.3V -> MFRC522 VCC
- GND -> MFRC522 GND
- GPIO 5 (VSPI_SCK) -> MFRC522 SCK (Serial Clock)
- GPIO 23 (VSPI_MOSI) -> MFRC522 MOSI (Master Out Slave In)
- GPIO 19 (VSPI_MISO) -> MFRC522 MISO (Master In Slave Out)
- GPIO 22 -> MFRC522 SDA/NSS/CS (Chip Select)
- GPIO 21 -> MFRC522 RST (Reset)
Wiring (Peripherals):
- GPIO 26 -> IN pin of the Relay Module.
- GPIO 13 -> Anode of Green LED (via a 330Ω resistor), Cathode to GND.
- GPIO 12 -> Anode of Red LED (via a 330Ω resistor), Cathode to GND.
Project Setup and Component
This project requires a component for the MFRC522 reader. We’ll use the esp-mfrc522
library.
- Create a new ESP-IDF project.
- Create a
components
folder in your project root. - Clone the library into this folder: https://github.com/huming2207/esp-mfrc522
Access Controller Code
The code below initializes the RFID reader, maintains a list of authorized card UIDs, and checks presented cards against this list to control the relay and LEDs.
stateDiagram-v2 direction LR [*] --> SCANNING SCANNING: Waiting for card... SCANNING --> CARD_DETECTED: Card enters reader field CARD_DETECTED: Reading card UID... CARD_DETECTED --> GRANTED: is_authorized(uid) == true CARD_DETECTED --> DENIED: is_authorized(uid) == false GRANTED: Green LED ON<br>Activate Relay (Unlock) GRANTED --> SCANNING: After 3 sec delay DENIED: Red LED Flashes DENIED --> SCANNING: After 1 sec delay style SCANNING fill:#DBEAFE,stroke:#2563EB,stroke-width:1px style CARD_DETECTED fill:#FEF3C7,stroke:#D97706,stroke-width:1px style GRANTED fill:#D1FAE5,stroke:#059669,stroke-width:2px style DENIED fill:#FEE2E2,stroke:#DC2626,stroke-width:2px
/*
* Chapter 220: Access Control Systems Integration
*
* This example implements a standalone RFID access controller using an
* ESP32 and an MFRC522 reader.
*
* Hardware:
* - ESP32 board
* - MFRC522 RFID Reader
* - Relay Module
* - Green and Red LEDs
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "mfrc522.h"
static const char* TAG = "ACCESS_CONTROL";
// --- Pin Configuration ---
#define MFRC522_SCK_PIN GPIO_NUM_5
#define MFRC522_MOSI_PIN GPIO_NUM_23
#define MFRC522_MISO_PIN GPIO_NUM_19
#define MFRC522_CS_PIN GPIO_NUM_22
#define MFRC522_RST_PIN GPIO_NUM_21
#define RELAY_PIN GPIO_NUM_26
#define LED_GREEN_PIN GPIO_NUM_13
#define LED_RED_PIN GPIO_NUM_12
// --- Authorized User Database ---
// In a real system, this would be stored in NVS or retrieved from a server.
// Replace these with the UIDs of your own cards/fobs.
// Each UID is 4 bytes long.
#define NUM_AUTHORIZED_CARDS 2
const uint8_t authorized_uids[NUM_AUTHORIZED_CARDS][4] = {
{0xDE, 0xAD, 0xBE, 0xEF}, // Placeholder 1
{0x12, 0x34, 0x56, 0x78} // Placeholder 2
};
void setup_peripherals(void) {
gpio_reset_pin(RELAY_PIN);
gpio_reset_pin(LED_GREEN_PIN);
gpio_reset_pin(LED_RED_PIN);
gpio_set_direction(RELAY_PIN, GPIO_MODE_OUTPUT);
gpio_set_direction(LED_GREEN_PIN, GPIO_MODE_OUTPUT);
gpio_set_direction(LED_RED_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(RELAY_PIN, 0); // Ensure lock is engaged
gpio_set_level(LED_GREEN_PIN, 0);
gpio_set_level(LED_RED_PIN, 0);
}
// Function to check if a UID is in our authorized list
bool is_authorized(uint8_t* uid) {
for (int i = 0; i < NUM_AUTHORIZED_CARDS; ++i) {
if (memcmp(uid, authorized_uids[i], 4) == 0) {
return true;
}
}
return false;
}
static void reader_task(void* pvParameters) {
// 1. Initialize SPI bus for MFRC522
spi_bus_config_t bus_cfg = {
.mosi_io_num = MFRC522_MOSI_PIN,
.miso_io_num = MFRC522_MISO_PIN,
.sclk_io_num = MFRC522_SCK_PIN,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(VSPI_HOST, &bus_cfg, 0));
// 2. Initialize MFRC522 reader
mfrc522_config_t mfrc_cfg = {
.spi_host_id = VSPI_HOST,
.cs_io_num = MFRC522_CS_PIN,
.rst_io_num = MFRC522_RST_PIN,
.is_spi_new = true // Important for IDF v5.x
};
mfrc522_handle_t mfrc522_handle;
ESP_ERROR_CHECK(mfrc522_create(&mfrc_cfg, &mfrc522_handle));
ESP_LOGI(TAG, "Access Control System Ready. Present card...");
uint8_t uid[10] = {0}; // Buffer to hold card UID
uint8_t uid_len = 0;
while (1) {
// 3. Scan for a card
esp_err_t err = mfrc522_find(mfrc522_handle, uid, &uid_len);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Card detected! UID: %02x:%02x:%02x:%02x", uid[0], uid[1], uid[2], uid[3]);
// 4. Check authorization
if (is_authorized(uid)) {
ESP_LOGI(TAG, "Access GRANTED.");
gpio_set_level(LED_GREEN_PIN, 1); // Turn on green LED
gpio_set_level(RELAY_PIN, 1); // Activate relay (unlock door)
vTaskDelay(pdMS_TO_TICKS(3000)); // Hold door unlocked for 3 seconds
gpio_set_level(LED_GREEN_PIN, 0);
gpio_set_level(RELAY_PIN, 0);
} else {
ESP_LOGW(TAG, "Access DENIED.");
gpio_set_level(LED_RED_PIN, 1); // Turn on red LED
vTaskDelay(pdMS_TO_TICKS(1000));
gpio_set_level(LED_RED_PIN, 0);
}
// Short delay to prevent re-reading the same card instantly
vTaskDelay(pdMS_TO_TICKS(1000));
}
vTaskDelay(pdMS_TO_TICKS(100)); // Small delay between scans
}
}
void app_main(void) {
setup_peripherals();
xTaskCreate(reader_task, "reader_task", 4 * 1024, NULL, 5, NULL);
}
Build, Flash, and Run
- Get Your Card UIDs: First, flash the code with a placeholder UID. When you scan your cards, the monitor will print their real UIDs. Copy these hex values into the
authorized_uids
array. - Build and Flash: Re-build the project with the correct UIDs and flash it to your ESP32.
- Test: The system will log “Present card…”.
- Present an authorized card: The green LED should light up, the relay should click on for 3 seconds, and then turn off.
- Present an unauthorized card: The red LED should flash briefly.
Variant Notes
- All ESP32 Variants: The core logic of this project relies on SPI and GPIO, which are available on all ESP32 variants. The example is universally applicable. Pin choices for SPI (HSPI vs VSPI on original ESP32) and other GPIOs can be easily changed.
- ESP32-S2/S3: These variants have built-in USB-OTG capabilities, which could be used to connect a USB-based card reader, offering an alternative to SPI/UART modules.
- Security Considerations: For a real-world application, this system is a starting point. A high-security system would not simply check a UID, which can be cloned. It would use more advanced cards (like Mifare DESFire) and perform cryptographic challenges. The ESP32 has the necessary cryptographic hardware accelerators to implement such advanced features.
Common Mistakes & Troubleshooting Tips
Mistake / Issue | Symptom(s) | Troubleshooting / Solution |
---|---|---|
Reader Not Initializing | Logs are stuck or show an error from mfrc522_create(). No “Present card…” message appears. |
1. Check SPI Wiring: Meticulously check VCC, GND, MOSI, MISO, SCK, and CS pins. MISO/MOSI are commonly swapped. 2. Check Reset Pin: Ensure the RST pin is wired correctly. An un-reset reader won’t respond. 3. Power: Ensure the reader has a stable 3.3V supply. |
Card Not Detected | System is ready, but presenting a card does nothing. |
1. Frequency Mismatch: Verify your card and reader are the same frequency (e.g., 13.56 MHz for MFRC522). A 125 kHz card will not work. 2. Proximity: Bring the card very close to the reader’s antenna, often touching it directly. |
Access Always Denied | You scan an authorized card, but the red LED flashes and the log says “Access DENIED”. |
1. UID Mismatch: The UID in the authorized_uids array must be an exact match to the UID printed in the log. 2. Copy/Paste Error: Copy the UID directly from the log output (e.g., DE:AD:BE:EF) and format it correctly in the array ({0xDE, 0xAD, 0xBE, 0xEF}). |
Relay Buzzing / Not Working | The relay makes a buzzing sound, clicks weakly, or doesn’t activate, possibly resetting the ESP32. | This is a classic power issue. The ESP32’s 3.3V pin cannot supply enough current for most relays. Solution: Power the relay module’s VCC from a separate 5V supply (like the ESP32 board’s VIN pin) but keep the GNDs connected. |
Card Read Repeatedly | After a valid scan, the door unlocks, but the card is immediately read again as soon as the lock timer expires. | This is why a short delay is needed after processing a card. Ensure there is a vTaskDelay() after the if/else block to give time to remove the card from the field before the next scan attempt. |
Exercises
- Store UIDs in NVS: Modify the code to store the authorized UID list in Non-Volatile Storage (NVS). Implement two functions:
add_authorized_card()
andremove_authorized_card()
, which could be triggered by pressing buttons connected to the ESP32. - Web-Based User Management: Create a Wi-Fi-enabled web server on the ESP32. The web page should display a list of authorized UIDs and provide a form to add or remove users remotely.
- Implement Time-Based Access: Add a real-time clock (RTC) module or use SNTP to get the current time. Modify the access logic so that a specific card is only granted access during certain hours (e.g., 9 AM to 5 PM on weekdays).
- Wiegand Reader Integration (Advanced): Get a standard Wiegand access control reader. Connect its DATA0 and DATA1 lines to two GPIO pins on the ESP32. Set up falling-edge interrupts on these pins. Write an interrupt service routine (ISR) that appends a 0 or 1 to a buffer based on which pin triggered the interrupt. A task would then process this buffer to reconstruct the card data.
Summary
- Modern access control systems consist of a reader, a controller, and a lock.
- RFID/NFC is the dominant technology for credentials, with readers communicating the card’s UID to a controller.
- The Wiegand protocol is a simple but common electrical standard for connecting readers to controllers.
- The ESP32 is an excellent platform for building a powerful and flexible access controller, using standard interfaces like SPI to connect to reader modules.
- Secure access control logic involves comparing a presented credential against a database of authorized users.
- For production systems, security must be layered: store credentials securely (NVS), use encrypted communication, and consider advanced, unclonable card technologies.
Further Reading
esp-mfrc522
Component: https://github.com/huming2207/esp-mfrc522- ESP-IDF SPI Master Driver Documentation: https://docs.espressif.com/projects/esp-idf/en/v5.1.1/esp32/api-reference/peripherals/spi_master.html
- Understanding Wiegand (External Resource): A good technical overview of the Wiegand interface. https://www.openpath.com/blogs/learning-center/wiegand-protocol