RFID Door Access System Reliability Issues After Power Cycling

I've built an RFID door access system using an Arduino UNO R3 with WiFi (CH340G), an RFID-RC522 reader, and two servos. The system worked fine for about a week, but now I'm experiencing several issues:

Main Issues:

  1. RFID Reader Problems After Power Cycling:
  • When I power on the system, the RFID reader's LED doesn't illuminate properly
  • The reader doesn't detect tags until I perform these steps:
    • Unplug and replug the Arduino
    • Disconnect and reconnect the RFID-RC522's VCC pin
    • Press the reset button on the Arduino
  1. Servo2 Instability:
  • Servo2 makes small, random movements periodically
  • During power outages/cuts, Servo2 moves erratically when power returns

Important: This issue started happening after I used the remote access feature through the Blynk app

System Configuration:

  • Arduino UNO R3 with WiFi (CH340G)
  • MFRC522 RFID reader
  • Two servos (SERVO1: MG90S micro servo, SERVO2: DS 35kg digital servo)
  • Dual power supplies:
    • 5V/5A for servos
    • 9V/2A for Arduino

The wiring follows the diagram I've attached. Both the ESP8266 and ATMEGA parts of the code seem to be working, but the system becomes unstable after power cycling.

ATMEGA part

#include <SPI.h>
#include <MFRC522.h>
#include <Servo.h>

#define SS_PIN    10
#define RST_PIN   9
#define SERVO_PIN_1 6
#define SERVO_PIN_2 5
#define BUZZER_PIN A0  // Changed from 8 to A0

MFRC522 mfrc522(SS_PIN, RST_PIN);
Servo servo1;
Servo servo2;

byte authorizedUIDs[12][4] = { 
 I deleted the tags serial for security matters
};

int angle1 = 0;
int angle2 = 0;
#define SERVO2_CLOSED_ANGLE  0
#define SERVO2_OPEN_ANGLE    90

unsigned long lastActionTime = 0;
const long delayTime = 4000;  // Hold time for Servo 1 (4 seconds)

// Variables for alarm system
byte failedAttempts = 0;
unsigned long lastFailedTime = 0;
const long failedResetTime = 30000;  // Reset failed attempts counter after 30 seconds

void setup() {
  Serial.begin(9600);
  SPI.begin();
  mfrc522.PCD_Init();
  servo1.attach(SERVO_PIN_1);
  servo1.write(angle1);
  
  servo2.attach(SERVO_PIN_2, 500, 2500);
  servo2.write(SERVO2_CLOSED_ANGLE);
  delay(200);
  servo2.detach();  // Detach servo2 after initialization
  
  Serial.println("System Initialized");
}

void loop() {
  // Check for commands from ESP8266
  if (Serial.available()) {
    char command = Serial.read();
    if (command == 'O') {  // 'O' for Open door via Blynk
      Serial.println("Remote command received");
      playBuzzer(true);
      delay(2000);
      openCloseServo2Sequence();
    }
  }

  // Reset failed attempts counter after timeout
  if (failedAttempts > 0 && (millis() - lastFailedTime) >= failedResetTime) {
    failedAttempts = 0;
    Serial.println("Failed attempts counter reset");
  }

  // Check if it's time to close Servo 1
  if ((millis() - lastActionTime) >= delayTime && angle1 != 0) {
    revertServo1Faster();
  }

  if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  byte *uid = mfrc522.uid.uidByte;
  if (isAuthorized(uid)) {
    Serial.println("Authorized Tag Detected!");
    // Send status to ESP8266
    Serial.print("A:");  // 'A' for Authorized
    for (int i = 0; i < mfrc522.uid.size; i++) {
      if (uid[i] < 0x10) Serial.print("0");
      Serial.print(uid[i], HEX);
    }
    Serial.println();
    
    failedAttempts = 0;  // Reset failed attempts on successful authorization
    playBuzzer(true);
    delay(2000);
    openCloseServo2Sequence();
    // lastActionTime is now set in toggleServo1Fast() for precise timing
  } else {
    Serial.print("Unauthorized Tag: ");
    // Send status to ESP8266
    Serial.print("U:");  // 'U' for Unauthorized
    for (int i = 0; i < mfrc522.uid.size; i++) {
      if (uid[i] < 0x10) Serial.print("0");
      Serial.print(uid[i], HEX);
    }
    Serial.println();
    
    failedAttempts++;
    lastFailedTime = millis();
    
    if (failedAttempts >= 3) {
      Serial.println("WARNING: Multiple unauthorized attempts detected!");
      // Send alarm status to ESP8266
      Serial.println("ALARM");
      playAlarm();
      failedAttempts = 0;  // Reset counter after alarm
    } else {
      playBuzzer(false);
      Serial.print("Failed attempts: ");
      Serial.println(failedAttempts);
    }
  }
}

