RFID Security Door Lock — Arduino Uno

by Shawn5123 in Circuits > Arduino

215 Views, 2 Favorites, 0 Comments

RFID Security Door Lock — Arduino Uno

image.png

This project is a fully functional RFID access-control security door lock built on an Arduino Uno. An RC522 RFID reader checks scanned cards against an authorized list, a green LED lights up and a servo motor unlocks when access is granted, a red LED and fail counter activate on denial, and after 9 failed attempts the system hard locks until an admin card resets it. An HC-SR04 ultrasonic sensor activates the reader only when someone approaches within 80cm, and a slide switch gives the owner a master arm/disarm override.

Supplies

Screenshot 2026-06-17 194704.png

Physical Components needed

  1. Arduino Uno R3
  2. RC522 RFID module (comes with 1 card + 1 fob)
  3. FS90 servo motor
  4. HC-SR04 ultrasonic distance sensor
  5. HC-SR501 PIR motion sensor
  6. Green LED x1
  7. Red LED x1
  8. Yellow LED x1
  9. 220Ω resistors x3
  10. Slide switch x1
  11. Breadboard x1
  12. Male-to-male jumper wires x~25
  13. Male-to-female jumper wires x~10

Libraries needed (install via Sketch > Manage Libraries):

  1. MFRC522 by GithubCommunity
  2. Servo (built into Arduino IDE)

The Idea

Screenshot 2026-06-17 194807.png

The inspiration for this project came from real-world access control systems, hotel key cards, office badge readers, apartment fobs. Instead of a mechanical key that can be copied or lost, an RFID card carries a unique digital ID that the system checks against an authorized list. The goal was to build something with a genuine purpose, not just a circuit demo.

Three cards are used with three distinct roles: one authorized card that unlocks the door, one unauthorized card that is intentionally denied (to demonstrate the security system working), and one admin card that gives the owner manual override control and the ability to reset a lockout. A slide switch acts as a master arm/disarm, useful when the owner is home and wants the door held open without scanning.

An HC-SR04 ultrasonic distance sensor adds intelligence, the RFID reader only activates when someone is actually within 80cm of the door, preventing phantom reads and saving processing cycles. A PIR motion sensor is wired in as a secondary detection component, part of a dual-sensor presence detector.

Research

Screenshot 2026-06-17 194900.png

Research began on Instructables and Arduino Project Hub, reviewing existing RFID door lock tutorials. Most examples used a single LED for feedback with the RFID reader polling constantly,no sensors, no lockout, no admin system. The key research questions were: how does the RC522 communicate with Arduino, how do you calculate distance from the HC-SR04 without a library, and how does INPUT_PULLUP eliminate external resistors for the switch.


RC522 RFID library:

The MFRC522 library by GithubCommunity was used to communicate with the RC522 module. [1] The library simplifies communication by handling SPI transactions, card detection, and UID reading automatically. The main functions used in this project were PICC_IsNewCardPresent(), PICC_ReadCardSerial(), PICC_HaltA(), and PCD_StopCrypto1(). The RC522 must be powered using 3.3V, as the library documentation and community reports indicate that applying 5V can permanently damage the module. [2]


HC-SR04 distance calculation:

The HC-SR04 was used without a library to avoid the timer conflict that occurs when NewPing and tone() both attempt to use Timer2. The distance measurement was instead calculated using Arduino's built in pulseIn() function. [3] According to Random Nerd Tutorials, the speed of sound is approximately 343 m/s, or 34300 cm/s. Since the sound wave travels to the object and then returns to the sensor, the distance can be calculated using the formula, distance (cm) = duration (µs) / 58. [4] A timeout value of 30000 µs was added to pulseIn() so the program would not become stuck if no echo signal was received. [5]


Arduino Servo library:

The built in Servo library was used to control the FS90 servo motor through PWM on pin D6. [6] According to Arduino's official documentation, the write() function moves the servo to a specified angle between 0 and 180 degrees, and the control signal should be connected to a PWM capable digital pin. [7] The servo's red power wire was connected directly to the 5V rail rather than an Arduino pin, since servo motors can draw a relatively large amount of current during operation.


