Categories
english Hacking Hardware Gadgets

RFID Door Opener…

recently i finished my latest project: a RFID Door Opener.

besides the fun while hacking it, it improves the security of the door. since it’s not an outside door but the door to the garage, it’s construction does not allow to mount a more secure lock. so locking it from the outside when leaving is a bit bothersome.
the new auto-lock feature, which allows the door to auto-lock itself, saves some time and works around the lazy user, who wouldn’t have locked it.
additionally it is now easy to add or remove the right of access by adding or deleting the RFID’s unique number from the system. revoking someone’s right of access is far more difficult with a normal key.

beside an arduino i’m using an easydriver to drive the stepper and a RFID reader from seeedstudio.

below you can see the schematics of the setup. the ends marked with Ard X go to a port of the arduino, which is the brain of the lock:

Schematics for the build
Schematics for the build

edit: since i’ve been asked what transistors i used for this build i’ll tell you:
as PNP transistor i’m using a ‘mje2955T‘, as NPN i’m using a ‘bc547‘.
it’s important to have the two different transistor types NPN and PNP.
NPN is the one closer to ‘Ard6’ and the PNP is the one switching the 12V power supply for the easydriver. the most important part is that the second transistor, switching the easy driver, will only work if you’re using a PNP transistor. these transistors switch when they get pulled to ground.

Code can be found on github and here:


/**
 * door lock application (c) 2011 Florian Klien
 * some code parts are borrowed from different authors ;) thx
 */

#include <NewSoftSerial.h>

#define rxPin 2
#define txPin 3

// door defs

#define DOOR_SENS  3 // analog
#define DRIVER_SWITCH 6
#define DOOR_SW 2 // analog

// motor defs
#define DIR_PIN 7
#define STEP_PIN 8
#define ledIN 5
#define ledOUT 11

NewSoftSerial rfid = NewSoftSerial( rxPin, txPin );

// The tag database consists of two parts. The first part is an array of
// tag values with each tag taking up 5 bytes. The second is a list of
// names with one name for each tag (ie: group of 5 bytes).
char* allowedTags[] = {
  "AABBCCDDEE",         // Tag 1
  "AABBCCDDEE",         // Tag 2
};

// List of names to associate with the matching tag IDs
char* tagName[] = {
  "User1",         // Tag 1
  "User2",         // Tag 2
};

// software version number:
char* software_version = "1.1";

// Check the number of tags defined
int numberOfTags = sizeof(allowedTags)/sizeof(allowedTags[0]);

int incomingByte = 0;    // To store incoming serial data

boolean locked = true;
int door_open = 0; // pseudo digital
boolean prev_status = false;
boolean auto_lock = true;
//unsigned long auto_lock_time = 0;
int auto_lock_delay = 5; // in seconds
int auto_lock_switch_time = 2; // in seconds
int status_led = 0;
unsigned long status_led_time = millis();
boolean status_led_on = false;

unsigned long status_breathe_time = millis();
int breathe_delay = 10;
boolean breathe_up = true;
int breathe_i = 15;

unsigned long last_successful_rfid_read = 0;
int rfid_success_timeout = 5000; // millis

float lock_speed = 1;

/**
 * Setup
 */
unsigned long time_door = millis();
unsigned long time_switch = millis();
long debounce = 500;

void setup() {
  pinMode(ledIN, OUTPUT);
  pinMode(ledOUT, OUTPUT);
  digitalWrite(ledIN, HIGH);
  digitalWrite(ledOUT, HIGH);
  delay(300);
  digitalWrite(ledIN, LOW);
  digitalWrite(ledOUT, LOW);

  pinMode(DRIVER_SWITCH, OUTPUT);
  digitalWrite(DRIVER_SWITCH, LOW);
  pinMode(DIR_PIN, OUTPUT);
  digitalWrite(DIR_PIN, LOW);
  pinMode(STEP_PIN, OUTPUT);
  digitalWrite(STEP_PIN, LOW);
  pinMode(DOOR_SENS,INPUT);
  pinMode(DOOR_SW,INPUT);
  
  Serial.begin(9600);   // Serial port for connection to host
  rfid.begin(9600);      // Serial port for connection to RFID module

  Serial.println("RFID reader starting up");
  delay(1000);
  Serial.println("done");
  Serial.print("Software Version no: ");
  Serial.println(software_version);
  Serial.print("door locked: ");
  Serial.print(locked, DEC);
  Serial.print("\n");
  Serial.print("door closed: ");
  Serial.print(!door_open, DEC);
  Serial.print("\n");
}

