ESP32 RFID Door Lock System with Voice Alerts Using ESP-NOW Protocol

In this course, I will show you a wonderful project using the ESP-NOW communication protocol on ESP32 boards, combined with RFID to wirelessly trigger a voice alert.

Description

I used the ESP-NOW communication protocol to trigger voice alerts on the receiver board and display information on an LCD connected to it. On the transmitter board, I connected an RC522 RFID reader and a 12V solenoid door lock.

Components used

Two ESP32 Bords, Micro SD Card, RC522 RFID Reader, 12V Solenoid Door Lock, 5V Relay Module, 1W 20Ohm Speaker, LCD Display, An external Power Supply, DFplayer Mini

ModuleFunctionality
ESP32Wireless communication via ESP-NOW
RC522 RFIDReading RFID UIDs
DFPlayer MiniPlaying voice messages
12V SolenoidDoor locking/unlocking
ProjectRFID Door Lock System with Voice Alerts

Schematic transmitter and receiver

Libraries to be installed

  • DFRobotDFPlayerMini.h
  • LiquidCrystal_I2C.h

Transmitter Code

// Include required libraries
#include <SPI.h>          // Enables SPI communication for RFID reader
#include <MFRC522.h>      // Library to interface with MFRC522 RFID reader
#include <Wire.h>         // (Not used here, but included for I2C communication)
#include <esp_now.h>      // Library to use ESP-NOW wireless communication
#include <WiFi.h>         // Required to set Wi-Fi mode to station for ESP-NOW

// Define pin numbers for RFID and solenoid
#define SS_PIN 5          // Slave Select pin for RFID
#define RST_PIN 21        // Reset pin for RFID
#define SOLENOID_PIN 2    // Pin connected to solenoid lock

// Define the MAC address of the receiving ESP32
uint8_t broadcastAddress[] = {0x70, 0xB8, 0xF6, 0x5B, 0xED, 0xB0}; // Replace with your receiver ESP32 MAC address

// Define the data structure to send (no padding)
struct __attribute__((packed)) dataPacket {
  bool state; // true if access granted, false otherwise
};

dataPacket packet;              // Create an instance of dataPacket
esp_now_peer_info_t peerInfo;   // Info about the peer to send data to

// Callback when ESP-NOW data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

// Create RFID object
MFRC522 rfid(SS_PIN, RST_PIN);  // Use defined pins for SPI communication

// Define the UID of the authorized RFID card
byte authorizedUID[] = {0x0B, 0x23, 0x9B, 0x15}; // Change this to match your card's UID

void setup() {
  Serial.begin(115200);            // Start serial communication for debugging
  pinMode(SOLENOID_PIN, OUTPUT);   // Set solenoid pin as output
  digitalWrite(SOLENOID_PIN, HIGH); // Lock solenoid initially (HIGH = locked)

  SPI.begin();           // Start SPI bus
  rfid.PCD_Init();       // Initialize the RFID reader
  WiFi.mode(WIFI_STA);   // Set Wi-Fi mode to Station (required for ESP-NOW)

  if (esp_now_init() != ESP_OK) {  // Initialize ESP-NOW
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  esp_now_register_send_cb(OnDataSent); // Register callback for data send status

  // Register peer to send data
  memcpy(peerInfo.peer_addr, broadcastAddress, 6); // Copy MAC address
  peerInfo.channel = 0;  // Use default channel
  peerInfo.encrypt = false; // No encryption

  if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Add peer
    Serial.println("Failed to add peer");
    return;
  }
}

void loop() {
  // Check if a new card is present
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
    return; // No card or failed to read card
  }

  Serial.print("UID tag: ");
  String content = ""; // Print and build UID string
  for (byte i = 0; i < rfid.uid.size; i++) {
    Serial.print(rfid.uid.uidByte[i] < 0x10 ? " 0" : " ");
    Serial.print(rfid.uid.uidByte[i], HEX);
    content.concat(String(rfid.uid.uidByte[i] < 0x10 ? " 0" : " "));
    content.concat(String(rfid.uid.uidByte[i], HEX));
  }
  Serial.println();
  Serial.println();

  // Compare UID with authorized UID
  bool authorized = true;
  for (byte i = 0; i < rfid.uid.size; i++) {
    if (rfid.uid.uidByte[i] != authorizedUID[i]) {
      authorized = false;
      break; // If any byte doesn't match, exit loop
    }
  }

  packet.state = authorized ? 1 : 0; // Set packet state based on authorization result
  esp_now_send(broadcastAddress, (uint8_t *) &packet, sizeof(packet)); // Send packet
  Serial.println(packet.state); // Print access state (1 or 0)
  delay(30); // Short delay to ensure packet is sent

  if (authorized) {
    Serial.println("Access Granted!");
    digitalWrite(SOLENOID_PIN, LOW);  // Unlock solenoid
    delay(2000);                      // Keep it unlocked for 2 seconds
    digitalWrite(SOLENOID_PIN, HIGH); // Lock again
  } else {
    Serial.println("Access Denied!");
  }

  rfid.PICC_HaltA(); // Stop communication with this card
}