INPUT_PULLUP for the slide switch:

The INPUT_PULLUP mode was used to enable Arduino's internal pull up resistor, which is approximately 20 kΩ. This keeps the input pin at 5V by default, while the switch connects the pin to ground when closed, eliminating the need for an external resistor. As a result, the logic is inverted, meaning a LOW reading indicates that the switch is on. This behavior is handled in the code by the pollSwitch() function.

Circuit Theory

Screenshot 2026-06-17 194411.png

Arduino Uno R3 (Abra)

The Arduino Uno R3 serves as the main microcontroller for the project. It provides 14 digital input/output pins, numbered D0 to D13, and 6 analog input pins, numbered A0 to A5. Since pins D0 and D1 are reserved for serial communication, there are 12 usable digital pins and 6 analog pins available, giving a total of 18 usable I/O connections for sensors and outputs.


RC522 RFID Module, Card, and Fob

The RC522 RFID module is used to read 13.56 MHz MIFARE RFID cards and key fobs through the SPI communication protocol. Each RFID card contains a unique 4 byte UID programmed at the factory, allowing individual identification of authorized users. The module operates at 3.3V and must not be powered with 5V, as this can permanently damage the hardware. The module includes both an RFID card and a key fob for testing and operation.


FS90 Servo Motor

The FS90 servo motor is used to simulate a door lock mechanism by rotating between locked and unlocked positions. In this project, the servo rotates from 0° to 90° to represent the movement of a door bolt. The motor is controlled using a PWM signal connected to pin D6. The orange wire is connected to the signal pin, the red wire is connected to the 5V power rail, and the brown wire is connected to ground.


HC-SR04 Ultrasonic Sensor

The HC-SR04 ultrasonic sensor measures distance by transmitting and receiving sound waves. It is used as a presence detection sensor so that RFID scanning only becomes active when a person is within 80 cm of the system. Distance calculations are performed manually using Arduino's pulseIn() function, eliminating the need for an external library.


HC-SR501 PIR Motion Sensor

The HC-SR501 PIR sensor detects movement by sensing changes in infrared radiation produced by warm objects such as people. The sensor is connected to analog pin A0 and is included as part of the project's dual sensor presence detection system alongside the HC-SR04. After power is applied, the sensor requires approximately 60 seconds to warm up before operating reliably.


Green, Red, and Yellow LEDs

Three LEDs provide visual feedback about the system state. The green LED indicates that access has been granted or the system is disarmed. The red LED indicates denied access or a lockout condition. The yellow LED indicates that administrator override mode is active. Each LED is connected in series with its own 220 Ω current limiting resistor.


Slide Switch

The slide switch functions as the system's master arm and disarm control. The center terminal is connected to pin D2, which is configured using INPUT_PULLUP. One outer terminal is connected to ground. When the switch is off, the system remains disarmed, the servo stays unlocked, and the green LED remains illuminated. When the switch is on, the system becomes armed and all security features are enabled.


220 Ω Resistors

Three 220 Ω resistors are used, one for each LED. These resistors limit the current flowing through the LEDs to prevent damage. Using the current limiting equation, R = (Vsupply − Vforward) / I = (5V − 2V) / 0.013A ≈ 230 Ω. The nearest standard resistor value is 220 Ω, which safely limits the current to approximately 13 mA.

Wiring & Connections

Screenshot 2026-06-17 193743.png

Arduino Pin Connections

D2 → Slide switch middle pin

Wire colour: Green

Configured using INPUT_PULLUP, so no external resistor is required.

D4 → Red LED through 220Ω resistor

Wire colour: Red

LED cathode connects directly to the GND rail.

D5 → Green LED through 220Ω resistor

Wire colour: Green

LED cathode connects directly to the GND rail.

D6 → FS90 servo signal wire

Wire colour: Orange

PWM output used to control the servo position.

D7 → Yellow LED through 220Ω resistor

Wire colour: Yellow

LED cathode connects directly to the GND rail.

D9 → RC522 RST

Wire colour: Grey

Used to reset the RFID module.

D10 → RC522 NSS

Wire colour: Purple

SPI chip select line.

D11 → RC522 MOSI

Wire colour: Purple

