Community project

Server Room Security System

Arduino
Photo of Server Room Security System
Generated with AI

Mark Christian S. Maraviles

Published September 30, 2026

This server room security system demonstrates multi-layer access control and environmental monitoring using an Arduino Uno. The system combines fingerprint authentication with a numeric keypad, motion detection, temperature monitoring, and hazard sensors (smoke and flame) to protect a miniature server room model. Intruders trigger motion alarms, while authorized users can unlock doors and windows via relay control.

The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for building the miniature room and connecting all sensors and outputs. The included firmware handles fingerprint matching, keypad entry, relay activation, environmental alerts, and real-time status display on an LCD screen, giving makers a foundation for understanding integrated security systems.

Wiring diagram

Wiring diagram for Server Room Security System

Gather all the parts

QtyComponent
1

R307 optical fingerprint sensor module

R307

The fingerprint reader that checks enrolled fingerprints before the server-room door is released.

1

4x4 Matrix Keypad

4x4

A 16-button (4 rows × 4 columns) membrane matrix keypad that uses 8 digital I/O lines (4 row + 4 column) to scan all keys. No dedicated power rail is required — rows and columns are driven directly by GPIO. Compatible with the Arduino Keypad library.

1

PCF8574 I2C keypad expander module

PCF8574

The small board that lets the keypad share the LCD's two data wires.

1

3-channel 5 V relay module for miniature model

3-channel, 5 V input

A 5 V relay board that switches the two miniature latches and miniature fan without loading Arduino pins.

1

HC-SR501 PIR Motion Sensor

HC-SR501

Passive Infrared (PIR) motion detection module with adjustable sensitivity and delay potentiometers. Operates on 5V supply; digital output is nominally 3.3V or 5V depending on module variant (verify before connecting directly to ESP32 3.3V GPIO — use a voltage divider if output is 5V). Outputs HIGH when motion is detected, LOW when idle. Ideal for triggering countdown timer resets in Focus Mode applications. No firmware library required — output read via standard GPIO digitalRead().

1

DHT22

DHT22 / AM2302, 5 V-compatible breakout

Digital temperature and humidity sensor

1

MQ-2 Gas Sensor

MQ-2

Analog combustible-gas and smoke sensor module. Detects LPG, propane, methane, hydrogen, alcohol, and smoke using a heated tin-dioxide (SnO2) sensing element whose resistance falls as gas concentration rises. The breakout board provides an analog output (AO) proportional to concentration plus a digital output (DO) that trips when concentration crosses an onboard potentiometer threshold. Requires a 5 V supply for the internal heater and a 20-30 s warm-up before readings stabilise; the AO voltage is relative, so the sensor needs a clean-air baseline calibration rather than reporting an absolute ppm value.

1

KY-026 Flame Sensor Module

KY-026

KY-026 infrared flame detector module with LM393 comparator, sensitivity trimmer, analog output for IR intensity, and threshold digital output. Common Arduino/ESP32 fire-fighting robot sensor; detects flame-like IR in roughly the 760-1100 nm range.

1

LCD 16x2 I2C

16x2 I2C

16x2 character LCD display with I2C backpack

1

Buzzer

5 V active

Piezo buzzer for sound output

1

5 mm red LED

Red

The red warning light that turns on for intrusion, smoke, flame, or overheating alarms.

1

5 mm green LED

Green

The green light that shows normal armed operation and a granted access interval.

1

220 ohm resistor

220 Ω

The resistor that limits current through the red warning LED.

1

220 ohm resistor

220 Ω

The resistor that limits current through the green status LED.

1

5 V miniature two-wire cooling fan

5 V

A small 5 V fan that represents the cooling fan in the miniature server room.

1

5 V miniature solenoid latch for model door

5 V, 1 A maximum

A small pull-type latch that represents the server-room door lock in the miniature model.

1

5 V miniature solenoid latch for model window

5 V, 1 A maximum

A small pull-type latch that represents the secured window in the miniature model.

1

5 V DC 3 A regulated power adapter

5 V DC, 3 A

A regulated plug-in supply that powers the miniature latches, fan, and relay contacts.

Assemble it in 7 steps

1. Prepare the miniature room

