Showing posts with label rfid. Show all posts
Showing posts with label rfid. Show all posts

Sunday, February 15, 2009

RFID Arduino Door Lock

Back again after a short hiatus...

This project actually looks rather silly and this time I have a video to prove it! A while back I bought an Adafruit Motor Shield (click here for more info) and a couple of servo motors. I was looking around for a quick project when I remembered my old friend the RFID reader (which you will remember from this post). Perhaps because my day job involves the hotel industry, I thought why not have a try at building a door lock?

I am actually rather proud of this one, because I actually set about the design process in a proper organized fashion. I thought about what I wanted it to do and then drew a diagram of what pins I would use then I set about designing the software in a reasonably organized way. I must be getting the hang of this - only a moderate amount of banging my head on my desk!

Without further ado, here is the exciting multimedia part of tonight's presentation!



Now for a couple of quick pictures. This shows the inside mechanism:




And here is the RFID reader on the outside of the door:



Oh no, another poorly drawn Eagle CAD schematic:




Hardwarewise, it is very simple. The only oddity here is that I have actually used the Analog pins of the Arduino as Digital pins. This is done quite simply by calling them pins 14 - 19. Why? This is because the Motor Sheild either uses or covers up most of the usual digital pins. Really, for a simple servo motoro like this, I am not actually using all those digitial pins, and, in fact, I probably don't need the Motor Sheild at all. An Arduino can drive a simple servo without any extra circuitry. However, I wanted to set this up so that a stepper motor could be substituted in later on.

Software

So, here is the code that makes it work:
/* ======== Arduino RFID door lock ============

copyright Chris Armour, Feb 2009

*/

#include <AFSoftSerial.h> //Adafruit soft serial library - slightly modified!
#include <ServoTimer1.h> //Ada Motorshield servo library

// =========Initialize variables and so on =========================//
ServoTimer1 servo1;
AFSoftSerial rfidSerial =  AFSoftSerial(14,15);
//Note that this uses a version of the AFSoftSerial library modified to support pin numbers > 9.
int LEDPin = 16;
int incomingByte[16];
int oldCardNum = 0;
int newCardNum = 0;
int goodCardNum1 = 56;
int goodCardNum2 = 51;
int Angle = 0;
int LockState = 1; //1 = unlocked 0 = locked
int LockLED = 17;
int InButton = 18;
int buttonWas = 0; // The state of the switch (pushed = 1, not pushed = 0) last time we looked
int buttonIs = 0; // Current state of the switch

//==========Setup pins & servo ===============//
void setup() {
Serial.begin(9600);
rfidSerial.begin(9600);
pinMode(LEDPin, OUTPUT);
pinMode(LockLED, OUTPUT);
pinMode(InButton, INPUT);
servo1.attach(10);
servo1.write(Angle);
}

//===========Functions=====================//
void getButton() {
buttonWas = buttonIs; // Set the old state of the button to be the current state since we're creating a new current state.
buttonIs = digitalRead(InButton); // Read the button state
}

void openClose(){ //Evaluates the LockState variable
 digitalWrite(LEDPin, HIGH); //Turns on pass/fail LED
   if (LockState == 1){
     Angle = 180; //Turns servo to UNLOCKED position
     LockState = 0; //toggles Lockstate
     digitalWrite(LockLED,HIGH); //Turns on LockLED to show door UNLOCKED
     }
   else {
     Angle = 0; //turns servo to LOCKED position
     LockState = 1; //toggles lock state
     digitalWrite(LockLED,LOW); //Turns off LockLED to show door LOCKED
     }
   servo1.write(Angle); //Move the servo appropriately
   delay(1000);
   digitalWrite(LEDPin,LOW); //after 1 second, turn off the pass/fail LED
}

//================= Main loop =========================//

void loop() {

getButton();
if((buttonIs==1)&&(buttonWas==0)) { //If the button has been pushed, call openClose to toggle the state of the servo & LEDs.
 openClose();
   }

if(incomingByte[11] > 0){
 oldCardNum = incomingByte[11]; //Sets the value of the last read RFID card to oldCardNum. Just using the 12 digit of the card ID.
}

if(rfidSerial.available() > 0) { //If the AFSoftSerial RX pin reads new incoming data.
 for (int i=0; i <= 16; i++){ //It reads the next 16 characters
     delay(10);
 incomingByte[i] = rfidSerial.read();
     newCardNum = incomingByte[11]; //Pick out ASCII character #12 since it is unique across the 5 cards I have.
      }
  }

if(oldCardNum != newCardNum) {  //Check if a new card # has been received.
  if(newCardNum == goodCardNum1 || newCardNum == goodCardNum2){ //If the new card number equals either of the key cards.
    openClose(); //run the openClose function
   }
else { //If the card isn't a good card, flash the pass/fail indicator 3 times.
  for (int x=0; x <=3; x++){
  digitalWrite(LEDPin, HIGH);
   delay(100);
   digitalWrite(LEDPin,LOW);
   delay(100);
  }
}
Serial.println(newCardNum); //serial output for debugging.
}

}


I am actually rather proud of this code. It uses actual functions for getting the value of the button and for evaluating the lock/unlock state. Of the five RFID cards that I have, two of them are hard coded as "good cards" and the other three trigger the flashing LEDs as "bad cards".

The main thing to note in this software is that the Lady Ada AFSoftSerial library had to be modified to use the Analog Pins (i.e., digital pins in the range 14 to 19). Apparently the AFSoftSerial library is hard coded to work with pins below 13. This is the one time I actually had to ASK SOMEONE for help on something related to the Arduino (in contrast to the Gumstix, which took endless asking to get working!). Anyway, here is a link to the forum posting for those who are curious. It is a simple fix that user mtbf0 came up with, but it was over my head!!

That's enough for this project. This one was fun since it actually moves and potentially does useful work!

Saturday, January 3, 2009

More on the RFID updater

In the spirit of full disclosure, I thought I would describe a bit more of the internals of how the RFID Updater works for those who might be interested.

Here is what it actually looks like in Facebook:


This is generated by three different PHP files.

rfid_test.php - This one actually does the interaction with the serial port on my Gumstix to read the value of the most recent RFID card:


<?php
include "php_serial.class.php";
$tagfile = 'tag.txt';

// configure the serial port using php_serial.class.php
$serial = new phpSerial;
$serial->deviceSet("/dev/ttyS2");
$serial->confBaudRate(9600);
$serial->confParity("none");
$serial->confCharacterLength(8);
$serial->confStopBits(1);
$serial->confFlowControl("none");
$serial->deviceOpen();

while(1){
$rfidTag = $serial->readPort();
if ($rfidTag != NULL) {
$filehandle = fopen($tagfile, "w");
$rfidTagTrim = trim($rfidTag, "\x02..\x03");
$rfidTagShort = substr($rfidTagTrim,7,5);
print("The shortened tag is $rfidTagShort\n");
fwrite($filehandle,$rfidTagShort);
fclose($filehandle);
if (strlen($rfidTagTrim) < 10) {
echo "oops";
}
}
usleep(1500000);
// $serial->flush;
}
?>


This writes the RFID tag number to a small file in the application directory. Note that this references the highly useful "php_serial.class.php" class which can be found at this location. I would be lost without the ability to interact with the serial port!

facebook_rfid.php - This presents the screen shown up above in Facebook and some basic info about me as a user and my last status. Most importantly, it calls up the "iFrame"!


<?php
// Copyright 2008 Chris Armour. All Rights Reserved.
//
// Application: RFID Updater
//
// RFID updater updates your status based on various RFID tokens set
//up in the system.
//

require_once 'facebook.php';

$appapikey = 'XXXXXXXXXXXXXXXXXXX';
$appsecret = 'XXXXXXXXXXXXXXXXXXX';
$facebook = new Facebook($appapikey, $appsecret);
$facebook->require_frame();
$user_id = $facebook->require_login();

echo "<img style='width: 49px; height: 50px; float: left;' alt='' src='http://gumstix.dlinkddns.com/rfid_update/rfid_components_large.gif'>";
echo "<h1> Radio Frequency ID Tag Status Updater</h1>";
echo "<br>";
echo "<br>";
echo "<br>";
echo "<fb:profile-pic uid='$user_id' linked='true' />";
echo "<p>Hello, <fb:name uid='$user_id' useyou='false'/>!</p>";
echo "<p>$user_id</p>";
echo "<p>Current status is: <fb:user-status uid='$user_id' linked='true'/></p>";
echo "<fb:prompt-permission perms='status_update'>Would you like to receive status updates from our application?</fb:prompt-permission>";
echo "<fb:prompt-permission perms='offline_access'>Do you want this application to have offline acceess?</fb:prompt-permission>";
echo "<fb:iframe src='http://gumstix.dlinkddns.com/rfid_update/run_script.php' style= 'width: 99%;height:350px' smartsize='false' frameborder='yes'>";
echo "</fb:iframe>";
?>


The interesting thing to note here is line "$facebook->require_frame();". This was actually MISSING from the sample application provided by Facebook and without it nothing worked! It took a while to figure this out.

run_script.php - Finally, the actual script that changes the darned status! This is what runs inside the "iFrame". Why did I do it this way? Because, the main application PHP script cannot run an auto-refresh. With no refresh, it never bothers to detect the new RFID card!! So, the solution is to have a window running an iFrame (which is basically a window running another web page) that then has an auto-refresh which checks the RFID tag file periodically to see if it has changed. If it changes, then it spits out a new Facebook update.

Here is the script:


<?php
// Copyright 2008 Chris Armour. All Rights Reserved.
//
// Application: This is the script that actually detects the stat change and updates the status.
//
// RFID updater updates your status based on various RFID tokens set
//up in the system.
//

