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. Image 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 Image DFPlayer Mini Specifications

Supports FAT16, FAT32 file system, maximum support 32GB TF card 30 levels volume adjustable, 10 levels EQ adjustable. The audio data is sorted by folder; supports up to 100 folders, each folder can be assigned to 1000 songs. Note: If the MCU system is 5V. It is recommended connect a 1K resistor in series. DFPlayerMini Pinout Image RC522 RFID Specifications

RC522 RFID Pinout Image Transmitter Circuit Diagram Image Receiver Circuit Diagram Image Libraries to be installed

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 Image 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 WiFi 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");           // Show default message
  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");   // Show message
    } else {
      myDFPlayer.play(2);              // Play welcome audio
      lcd.print("Welcome Home");       // Show message
    }
  }


  // 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");       // Restore default message
    lcd.setCursor(0, 1);
    lcd.print("Please");
  }
}
Audios used Image Click Here to get the Audios

Receiver Circuit Image 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 Image Correct UID Detection Image Application
poster_article Mastering Electronics Projects