Community project
RFID Access Control System

This RFID access control system uses an Arduino Uno to read RFID cards and control a servo-driven lock mechanism. When an authorized card is scanned, the system unlocks the door for a set duration, displays status messages on an LCD screen, and provides audio feedback via a buzzer. The guide includes a complete wiring diagram, parts list, and step-by-step assembly instructions to get the system operational.
Builders will learn how to integrate SPI communication with the MFRC522 RFID reader, I2C control of the LCD display, and servo actuation for the lock mechanism. The provided Arduino firmware handles card detection, authorization logic, and timed lock/unlock sequences, making this an excellent foundation for understanding access control systems and multi-component Arduino projects.
Wiring diagram
Interactive · read-only
Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Parts list
Bill of materials| Component | Qty | Notes |
|---|---|---|
| MFRC522 RFID ModuleRC522 13.56 MHz | 1 | 13.56 MHz RFID reader/writer module based on the NXP MFRC522 IC. Communicates over SPI and is commonly sold as an RC522 breakout with an onboard antenna. |
| LCD 16x2 I2C16x2, I2C address 0x27 | 1 | 16x2 character LCD display with I2C backpack |
| Buzzer5 V piezo | 1 | Piezo buzzer for sound output |
| SG90 ServoSG90 9 g | 1 | Micro servo motor (SG90) |
| USB-C 5V Adapter5 V, 2 A minimum | 1 | USB-C wall adapter delivering regulated 5 V to the board's USB or VBUS rail. Default wired power source for desktop / stationary projects. |
Assembly
5 stepsArrange the dark circuit layout
Place the Arduino Uno centrally on a dark gray breadboard or mounting panel. Position the RC522 reader to its left, the LCD above it, the SG90 at the right, and the piezo buzzer at the lower right. Keep the RFID antenna clear of metal and large wire bundles.
- Tip: Use short, neatly routed jumper wires so the diagram-like layout stays easy to trace.
- Tip: Keep the RC522 antenna face exposed for reliable card scans.
- ⚠ Do not place the RC522 directly on a metal plate or against the Uno USB connector.
Wire the RC522 RFID reader
Connect RC522 VCC to the Uno 3.3 V rail and GND to common ground. Connect SCK to D13, MOSI to D11, MISO to D12, SDA/SS to D10, and RST to D9. For a physical 5 V Uno, insert 5 V-to-3.3 V resistor dividers or a proper high-speed level shifter on the Uno outputs D13, D11, D10, and D9 before they reach the RC522 inputs.
- Tip: The RC522 pin labelled SDA is SPI chip select, not I2C SDA.
- Tip: Use 1 kΩ from each Uno output to the RC522 input node and 2 kΩ from that node to GND for each divider.
- ⚠ Never connect RC522 VCC to 5 V.
- ⚠ Do not omit the level reduction on D13, D11, D10, and D9; RC522 logic is 3.3 V.
Wire the I2C LCD
Connect LCD VCC to 5 V and GND to common ground. Connect LCD SDA to Uno A4 and LCD SCL to Uno A5.
- Tip: Most 16x2 I2C backpacks use address 0x27; the supplied firmware uses that address.
- Tip: Adjust the small contrast trimmer on the LCD backpack if characters are not visible.
- ⚠ Keep A4 and A5 exclusively for the I2C LCD bus in this project.
Wire the buzzer and servo
Connect buzzer SIGNAL to D3 and buzzer GND to common ground. Connect the SG90 signal wire (orange/yellow) to D6, its red wire to regulated 5 V, and its brown/black wire to common ground.
- Tip: Set the servo horn mechanically after the firmware first moves it to the locked position.
- Tip: Add a 470–1000 µF electrolytic capacitor across the 5 V and GND rails close to the servo if you see resets or LCD flicker.
- ⚠ Do not rely on the Uno 5 V pin alone to power a mechanically loaded servo.
- ⚠ The servo supply ground and Uno ground must be connected together.
Connect regulated power and inspect
Use the regulated 5 V, 2 A minimum USB power adapter to feed the project 5 V and GND rails. Confirm that the Uno receives power through its normal USB input or a safe regulated 5 V distribution, and verify every module shares the same ground before powering on.
- Tip: Route red wires as 5 V, black wires as ground, and use distinct colors for SPI, I2C, and servo signal wires to create a realistic easy-to-read diagram.
- Tip: After powering, scan any 13.56 MHz card or fob: its UID appears on the LCD, the buzzer chirps, and the servo unlocks briefly.
- ⚠ Do not connect two independent 5 V sources to the Uno 5 V rail at the same time.
- ⚠ Double-check RC522 power polarity before turning on the supply.
Pin assignments
Board wiring reference| Pin | Connection | Type |
|---|---|---|
| 5V | power_adapter +5V | power |
| GND | power_adapter GND | ground |
| 3V3 | rfid_reader VCC | power |
| GND | rfid_reader GND | ground |
| GPIO 13 | rfid_reader SCK | spi |
| GPIO 11 | rfid_reader MOSI | spi |
| GPIO 12 | rfid_reader MISO | spi |
| GPIO 10 | rfid_reader SDA | spi |
| GPIO 9 | rfid_reader RST | digital |
| 5V | lcd_display VCC | power |
| GND | lcd_display GND | ground |
| GPIO 18 | lcd_display SDA | i2c |
| GPIO 19 | lcd_display SCL | i2c |
| GND | piezo_buzzer GND | ground |
| GPIO 3 | piezo_buzzer SIGNAL | digital |
| 5V | servo_motor VCC | power |
| GND | servo_motor GND | ground |
| GPIO 6 | servo_motor SIGNAL | pwm |
Firmware
Arduino#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
// Forward declarations
void showStatus(const String &line1, const String &line2);
String cardUid();
void lockDoor();
void unlockDoor(const String &uid);
const byte RFID_SS_PIN = 10;
const byte RFID_RST_PIN = 9;
const byte BUZZER_PIN = 3;
const byte SERVO_PIN = 6;
const byte SERVO_LOCKED_ANGLE = 10;
const byte SERVO_UNLOCKED_ANGLE = 95;
const unsigned long UNLOCK_TIME_MS = 3000UL;
MFRC522 rfid(RFID_SS_PIN, RFID_RST_PIN);
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo lockServo;
bool doorUnlocked = false;
unsigned long unlockStartedAt = 0;
String shownLine1;
String shownLine2;
void showStatus(const String &line1, const String &line2) {
if (line1 == shownLine1 && line2 == shownLine2) return;
lcd.setCursor(0, 0); lcd.print(" ");
lcd.setCursor(0, 0); lcd.print(line1.substring(0, 16));
lcd.setCursor(0, 1); lcd.print(" ");
lcd.setCursor(0, 1); lcd.print(line2.substring(0, 16));
shownLine1 = line1;
shownLine2 = line2;
}
String cardUid() {
String uid;
for (byte i = 0; i < rfid.uid.size; ++i) {
if (rfid.uid.uidByte[i] < 0x10) uid += '0';
uid += String(rfid.uid.uidByte[i], HEX);
if (i + 1 < rfid.uid.size) uid += ':';
}
uid.toUpperCase();
return uid;
}
void lockDoor() {
lockServo.write(SERVO_LOCKED_ANGLE);
doorUnlocked = false;
showStatus("Scan RFID card", "Door locked");
}
void unlockDoor(const String &uid) {
lockServo.write(SERVO_UNLOCKED_ANGLE);
doorUnlocked = true;
unlockStartedAt = millis();
showStatus("Access granted", uid);
tone(BUZZER_PIN, 1600, 110);
delay(140);
tone(BUZZER_PIN, 2200, 140);
}
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
Wire.begin();
lcd.init();
lcd.backlight();
lockServo.attach(SERVO_PIN);
SPI.begin();
rfid.PCD_Init();
delay(50);
lockDoor();
}
void loop() {
if (doorUnlocked && millis() - unlockStartedAt >= UNLOCK_TIME_MS) lockDoor();
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;
String uid = cardUid();
unlockDoor(cardUid());
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.
Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.