/**
 * Loop
 * non-blocking version of each function!
 */
void loop() {
  readRFID();
  doorSensor();
  doorSwitch();
  statusLed();
}

void doorSensor(){
  // pseudo digital
  door_open = analogRead(DOOR_SENS);
  if(millis() - time_door > debounce){
    if (door_open <= 500 && prev_status == false){
      Serial.println("Door: opened");
      prev_status = true;
      locked = false;
    }else  if(door_open > 500 && prev_status == true){
      Serial.println("Door: closed");
      prev_status = false;
      if(auto_lock){
        Serial.println("locking door automatically...");
        delay(auto_lock_delay*1000); 
        lock();
        locked = true;
      }
    }
    time_door = millis();  
  }
}

void doorSwitch(){
  int dstimer = 0;
  int door_switch = analogRead(DOOR_SW); // pseudo digital
  if(millis() - time_switch > debounce && door_switch >= 300){
    while (analogRead(DOOR_SW) >= 300) {
      delay(100);
      dstimer++;
    }
    Serial.println(door_switch,DEC);
    Serial.println(dstimer,DEC);
    if (dstimer < auto_lock_switch_time*10) { //button has been pressed less than 2 seconds = 1000/100
        if (locked == false){
          Serial.println("door locked");
          locked = true;
          lock();
        }else if(locked == true){
          Serial.println("door unlocked");
          locked = false;
          unlock();
      }
    }else {
      // auto_unlock off/on
      if(auto_lock == true){
        Serial.println("auto_lock off");
        auto_lock = false;
      }else{
        Serial.println("auto_lock on");
        auto_lock = true;
      }
      analogWrite(ledIN, 0); // resetting output
    
    }
    time_switch = millis();  
  }
}

// breathing status led on the inside
void statusBreathe(){
  if( (status_breathe_time + breathe_delay) < millis() ){
    analogWrite(ledIN, breathe_i/1.5);
    status_breathe_time = millis();
    if (breathe_up == true){
      if (breathe_i > 150) {
        breathe_delay = 4;
      }
      if ((breathe_i > 125) && (breathe_i < 151)) {
        breathe_delay = 5;
      }
      if (( breathe_i > 100) && (breathe_i < 126)) {
        breathe_delay = 7;
      }
      if (( breathe_i > 75) && (breathe_i < 101)) {
        breathe_delay = 10;
      }
      if (( breathe_i > 50) && (breathe_i < 76)) {
        breathe_delay = 14;
      }
      if (( breathe_i > 25) && (breathe_i < 51)) {
        breathe_delay = 18;
      }
      if (( breathe_i > 1) && (breathe_i < 26)) {
        breathe_delay = 19;
      }
      breathe_i += 1;
      if( breathe_i >= 255 ){
        breathe_up = false;
      }
    }else{
      if (breathe_i > 150) {
        breathe_delay = 4;
      }
      if ((breathe_i > 125) && (breathe_i < 151)) {
        breathe_delay = 5;
      }
      if (( breathe_i > 100) && (breathe_i < 126)) {
        breathe_delay = 7;
      }
      if (( breathe_i > 75) && (breathe_i < 101)) {
        breathe_delay = 10;
      }
      if (( breathe_i > 50) && (breathe_i < 76)) {
        breathe_delay = 14;
      }
      if (( breathe_i > 25) && (breathe_i < 51)) {
        breathe_delay = 18;
      }
      if (( breathe_i > 1) && (breathe_i < 26)) {
        breathe_delay = 19;
      }
      breathe_i -= 1;
      if( breathe_i <= 15 ){
        breathe_up = true;
        breathe_delay = 970/2;
      }
    }
  }
}

