This tutorial builds a smart access control system using ESP32, featuring dual authentication methods: RFID card (RC522) and password keypad. The system includes OLED display for password input feedback, servo motor for door locking/unlocking, and buzzer for authentication sound feedback.
(1) x Elegoo ESP32
(1) x 128x64 OLED Display (SSD1306)
(1) x RC522 RFID Module
(1) x 4x4 Matrix Keypad
(1) x Servo Motor (SG90)
(1) x Active Buzzer
| Component | ESP32 Pin | Description |
|---|---|---|
| OLED VCC | 3.3V | OLED power |
| OLED GND | GND | OLED ground |
| OLED SDA | GPIO 21 | I2C data line |
| OLED SCL | GPIO 22 | I2C clock line |
| RC522 VCC | 3.3V | RC522 power |
| RC522 GND | GND | RC522 ground |
| RC522 RST | GPIO 34 | RC522 reset |
| RC522 SDA(SS) | GPIO 2 | SPI chip select |
| RC522 MOSI | GPIO 23 | SPI data output |
| RC522 MISO | GPIO 19 | SPI data input |
| RC522 SCK | GPIO 18 | SPI clock |
| Servo Signal | GPIO 4 | Servo PWM control |
| Buzzer | GPIO 5 | Active buzzer control |
| Keypad Row 0 | GPIO 13 | Row pin 0 |
| Keypad Row 1 | GPIO 12 | Row pin 1 |
| Keypad Row 2 | GPIO 14 | Row pin 2 |
| Keypad Row 3 | GPIO 27 | Row pin 3 |
| Keypad Col 0 | GPIO 26 | Column pin 0 |
| Keypad Col 1 | GPIO 25 | Column pin 1 |
| Keypad Col 2 | GPIO 33 | Column pin 2 |
| Keypad Col 3 | GPIO 32 | Column pin 3 |
Smart_Access_Control.ino (Main Program)
├── Calls → RC522 Module
│ ├── RC522_control.h (Pin Definitions, Servo Config, Function Declarations)
│ └── RC522_control.cpp (RFID Initialization, Authorization, Door Control)
└── Calls → Keypad & OLED Module
├── key.h (OLED Config, Function Declarations)
└── key.cpp (Keypad Input, Password Verification, OLED Display)
Function: Define RC522 pin macros, servo configuration parameters, authorized UID declarations, and function interfaces.
Pin Macro Definitions:
#define RST_PIN 34 // RC522 reset pin
#define SS_PIN 2 // RC522 chip select pin (SPI SS)
#define SERVO_PIN 4 // Servo motor signal pin (PWM compatible)
RST_PIN: RC522 module reset pin (GPIO 34)SS_PIN: RC522 chip select pin (GPIO 2, SPI Slave Select)SERVO_PIN: Servo motor signal pin (GPIO 4, PWM compatible)Servo Configuration Macros:
#define LOCKED_ANGLE 0 // Door locked angle (degrees)
#define UNLOCKED_ANGLE 30 // Door unlocked angle (degrees)
#define UNLOCK_DURATION 3000 // Unlock hold duration (milliseconds, 3 seconds)
LOCKED_ANGLE: Door locked angle (0 degrees)UNLOCKED_ANGLE: Door unlocked angle (30 degrees)UNLOCK_DURATION: Unlock hold duration (3000 milliseconds, i.e., 3 seconds)Authorized UID Configuration:
extern const byte authorizedUIDs[][4]; // External declaration: List of authorized card UIDs
extern const int AUTHORIZED_COUNT; // External declaration: Number of authorized cards
authorizedUIDs: List of authorized card UIDs (defined in .cpp)AUTHORIZED_COUNT: Number of authorized cards (automatically calculated)Global Object Declarations:
extern MFRC522 mfrc522; // External declaration: RC522 instance
extern MFRC522::MIFARE_Key key; // External declaration: MIFARE key instance
extern Servo doorServo; // External declaration: Servo motor instance
mfrc522: RC522 module objectkey: MIFARE card key object (used for authentication)doorServo: Servo motor object (controls door lock)Function Declarations:
void initRC522(); // Initialize RC522 RFID reader
void initServo(); // Initialize servo motor
void initMifareKey(); // Initialize MIFARE encryption key
void printCardUID(); // Print detected card UID to serial port
bool checkAuthorization(byte* cardUid); // Verify if card UID is in authorized list
void unlockDoor(); // Door unlock logic
(1) Global Variable Definitions
const byte authorizedUIDs[][4] = {
{0x83, 0xE8, 0x8D, 0x04},
{0x31, 0x1A, 0xCE, 0x05},
{0xFB, 0x5A, 0x25, 0x02},
};
const int AUTHORIZED_COUNT = sizeof(authorizedUIDs) / sizeof(authorizedUIDs[0]);
MFRC522 mfrc522(SS_PIN, RST_PIN); // RC522 module object
MFRC522::MIFARE_Key key; // MIFARE card key object
Servo doorServo; // Servo motor object
(2) initRC522()
Function: Initialize RC522 RFID module, including SPI bus and hardware initialization.
SPI.begin(); // Initialize SPI bus
pinMode(RST_PIN, INPUT_PULLUP);
mfrc522.PCD_Init(); // Initialize RC522 module hardware
(3) initServo()
Function: Initialize door lock servo motor, set initial state to locked.
doorServo.attach(SERVO_PIN); // Attach servo to the specified GPIO pin
doorServo.write(LOCKED_ANGLE); // Initial state: Locked (set to locked angle)
delay(500); // Wait 500ms for servo to reach target position stably
(4) initMifareKey()
Function: Initialize MIFARE card authentication key using default factory key.
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF; // Assign each byte of the 6-byte key
}
(5) printCardUID()
Function: Read and print the UID of detected IC card via serial port.
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
Serial.print(mfrc522.uid.uidByte[i], HEX);
}
(6) checkAuthorization(byte cardUid)*
Function: Verify if the current card's UID is in the authorized list.
for (int i = 0; i < AUTHORIZED_COUNT; i++) {
if (memcmp(cardUid, authorizedUIDs[i], 4) == 0) {
return true;
}
}
return false;
memcmp: Efficiently compare 4-byte UID (returns 0 if fully matched)(7) unlockDoor()
Function: Execute the complete cycle of unlock → delay → auto-lock.
doorServo.write(UNLOCKED_ANGLE); // Rotate servo to unlocked angle
delay(UNLOCK_DURATION); // Maintain unlocked state for specified duration
doorServo.write(LOCKED_ANGLE); // Rotate servo back to locked angle
Function: Define OLED display configuration, keypad-related function declarations, and global variables.
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // No reset pin
extern Adafruit_SSD1306 oled;
SCREEN_WIDTH/SCREEN_HEIGHT: OLED display resolution (128x64 pixels)OLED_RESET: Reset pin (-1 means not used)oled: OLED display object (instantiated in .cpp)Function Declarations:
void initKeypadAndOled(); // Initialize keypad + OLED
void getkeypad(); // Read keypad input
void displayInputOnOled(); // Display input digits on OLED
Global Variable:
extern String inputBuffer; // Global input buffer (stores keypad input digits)
(1) Constant Definitions
const long PASSWORD = 123456; // Preset password
const byte ROWS = 4;
const byte COLS = 4;
char hexaKeys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {13, 12, 14, 27};
byte colPins[COLS] = {26,25,33,32};
PASSWORD: Preset password (123456)ROWS/COLS: Number of keypad rows/columns (4x4 matrix)hexaKeys: Keypad key mapping tablerowPins/colPins: Keypad row/column pin definitionsKeypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
(2) Global Objects
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
String inputBuffer = "";
oled: OLED display object, using I2C communicationinputBuffer: Global input buffer (only stores digits 0-9)(3) initKeypadAndOled()
Function: Initialize keypad and OLED display module.
Wire.begin(21, 22); // ESP32 hardware I2C pins: SDA=21, SCL=22
if(!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("OLED initialization failed!"));
while(1); // Halt if initialization fails
}
oled.setTextColor(WHITE); // White font color
oled.setTextSize(2); // Font size (2x)
(4) checkPassword()
Function: Password verification function, convert input buffer to integer and compare with preset password.
long inputNum = inputBuffer.toInt();
return (inputNum == PASSWORD);
(5) displayPwdResult(bool isMatch)
Function: Display password verification result.
if (isMatch) {
oled.println("Correct!");
oled.display();
unlockDoor();
} else {
oled.println("Error!");
oled.display();
}
delay(1500); // Display for 1.5 seconds
inputBuffer = ""; // Clear input buffer
displayInputOnOled(); // Return to input display
(6) getkeypad()
Function: Read keypad input, handle digit input, password verification, and delete operation.
char customKey = customKeypad.getKey();
if (customKey >= '0' && customKey <= '9') {
if (inputBuffer.length() < 6) {
inputBuffer += customKey;
displayInputOnOled();
}
}
else if (customKey == '#') {
if (inputBuffer.length() == 6) {
bool isCorrect = checkPassword();
displayPwdResult(isCorrect);
} else {
oled.println("Please enter 6 digits");
delay(1000);
displayInputOnOled();
}
}
else if (customKey == '*') {
if (inputBuffer.length() > 0) {
inputBuffer.remove(inputBuffer.length() - 1);
displayInputOnOled();
}
}
(7) displayInputOnOled()
Function: Display entered digits on OLED.
oled.clearDisplay();
oled.setTextSize(2);
oled.setCursor(0, 0);
oled.println("PASSWORD");
oled.setCursor(0, 30);
oled.println(inputBuffer);
oled.display();
Global Variables:
int buzzer = 5; // the pin of the active buzzer
#define RFID_CHECK_INTERVAL 100
unsigned long lastRFIDCheck = 0; // Record last RC522 check time (for frequency limiting)
buzzer: Active buzzer pin (GPIO 5)RFID_CHECK_INTERVAL: RFID check interval (100 milliseconds)lastRFIDCheck: Last RC522 check time (used for frequency limiting)setup()
Function: Initialize system hardware and all modules.
Serial.begin(9600);
while (!Serial); // Wait for serial port to initialize
pinMode(buzzer, OUTPUT); // initialize the buzzer pin as an output
initKeypadAndOled(); // Initialize keypad and OLED
initRC522(); // Initialize RC522 RFID module (SPI communication)
initServo(); // Initialize door lock servo motor
initMifareKey(); // Initialize MIFARE card authentication key
handleRFID()
Function: Handle RC522 RFID card detection, UID reading, and authorization verification (frequency-limited).
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
printCardUID(); // Print detected card UID via serial port
if (mfrc522.uid.size == 4) {
if (checkAuthorization(mfrc522.uid.uidByte)) {
digitalWrite(buzzer, HIGH);
delay(20);
digitalWrite(buzzer, LOW);
delay(20);
Serial.println(F("RFID Authorization Passed - Unlocking Door!"));
unlockDoor();
} else {
Serial.println(F("RFID Unauthorized Card!"));
}
}
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
loop()
Function: Main loop, handle IR keypad priority and RFID frequency-limited detection.
getkeypad(); // IR Priority Processing (Ensure Sensitivity)
unsigned long now = millis();
if (now - lastRFIDCheck >= RFID_CHECK_INTERVAL) {
lastRFIDCheck = now;
handleRFID(); // Limit SPI communication frequency to avoid interfering IR
}
Modify Location: key.cpp
const long PASSWORD = 123456; // Preset password
Modify Location: RC522_control.cpp
const byte authorizedUIDs[][4] = {
{0x83, 0xE8, 0x8D, 0x04},
{0x31, 0x1A, 0xCE, 0x05},
{0xFB, 0x5A, 0x25, 0x02},
};
AUTHORIZED_COUNT is automatically calculated, no manual modification neededModify Location: RC522_control.h
#define LOCKED_ANGLE 0 // Door locked angle (degrees)
#define UNLOCKED_ANGLE 30 // Door unlocked angle (degrees)
LOCKED_ANGLE: Door locked angle, adjust according to actual mechanical structureUNLOCKED_ANGLE: Door unlocked angle, typically 30-90 degreesModify Location: RC522_control.h
#define UNLOCK_DURATION 3000 // Unlock hold duration (milliseconds)
Modify Location: Smart_Access_Control.ino
#define RFID_CHECK_INTERVAL 100
Modify Location: displayInputOnOled() in key.cpp
oled.setTextSize(2); // Modify font size
oled.setCursor(0, 0); // Modify display position
oled.println("PASSWORD"); // Modify title text
setTextSize() parameter to adjust font size (1=8x8, 2=16x16)setCursor() parameter to adjust display positionprintln() content to customize title textModify Location: Smart_Access_Control.ino
digitalWrite(buzzer, HIGH);
delay(20);
digitalWrite(buzzer, LOW);
delay(20);
delay() parameters to adjust buzzer beep durationModify Location: key.cpp
if (inputBuffer.length() < 6) {