Transmitter Circuit

Receiver code

// Include necessary libraries
#include <esp_now.h>               // ESP-NOW protocol for communication
#include <WiFi.h>                  // Required for WiFi functions (needed by ESP-NOW)
#include "Arduino.h"               // Core Arduino functions
#include "DFRobotDFPlayerMini.h"   // DFPlayer Mini library to control the MP3 module
#include <Wire.h>                  // I2C communication library
#include <LiquidCrystal_I2C.h>     // Library for I2C-based LCD display

// Define software serial for non-ESP32 boards
#ifdef ESP32
#define FPSerial Serial1           // On ESP32, use hardware serial Serial1 for DFPlayer
#else
#include <SoftwareSerial.h>        // For non-ESP32, include SoftwareSerial
SoftwareSerial FPSerial(26, 27);   // RX = 26, TX = 27 (DFPlayer communication)
#endif

DFRobotDFPlayerMini myDFPlayer;    // Create DFPlayer object
LiquidCrystal_I2C lcd(0x27, 16, 2); // Initialize LCD with I2C address 0x27, 16 columns and 2 rows

// Structure for received ESP-NOW data
struct __attribute__((packed)) dataPacket {
  bool state; // Boolean representing access status (authorized or not)
};

unsigned long previousMillis = 0;  // Store last time LCD was updated
const long interval = 1000;        // Interval time to keep welcome message (1 second)
bool playStarted = false;          // Flag to indicate playback start
bool dataReceived = false;         // Flag to indicate new data received

// Object to store incoming data
dataPacket receivedPacket;

// Callback function called when ESP-NOW receives data
void OnDataRecv(const esp_now_recv_info* info, const uint8_t *incomingData, int len) {
  memcpy(&receivedPacket, incomingData, sizeof(receivedPacket)); // Copy received data into our structure
  Serial.print("UID State: ");
  Serial.println(receivedPacket.state); // Print UID state to Serial
  dataReceived = true; // Set flag to true
}

void setup() {
#ifdef ESP32
  FPSerial.begin(9600, SERIAL_8N1, 26, 27); // ESP32-specific Serial1 initialization (RX, TX)
#else
  FPSerial.begin(9600); // For other boards, just begin serial
#endif

  Serial.begin(115200); // Start Serial for debug output
  lcd.init();           // Initialize LCD
  lcd.backlight();      // Turn on LCD backlight
  lcd.setCursor(0, 0);
  lcd.print("Initializing..."); // Print init message
  delay(2000);

  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer..."));

  // Initialize DFPlayer and check for errors
  if (!myDFPlayer.begin(FPSerial)) {
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1. Recheck connection!"));
    Serial.println(F("2. Insert SD card!"));
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("DFPlayer Error!");
    while (true); // Stop program here if DFPlayer fails
  }

  Serial.println(F("DFPlayer Mini online."));
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("DFPlayer Ready"); // Show ready message
  delay(1000);

  myDFPlayer.volume(30); // Set maximum volume (0–30)

  WiFi.mode(WIFI_STA);   // Set ESP32 as Wi-Fi station (required for ESP-NOW)
  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW Init Failed");
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("ESP-NOW Error!");
    return; // Stop further execution if ESP-NOW fails
  }

  esp_now_register_recv_cb(OnDataRecv); // Register callback to handle received data

  myDFPlayer.play(1); // Play welcome/default track
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Your RFID Card");
  lcd.setCursor(0, 1);
  lcd.print("Please");
}

void loop() {
  // If data was received, process it
  if (dataReceived) {
    dataReceived = false; // Reset flag
    playStarted = true;   // Set playback flag
    previousMillis = millis(); // Save current time

    lcd.clear();
    lcd.setCursor(0, 0);

    if (receivedPacket.state == 0) {
      myDFPlayer.play(1); // Play unauthorized UID audio
      lcd.print("Unauthorized UID");
    } else {
      myDFPlayer.play(2); // Play welcome audio
      lcd.print("Welcome Home");
    }
  }

  // After a delay, restore the LCD to default message
  if (playStarted && (millis() - previousMillis >= interval)) {
    playStarted = false; // Reset flag
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Your RFID Card");
    lcd.setCursor(0, 1);
    lcd.print("Please");
  }
}

Receiver Circuit

Explanation

When the RFID reader detects an unauthorized UID, the transmitter board will wirelessly trigger a voice alert—’Unauthorized UID Detected’—on the receiver board, and the 12V solenoid door lock will remain locked. However, when the RFID reader detects a valid UID, the transmitter board (ESP32) will send a signal to trigger another voice alert—’Welcome Home’—on the receiver board, and the 12V solenoid door lock will unlock for 2 seconds.

Wrong UID Detection

Correct UID Detection

Application

  • Smart Home Access Control
  • Office Entry Management
  • School/University Lab or Classroom Access
  • Warehouse or Storage Room Security
  • Hotel Room Door Automation
  • Church or Community Centers
  • Garage or Gate Entry System
  • Industrial Machine Access Control
  • Medical Facility or Pharmacy Access

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *