Security Override Panel

Security Override Panel

This tutorial creates an RFID-based security system with visual LED feedback. The system validates RFID cards and provides different LED animations based on the authentication result.

Gameplay Mechanics

The Challenge

Players must find the correct keycard to activate the security override. The keycard is hidden inside the escape room. This puzzle is meant to be the first task the players must complete in the escape room. As such, it is meant to be an easy introduction.

The difficulty of this puzzle can be increased by adding multiple keycards, and increasing the difficulty of where the keycard is hidden.

How Players Interact

  • Observe current values: LED ring is in a circular red pattern
  • Tap keycard: Players tap a keycard to an RFID reader
  • Read result: LED ring shows Invalid card (red flash) or Success animation (green cascade). LED ring transitions to solved/idle state, after success animation.

Required Components

Hardware

  • 1x Arduino Uno R3
  • 1x MFRC522 RFID Reader Module
  • 1x NeoPixel Ring (12 LEDs)
  • 2-3x RFID Cards/Tags (13.56 MHz)
  • Jumper wires
  • Breadboard (optional)

Software Requirements

  • Arduino IDE (1.8.0 or later)
  • MFRC522 Library by GithubCommunity
  • Adafruit NeoPixel Library
  • SPI Library (built-in)

Wiring Diagram

Security Override Panel wiring diagram

RFID Reader

  • SDA → Pin 10
  • SCK → Pin 13
  • MOSI → Pin 11
  • MISO → Pin 12
  • IRQ → Not connected
  • GND → GND
  • RST → Pin 9
  • 3.3V → 3.3V

NeoPixel Ring

  • DIN → Pin 6
  • VCC → 5V
  • GND → GND

Serial Communication

  • Used for debugging
  • JSON output sent to Escape Room server
Important: The MFRC522 module operates at 3.3V. Connecting it to 5V will damage the module. Always double-check your connections before powering on.

The Code

1Include Required Libraries

First, we need to include all the necessary libraries for RFID reading and LED control:

// Include required libraries
#include <SPI.h>
#include <MFRC522.h>
#include <Adafruit_NeoPixel.h>

What each library does:

SPI.h: Enables communication between Arduino and the RFID reader using the SPI protocol. SPI is a synchronous serial communication protocol that uses 4 wires (MISO, MOSI, SCK, SS) for high-speed data transfer between microcontrollers and peripheral devices.

MFRC522.h: Provides functions to interact with the MFRC522 RFID reader chip. This handles the radio frequency communication protocols needed to read 13.56 MHz RFID cards.

Adafruit_NeoPixel.h: Controls WS2812B (NeoPixel) LEDs, which are individually addressable RGB LEDs that can be chained together and controlled with a single data pin.


2Define Variables and Constants

Set up the configuration for your specific hardware setup:

// PUZZLE variables
int SOLVED = 0;

// RFID variables
String CARD_ID = "5A 49 8E AB"; // Your valid card ID
const int RST_PIN = 9;     
const int SS_PIN = 10;
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance

// LED ring variables
const int LED_PIN = 6;
const int LED_COUNT = 12;
int thisLED = 0;
int nextLED = 0;
int nextnextLED = 0;
int PATTERN = 1; // 1=start, 2=success, 3=invalid, 0=solved
Adafruit_NeoPixel ring(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

What each variable does:

SOLVED: Acts as a boolean flag to prevent multiple successful reads after the puzzle is completed.

CARD_ID: The hexadecimal UID (Unique Identifier) of the authorized RFID card. Each RFID card has a unique 4-byte identifier burned into it during manufacturing.

RST_PIN/SS_PIN: Hardware control pins. RST resets the RFID module, SS (Slave Select) tells the module when the Arduino wants to communicate.

MFRC522 object: Creates an instance of the RFID reader controller with specified pins.

LED animation variables: Track positions for creating a smooth "comet tail" effect during scanning.

PATTERN states:

    1 = Scanning mode (red spinner)

    2 = Success animation (green cascade)

    3 = Invalid card (red flash)

    0 = Solved/idle state

Ring object: Initializes NeoPixel controller with color order (GRB) and communication frequency (800KHz).


3Setup Function

Initialize all components in the setup function:

void setup() {
    SPI.begin();           // Initialize SPI bus
    mfrc522.PCD_Init();   // Initialize MFRC522 reader
    
    ring.begin();         // Initialize LED ring
    ring.show();
    ring.setBrightness(200);
    
    Serial.begin(9600);   // Initialize serial communication
}

What each line accomplishes:

SPI.begin(): Configures pins 11 (MOSI), 12 (MISO), and 13 (SCK) for SPI communication. Sets the Arduino as SPI master device.

mfrc522.PCD_Init(): Performs soft reset of RFID module, configures antenna gain, and sets up communication parameters for 13.56 MHz cards.

ring.begin(): Allocates memory for LED color data and configures the data pin for output.

ring.show(): Sends the current color data to the physical LEDs. Initially all zeros, so LEDs turn off.

ring.setBrightness(): Scales all color values globally (useful for power management and preventing eye strain).

Serial.begin(): Opens UART communication for debugging and JSON output to escape room control system.


4Main Loop - Card Detection

The main loop continuously checks for RFID cards and validates them:

void loop() {
    // Check if a new card is present
    if (!mfrc522.PICC_IsNewCardPresent()) {
        // Continue scanning animation
    }
    
    // Read the card if not already solved
    if (!mfrc522.PICC_ReadCardSerial() && SOLVED == 0) {
        PATTERN = 1; // Keep scanning pattern
    } 
    else if (SOLVED != 1) {
        // Convert card UID to string
        String tagString = "";
        for (byte i = 0; i < mfrc522.uid.size; i++) {
            tagString.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
            tagString.concat(String(mfrc522.uid.uidByte[i], HEX));
        }
        tagString.toUpperCase();
        
        // Check if card is valid
        if (tagString.substring(1) == CARD_ID) {
            Serial.println("{\"shortName\":\"SOP\",\"type\":\"SOLVE\",\"solved\":true}");
            PATTERN = 2; // Success pattern
            SOLVED = 1;
        } else {
            PATTERN = 3; // Error pattern
        }
    }
    
    // Handle LED patterns...
}

Breakdown of the code:

PICC_IsNewCardPresent(): Sends radio frequency pulses to detect if any RFID card is within range (~5cm). Returns false if no card detected, allowing animation to continue.

PICC_ReadCardSerial(): Attempts to read the UID from detected card. This involves:

    Sending authentication request

    Reading card's response

    Storing UID in mfrc522.uid structure

UID to String conversion:

    Iterates through each byte of the UID (usually 4 bytes)

    Adds leading zero for single-digit hex values (0x09 becomes "09")

    Converts to hexadecimal string representation

    The ternary operator ? : adds " 0" if value < 0x10, otherwise just " "

substring(1): Removes the first space character added during concatenation.

JSON output: Formatted for integration with escape room server using standard JSON structure.


5LED Pattern Animations

Create different visual feedback patterns for each state:

Scanning Pattern (Red Spinner)

if (PATTERN == 1) {
    // Calculate LED positions for trailing effect
    nextLED = thisLED - 1;
    nextnextLED = thisLED - 2;
    
    // Handle wraparound
    if (thisLED == ring.numPixels()) {
        thisLED = 0;
        nextLED = 11;
        nextnextLED = 10;
    }
    
    // Clear all LEDs
    ring.fill(0, 0, 12);
    ring.show();
    
    // Set main LED (brightest)
    ring.setBrightness(250);
    ring.setPixelColor(thisLED, 200, 0, 0);
    
    // Set trailing LEDs (dimmer)
    ring.setBrightness(150);
    ring.setPixelColor(nextLED, 200, 0, 0);
    
    ring.setBrightness(25);
    ring.setPixelColor(nextnextLED, 200, 0, 0);
    
    ring.show();
    thisLED++;
    delay(80);
}

Animation breakdown:

Creates a "comet" effect with three LEDs of decreasing brightness.

Wraparound logic ensures smooth circular motion (when lead LED goes past 11, it wraps to 0).

The brightness gradient (250→150→25) creates the illusion of motion blur.

80ms delay creates ~12.5 FPS animation.

Success Pattern (Green Cascade)

else if (PATTERN == 2) {
    // Clear ring
    ring.fill(0, 0, 12);
    ring.show();
    delay(125);
    
    // Cascade effect
    for (int i = ring.numPixels() - 1; i >= 0; i--) {
        ring.setPixelColor(i, 0, 200, 0);
        ring.show();
        delay(50);
    }
    
    // Flash sequence
    for (int flash = 0; flash < 3; flash++) {
        ring.fill(ring.Color(0, 255, 0), 0, 12);
        ring.show();
        delay(500);
        ring.fill(0, 0, 12);
        ring.show();
        delay(500);
    }
    
    PATTERN = 0; // Turn off lights
}

Animation breakdown:

Initial clear provides visual separation from scanning mode.

Cascade fills LEDs one by one from position 11 to 0 (counterclockwise).

Triple flash provides success feedback.

Total animation time: ~4.7 seconds (125ms + 600ms cascade + 3000ms flashing).

Error Pattern (Red Flash)

else if (PATTERN == 3) {
    // Flash all LEDs red
    ring.fill(ring.Color(255, 0, 0), 0, 12);
    ring.show();
    delay(2000);
    PATTERN = 1; // Return to scanning
}

Animation breakdown:

Immediate full-ring red flash indicates unauthorized access attempt.

2-second duration ensures user sees the rejection.

Automatic return to scanning mode for retry.

Testing Your Security Panel

1Finding Your Card's UID

To find the UID of your RFID card, upload this test sketch:

void loop() {
    if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
        Serial.print("Card UID: ");
        for (byte i = 0; i < mfrc522.uid.size; i++) {
            Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
            Serial.print(mfrc522.uid.uidByte[i], HEX);
        }
        Serial.println();
    }
}

What the test code does:

Combines card detection and reading in single if statement for efficiency.

Prints UID in same format expected by main code.

Essential first step - you must know your card's UID before configuring the system.

2Troubleshooting Common Issues

RFID Reader Not Working

  • Check SPI connections (especially MISO/MOSI)
  • Verify 3.3V power (not 5V!)
  • Ensure RST pin is connected
  • Try a different RFID card

LEDs Not Lighting

  • Check data pin connection (Pin 6)
  • Verify 5V power supply
  • Test with simple blink sketch
  • Check LED count matches your ring

3Integration with Escape Room System

The code outputs JSON format which is sent to the Escape Room server.

{
    "shortName": "SOP",
    "type": "SOLVE",
    "solved": true
}

This JSON is parsed by a Node-RED server and triggers the launch of the shuttlecraft from the Nebula Dawn.

Possible Enhancements

Multiple Valid Cards

Store an array of valid UIDs to allow different access levels or team-based puzzles.

Sound Effects

Add a buzzer or speaker module for audio feedback on success/failure.

Network Integration

Use an ESP32 instead of Arduino Uno to add WiFi capabilities for remote monitoring.

Get in touch!

We are currently looking for educators and students to collaborate with on future projects.

Get in Touch!

hello@escapehumber.ca