void statusLed(){
  if(auto_lock == false){
    status_led = 150;
  }else{
    // set this to > 0 if you want the status led to blink in default mode
    status_led = 0; 
    if(status_led == 0){
      statusBreathe();
    }
  }
  if(millis() - status_led_time >= status_led && status_led != 0){
    status_led_on = !status_led_on;
    digitalWrite(ledIN,status_led_on);
    status_led_time = millis();
  }
}

void readRFID(){
  byte i         = 0;
  byte val       = 0;
  byte checksum  = 0;
  byte bytesRead = 0;
  byte tempByte  = 0;
  byte tagBytes[6];    // "Unique" tags are only 5 bytes but we need an extra byte for the checksum
  char tagValue[10];

  if(rfid.available()>0){
    if((val = rfid.read()) == 2) {        // Check for header
    bytesRead = 0;
    while (bytesRead < 12) {            // Read 10 digit code + 2 digit checksum
      val = rfid.read();
      Serial.print(val,BYTE);
      // Append the first 10 bytes (0 to 9) to the raw tag value
      if (bytesRead < 10)
      {
        tagValue[bytesRead] = val;
      }

      // Check if this is a header or stop byte before the 10 digit reading is complete
      if((val == 0x0D)||(val == 0x0A)||(val == 0x03)||(val == 0x02)) {
        break;                          // Stop reading
      }

      // Ascii/Hex conversion:
      if ((val >= '0') && (val <= '9')) {
        val = val - '0';
      }
      else if ((val >= 'A') && (val <= 'F')) {
        val = 10 + val - 'A';
      }

      // Every two hex-digits, add a byte to the code:
      if (bytesRead & 1 == 1) {
        // Make space for this hex-digit by shifting the previous digit 4 bits to the left
        tagBytes[bytesRead >> 1] = (val | (tempByte << 4));

        if (bytesRead >> 1 == 5) {                // If we're at the checksum byte,
          checksum ^= tagBytes[bytesRead >> 1];   // Calculate the checksum... (XOR)
        };
      } else {
        tempByte = val;                           // Store the first hex digit first
      };
  
      bytesRead++;                                // Ready to read next digit
    }
  

    // Send the result to the host connected via USB
    if (bytesRead == 12) {                        // 12 digit read is complete
      tagValue[10] = '\0';                        // Null-terminate the string

      Serial.print("Tag read: ");
      for (i=0; i<5; i++) {
        // Add a leading 0 to pad out values below 16
        if (tagBytes[i] < 16) {
          Serial.print("0");
        }
        Serial.print(tagBytes[i], HEX);
      }
      Serial.println();

      Serial.print("Checksum: ");
      Serial.print(tagBytes[5], HEX);
      Serial.println(tagBytes[5] == checksum ? " -- passed." : " -- error.");

      // Show the raw tag value
      //Serial.print("VALUE: ");
      //Serial.println(tagValue);
      Serial.print("door_open: ");
      Serial.println(door_open,DEC);
      // Search the tag database for this particular tag
      int tagId = findTag( tagValue );

      // Only fire the strike plate if this tag was found in the database
      if( tagId > 0 )
      {
        Serial.print("Authorized tag ID ");
        Serial.print(tagId);
        if(door_open > 500 && (last_successful_rfid_read + rfid_success_timeout) < millis() ){
          Serial.print(": unlocking for ");
          Serial.println(tagName[tagId - 1]);   // Get the name for this tag from the database
          unlock();
          last_successful_rfid_read = millis();
          delay(2000);
        }
      } else {
        Serial.println("Tag not authorized");
        //failSound();
        for (int i=0;i<7;i++){ // FIXXME nonblocking version?
          digitalWrite(ledOUT, HIGH);
          digitalWrite(ledIN, HIGH);
          delay(100);
          digitalWrite(ledOUT, LOW);
          digitalWrite(ledIN, LOW);
          delay(80);
        }
      }
      Serial.println();     // Blank separator line in output
    }

    bytesRead = 0;
  }
  }   
}