bool isAuthorized(byte *uid) {
  for (int i = 0; i < 12; i++) {
    if (memcmp(uid, authorizedUIDs[i], 4) == 0) {
      return true;
    }
  }
  return false;
}

void openCloseServo2Sequence() {
  if (!servo2.attached()) {
    servo2.attach(SERVO_PIN_2, 500, 2500);
    delay(100);
  }
  
  Serial.println("Servo #2: Opening...");
  int step = (angle2 < SERVO2_OPEN_ANGLE) ? 1 : -1;
  while (angle2 != SERVO2_OPEN_ANGLE) {
    angle2 += step;
    servo2.write(angle2);
    delay(5);
  }
  
  Serial.println("Servo #2 at 90°. Holding for 2.5 seconds...");
  delay(2000);  // Hold for 2 seconds
  
  Serial.println("Opening Servo #1 during last half second of hold...");
  // Open Servo #1 quickly during the last half second
  toggleServo1Fast();
  
  delay(500);  // Complete the 2.5 second hold time
  
  Serial.println("Servo #2: Closing slowly...");
  step = (angle2 > SERVO2_CLOSED_ANGLE) ? -1 : 1;
  while (angle2 != SERVO2_CLOSED_ANGLE) {
    angle2 += step;
    servo2.write(angle2);
    delay(15);
  }
  
  delay(200);  // Give time to stabilize before detaching
  servo2.detach();  // Detach servo2 after use
  Serial.println("Servo #2 closed (0°) slowly and detached.");
}

void toggleServo1() {
  if (!servo1.attached()) {
    servo1.attach(SERVO_PIN_1);
    delay(50);
  }
  int targetAngle = (angle1 == 0) ? 144 : 0;
  int step = (angle1 < targetAngle) ? 1 : -1;
  while (angle1 != targetAngle) {
    angle1 += step;
    servo1.write(angle1);
    delay(5);
  }
  Serial.print("Servo #1 moved to ");
  Serial.print(angle1);
  Serial.println("°.");
}

void toggleServo1Fast() {
  if (!servo1.attached()) {
    servo1.attach(SERVO_PIN_1);
    delay(50);
  }
  int targetAngle = 144;  // Opening only
  int step = 3;  // Move 3 degrees at a time for faster movement
  while (angle1 < targetAngle) {
    angle1 += step;
    if (angle1 > targetAngle) angle1 = targetAngle;  // Prevent overshooting
    servo1.write(angle1);
    delay(2);  // Shorter delay for faster movement
  }
  Serial.print("Servo #1 quickly moved to ");
  Serial.print(angle1);
  Serial.println("°.");
  
  // Set lastActionTime when Servo 1 reaches its open position
  // This ensures exactly 4 seconds of hold time
  lastActionTime = millis();
  Serial.print("Servo #1 will hold for ");
  Serial.print(delayTime/1000);
  Serial.println(" seconds");
}