Build a small cardboard, foam-board, or acrylic room with a model door and window. Make two simple sliding latch bars from light plastic or cardboard so each 5 V miniature solenoid can pull one bar back by its short 3 mm movement.

  • Test that each latch bar slides freely by hand before attaching a solenoid.
  • These small solenoids are demonstration parts; keep the door and window light.
  • Do not use these miniature latches on a real door, window, cabinet containing valuables, or an emergency exit.

2. Mount the small outputs

Glue or screw the 5 V fan near the top of the miniature room. Mount the door and window solenoids so each moving pin pulls its latch bar only when it receives a short pulse.

  • Leave a little slack in the wires so opening the model door does not pull on the solenoid.
  • The fan must be able to spin without touching cardboard or loose wires.
  • The solenoids are intermittent-duty parts: holding them on can make them hot and damage them.

3. Wire the Arduino sensors

With the Uno unplugged, connect every sensor and display exactly as shown in the wiring diagram. The fingerprint sensor TX goes to D2 and RX goes to D3; the LCD and keypad expander share A4/SDA and A5/SCL.

  • Keep the MQ-2 sensor outside or at the roof of the model because it is physically large and warms up.
  • Make sure all sensor GND wires meet Arduino GND so every signal has the same reference.
  • Do not swap 5 V and GND on the LCD or fingerprint sensor — swapped power can damage them.

4. Wire the lights and buzzer

Connect D11 through the red 220 ohm resistor to the red LED long leg, then connect its short leg to GND. Connect D12 through the green 220 ohm resistor to the green LED long leg, then connect its short leg to GND. Connect the buzzer signal to D10 and its GND pin to Arduino GND.

  • The LED long leg is positive; the short leg is negative.
  • The resistors protect the LEDs from receiving too much current.
  • Never connect an LED directly between an Arduino pin and GND without its 220 ohm resistor; it can damage the LED or the Arduino pin.

5. Connect the relay control side

Connect relay VCC to Arduino 5 V, relay GND to Arduino GND, IN1 to D4, IN2 to D5, and IN3 to D6. Use a 3-channel relay board marked for 5 V Arduino inputs.

  • The relay clicks when switched; that sound is normal.
  • Keep relay screw terminals away from loose sensor jumper wires.
  • Do not power the fan or solenoids from Arduino 5 V. Their current can overload the board.

6. Connect the separate 5 V miniature-load supply

Keep the Uno unplugged while wiring the load supply. Connect the adapter +5V to relay COM1, COM2, and COM3. Connect NO1 to the model door solenoid +5V, NO2 to the model window solenoid +5V, and NO3 to the fan +5V. Join the negative wires of both solenoids and the fan back to the adapter GND.

  • Use the relay terminals marked COM and NO, not NC: the miniature latches should receive power only for a brief release pulse.
  • A 5 V, 3 A regulated adapter provides enough margin for the two 1 A miniature latches and fan.
  • Do not connect the separate adapter +5 V to Arduino 5 V; it is for relay contacts and the miniature loads only.
  • Check the adapter label says 5 V DC before plugging it in — a higher-voltage adapter can damage the fan and latches.

7. Power and test the model

First plug the Uno into USB, then plug in the separate 5 V adapter. Test the green light, enter the keypad code, and test a registered fingerprint; either should give the door latch a short pull. Warm the DHT22 gently to test the fan, and use the sensor modules only as a classroom demonstration of alarm behavior.

  • The default keypad code is 1234; change it in the firmware before presenting the model.
  • The MQ-2 needs a warm-up period before its number becomes stable.
  • Do not use flame, smoke, or a heat source near the cardboard model. These modules are for a low-risk demonstration, not real fire testing.

Review all connections

1. Connections between "fingerprint_1" and "Arduino"

Functionfingerprint_1Arduino
powerVCC5V
groundGNDGND
uartTXGPIO 0
uartRXGPIO 1

2. Connections between "keypad_expander_1" and "Arduino"