SPI data sent from Arduino to RC522.

D12 → RC522 MISO

Wire colour: Purple

SPI data sent from RC522 to Arduino.

D13 → RC522 SCK

Wire colour: Purple

SPI clock signal.

A0 → PIR sensor OUT

Wire colour: Blue

Motion sensor output signal.

A1 → HC-SR04 TRIG

Wire colour: Yellow

Used as a digital output to trigger the ultrasonic pulse.

A2 → HC-SR04 ECHO

Wire colour: Yellow

Used as a digital input to receive the echo signal.

3.3V → RC522 VCC

Wire colour: Red

Only the RC522 is connected to this pin. Do not connect the RC522 to 5V.

5V → Breadboard positive power rail

Wire colour: Red

Provides power to the servo, sensors, and LEDs.

GND → Breadboard ground rail

Wire colour: Black

Common ground connection shared by all components.


LED Wiring

All three LEDs use the same circuit configuration:

Arduino Pin → 220Ω Resistor → LED Anode (Long Leg)

LED Cathode (Short Leg) → GND Rail

Although the resistor can be placed on either side of the LED, it is connected to the anode side in this project because that is the standard convention used in most electronic circuits.


FS90 Servo Wiring

The FS90 servo motor uses three connections:

Orange Wire → D6 (PWM Signal)

Red Wire → 5V Rail (Power)

Brown Wire → GND Rail

The servo receives its control signal from D6, while power is supplied directly from the breadboard's 5V rail because the motor draws significantly more current than an Arduino output pin can safely provide.

Assembly Process

Screenshot 2026-06-17 192846.png

Build and test one component at a time. Testing each part individually makes it much easier to find and fix problems before moving on to the next stage.


01

Power Rails

Connect the Arduino 5V pin to the breadboard's positive power rail and connect Arduino GND to the negative rail. These rails distribute power to all components in the circuit, so this step should always be completed first.


02

RC522 RFID Module

Connect the RC522 module using the following wiring: VCC to 3.3V, GND to GND, NSS to D10, SCK to D13, MOSI to D11, MISO to D12, and RST to D9. Leave the IRQ pin disconnected. Upload the UID Scanner sketch and open the Serial Monitor at 9600 baud. Scan each RFID card and key fob, then record their UIDs for use later in the project.


03

LED Indicators

Place the green, red, and yellow LEDs on the breadboard. Connect a 220 Ω resistor between each Arduino output pin and the LED's anode, which is the longer leg. Connect the cathode, the shorter leg, directly to the ground rail. Connect the green LED to D5, the red LED to D4, and the yellow LED to D7. Upload a simple LED test sketch to verify that each LED functions correctly.


04

FS90 Servo Motor

Connect the orange signal wire to D6, the red power wire to the 5V rail, and the brown wire to the ground rail. Power the circuit and confirm that the servo starts at 0 degrees. Scan an authorized RFID card and verify that the servo rotates to 90 degrees before returning to 0 degrees after three seconds.


05

HC-SR04 Ultrasonic Sensor

Connect VCC to the 5V rail, GND to the ground rail, TRIG to A1, and ECHO to A2. Once connected, approach the sensor and confirm that the green LED briefly flashes when you are within 80 cm. This flash is generated by the readyPulse() function and indicates that RFID scanning has been enabled.


06

PIR Motion Sensor

Connect VCC to the 5V rail, GND to the ground rail, and OUT to A0. After powering the sensor, allow approximately 60 seconds for calibration. During this warm up period, PIR sensors may produce false triggers as they adjust to the ambient room temperature. In the code, this sensor is referenced as PIN_IR.


07

Slide Switch

Connect the center terminal of the slide switch to D2. Connect either outer terminal to the ground rail and leave the remaining outer terminal unconnected. When the switch is in the disarmed position, the green LED remains on and the servo stays unlocked. When switched to the armed position, the security system becomes active and responds to RFID cards and sensors.


08

Enroll RFID UIDs and Upload the Final Sketch

Copy the recorded UIDs from Step 02 into the code. Place the authorized card UID into authorizedUIDs[0] and the administrator card UID into adminUID[]. Leave the demonstration access denied card out of the authorized list so it can be used to test rejection behavior. Set NUM_AUTHORIZED equal to 1, upload the final sketch, and test all system functions to verify correct operation.

The Code

Screenshot 2026-06-17 193713.png

The Code, Explained

The final sketch uses two libraries, a two dimensional array, nested for loops, and fourteen custom functions. Each major system state has its own function, making the main loop() easy to read and understand. Instead of containing all the logic directly, loop() simply calls functions that describe what the system should be doing.

Libraries and Pin Constants

All pin numbers, timing values, and configuration settings are defined as named constants near the top of the sketch. This makes the code easier to maintain because values such as the unlock time or detection distance only need to be changed in one location.

RFID_Security_Lock_FINAL.ino — Constants

const int PIN_SWITCH = 2;
const int PIN_RED_LED = 4;
const int PIN_GREEN_LED = 5;
const int PIN_SERVO = 6;
const int PIN_YELLOW_LED = 7;
const int DETECT_CM = 80;
const int MAX_FAILS = 9;

Authorized UID Array, Level 4 Arrays

Authorized RFID cards are stored in a two dimensional byte array. The NUM_AUTHORIZED constant automatically determines how many cards the program checks. To add another authorized card, simply add a new row to the array and increase the constant value.

RFID_Security_Lock_FINAL.ino — UID Storage

const int NUM_AUTHORIZED = 1;

byte authorizedUIDs[NUM_AUTHORIZED][4] = {
{ 0xC5, 0xF3, 0xD5, 0x05 }
};

byte adminUID[4] = { 0x8D, 0xF9, 0xD5, 0x05 };

checkUID(), Level 4 Nested For Loops

The checkUID() function compares the scanned card against every authorized card stored in the array. The outer loop moves through each stored card, while the inner loop compares all four bytes of the UID. If a mismatch is found, the break statement immediately exits the inner loop, reducing unnecessary comparisons and improving efficiency.

RFID_Security_Lock_FINAL.ino — checkUID()

bool checkUID() {
for (int card = 0; card < NUM_AUTHORIZED; card++) {
bool match = true;

for (int b = 0; b < 4; b++) {
if (rfid.uid.uidByte[b] != authorizedUIDs[card][b]) {
match = false;
break;
}
}

if (match) return true;
}

return false;
}

HC-SR04 Distance Detection

The ultrasonic sensor is controlled manually instead of using a library. This approach avoids timer conflicts and keeps the code simple. A short trigger pulse is sent to the sensor, pulseIn() measures the returning echo, and the duration is converted into distance using the speed of sound. A timeout of 30000 microseconds prevents the program from waiting indefinitely if no echo is detected.

RFID_Security_Lock_FINAL.ino — personPresent()

bool personPresent() {
digitalWrite(PIN_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);

long duration = pulseIn(PIN_ECHO, HIGH, 30000);
int distCm = duration / 58;

bool ultrasonicHit = (distCm > 0 && distCm <= DETECT_CM);
bool irHit = (digitalRead(PIN_IR) == IR_DETECT);

return (ultrasonicHit && irHit);
}

Lockout System, Level 4 For Loop

After nine failed card scans, the system enters lockout mode. The servo immediately moves to the locked position and the red LED flashes five times before remaining on continuously. Only the administrator card can clear the lockout state. A for loop controls the flashing sequence without requiring repetitive code.

RFID_Security_Lock_FINAL.ino — triggerLockout()

void triggerLockout() {
isLockedOut = true;
adminUnlocked = false;
lockServo.write(ANGLE_LOCKED);

for (int i = 0; i < 5; i++) {
digitalWrite(PIN_RED_LED, HIGH);
delay(150);
digitalWrite(PIN_RED_LED, LOW);
delay(150);
}

digitalWrite(PIN_RED_LED, HIGH);
}

Software Debouncing

Mechanical switches do not change state cleanly. When toggled, they rapidly alternate between on and off for a few milliseconds before settling. Without debouncing, a single switch movement could be interpreted as multiple presses. The pollSwitch() function waits briefly after detecting a change and then checks again to confirm that the new state is stable.

RFID_Security_Lock_FINAL.ino — pollSwitch()