void revertServo1Faster() {
  Serial.println("Auto-revert triggered for Servo #1...");
  if (!servo1.attached()) {
    servo1.attach(SERVO_PIN_1);
    delay(50);
  }
  
  int step = (angle1 > 0) ? -1 : 1;
  while (angle1 != 0) {
    angle1 += step;
    servo1.write(angle1);
    delay(2);
  }
  servo1.detach();
  Serial.println("Servo #1 is now at 0° and detached.");
}

void playBuzzer(bool authorized) {
  if (authorized) {
    // Two clear beeps for authorized access
    analogWrite(BUZZER_PIN, 200);
    delay(200);
    analogWrite(BUZZER_PIN, 0);
    delay(200);
    analogWrite(BUZZER_PIN, 200);
    delay(200);
    analogWrite(BUZZER_PIN, 0);
  } else {
    // Error-like sound for unauthorized access
    // Rapidly descending tone to indicate error
    for (int i = 255; i > 100; i -= 30) {
      analogWrite(BUZZER_PIN, i);
      delay(60);
    }
    analogWrite(BUZZER_PIN, 0);
    delay(100);
    
    // One more error beep
    analogWrite(BUZZER_PIN, 200);
    delay(400);
    analogWrite(BUZZER_PIN, 0);
  }
}

void playAlarm() {
  Serial.println("ALARM: Unauthorized access attempts detected!");
  
  // Play loud alarm pattern for about 5 seconds
  for (int j = 0; j < 10; j++) {  // 10 cycles of the pattern
    // High-pitched rapid beeps
    for (int i = 0; i < 3; i++) {  // 3 quick beeps per cycle
      analogWrite(BUZZER_PIN, 250);  // Loud!
      delay(100);
      analogWrite(BUZZER_PIN, 0);
      delay(100);
    }
    
    // Brief pause between cycles
    delay(200);
  }
  
  analogWrite(BUZZER_PIN, 0);  // Ensure buzzer is off
  Serial.println("Alarm finished");
}

ESP PART

#define BLYNK_TEMPLATE_ID "xxxx"
#define BLYNK_TEMPLATE_NAME "xxxx"
#define BLYNK_AUTH_TOKEN "xxxx"

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

// WiFi credentials
char ssid[] = "YourWiFiName"; 
char pass[] = "YourWiFiPassword";

String inputBuffer = "";
bool stringComplete = false;

// Button press cooldown variables
unsigned long lastButtonPressTime = 0;
const long buttonCooldown = 5000; // 5 seconds cooldown between button presses

// System initialization
bool systemInitialized = false;
unsigned long startupTime = 0;
const long initializationDelay = 5000; // 5 seconds delay after boot

// Blynk virtual pins
#define BLYNK_BUTTON_PIN V1   // Button to open door
#define BLYNK_LOG_PIN V2      // Log display
#define BLYNK_STATUS_PIN V3   // Status display

// Blynk button handler with cooldown
BLYNK_WRITE(BLYNK_BUTTON_PIN) {
  // Only process commands after initialization period
  if (!systemInitialized) {
    return;
  }
  
  int buttonState = param.asInt();
  unsigned long currentTime = millis();
  
  // Only process button press (state 1) and enforce cooldown period
  if (buttonState == 1 && (currentTime - lastButtonPressTime > buttonCooldown)) {
    // Button is pressed, send command to Arduino
    Serial.write('O');  // 'O' for Open
    Blynk.virtualWrite(BLYNK_LOG_PIN, "Remote access initiated");
    Blynk.virtualWrite(BLYNK_STATUS_PIN, "OPENING DOOR");
    
    // Update last press time
    lastButtonPressTime = currentTime;
  }
}

void setup() {
  // Start serial communications
  Serial.begin(9600);  // For communicating with Arduino
  
  // Clear any startup data in serial buffer
  while (Serial.available()) {
    Serial.read();
  }
  
  // Connect to Blynk
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  
  Blynk.virtualWrite(BLYNK_STATUS_PIN, "INITIALIZING...");
  Blynk.virtualWrite(BLYNK_LOG_PIN, "System starting, please wait...");
  
  // Record startup time to enforce initialization delay
  startupTime = millis();
}