Functionkeypad_expander_1Arduino
powerVCC5V
groundGNDGND
i2cSDAGPIO 18
i2cSCLGPIO 19
digitalP0 → 4x4 Matrix Keypad R1EXT
digitalP1 → 4x4 Matrix Keypad R2EXT
digitalP2 → 4x4 Matrix Keypad R3EXT
digitalP3 → 4x4 Matrix Keypad R4EXT
digitalP4 → 4x4 Matrix Keypad C1EXT
digitalP5 → 4x4 Matrix Keypad C2EXT
digitalP6 → 4x4 Matrix Keypad C3EXT
digitalP7 → 4x4 Matrix Keypad C4EXT

3. Connections between "relay_1" and "Arduino"

Functionrelay_1Arduino
powerVCC5V
groundGNDGND
digitalIN1GPIO 4
digitalIN2GPIO 5
digitalIN3GPIO 6
powerCOM2 → 5 V DC 3 A regulated power adapter +5VEXT
powerCOM3 → 5 V DC 3 A regulated power adapter +5VEXT
powerNO1 → 5 V miniature solenoid latch for model door +5VEXT
powerNO2 → 5 V miniature solenoid latch for model window +5VEXT
powerNO3 → 5 V miniature two-wire cooling fan +5VEXT

4. Connections between "pir_1" and "Arduino"

Functionpir_1Arduino
powerVCC5V
groundGNDGND
digitalOUTGPIO 7

5. Connections between "dht22_1" and "Arduino"

Functiondht22_1Arduino
powerVCC5V
groundGNDGND
dataDATAGPIO 8

6. Connections between "mq2_1" and "Arduino"

Functionmq2_1Arduino
powerVCC5V
groundGNDGND
analogAOGPIO 14

7. Connections between "flame_1" and "Arduino"

Functionflame_1Arduino
powerVCC5V
groundGNDGND
dataDOGPIO 9
analogAOGPIO 15

8. Connections between "lcd_1" and "Arduino"

Functionlcd_1Arduino
powerVCC5V
groundGNDGND
i2cSDAGPIO 18
i2cSCLGPIO 19

9. Connections between "buzzer_1" and "Arduino"

Functionbuzzer_1Arduino
groundGNDGND
digitalSIGNALGPIO 10

10. Connections between "red_resistor_1" and "Arduino"

Functionred_resistor_1Arduino
digitalEnd 1GPIO 11
digitalEnd 2 → 5 mm red LED AnodeEXT

11. Connections between "red_led_1" and "Arduino"

Functionred_led_1Arduino
groundCathodeGND

12. Connections between "green_resistor_1" and "Arduino"

Functiongreen_resistor_1Arduino
digitalEnd 1GPIO 12
digitalEnd 2 → 5 mm green LED AnodeEXT

13. Connections between "green_led_1" and "Arduino"

Functiongreen_led_1Arduino
groundCathodeGND

14. Connections between "supply_12v_1" and "Arduino"

Functionsupply_12v_1Arduino
power+5V → 3-channel 5 V relay module for miniature model COM1EXT
groundGND → 5 V miniature solenoid latch for model door GNDEXT

15. Connections between "door_lock_1" and "Arduino"

Functiondoor_lock_1Arduino
groundGND → 5 V miniature solenoid latch for model window GNDEXT

16. Connections between "window_lock_1" and "Arduino"

Functionwindow_lock_1Arduino
groundGND → 5 V miniature two-wire cooling fan GNDEXT

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_Fingerprint.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>


// Forward declarations
void setTransient(const String &message, unsigned long durationMs);
void pulseDoorLatch();
void pulseWindowLatch();
void updateLatches();
void pollKeypad();
bool fingerprintMatched();
void showStatus(float temperature, float humidity, int smoke, bool emergency, bool intrusion);

const uint8_t FINGERPRINT_RX_PIN = 0;
const uint8_t FINGERPRINT_TX_PIN = 1;
const uint8_t DOOR_RELAY_PIN = 4;
const uint8_t WINDOW_RELAY_PIN = 5;
const uint8_t FAN_RELAY_PIN = 6;
const uint8_t PIR_PIN = 7;
const uint8_t DHT_PIN = 8;
const uint8_t FLAME_DIGITAL_PIN = 9;
const uint8_t BUZZER_PIN = 10;
const uint8_t RED_LED_PIN = 11;
const uint8_t GREEN_LED_PIN = 12;
const uint8_t MQ2_PIN = A0;
const uint8_t FLAME_ANALOG_PIN = A1;