void pollSwitch() {
bool current = digitalRead(PIN_SWITCH);

if (current == switchState) return;

delay(DEBOUNCE_MS);

current = digitalRead(PIN_SWITCH);

if (current == switchState) return;

switchState = current;

if (current == LOW) disarmSystem();
else armSystem();
}

Why Use the F() Macro?

The Arduino Uno contains only 2 KB of RAM. Text strings stored with Serial.println() consume this limited memory. Wrapping strings in the F() macro stores them in flash memory instead, leaving more RAM available for variables and program execution.

Common Mistake, Incorrect UIDs

The placeholder UIDs included in the code will not match real RFID cards. Before uploading the final sketch, run the UID scanner program, scan each RFID card, and copy the exact hexadecimal values into the appropriate arrays.

Common Mistake, Servo Jitter

If the LEDs flicker or the servo behaves erratically while moving, add a 100 µF electrolytic capacitor across the 5V and GND rails. Servos draw a brief surge of current when they start moving, and the capacitor helps smooth these voltage drops. Connect the capacitor's longer leg to 5V and the shorter leg to ground.

System Behaviour

Screenshot 2026-06-17 193639.png

System Behaviour Reference:


Nobody Within 80 cm

When no person is detected within 80 cm of the system, all LEDs remain off and the servo stays in the locked position at 0 degrees. No messages are printed to the Serial Monitor.


Person Detected

When both presence sensors detect a person within range, the green LED briefly blinks to indicate that RFID scanning is active. The servo remains locked at 0 degrees and no Serial Monitor message is displayed.


Authorized Card

When an authorized RFID card is scanned, the green LED turns on for three seconds and the servo rotates to 90 degrees to simulate unlocking the door. The Serial Monitor displays "ACCESS GRANTED".


Unknown Card

When an unauthorized RFID card is scanned, the red LED turns on for two seconds and the servo remains locked at 0 degrees. The Serial Monitor displays "ACCESS DENIED" along with the current failed attempt count.


Lockout Triggered

After nine consecutive failed card scans, the system enters lockout mode. The red LED flashes five times before remaining on continuously. The servo remains locked at 0 degrees and the Serial Monitor displays "LOCKOUT TRIGGERED".


Admin Card

When the administrator card is scanned, the yellow LED turns on and the servo rotates to 90 degrees, unlocking the door regardless of normal access permissions. The Serial Monitor displays "Admin: door UNLOCKED".


Admin Card Scanned Again

Scanning the administrator card a second time exits override mode. The yellow LED turns off, the servo returns to the locked position at 0 degrees, and the Serial Monitor displays "Admin: door RE-LOCKED".


Admin Card During Lockout

If the administrator card is scanned while the system is locked out, the lockout state is cleared immediately. The yellow LED turns on, the servo unlocks to 90 degrees, and the Serial Monitor displays "Lockout CLEARED".


Switch OFF (Disarmed)

When the slide switch is moved to the disarmed position, the green LED remains on continuously and the servo stays unlocked at 90 degrees. RFID scanning and security functions are disabled. The Serial Monitor displays "System DISARMED".


Switch ON (Armed)

When the slide switch is moved to the armed position, all LEDs turn off and the servo returns to the locked position at 0 degrees. RFID scanning and all security features become active. The Serial Monitor displays "System ARMED".

References

The following sources were consulted during research and development of this project.

[1] — MFRC522 Library

GithubCommunity. Arduino RFID Library for MFRC522 (SPI). Arduino Libraries.

https://www.arduinolibraries.info/libraries/mfrc522

Used for: RC522 SPI communication, card detection, UID reading. Functions: PICC_IsNewCardPresent(), PICC_ReadCardSerial(), PICC_HaltA(), PCD_StopCrypto1(), PCD_Init().

[2] — RC522 Wiring Reference

miguelbalboa. Arduino RFID Library for MFRC522 — GitHub.

https://github.com/miguelbalboa/rfid

Used for: RC522 pin mapping (NSS→D10, SCK→D13, MOSI→D11, MISO→D12, RST→D9), confirmed 3.3V VCC requirement, IRQ pin left unconnected.