/**
 * Fire the relay to activate the strike plate for the configured
 * number of seconds.
 */
void unlock() {
  digitalWrite(ledOUT, HIGH);
  digitalWrite(ledIN, HIGH);
  delay(100);
  // if your stepper is powerful enough you can use full speed
  rotateDeg(-800, 0.6);
  digitalWrite(ledIN, LOW);  
  digitalWrite(ledOUT, LOW);
  locked = false;
}

void lock(){
  digitalWrite(ledOUT, HIGH);
  digitalWrite(ledIN, HIGH);
  delay(100);
  rotateDeg(800, 1);
  digitalWrite(ledIN, LOW);  
  digitalWrite(ledOUT, LOW);
  locked = true;
}

void rotate(int steps, float speed){
  
  // power driver
  digitalWrite(DRIVER_SWITCH,HIGH);
  delay(200);
  //rotate a specific number of microsteps (8 microsteps per step) - (negitive for reverse movement)
  //speed is any number from .01 -> 1 with 1 being fastest - Slower is stronger
  int dir = (steps > 0)? HIGH:LOW;
  steps = abs(steps);

  digitalWrite(DIR_PIN,dir); 

  float usDelay = (1/speed) * 250;

  for(int i=0; i < steps; i++){
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(usDelay); 

    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(usDelay);
  }

  // unpower driver
  delay(200);
  digitalWrite(DRIVER_SWITCH,LOW);
} 

void rotateDeg(float deg, float speed){
  // power driver
  digitalWrite(DRIVER_SWITCH,HIGH);
  delay(200);
  //rotate a specific number of degrees (negative for reverse movement)
  //speed is any number from .01 -> 1 with 1 being fastest - Slower is stronger
  int dir = (deg > 0)? HIGH:LOW;
  digitalWrite(DIR_PIN,dir); 

  int steps = abs(deg)*(1/0.225);
  float usDelay = (1/speed) * 250;

  for(int i=0; i < steps; i++){
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(usDelay); 

    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(usDelay);
  }
  // unpower driver
  delay(200);
  digitalWrite(DRIVER_SWITCH,LOW);
}

/**
 * Search for a specific tag in the database
 */
int findTag( char tagValue[10] ) {
  for (int thisCard = 0; thisCard < numberOfTags; thisCard++) {
    // Check if the tag value matches this row in the tag database
    if(strcmp(tagValue, allowedTags[thisCard]) == 0)
    {
      // The row in the database starts at 0, so add 1 to the result so
      // that the card ID starts from 1 instead (0 represents "no match")
      return(thisCard + 1);
    }
  }
  // If we don't find the tag return a tag ID of 0 to show there was no match
  return(0);
}

28 replies on “RFID Door Opener…”

no. currently it does not work with NFC devices. NFC operates on 13.56 MHz. This RFID reader operates on 125 KHz. I chose it because it was cheap. The reader cost around 10 euros and a set of 5 tags (3 keychains and 2 creditcard-sized cards) cost 2.8 euros. 13.56 MHz tags cost around 2.5 euros a piece.
If one can find a reader for NFC it should be possible. maybe i’ll have a look at this or this device for version 2 🙂

Hey man, here in Moscow Russia cheap one-time readonly RFID/NFC labels are used as a one-time metropolitain tickets.
Since one can purchase a long-term card, lots of people just purchase one-time cards. I can grab and send you a bunch of them for free, if you want.
They only have a unique serial number on them.

there are also long-term cards available, one publicly sold is called Troika, based on Mifare Plus 2K SL1 and costs about $1.3 for a blank card

thx for the offer!

I doubt they would work on the same frequency. Anyway, this setup is not in use anymore.

thx
Florian

it’s thermal insulation. the temperature outside is rather low; and since it’s technically an inside door (not built to keep the heat in) heating on the inside is not that efficient.