const uint8_t KEYPAD_ADDRESS = 0x20;
const uint8_t LCD_ADDRESS = 0x27;
const char ACCESS_CODE[] = "1234"; // Change this before displaying the model.
const int SMOKE_THRESHOLD = 450;
const float FAN_ON_C = 30.0;
const float FAN_OFF_C = 28.0;
const unsigned long LATCH_PULSE_MS = 800;
const unsigned long DISPLAY_PERIOD_MS = 1000;

// Most small relay boards are active-low.
const uint8_t RELAY_ON = LOW;
const uint8_t RELAY_OFF = HIGH;

// Use the Uno hardware UART for the R307. This avoids the ESP-only serial
// implementation that is incompatible with the browser simulator.
Adafruit_Fingerprint finger(&Serial);
DHT dht(DHT_PIN, DHT22);
LiquidCrystal_I2C lcd(LCD_ADDRESS, 16, 2);

const char keyMap[4][4] = {{'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'}};
char enteredCode[5] = "";
uint8_t enteredLength = 0;
bool armed = true;
bool fanOn = false;
bool releasedForThisAlarm = false;
unsigned long doorPulseUntil = 0;
unsigned long windowPulseUntil = 0;
unsigned long lastDisplay = 0;
String transientMessage = "Starting...";
unsigned long transientUntil = 0;

void setTransient(const String &message, unsigned long durationMs) {
  transientMessage = message;
  transientUntil = millis() + durationMs;
}

void pulseDoorLatch() {
  doorPulseUntil = millis() + LATCH_PULSE_MS;
  setTransient("Door unlocked", 1500);
}

void pulseWindowLatch() {
  windowPulseUntil = millis() + LATCH_PULSE_MS;
}

void updateLatches() {
  // These miniature 5 V latches are intermittent-duty parts: pulse them briefly only.
  digitalWrite(DOOR_RELAY_PIN, millis() < doorPulseUntil ? RELAY_ON : RELAY_OFF);
  digitalWrite(WINDOW_RELAY_PIN, millis() < windowPulseUntil ? RELAY_ON : RELAY_OFF);
}

void pollKeypad() {
  for (uint8_t row = 0; row < 4; row++) {
    uint8_t pattern = 0xFF;
    bitClear(pattern, row);
    Wire.beginTransmission(KEYPAD_ADDRESS); Wire.write(pattern); Wire.endTransmission();
    delayMicroseconds(80);
    Wire.requestFrom(KEYPAD_ADDRESS, (uint8_t)1);
    if (!Wire.available()) continue;
    uint8_t reading = Wire.read();
    for (uint8_t col = 0; col < 4; col++) {
      if (!bitRead(reading, col + 4)) {
        char key = keyMap[row][col];
        while (true) {
          Wire.beginTransmission(KEYPAD_ADDRESS); Wire.write(pattern); Wire.endTransmission();
          Wire.requestFrom(KEYPAD_ADDRESS, (uint8_t)1);
          if (!Wire.available() || bitRead(Wire.read(), col + 4)) break;
          delay(10);
        }
        Wire.beginTransmission(KEYPAD_ADDRESS); Wire.write(0xFF); Wire.endTransmission();
        if (key == '*') {
          enteredLength = 0; enteredCode[0] = '\0'; setTransient("Code cleared", 1000);
        } else if (key == '#') {
          if (strcmp(enteredCode, ACCESS_CODE) == 0) pulseDoorLatch();
          else setTransient("Access denied", 2000);
          enteredLength = 0; enteredCode[0] = '\0';
        } else if (key == 'A') {
          armed = !armed; setTransient(armed ? "System armed" : "System disarmed", 1500);
        } else if (key >= '0' && key <= '9' && enteredLength < 4) {
          enteredCode[enteredLength++] = key; enteredCode[enteredLength] = '\0';
          setTransient("Code: " + String(enteredLength) + " digits", 700);
        }
        return;
      }
    }
  }
  Wire.beginTransmission(KEYPAD_ADDRESS); Wire.write(0xFF); Wire.endTransmission();
}