$logfile = 'log.txt';
$tagfile = 'tag.txt';
$filehandle1 = fopen($logfile, "r");
$filehandle2 = fopen($tagfile, "r");
$old_state = fread($filehandle1, filesize($logfile));
$tag_num = fread($filehandle2, filesize($tagfile));
fclose($filehandle1);
fclose($filehandle2);
require_once 'facebook.php';
$appapikey = 'XXXXXXXXXXXXXXXXXXXX';
$appsecret = 'XXXXXXXXXXXXXXXXX';
$facebook = new Facebook($appapikey, $appsecret);


echo "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>";
echo "<html>";
echo "<head>";
echo "<meta content='text/html; charset=ISO-8859-1' http-equiv='content-type'>";
echo "<meta http-equiv='refresh' content='10'>";
echo "</head>";
echo "<body>";
$csv = "588777472";


if ($tag_num == $old_state){
echo "<p>Place an RFID tag near the reader to update your status.<p>";
}
elseif ($tag_num != $old_state){
switch($tag_num){
case "51EAE":
$result = $facebook->api_client->Users_setStatus('is gone for a workout.',false);
echo "<p>Status changed to 51EAE - gone for a workout.</p>";
echo "<p>Return code is = $result</p>";
$result=$facebook->api_client->notifications_sendEmail($csv,"RFID Status now gone for a workout", "RFID Updater has changed your status to Gone for a workout","<b><i><u>RFID Updater has reset status to gone for a workout.</u></i></b>");
break;
case "A503F":
$result = $facebook->api_client->Users_setStatus('is not in the office',false);
echo "<p>Status changed to A503F - not in the office</p>";
echo "<p>Return code is = $result</p>";
$result=$facebook->api_client->notifications_sendEmail($csv,"RFID Status now not in the office", "RFID Updater has reset your status to not in the office","<b><i><u>RFID Updater has reset your status to not in the office.</u></i></b>");
break;
case "12B8F":
$result = $facebook->api_client->Users_setStatus('is at work.',false);
echo "<p>Status changed to 12B8F - at work</p>";
echo "<p>Return code is = $result</p>";
$result=$facebook->api_client->notifications_sendEmail($csv,"RFID Status is at work", "RFID Updater has reset your status to at work","<b><i><u>RFID Updater has reset your status to at work.</u></i></b>");
break;
case "48EEF":
$result = $facebook->api_client->Users_setStatus('is testing the RFID updater',false);
echo "<p>Status changed to 48EEF - testing</p>";
echo "<p>Return code is = $result</p>";
$result=$facebook->api_client->notifications_sendEmail($csv,"RFID Status now Testing", "RFID Updater has reset your status to testing","<b><i><u>RFID Updater has reset your status to testing.</u></i></b>");
break;
case "5BC0C":
$result = $facebook->api_client->Users_setStatus('is watching TV',false);
echo "<p>status changed to 5BC0C - watching TV</p>";
echo "<p>Return code is = $result</p>";
$result=$facebook->api_client->notifications_sendEmail($csv,"RFID Status now watching TV", "RFID Updater has reset your status to watching TV","<b><i><u>RFID Updater has reset your status to watching TV.</u></i></b>");
break;
default:
echo "<p>Card not read. Try again.</p>";
}
}
echo "</body>";
echo "</html>";

$old_state = $tag_num;
$filehandle3 = fopen($logfile, "w");
fwrite($filehandle3,$old_state);
fclose($filehandle3);

?>


Friday, January 2, 2009

RFID Facebook Updater

I have an RFID reader I bought from www.sparkfun.com and 5 credit card style RFID cards. The reader is hooked up to my Gumstix small PC via serial.

I have two PHP scripts running on the Gumstix. One continuously monitors the RFID reader to see if there has been a card read and it writes that value to a file. The other script is the actual Facebook application that picks up the RFID tag value and lines it up with statuses I have assigned the different cards.

Anyway, very cool to learn how to use RFID and write a very simple Facebook application. In a broader application, this does show that you can "hardware-enable" Facebook so that you could have statuses, new stories, messages generated by a variety of different "real-world" events. This could be a home alarm system or just your plants saying they need to be watered.

While it is possible to generate these kinds of messages through other means (email, SMS etc), Facebook offers a reasonably comprehensive framework for authentication & messaging and a more-or-less documented API.

Here is a shot of the updater & my Gumstix:


And here is a diagram:


I have pretty much stopped work on this as my Arduino-based stuff has come back to the fore front. Some of the things I would like to do with this are:
  • Have the status messages stored in a SQL database so they can be updated more easily (currently they are hardcoded into the PHP script for the FB application).
  • Have a "remote reader" with an Arduino & an Xbee radio so your status can be updated anywhere in the house.
  • Think about how to scale this up so multiple people could use the application. How would it be hosted, how to architect the reader (Arduino with Xport Ethernet, maybe?). Would there be a demand for a Facebook "hardware add-on"?