void loop() {
  Blynk.run();  // Process Blynk commands
  
  // Check if initialization period has passed
  if (!systemInitialized && (millis() - startupTime > initializationDelay)) {
    systemInitialized = true;
    Blynk.virtualWrite(BLYNK_STATUS_PIN, "SYSTEM READY");
    Blynk.virtualWrite(BLYNK_LOG_PIN, "WiFi connected, system active");
  }
  
  // Only process Arduino messages after initialization
  if (systemInitialized) {
    // Check for messages from Arduino
    while (Serial.available()) {
      char inChar = (char)Serial.read();
      inputBuffer += inChar;
      
      // End of message received
      if (inChar == '\n') {
        stringComplete = true;
      }
    }
    
    // Process complete messages
    if (stringComplete) {
      parseMessage(inputBuffer);
      inputBuffer = "";
      stringComplete = false;
    }
  }
}

void parseMessage(String message) {
  message.trim();  // Remove any trailing whitespace or newlines
  
  if (message.startsWith("A:")) {  // Authorized access
    String cardID = message.substring(2);
    Blynk.virtualWrite(BLYNK_LOG_PIN, "Card access granted: " + cardID);
    Blynk.virtualWrite(BLYNK_STATUS_PIN, "DOOR OPENING");
  }
  else if (message.startsWith("U:")) {  // Unauthorized access
    String cardID = message.substring(2);
    Blynk.virtualWrite(BLYNK_LOG_PIN, "Access denied: " + cardID);
    Blynk.virtualWrite(BLYNK_STATUS_PIN, "UNAUTHORIZED CARD");
  }
  else if (message == "ALARM") {
    Blynk.virtualWrite(BLYNK_LOG_PIN, "SECURITY ALERT: Multiple unauthorized attempts");
    Blynk.virtualWrite(BLYNK_STATUS_PIN, "SECURITY ALARM");
  }
  else if (message == "Remote command received") {
    // Confirmation that the Arduino received our command
    Blynk.virtualWrite(BLYNK_LOG_PIN, "Remote access processing");
  }
  else if (message.indexOf("Servo #1 is now at 0°") >= 0) {
    // When door is closed again
    Blynk.virtualWrite(BLYNK_STATUS_PIN, "DOOR CLOSED");
  }
}

My Questions:

  1. Could this be a power initialization sequence issue? Maybe the RFID reader needs to be reset in a specific order?
  2. Is there a way to make the system more resilient to power interruptions?
  3. What might be causing Servo2's random movements? Power noise?
  4. Are there any modifications to my circuit or code that could fix these issues?
  5. Why would using the remote access feature through Blynk cause the RFID reader to stop functioning properly?

Thanks for any help!

I wonder what is that?

Well you have made the classic mistake of wiring a 3V3 system directly into a 5V system. Most of the internet gets this wrong.
This is how it should be wired.

It's an enhanced Arduino UNO with integrated ESP8266 WiFi. Features a CH340G USB chip for programming and a convenient switch to toggle between programming the ATmega328P or the ESP8266 WiFi module - perfect for IoT projects without requiring separate modules.

Thank you for the diagram - now I understand why The RFID reader still gets power through the data pins even when I unplug VCC.

Would this bi-directional logic level converter in the picture work instead of the resistor setup you showed? It has 4 channels which should cover the SPI connections.

I appreciate your help figuring this out

What is required that just Esp8266/Esp32 can't do??
To my opinion, it's perfect for confusion if you don't have sufficient experience with both of them.

May I suggest you to verify servos with simple example code for possible power issues.
Then separately RFID.
When both work, put them together.
Blynk would be very last step.

Since we cannot see your tests, does this message appear even problems happen?

Yes, it is showing. I will add a level shifter as recommended by @Grumpy_Mike and see if that resolves the issue. If not, the RFID may already be damaged. I'll provide an update tomorrow.