{"id":52340,"date":"2026-11-01T10:50:59","date_gmt":"2026-11-01T10:50:59","guid":{"rendered":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/"},"modified":"2026-11-01T10:50:59","modified_gmt":"2026-11-01T10:50:59","slug":"rfid-door-lock","status":"publish","type":"siratek_tutorial","link":"https:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/","title":{"rendered":"Project, an RFID Door Lock"},"content":{"rendered":"<body><p><\/p>\n<h2>What you will build<\/h2>\n<p>A cabinet that opens when you present a card. A reader on the outside, a solenoid latch on the inside, a release button, and a list of accepted cards that survives being unplugged \u2014 with a master card that adds and removes cards without a computer.<\/p>\n<h2>Read this first: what this is for<\/h2>\n<p>Two limits, both from the component pages, and both of which decide where this project may be fitted.<\/p>\n<p><strong>A card\u2019s UID is not a password.<\/strong> <em>Reading RFID Cards with an RC522<\/em> puts it plainly: a UID can be read by anybody with a reader, blank cards that let you write any UID you like are cheap, and MIFARE Classic\u2019s own encryption has been thoroughly broken in published research. Anyone who can stand near your card for a second can make one that opens this.<\/p>\n<p><strong>The lock is fail-secure, and it is a small latch.<\/strong> With no power the bolt stays out, so a power cut leaves it shut. <em>A Mini Electronic Lock<\/em> says what follows from that: it suits a cabinet, a drawer, a store room or a workshop cupboard, and it must <strong>not<\/strong> go on a door that a person might need to get out through, or anything on a fire escape route. The latch itself is a spring bolt in thin metal held by four screws \u2014 it resists a casual pull and not much more.<\/p>\n<p>So: <strong>a convenient cabinet that opens to a card.<\/strong> That is a genuinely useful thing and it is what this project is. It is not an access control system, and anyone you build one for should be told the same.<\/p>\n<h2>Before you begin<\/h2>\n<h3>Parts<\/h3>\n<table>\n<thead>\n<tr>\n<th>Part<\/th>\n<th>Siratek product<\/th>\n<th>Its own tutorial<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Arduino Uno<\/td>\n<td>2219<\/td>\n<td><em>Getting Started with Arduino Uno<\/em><\/td>\n<\/tr>\n<tr>\n<td>RC522 reader with card and fob<\/td>\n<td>2609<\/td>\n<td><em>Reading RFID Cards with an RC522<\/em><\/td>\n<\/tr>\n<tr>\n<td>Mini electronic lock<\/td>\n<td>2795<\/td>\n<td><em>A Mini Electronic Lock<\/em><\/td>\n<\/tr>\n<tr>\n<td>Relay module<\/td>\n<td>3308<\/td>\n<td><em>Switching Bigger Loads with a Relay<\/em><\/td>\n<\/tr>\n<tr>\n<td>Green and red LEDs with 220 ohm resistors<\/td>\n<td>2234<\/td>\n<td><em>Your First Circuit, an LED and a Resistor<\/em><\/td>\n<\/tr>\n<tr>\n<td>Active buzzer<\/td>\n<td>2520<\/td>\n<td><em>Active Buzzer, a Beep Without Code<\/em><\/td>\n<\/tr>\n<tr>\n<td>Push button<\/td>\n<td>2353<\/td>\n<td><em>Read a Push Button<\/em><\/td>\n<\/tr>\n<tr>\n<td>Breadboard and jumper wires<\/td>\n<td><\/td>\n<td><em>Using a Breadboard<\/em><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Also needed: a <strong>DC supply for the lock<\/strong>, a 1N4007 diode, and a multimeter.<\/p>\n<h3>Settle the lock\u2019s supply before anything else<\/h3>\n<p>The Siratek lock has <strong>no voltage printed on it anywhere<\/strong>. These are commonly 12 V, and 6 V versions exist, and guessing is how you destroy one.<\/p>\n<p>Measure the coil resistance across its two wires, then <code>current = voltage \/ resistance<\/code>. A coil reading 20 ohms draws 0.6 A at 12 V; one reading 7 ohms draws 1.7 A, which is a very different supply. Expect <strong>somewhere between 0.5 and 1.5 amps<\/strong>, which is far beyond anything the Arduino can give and the reason the relay is here.<\/p>\n<p>The full procedure is on the lock\u2019s own page. Do it first.<\/p>\n<h3>The lock must only ever be energised briefly<\/h3>\n<p>The coil has no thermal protection. Held on, it becomes too hot to touch within about a minute and can be damaged permanently.<\/p>\n<p>Every sketch here energises for <strong>one and a half seconds<\/strong> and has a <strong>hard ceiling checked independently<\/strong> of whatever decided to open the lock. If you take one habit from this project, take that one: on a part that can be destroyed by being left on, the limit must not consult the logic that might be wrong.<\/p>\n<h3>3.3 volts, and a pin that looks ordinary<\/h3>\n<p>The RC522\u2019s supply pin is <strong>3.3 V<\/strong>. Five volts there destroys the reader, and the pin sits in a row of eight that all look alike. Check it twice before powering up; this is the single most common way these modules die.<\/p>\n<p>Its SPI pins are 5 V signals going into a 3.3 V part, which works on a bench and is out of specification. For something that will live on a cabinet door, put a level converter on <code>MOSI<\/code> and <code>SCK<\/code>. <em>Mixing 3.3 V and 5 V Safely<\/em> is the page.<\/p>\n<h3>What happens at power-up, which is a safety question<\/h3>\n<p>When the board resets, every pin starts as a floating input. If the relay\u2019s resting level is the one that energises the lock, the lock pulses open at every reset \u2014 and on a cabinet lock that is a security hole you cannot see, because it lasts a fraction of a second.<\/p>\n<p>The sketch writes the resting level <strong>before<\/strong> <code>pinMode()<\/code> makes the pin an output, which is the order that avoids the pulse. Test it deliberately: press the reset button with the lock connected and watch the bolt.<\/p>\n<h2>How it works<\/h2>\n<p>The card list lives in EEPROM, so it survives a power cut, and three states keep the lock honest:<\/p>\n<table>\n<thead>\n<tr>\n<th>State<\/th>\n<th>What it means<\/th>\n<th>What ends it<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>IDLE<\/code><\/td>\n<td>Waiting for a card<\/td>\n<td>A card, or the release button<\/td>\n<\/tr>\n<tr>\n<td><code>OPEN<\/code><\/td>\n<td>Lock energised<\/td>\n<td>The open time, or the hard ceiling<\/td>\n<\/tr>\n<tr>\n<td><code>ENROLLING<\/code><\/td>\n<td>Master card presented; next card is added or removed<\/td>\n<td>A card, or thirty seconds<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The decision is deliberately boring:<\/p>\n<ol>\n<li>A card arrives. Its UID is read.<\/li>\n<li>If it is the <strong>master<\/strong> card, go to <code>ENROLLING<\/code>.<\/li>\n<li>If it is in the list, open.<\/li>\n<li>If it is not, refuse \u2014 and if refusals keep coming, slow down.<\/li>\n<li>Whatever happened, the lock closes on time.<\/li>\n<\/ol>\n<h3>Enrolment without a computer<\/h3>\n<p>A cabinet whose card list can only be changed by editing a sketch is a cabinet you will stop using.<\/p>\n<p>So one card is the <strong>master<\/strong>. Present it, and the next card presented is added to the list \u2014 or, if it is already in the list, removed from it. A beep pattern says which happened, and after thirty seconds with no card the mode gives up on its own.<\/p>\n<p>The master card never opens the lock. That is a small decision with a real consequence: the card you carry to administer the thing is not the card you carry to use it, so losing one does not lose both.<\/p>\n<h3>Why the refusal counter exists, and what it does not fix<\/h3>\n<p>After five refused cards in a row the project waits thirty seconds before reading another, and says so on its red LED.<\/p>\n<p>Be clear about what this achieves. It makes trying thousands of random UIDs impractical. It does <strong>nothing<\/strong> against the real attack, which is cloning a card that is already accepted. It is worth the eight lines anyway, in the same way a lock on a garden gate is worth having, but it does not change the paragraph at the top of this page.<\/p>\n<h3>EEPROM, and writing to it sparingly<\/h3>\n<p>An ATmega328P\u2019s EEPROM is good for roughly <strong>100,000 writes per cell<\/strong>. That is plenty for a list that changes when somebody is given a card, and nothing like enough for something written every time the lock opens.<\/p>\n<p>So the list is written only on enrolment, and the open count is not stored at all. If you want a log of every opening, that belongs on an SD card \u2014 see <em>Making it yours<\/em>.<\/p>\n<h2>Build it in stages<\/h2>\n<h3>Stage 1: the reader, nothing else<\/h3>\n<p>Wire the RC522 and run its tutorial\u2019s example. Present the card and the fob and <strong>write their UIDs down<\/strong>. You need one of them to be the master, and you cannot set the master without knowing a UID.<\/p>\n<p>If the reader is not found, the cause is <code>3.3V<\/code> or a wrong SPI pin, in that order of likelihood.<\/p>\n<h3>Stage 2: the indicators<\/h3>\n<p>Green LED, red LED and the buzzer, with the reader logic still just printing. Accepted and refused should be unmistakable across a room.<\/p>\n<h3>Stage 3: the relay and the lock, on the bench<\/h3>\n<p>Wire the relay\u2019s control side only and hear it click. Then the load side, with the diode, with the lock\u2019s own supply, grounds joined.<\/p>\n<p><strong>Clamp the lock or hold it.<\/strong> It jumps when it fires, and the impulse is sharper than you expect.<\/p>\n<p>Then press the Arduino\u2019s reset button, repeatedly, and watch the bolt. It must not move. If it twitches, your relay\u2019s resting level is wrong and the project is not ready for a cabinet door.<\/p>\n<h3>Stage 4: put it together<\/h3>\n<p>The complete sketch below. Set <code>MASTER_UID<\/code> to one of the UIDs you wrote down in stage 1.<\/p>\n<h3>Stage 5: fit it, and only to the right thing<\/h3>\n<p>Four screws, a cabinet or a drawer, the striker plate aligned so the bolt drops in cleanly. Put the reader on the outside and <strong>everything else on the inside<\/strong>, including the release button \u2014 a release button on the outside is a lock with a button that opens it.<\/p>\n<h2>Pin reference<\/h2>\n<table>\n<thead>\n<tr>\n<th>Device<\/th>\n<th>Pin<\/th>\n<th>Connect to<\/th>\n<th>Notes<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>RC522<\/td>\n<td><code>3.3V<\/code><\/td>\n<td>Uno <strong><code>3.3V<\/code><\/strong><\/td>\n<td><strong>Never <code>5V<\/code><\/strong><\/td>\n<\/tr>\n<tr>\n<td>RC522<\/td>\n<td><code>GND<\/code><\/td>\n<td>Uno <code>GND<\/code><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>RC522<\/td>\n<td><code>RST<\/code><\/td>\n<td>Uno <code>D9<\/code><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>RC522<\/td>\n<td><code>SDA<\/code><\/td>\n<td>Uno <code>D10<\/code><\/td>\n<td>SPI chip select, not I2C<\/td>\n<\/tr>\n<tr>\n<td>RC522<\/td>\n<td><code>MOSI<\/code><\/td>\n<td>Uno <code>D11<\/code><\/td>\n<td>Converter for a permanent build<\/td>\n<\/tr>\n<tr>\n<td>RC522<\/td>\n<td><code>MISO<\/code><\/td>\n<td>Uno <code>D12<\/code><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>RC522<\/td>\n<td><code>SCK<\/code><\/td>\n<td>Uno <code>D13<\/code><\/td>\n<td>Converter for a permanent build<\/td>\n<\/tr>\n<tr>\n<td>Green LED<\/td>\n<td>anode<\/td>\n<td>Uno <code>D5<\/code><\/td>\n<td>Through 220 ohms<\/td>\n<\/tr>\n<tr>\n<td>Red LED<\/td>\n<td>anode<\/td>\n<td>Uno <code>D6<\/code><\/td>\n<td>Through 220 ohms<\/td>\n<\/tr>\n<tr>\n<td>Relay<\/td>\n<td><code>IN<\/code><\/td>\n<td>Uno <code>D7<\/code><\/td>\n<td>LOW energises on this board<\/td>\n<\/tr>\n<tr>\n<td>Relay<\/td>\n<td><code>VCC<\/code>, <code>GND<\/code><\/td>\n<td>Uno <code>5V<\/code>, <code>GND<\/code><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>Buzzer<\/td>\n<td><code>+<\/code><\/td>\n<td>Uno <code>D8<\/code><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>Release button<\/td>\n<td>one leg<\/td>\n<td>Uno <code>D2<\/code><\/td>\n<td><code>INPUT_PULLUP<\/code>, <strong>inside<\/strong><\/td>\n<\/tr>\n<tr>\n<td>Relay<\/td>\n<td><code>COM<\/code><\/td>\n<td>Lock supply <code>+<\/code><\/td>\n<td>Load side<\/td>\n<\/tr>\n<tr>\n<td>Relay<\/td>\n<td><code>NO<\/code><\/td>\n<td>Lock red<\/td>\n<td>Locked when unpowered<\/td>\n<\/tr>\n<tr>\n<td>Lock<\/td>\n<td>black<\/td>\n<td>Lock supply <code>-<\/code><\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td>Diode<\/td>\n<td>across the lock<\/td>\n<td>Banded end to red<\/td>\n<td>1N4007, required<\/td>\n<\/tr>\n<tr>\n<td>Lock supply<\/td>\n<td><code>-<\/code><\/td>\n<td><strong>Also Uno <code>GND<\/code><\/strong><\/td>\n<td>Shared reference<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><code>D13<\/code> is the SPI clock here, so the onboard LED flickers while the reader is being polled. That is the bus working, not a fault, and it is why the indicators are on <code>D5<\/code> and <code>D6<\/code>.<\/p>\n<figure class=\"siratek-tutorial-figure\"><img decoding=\"async\" src=\"http:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/rfid-door-lock-wiring.svg\" alt=\"An RFID cabinet lock with an RC522 reader on its 3.3 volt supply, a relay driving a solenoid latch from a separate supply with a flyback diode, a release button on the inside, and the enrolment master card\" loading=\"lazy\"><figcaption>Fail-secure, so it suits a cabinet and not a door people exit through. The card list lives in EEPROM.<\/figcaption><\/figure>\n<h2>The complete sketch<\/h2>\n<pre><code>\/\/ Siratek: an RFID cabinet lock.\n\/\/ See the top of the page for what a UID is and is not.\n\n#include &lt;SPI.h&gt;\n#include &lt;MFRC522.h&gt;\n#include &lt;EEPROM.h&gt;\n\nMFRC522 reader(10, 9);\n\nconst int greenPin  = 5;\nconst int redPin    = 6;\nconst int lockPin   = 7;\nconst int buzzerPin = 8;\nconst int buttonPin = 2;\n\n\/\/ Relay polarity: see Switching Bigger Loads with a Relay.\nconst int LOCK_ENERGISED = LOW;\nconst int LOCK_RESTING   = HIGH;\n\nconst unsigned long OPEN_FOR     = 1500;    \/\/ the bolt is in for this long\nconst unsigned long ABSOLUTE_MAX = 3000;    \/\/ and never longer than this\nconst unsigned long ENROL_WINDOW = 30000;\n\n\/\/ ---- From stage 1. The master administers; it never opens. ----\nconst byte MASTER_UID[4] = {0xDE, 0xAD, 0xBE, 0xEF};\n\/\/ ---------------------------------------------------------------\n\n\/\/ EEPROM layout: a marker, a count, then up to 8 four-byte UIDs.\nconst int EEPROM_MARKER_ADDR = 0;\nconst int EEPROM_COUNT_ADDR  = 1;\nconst int EEPROM_LIST_ADDR   = 2;\nconst byte MARKER = 0x5A;\nconst int MAX_CARDS = 8;\n\nenum State { IDLE, OPEN, ENROLLING };\nState state = IDLE;\nunsigned long stateSince = 0;\n\nint refusals = 0;\nunsigned long lockedOutUntil = 0;\n\nvoid beep(int times, int lengthMs) {\n  for (int i = 0; i &lt; times; i++) {\n    digitalWrite(buzzerPin, HIGH);\n    delay(lengthMs);\n    digitalWrite(buzzerPin, LOW);\n    if (i &lt; times - 1) {\n      delay(lengthMs);\n    }\n  }\n}\n\nbyte cardCount() {\n  if (EEPROM.read(EEPROM_MARKER_ADDR) != MARKER) {\n    return 0;                       \/\/ never initialised\n  }\n  byte n = EEPROM.read(EEPROM_COUNT_ADDR);\n  return n &gt; MAX_CARDS ? 0 : n;\n}\n\nvoid readCard(byte index, byte out[4]) {\n  for (byte i = 0; i &lt; 4; i++) {\n    out[i] = EEPROM.read(EEPROM_LIST_ADDR + index * 4 + i);\n  }\n}\n\nbool sameUid(const byte a[4], const byte b[4]) {\n  for (byte i = 0; i &lt; 4; i++) {\n    if (a[i] != b[i]) {\n      return false;\n    }\n  }\n  return true;\n}\n\n\/\/ -1 if not in the list, otherwise its index.\nint findCard(const byte uid[4]) {\n  byte n = cardCount();\n  byte stored[4];\n  for (byte i = 0; i &lt; n; i++) {\n    readCard(i, stored);\n    if (sameUid(stored, uid)) {\n      return i;\n    }\n  }\n  return -1;\n}\n\nbool addCard(const byte uid[4]) {\n  byte n = cardCount();\n  if (n &gt;= MAX_CARDS) {\n    return false;\n  }\n  for (byte i = 0; i &lt; 4; i++) {\n    EEPROM.update(EEPROM_LIST_ADDR + n * 4 + i, uid[i]);\n  }\n  EEPROM.update(EEPROM_COUNT_ADDR, n + 1);\n  EEPROM.update(EEPROM_MARKER_ADDR, MARKER);\n  return true;\n}\n\nvoid removeCard(int index) {\n  byte n = cardCount();\n  byte moved[4];\n  \/\/ Move the last one into the gap, then shorten the list.\n  if (index != n - 1) {\n    readCard(n - 1, moved);\n    for (byte i = 0; i &lt; 4; i++) {\n      EEPROM.update(EEPROM_LIST_ADDR + index * 4 + i, moved[i]);\n    }\n  }\n  EEPROM.update(EEPROM_COUNT_ADDR, n - 1);\n}\n\nvoid release() {\n  digitalWrite(lockPin, LOCK_RESTING);\n  digitalWrite(greenPin, LOW);\n  state = IDLE;\n  stateSince = millis();\n}\n\nvoid openLock(const __FlashStringHelper* why) {\n  if (state == OPEN) {\n    return;                         \/\/ already open; do not extend it\n  }\n  state = OPEN;\n  stateSince = millis();\n  digitalWrite(lockPin, LOCK_ENERGISED);\n  digitalWrite(greenPin, HIGH);\n  beep(1, 80);\n  Serial.print(F(\"open: \"));\n  Serial.println(why);\n}\n\nvoid refuse() {\n  refusals++;\n  digitalWrite(redPin, HIGH);\n  beep(2, 60);\n  delay(400);\n  digitalWrite(redPin, LOW);\n\n  Serial.print(F(\"refused (\"));\n  Serial.print(refusals);\n  Serial.println(F(\")\"));\n\n  if (refusals &gt;= 5) {\n    lockedOutUntil = millis() + 30000UL;\n    refusals = 0;\n    Serial.println(F(\"too many refusals: pausing for 30 seconds\"));\n  }\n}\n\nvoid setup() {\n  \/\/ The resting level FIRST, so the lock cannot pulse on at reset.\n  digitalWrite(lockPin, LOCK_RESTING);\n  pinMode(lockPin, OUTPUT);\n\n  pinMode(greenPin, OUTPUT);\n  pinMode(redPin, OUTPUT);\n  pinMode(buzzerPin, OUTPUT);\n  pinMode(buttonPin, INPUT_PULLUP);\n\n  Serial.begin(9600);\n  SPI.begin();\n  reader.PCD_Init();\n\n  Serial.print(F(\"Siratek: cabinet lock. Cards enrolled: \"));\n  Serial.println(cardCount());\n  stateSince = millis();\n}\n\nvoid loop() {\n  \/\/ 1. The limits. Checked before anything else, and they read only the clock.\n  if (state == OPEN &amp;&amp; millis() - stateSince &gt;= OPEN_FOR) {\n    release();\n    Serial.println(F(\"closed\"));\n  }\n  if (state == OPEN &amp;&amp; millis() - stateSince &gt;= ABSOLUTE_MAX) {\n    release();\n    Serial.println(F(\"closed: absolute limit\"));\n  }\n\n  \/\/ 2. Enrolment gives up on its own.\n  if (state == ENROLLING &amp;&amp; millis() - stateSince &gt;= ENROL_WINDOW) {\n    state = IDLE;\n    digitalWrite(redPin, LOW);\n    Serial.println(F(\"enrolment timed out\"));\n  }\n\n  \/\/ 3. The release button, on the inside.\n  if (digitalRead(buttonPin) == LOW &amp;&amp; state != OPEN) {\n    openLock(F(\"release button\"));\n    return;\n  }\n\n  \/\/ 4. Refusal lockout.\n  if (millis() &lt; lockedOutUntil) {\n    digitalWrite(redPin, (millis() % 1000) &lt; 500);\n    return;\n  }\n\n  \/\/ 5. A card, or nothing.\n  if (!reader.PICC_IsNewCardPresent() || !reader.PICC_ReadCardSerial()) {\n    return;\n  }\n\n  if (reader.uid.size != 4) {\n    \/\/ Seven-byte UIDs are common on newer cards and phones. This list\n    \/\/ holds four-byte ones, and comparing only the first four would\n    \/\/ accept cards it should not.\n    Serial.println(F(\"refused: not a 4-byte UID\"));\n    refuse();\n    reader.PICC_HaltA();\n    reader.PCD_StopCrypto1();\n    return;\n  }\n\n  byte uid[4];\n  for (byte i = 0; i &lt; 4; i++) {\n    uid[i] = reader.uid.uidByte[i];\n  }\n\n  if (state == ENROLLING) {\n    int found = findCard(uid);\n    if (found &gt;= 0) {\n      removeCard(found);\n      beep(3, 70);\n      Serial.println(F(\"card removed\"));\n    } else if (addCard(uid)) {\n      beep(2, 150);\n      Serial.println(F(\"card added\"));\n    } else {\n      beep(5, 50);\n      Serial.println(F(\"list full\"));\n    }\n    state = IDLE;\n    digitalWrite(redPin, LOW);\n\n  } else if (sameUid(uid, MASTER_UID)) {\n    state = ENROLLING;\n    stateSince = millis();\n    digitalWrite(redPin, HIGH);\n    beep(1, 300);\n    Serial.println(F(\"master: present a card to add or remove it\"));\n\n  } else if (findCard(uid) &gt;= 0) {\n    refusals = 0;\n    openLock(F(\"known card\"));\n\n  } else {\n    refuse();\n  }\n\n  reader.PICC_HaltA();\n  reader.PCD_StopCrypto1();\n}<\/code><\/pre>\n<h3>Five decisions worth explaining<\/h3>\n<p><strong>The resting level is written before <code>pinMode()<\/code>.<\/strong> One line in an unusual order, and it is the difference between a cabinet that stays shut through a reset and one that flicks open every time the power wavers. Test it rather than trusting it.<\/p>\n<p><strong>Two independent time limits.<\/strong> <code>OPEN_FOR<\/code> is the normal one; <code>ABSOLUTE_MAX<\/code> exists in case something is wrong with the first. On a coil that can be destroyed by being left on, that redundancy costs four lines.<\/p>\n<p><strong><code>openLock()<\/code> refuses to extend an open that is already running.<\/strong> Without that, a card held against the reader or a stuck button would keep pushing the timestamp forward and the lock would stay energised indefinitely \u2014 exactly what must not happen.<\/p>\n<p><strong>The limits are the first thing in <code>loop()<\/code>, before the button and before the reader.<\/strong> So a card presented during an open cannot delay the close, and neither can anything else.<\/p>\n<p><strong><code>EEPROM.update()<\/code>, not <code>EEPROM.write()<\/code>.<\/strong> <code>update()<\/code> only writes when the byte is actually different, which matters on a part rated for about 100,000 writes per cell.<\/p>\n<h2>Testing it<\/h2>\n<table>\n<thead>\n<tr>\n<th>Try this<\/th>\n<th>It should<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Press the Arduino\u2019s reset with the lock connected<\/strong><\/td>\n<td>The bolt must not move at all<\/td>\n<\/tr>\n<tr>\n<td>Present an unknown card<\/td>\n<td>Red, two beeps, nothing opens<\/td>\n<\/tr>\n<tr>\n<td>Present the master, then an unknown card<\/td>\n<td>Two long beeps: added<\/td>\n<\/tr>\n<tr>\n<td>Present that card<\/td>\n<td>Opens<\/td>\n<\/tr>\n<tr>\n<td>Present the master, then that same card<\/td>\n<td>Three beeps: removed<\/td>\n<\/tr>\n<tr>\n<td>Present it again<\/td>\n<td>Refused<\/td>\n<\/tr>\n<tr>\n<td>Unplug everything, plug it back in<\/td>\n<td>The list is still there; the count prints at startup<\/td>\n<\/tr>\n<tr>\n<td>Present the master card alone<\/td>\n<td>Enrolment mode, then it gives up after thirty seconds<\/td>\n<\/tr>\n<tr>\n<td>Hold a known card against the reader<\/td>\n<td>Opens once, closes on time, does not re-open while held<\/td>\n<\/tr>\n<tr>\n<td>Five unknown cards in a row<\/td>\n<td>Red blinks, reader ignored for thirty seconds<\/td>\n<\/tr>\n<tr>\n<td>Press the release button<\/td>\n<td>Opens<\/td>\n<\/tr>\n<tr>\n<td><strong>Energise it, then pull the lock\u2019s supply<\/strong><\/td>\n<td>The bolt springs out at once: fail-secure<\/td>\n<\/tr>\n<tr>\n<td>Feel the lock after ten operations<\/td>\n<td>Cool. Warm means the limits are not working<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The first and the last-but-one are the two that matter. One proves the lock cannot be opened by interrupting the power; the other proves it closes when the power goes. Both are properties of the build rather than the code, and both are worth demonstrating to whoever will use the cabinet.<\/p>\n<h2>Making it yours<\/h2>\n<ul>\n<li><strong>Log every opening.<\/strong> The SD card module and the DS3231 give a dated line per card, which is the single most useful addition: not security, but a genuine record of who opened what and when. <em>Project, a Standalone Data Logger<\/em> is the pattern, and it keeps those writes off the EEPROM.<\/li>\n<li><strong>Add a keypad.<\/strong> A card <em>and<\/em> a four-digit code is meaningfully better than a card alone, because cloning the card is then not enough. The 4\u00d74 keypad page covers it.<\/li>\n<li><strong>A fingerprint sensor instead.<\/strong> Nothing to clone and nothing to lose, with its own limits set out on its page. The lock side of this project is unchanged.<\/li>\n<li><strong>Show something.<\/strong> A 16\u00d72 I2C LCD saying \u201cpresent card\u201d, \u201cwelcome\u201d or \u201cenrolment\u201d makes the project explain itself to someone who has never seen it.<\/li>\n<li><strong>Put the reader on a door and the brain inside.<\/strong> A long cable to the reader is the right arrangement; a long cable to the lock is not, because a cut cable at the lock end is a way in.<\/li>\n<\/ul>\n<p>What not to do: fit this to a room door, a front door, or anything on an escape route. Fail-secure plus a spring latch plus a cloneable card is the wrong combination for all three.<\/p>\n<h2>Troubleshooting<\/h2>\n<table>\n<thead>\n<tr>\n<th>Symptom<\/th>\n<th>Likely cause<\/th>\n<th>Check or fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Reader not found at startup<\/td>\n<td><code>3.3V<\/code>, or SPI wiring<\/td>\n<td>Never <code>5V<\/code>; <code>SDA<\/code> is <code>D10<\/code>, <code>RST<\/code> is <code>D9<\/code><\/td>\n<\/tr>\n<tr>\n<td>Reader worked once, now dead<\/td>\n<td>5 V applied to it<\/td>\n<td>That board is 3.3 V only<\/td>\n<\/tr>\n<tr>\n<td>Cards read intermittently<\/td>\n<td><code>PCD_StopCrypto1()<\/code> missing<\/td>\n<td>It is at the end of every path<\/td>\n<\/tr>\n<tr>\n<td>Bolt twitches at reset<\/td>\n<td>Resting level set after <code>pinMode<\/code><\/td>\n<td>Write it before<\/td>\n<\/tr>\n<tr>\n<td>Nothing moves, relay clicks<\/td>\n<td>Lock supply voltage or current<\/td>\n<td>Measure at the lock\u2019s own wires<\/td>\n<\/tr>\n<tr>\n<td>Bolt moves weakly<\/td>\n<td>Supply sagging<\/td>\n<td>0.5 to 1.5 A; measure the coil first<\/td>\n<\/tr>\n<tr>\n<td>Lock gets hot<\/td>\n<td>Held energised<\/td>\n<td>The two limits exist for this; check both<\/td>\n<\/tr>\n<tr>\n<td>Lock hot despite the limits<\/td>\n<td>Something calls <code>openLock()<\/code> repeatedly<\/td>\n<td>It refuses to extend; check the call sites<\/td>\n<\/tr>\n<tr>\n<td>Arduino resets when the lock fires<\/td>\n<td>Shared supply<\/td>\n<td>Separate supply, grounds joined<\/td>\n<\/tr>\n<tr>\n<td>Relay contacts pit quickly<\/td>\n<td>No flyback diode<\/td>\n<td>1N4007, banded end to the positive side<\/td>\n<\/tr>\n<tr>\n<td>Card list lost after a power cut<\/td>\n<td>EEPROM marker never written<\/td>\n<td><code>addCard()<\/code> writes it; enrol one card<\/td>\n<\/tr>\n<tr>\n<td>Master card opens the lock<\/td>\n<td>It is also in the list<\/td>\n<td>Remove it; the master administers only<\/td>\n<\/tr>\n<tr>\n<td>A phone or newer card is refused<\/td>\n<td>Seven-byte UID<\/td>\n<td>By design; the list holds four-byte ones<\/td>\n<\/tr>\n<tr>\n<td>Door locked after a power cut<\/td>\n<td>Fail-secure, as designed<\/td>\n<td>Do not use this on a door people exit through<\/td>\n<\/tr>\n<tr>\n<td>Someone opened it with a copied card<\/td>\n<td>The honest limit of this project<\/td>\n<td>See the top of the page<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Sources and attribution<\/h2>\n<p>All text, wiring guidance and code in this project are original Siratek work.<\/p>\n<ul>\n<li>Reader behaviour, the 3.3 V supply limit, the SPI pin assignment, the UID length check, the <code>PCD_StopCrypto1()<\/code> requirement and the honest account of what a UID is and is not are as set out in <em>Reading RFID Cards with an RC522<\/em>, which cites the published research on MIFARE Classic.<\/li>\n<li>The lock\u2019s momentary duty, its absent voltage marking and the measurement method, the flyback diode and the fail-secure consequences are as set out in <em>A Mini Electronic Lock<\/em>, read from the Siratek product photographs for item 2795.<\/li>\n<li>Relay switching and the startup behaviour are as set out in <em>Switching Bigger Loads with a Relay<\/em>.<\/li>\n<li>The EEPROM card list, the master-card enrolment scheme, the refusal lockout and the two independent time limits are Siratek\u2019s own design decisions for this project. The roughly 100,000 write endurance figure is the ATmega328P datasheet\u2019s.<\/li>\n<\/ul>\n<p><strong>This project has not yet been built and run on hardware<\/strong>, and the lock\u2019s supply voltage is a measurement the builder must take rather than a figure this page can give. The limits on what this project is suitable for are stated at the top and are not softened anywhere below. No third-party tutorial text, diagrams, photographs or code has been reproduced here.<\/p>\n<p><\/p>\n<\/body>","protected":false},"excerpt":{"rendered":"<p>A cabinet that opens when you present a card. A reader on the outside, a solenoid latch on the inside, a release button, and a list of accepted cards that survives being unplugged \u2014 with a master card that adds&#8230;<\/p>\n","protected":false},"featured_media":0,"template":"","siratek_tutorial_level":[],"class_list":["post-52340","siratek_tutorial","type-siratek_tutorial","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Project, an RFID Door Lock - siratek<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Project, an RFID Door Lock - siratek\" \/>\n<meta property=\"og:description\" content=\"A cabinet that opens when you present a card. A reader on the outside, a solenoid latch on the inside, a release button, and a list of accepted cards that survives being unplugged \u2014 with a master card that adds...\" \/>\n<meta property=\"og:url\" content=\"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/\" \/>\n<meta property=\"og:site_name\" content=\"siratek\" \/>\n<meta property=\"og:image\" content=\"http:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/rfid-door-lock-wiring.svg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"17 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/rfid-door-lock\\\/\",\"url\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/rfid-door-lock\\\/\",\"name\":\"Project, an RFID Door Lock - siratek\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/siratek.net\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/rfid-door-lock\\\/#primaryimage\"},\"image\":{\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/rfid-door-lock\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/siratek.net\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/rfid-door-lock-wiring.svg\",\"datePublished\":\"2026-11-01T10:50:59+00:00\",\"breadcrumb\":{\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/rfid-door-lock\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/rfid-door-lock\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/rfid-door-lock\\\/#primaryimage\",\"url\":\"https:\\\/\\\/siratek.net\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/rfid-door-lock-wiring.svg\",\"contentUrl\":\"https:\\\/\\\/siratek.net\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/rfid-door-lock-wiring.svg\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"http:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/rfid-door-lock\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/siratek.net\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorials\",\"item\":\"https:\\\/\\\/siratek.net\\\/index.php\\\/tutorials\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Project, an RFID Door Lock\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/siratek.net\\\/#website\",\"url\":\"https:\\\/\\\/siratek.net\\\/\",\"name\":\"siratek\",\"description\":\"Your Best Electronic Retail Shop\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/siratek.net\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Project, an RFID Door Lock - siratek","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/","og_locale":"en_US","og_type":"article","og_title":"Project, an RFID Door Lock - siratek","og_description":"A cabinet that opens when you present a card. A reader on the outside, a solenoid latch on the inside, a release button, and a list of accepted cards that survives being unplugged \u2014 with a master card that adds...","og_url":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/","og_site_name":"siratek","og_image":[{"url":"http:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/rfid-door-lock-wiring.svg","type":"","width":"","height":""}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/","url":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/","name":"Project, an RFID Door Lock - siratek","isPartOf":{"@id":"https:\/\/siratek.net\/#website"},"primaryImageOfPage":{"@id":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/#primaryimage"},"image":{"@id":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/#primaryimage"},"thumbnailUrl":"http:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/rfid-door-lock-wiring.svg","datePublished":"2026-11-01T10:50:59+00:00","breadcrumb":{"@id":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/#primaryimage","url":"https:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/rfid-door-lock-wiring.svg","contentUrl":"https:\/\/siratek.net\/wp-content\/uploads\/2026\/11\/rfid-door-lock-wiring.svg"},{"@type":"BreadcrumbList","@id":"http:\/\/siratek.net\/index.php\/tutorials\/rfid-door-lock\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/siratek.net\/"},{"@type":"ListItem","position":2,"name":"Tutorials","item":"https:\/\/siratek.net\/index.php\/tutorials\/"},{"@type":"ListItem","position":3,"name":"Project, an RFID Door Lock"}]},{"@type":"WebSite","@id":"https:\/\/siratek.net\/#website","url":"https:\/\/siratek.net\/","name":"siratek","description":"Your Best Electronic Retail Shop","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/siratek.net\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"}]}},"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/siratek_tutorial\/52340","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/siratek_tutorial"}],"about":[{"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/types\/siratek_tutorial"}],"version-history":[{"count":0,"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/siratek_tutorial\/52340\/revisions"}],"wp:attachment":[{"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/media?parent=52340"}],"wp:term":[{"taxonomy":"siratek_tutorial_level","embeddable":true,"href":"https:\/\/siratek.net\/index.php\/wp-json\/wp\/v2\/siratek_tutorial_level?post=52340"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}