bool fingerprintMatched() {
  if (finger.getImage() != FINGERPRINT_OK) return false;
  if (finger.image2Tz() != FINGERPRINT_OK) { setTransient("Print not read", 1500); return false; }
  if (finger.fingerFastSearch() == FINGERPRINT_OK) { setTransient("Fingerprint OK", 1500); return true; }
  setTransient("Print denied", 1500);
  return false;
}

void showStatus(float temperature, float humidity, int smoke, bool emergency, bool intrusion) {
  if (millis() - lastDisplay < DISPLAY_PERIOD_MS && millis() > transientUntil) return;
  lastDisplay = millis();
  lcd.setCursor(0, 0);
  String upper;
  if (emergency) upper = "FIRE/SMOKE ALERT";
  else if (intrusion) upper = "INTRUSION ALERT ";
  else if (millis() < transientUntil) upper = transientMessage;
  else upper = "T:" + String(temperature, 1) + "C H:" + String(humidity, 0) + "%";
  upper += "                ";
  lcd.print(upper.substring(0, 16));
  lcd.setCursor(0, 1);
  String lower = "S:" + String(smoke) + (armed ? " ARMED" : " DISARMED") + "       ";
  lcd.print(lower.substring(0, 16));
}

void setup() {
  pinMode(DOOR_RELAY_PIN, OUTPUT); pinMode(WINDOW_RELAY_PIN, OUTPUT); pinMode(FAN_RELAY_PIN, OUTPUT);
  pinMode(PIR_PIN, INPUT); pinMode(FLAME_DIGITAL_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT); pinMode(RED_LED_PIN, OUTPUT); pinMode(GREEN_LED_PIN, OUTPUT);
  digitalWrite(DOOR_RELAY_PIN, RELAY_OFF); digitalWrite(WINDOW_RELAY_PIN, RELAY_OFF);
  digitalWrite(FAN_RELAY_PIN, RELAY_OFF); digitalWrite(BUZZER_PIN, LOW);
  digitalWrite(RED_LED_PIN, LOW); digitalWrite(GREEN_LED_PIN, HIGH);
  Wire.begin();
  lcd.init(); lcd.backlight(); lcd.clear(); lcd.print("Mini server room"); lcd.setCursor(0, 1); lcd.print("Starting sensors");
  dht.begin();
  Serial.begin(57600); finger.begin(57600); delay(300);
  if (!finger.verifyPassword()) setTransient("Finger sensor err", 3000);
  Wire.beginTransmission(KEYPAD_ADDRESS); Wire.write(0xFF); Wire.endTransmission();
}

void loop() {
  pollKeypad();
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();
  if (isnan(humidity)) humidity = 0;
  if (isnan(temperature)) temperature = 0;
  int smoke = analogRead(MQ2_PIN);
  bool flame = digitalRead(FLAME_DIGITAL_PIN) == LOW;
  bool emergency = flame || smoke >= SMOKE_THRESHOLD || temperature >= 55.0;
  bool intrusion = armed && digitalRead(PIR_PIN) == HIGH && !emergency;

  if (temperature >= FAN_ON_C) fanOn = true;
  if (temperature <= FAN_OFF_C) fanOn = false;
  digitalWrite(FAN_RELAY_PIN, fanOn ? RELAY_ON : RELAY_OFF);

  if (emergency) {
    // Model-only response: momentarily open both small latches once per alarm event.
    if (!releasedForThisAlarm) { pulseDoorLatch(); pulseWindowLatch(); releasedForThisAlarm = true; }
    digitalWrite(RED_LED_PIN, HIGH); digitalWrite(GREEN_LED_PIN, LOW); tone(BUZZER_PIN, 1800);
  } else {
    releasedForThisAlarm = false;
    digitalWrite(RED_LED_PIN, intrusion ? HIGH : LOW); digitalWrite(GREEN_LED_PIN, intrusion ? LOW : HIGH);
    if (intrusion) tone(BUZZER_PIN, 1200); else noTone(BUZZER_PIN);
    if (millis() >= doorPulseUntil && fingerprintMatched()) pulseDoorLatch();
  }
  updateLatches();
  showStatus(temperature, humidity, smoke, emergency, intrusion);
  delay(50);
}

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.

Open in Schematik