i think so. as long as it can talk to an arduino (need to figure out the right protocol). but make sure you keep the logic that decide what key id may enter on the INSIDE 🙂
in my case the only wires that go outside is the antenna and the status led. with the parallax it probably would be the serial communication between the rfid module and the arduino (no problem either).

It’s a standard lock.

One side takes a normal Key, the other had a nob mounted. I took the nob off and mounted the gear wheel.

that’s it.

Hi flo thx for ur sharing. i wanted to ask, how many cards can be supported with this module and also tell me if there is any alternative for Arduino?
thx

I don’t know how many cards are supported. Given that this module uses the more unpopular 125 KHz frequency, I guess it’s debatable.

I think you could use any microcontroller. You might even get it to work with a Raspberry Pi, as you do not need any A/D transformation. I probably would have used a Raspberry Pi, if it had been out yet.

hth

Great project. I am just wondering what kind of switch did you use for door contact?

this was just a simple switch. I don’t know exactly.
You could use any switch. Just make sure to update your firmware accordingly (depending on contact or no-contact on button press)

My easy stepper driver gets very hot. Everything works well. What can possibly go wrong? The motor spins great, just the stepper driver gets hot.

As I remember it, it has been some time, I had the same issue. It turned out that I got rid of the problem by correctly dimensioning the two resistors close to the PNP Transistor. For me 4k7 and 2k2 worked great. With other dimensions I had the same issues and my stepper driver would get very hot, even when idle.

If you don’t have the same resistors as I have, try the same dimension. Otherwise maybe play around with the dimensions of the resistors to find your sweet spot.

hth
Florian

I’ve now have the same resistors as you have. However, the motor would not spin when connected to the schematic. But, it would spin if I just supply it with power, but the excessive heat musters.

It’s hard to say what the problem is. It could be anything. when everything is connected to the circuit ‘ARD6’ must be high, otherwise the motor will not spin, additionally ‘ARD7’ and ‘ARD8’ have to control the motor.

hth
Florian

hi,

Great project !

but i have a problem :

“NewSoftSerial” does not name a type.

Since arduino 1.0, Byt is no longer supported. Please use Serial.Whrite() instead.

What should i do ?

Hello, can you tell where to find the stepper motor, or where did you buy it ?, or model number and data to search for it, THANKS

Hello !

Is it possible to get a plan about the mechanical system ?

Your project is the only one, I found, to turn the key instead of changing deadbolt or latches.

I’m disabled and a renter. I can make some screw holes but cannot change the whole lock system in my condo which has one non standard deadbolt for 5 latches (3 on door side, 1 in the ground, one in the ceiling).

I need to open my door easily (which is currently not the case) and grant access to a lot of people (nurses, GP, house helps, etc) with only 2 keys (my landlord has lost the key duplication card).

Thus a system like yours is more relevant as any smartlocks are incompatible with my door and replace latches will be an heavy (and forbidden) task.

Feel free to e-mail me (English or French) and many thanks for your answer.

N

HI, great job, thanks.
As NN ask, Is it possible to get a plan about the mechanical system ?
Your project is the only one, I found, to turn the key instead of changing deadbolt or latches.
Every things I had tried with a stepper motor I can’t turn the key in case of emergency so I would really appreciate if you could have a plan or explain a bit more how you are using a stepper of that size and being able to turn the key.
Many thanks
Normand

Hello Normand,

I don’t have mechanical plans, I’m sorry. I did put this together as I went along.

Locking/Unlocking the system manually in case of emergency or with a key from outside was important for my setup. So I made sure to unpower the stepper motor, to not lock it up. You can turn the lock when the motor has no power. The schematics for this are in the post.

The system is no longer in place, so there are not even measurements that I can provide.

I thought of welding the original nob of the lock on the outside of gear, to have a manual ‘override’ if you will. That would have placed the gear in between the door and the nob. In the described setup, one had to turn the gear on the chain, which worked, but was not very user-friendly.

hope this helps
Florian

Leave a Reply to Leandro Cancel reply

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

verify you\'re human: * Time limit is exhausted. Please reload CAPTCHA.