RFID door lock with a solenoid: relays, flyback diodes and not frying your Arduino
- Build Logs
- 29 Jul, 2026
A solenoid lock is the first thing most people build that can genuinely damage the microcontroller driving it. It is a 12V coil that pulls hundreds of milliamps and, at the moment you switch it off, tries very hard to send that energy back down the wire. Get the isolation right and this is a weekend project. Get it wrong and you will be buying another Arduino.
This build is an RFID access control: tap a card, the bolt retracts for three seconds, unknown cards are refused and everything is logged over serial.
What you will need
| Part | Qty | Approx. |
|---|---|---|
| Arduino Uno R3 (compatible) | 1 | ₹418 |
| RC522 RFID Kit (reader + card + keyfob) | 1 | ₹143 |
| 12V DC Electric Solenoid Lock | 1 | ₹345 |
| 1 Channel 5V Relay Module with Optocoupler | 1 | ₹126 |
| 13.56MHz RFID Tag (extra users) | 2–3 | ₹54 |
Roughly ₹1,090, plus a 12V supply rated for at least 1A.
The part everyone skips: why an optocoupler relay
The solenoid draws around 500 mA at 12V while energised. Two separate problems follow from that.
Current. An Arduino pin sources 20 mA safely, 40 mA absolute maximum. It is roughly twenty-five times short. So the pin does not drive the lock — it drives a relay that switches a separate 12V supply.
Inductive kickback. This is the one that kills boards. A solenoid is a coil, and current through a coil cannot stop instantly. When the relay contacts open, the collapsing magnetic field generates a large reverse voltage spike — easily a few hundred volts for a few microseconds. It arcs across the relay contacts, radiates noise into everything nearby, and finds its way back into your logic.
The relay module listed above has an optocoupler on its input: the Arduino side and the relay-coil side are joined only by a beam of light inside a small black package. There is no electrical path for a spike to travel back up. Boards without the optocoupler share a ground with your Arduino and will happily pass that noise straight into it.
Fit a flyback diode as well — a 1N4007 across the solenoid terminals, banded (cathode) end to +12V. It gives the collapsing field a loop to dissipate into instead of arcing. It costs about a rupee and it is the difference between a lock that works for years and a build that becomes intermittent after a fortnight.
Fit the diode backwards and you create a dead short across your 12V supply the moment you power up. Check the band twice.
Wiring
| From | To | Note |
|---|---|---|
| RC522 SDA | Uno D10 | |
| RC522 SCK | Uno D13 | SPI, fixed pins |
| RC522 MOSI | Uno D11 | |
| RC522 MISO | Uno D12 | |
| RC522 RST | Uno D9 | |
| RC522 3.3V | Uno 3.3V | never 5V |
| RC522 GND | Uno GND | |
| Relay IN | Uno D7 | |
| Relay VCC / GND | Uno 5V / GND | |
| Relay COM | 12V supply + | |
| Relay NO | Solenoid + | normally open |
| Solenoid − | 12V supply − | |
| 1N4007 across solenoid | band to + | flyback |
The RC522 is a 3.3V part. Its VCC pin goes to the Uno's 3.3V rail, not 5V. The logic pins tolerate 5V signalling from the Uno, but the supply pin does not. Feeding it 5V is the second most common way to kill this build, and it usually half-works first — the reader responds, then reads become flaky as the chip cooks.
Use the NO (normally open) contact so the lock is unpowered by default. On NC the solenoid would be energised continuously, drawing half an amp all day, getting hot, and unlocking your door the moment the power fails.
Reading a card
Install the MFRC522 library from the Library Manager, then run this to find out what your cards are called.
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 10
#define RST_PIN 9
MFRC522 rfid(SS_PIN, RST_PIN);
void setup() {
Serial.begin(9600);
SPI.begin();
rfid.PCD_Init();
Serial.println(F("Tap a card to read its UID"));
}
void loop() {
if (!rfid.PICC_IsNewCardPresent()) return;
if (!rfid.PICC_ReadCardSerial()) return;
Serial.print(F("UID: "));
for (byte i = 0; i < rfid.uid.size; i++) {
if (rfid.uid.uidByte[i] < 0x10) Serial.print('0');
Serial.print(rfid.uid.uidByte[i], HEX);
if (i < rfid.uid.size - 1) Serial.print(':');
}
Serial.println();
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
Tap each card and write down the UIDs. Note the PICC_HaltA() and PCD_StopCrypto1() at the end — without them the reader stays locked onto the card and will not register the next tap until you power cycle. That is the "it only reads once" bug.
The access control
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 10
#define RST_PIN 9
#define RELAY_PIN 7
// Most of these relay modules are ACTIVE LOW: pulling IN low
// energises the coil. Check yours; invert these if needed.
#define RELAY_ON LOW
#define RELAY_OFF HIGH
const unsigned long UNLOCK_MS = 3000;
MFRC522 rfid(SS_PIN, RST_PIN);
// Replace with the UIDs you just read
const char* allowed[] = {
"A1:B2:C3:D4",
"12:34:56:78"
};
const byte allowedCount = 2;
void setup() {
Serial.begin(9600);
SPI.begin();
rfid.PCD_Init();
digitalWrite(RELAY_PIN, RELAY_OFF); // set state BEFORE the pin is an output
pinMode(RELAY_PIN, OUTPUT);
Serial.println(F("Ready"));
}
String uidString() {
String s;
for (byte i = 0; i < rfid.uid.size; i++) {
if (rfid.uid.uidByte[i] < 0x10) s += '0';
s += String(rfid.uid.uidByte[i], HEX);
if (i < rfid.uid.size - 1) s += ':';
}
s.toUpperCase();
return s;
}
bool isAllowed(const String& uid) {
for (byte i = 0; i < allowedCount; i++) {
if (uid.equalsIgnoreCase(allowed[i])) return true;
}
return false;
}
void loop() {
if (!rfid.PICC_IsNewCardPresent()) return;
if (!rfid.PICC_ReadCardSerial()) return;
String uid = uidString();
if (isAllowed(uid)) {
Serial.print(F("GRANTED ")); Serial.println(uid);
digitalWrite(RELAY_PIN, RELAY_ON);
delay(UNLOCK_MS);
digitalWrite(RELAY_PIN, RELAY_OFF);
} else {
Serial.print(F("DENIED ")); Serial.println(uid);
}
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
Note the ordering in setup(): write the OFF state to the pin before calling pinMode(OUTPUT). An Arduino pin defaults to INPUT and floats at reset; make it an output first and write afterwards and you get a brief pulse that clicks the relay and thumps the solenoid every time the board resets.
Things that go wrong
Reader works, then goes flaky after a few minutes. RC522 wired to 5V. Move it to 3.3V and hope you caught it early.
Only reads the first card, then nothing. Missing PICC_HaltA() / PCD_StopCrypto1().
Relay clicks but the lock does not move. Almost always the 12V supply. A solenoid needs real current; a spare 12V/500 mA adapter is marginal and will buzz rather than throw the bolt. Use 1A or better.
Arduino resets when the lock fires. You are running the solenoid off the same supply as the board. Separate them, and make sure the flyback diode is fitted.
Relay is on when it should be off. Your module is active-high, not active-low. Swap RELAY_ON and RELAY_OFF.
Reads are inconsistent at distance. The RC522 has about 3 cm of range and metal nearby detunes the antenna. Mounting it behind a metal plate will not work.
Before you put this on a real door
Be honest about what this is. UID-based access control is identification, not authentication — a card's UID is broadcast in the clear and is trivially cloneable with hardware that costs less than this build. It is entirely appropriate for a lab cupboard, a project room or a college demo. It is not appropriate for your front door.
Also think about the failure mode. A solenoid on NO is fail-secure: cut the power and the door stays locked, with you outside it. That is a fire-safety consideration in any real installation, and the reason a mechanical override key is not optional.
Where to take it next
Add an LCD to show the cardholder's name, or move the whitelist off the sketch and onto an SD card so you can add users without reflashing. Swap the Uno for a NodeMCU and the same logic can post every tap to a dashboard.
Ask us if you get stuck on the wiring. Send a photo of the board and we will tell you what is wrong with it.