Arduino-based RFID reader

This tutorial shows how to make an Arduino-based RFID reader that reads Mifare tags and stores them in EEPROM. It is a modification of Alex Zivanovic‘s code on Tinker.it. Thanks to Alex and Massimo Banzi for the reference. Once you’ve got it running, go on to the RFID to Web example, which provides a Processing interface sketch provides a GUI for the command-line interface written into the Arduino code.

The entire sketch can be downloaded here:

arduino_rfid_reader_0001

The Circuit

Parts:

First, add wires to the breadboard to carry voltage and ground to the two vertical bus rows on either side:

Figure 1. Ground and voltage wires
Figure 1. Ground and voltage wires. Click the image to enlarge.

Next add an Arduino Pro Mini (a regular Arduino Mini will work also):

Figure 2. Insert the Arduino Pro Mini
Figure 2. Insert the Arduino Pro Mini

Next, add the voltage and ground wires for the SM130 RFID module. Make sure to leave slack in the lowest ground wire on the left. The SM130 module will plug into the same row of holes.

rfid-mini_0005
Figure 3. Power and ground wires for the SM130 module.

Next, add the I2C data and clock lines for the module. These connect to two female headers soldered on to the top of the Arduino Pro Mini. These headers connect to the SDA and SCL lines, which are on analog pins 4 and 5, respectively. See the Arduino Pro Mini page for details. Note: if you are using an Arduino Mini, the pins are in a different place. I added a tri-color LED to digital pins 7, 8, and 9, but you can use three regular LEDs instead.

rfid-mini_0003
Figure 4. Data and Clock lines for the SM130 module

Next, add the SM130 module. The top lines up with the red voltage wires on the board. Make sure to leave a row of holes on the right side for the antenna.

rfid-mini_0002
Figure 5. Add the SM130 module

Finally, add the antenna and the FTDI USB-to-serial cable. The top of the antenna lines up with the second pin down on the right hand side of the module. The green pin of the FTDI cable aligns with the right hand top pin of the Pro Mini.

Figure 6. The completed circuit
Figure 6. The completed circuit

The Arduino Code

The Arduino code for this project continually seeks new tags.  When a new tag is acquired, it checks it against a list of tags stored in the EEPROM of the microcontroller.  If it’s not there, it adds it to its database.  It also sends and receives messages serially as a simple command-line interface. When it starts up, open the Arduino serial monitor, and you should see the following:

n - add card to database
c - clear entire database
d - delete card from database
p - print database
Waiting for card

If you bring a tag in range of the reader’s antenna, you’ll get the following message:

That tag is new
0:0A64EF28
Waiting for card

Do it again with the same tag and you’ll get:

That tag is already stored
0:0A64EF28
Waiting for card

Type c in the message box and send it and you’ll get a whole list of the database, like so:

0:0A64EF28
1:00000000
2:00000000
...
...
...
100:00000000
101:00000000

Later, you’ll use this serial interface to build a GUI for the reader. For first, take a look at the Arduino code in depth. You need to inclued the Wire library, which provides an API for the I2C connection to the reader, and the EEPROM library, which allows you to save data in the Arduino’s nonvolatile memory:

#include <Wire.h>
#include <EEPROM.h>

Next you’re going to define a few constants, including the maximum number of cards that you can save in memory (based on the size of the EEPROM) and the pin numbers for the indicator LEDs:

#define MAX_NO_CARDS 102
#define waitingLED 7
#define successLED 8
#define failureLED 9

Finally, a couple global variables for toggling the state of the waiting LED, and a four-byte array for holding the most recent tag read:

int toggleState = 0;    // state of the toggling LED
long toggleTime = 0;    // delay time of the toggling LED
byte tag[4];            // tag serial numbers are 4 bytes long

The setup() method initializes the reader via I2C, initializes the LED states, and sends the opening serial message. Then it waits 2 seconds for the reader to start up:

void setup() {
  Wire.begin();                      // join i2c bus
  Serial.begin(9600);                // set up serial port

  Wire.beginTransmission(0x42);      // the RFID reader's address is 42
  Wire.send(0x01);                   // Length
  Wire.send(0x80);                   // reset reader
  Wire.send(0x81);                   // Checksum
  Wire.endTransmission();            

  // initialize the LEDs:
  pinMode(waitingLED, OUTPUT);
  pinMode(successLED, OUTPUT);
  pinMode(failureLED, OUTPUT);

  // print user instructions serially:
  Serial.println("n - add card to database");
  Serial.println("c - clear entire database");
  Serial.println("d - delete card from database");
  Serial.println("p - print database"); 

  // delay to allow reader startup time:
  delay(2000);
}

The main loop() method listens for serial data coming in, and takes appropriate action depending on what’s sent. If no serial data comes in, it runs seekNewTag() to keep the reader in seek mode. The 200 millisecond delay at the end of the main loop is there to keep the reader from being prompted too frequently:

void loop() {
  if (Serial.available() > 0) {
    // read the latest byte:
    char incomingByte = Serial.read();
    switch (incomingByte) {
    case 'n':            // if user enters 'n' then store tag number
      seekNewTag();
      break;
    case 'c':
      eraseEEPROM();    // if user enters 'c' then erase database
      Serial.println("Database deleted");
      break;
    case 'd':           // if user enters 'd' then delete the last tag
      seekAndDeleteTag();
      break;
    case'p':            // if user enters 'p' then print the database
      printTags();
      break;
    }
  }
  else {
    //if there's no serial data coming in,
    // the default action is to seek new tags:
    seekNewTag();
  }
  // delay before next command to the reader:
  delay(200);
}

If you send the Arduino a c, it erases the EEPROM with the following method:

// erase the entire EEPROM memory storage:
void eraseEEPROM(){
  for(int i = 0; i < 512; i++){
    EEPROM.write(i, 0);
  }
}

When a new tag is read, the following method saves it to EEPROM:

// writes tag number in a location:
void writeTag(int startingAddress, byte byte0, byte byte1, byte byte2, byte byte3){
  EEPROM.write( startingAddress*5, byte(1));
  EEPROM.write(startingAddress*5+1, byte0);
  EEPROM.write(startingAddress*5+2, byte1);
  EEPROM.write(startingAddress*5+3, byte2);
  EEPROM.write(startingAddress*5+4, byte3);

}

If you send the Arduino a d, it deletes the most recently read tag using the following method:

// delete tag from a specified location
void deleteTag(int startingAddress){
  EEPROM.write( startingAddress*5, byte(0));
  EEPROM.write(startingAddress*5+1, byte(0));
  EEPROM.write(startingAddress*5+2, byte(0));
  EEPROM.write(startingAddress*5+3, byte(0));
  EEPROM.write(startingAddress*5+4, byte(0));
}

The following method checks to find the first empty space in the EEPROM database:

// find the first empty entry in the database:
int findEmptyTag(){
  for (int startingAddress = 0; startingAddress< MAX_NO_CARDS; startingAddress++){
    byte value = EEPROM.read(startingAddress*5);
    if (value == byte(0)) {
      return(startingAddress);
    }
  }
  return(200);
}

If you send the Arduino a p, it prints the entire database using the following two methods:

// print the entire database
void printTags(){
  for (int thisTag = 0; thisTag< MAX_NO_CARDS; thisTag++){
    printOneTag(thisTag);
  }
}

// print a single tag given the tag's address:
void printOneTag(int address) {
  Serial.print(address);
  Serial.print(":");
  for (int offset = 1; offset < 5; offset++) {
    int thisByte = int(EEPROM.read(address*5+offset));
    // if the byte is less than 16, i.e. only one hex character
    // add a leading 0:
    if (thisByte < 0x10) {
      Serial.print("0");
    }
    // print the value:
    Serial.print(thisByte,HEX);
  }
  // add a final linefeed and carriage return:
  Serial.println();
}

This method reads the EEPROM database to find a given tag:

//lookup tag in the database:
int lookupTag(byte byte0, byte byte1, byte byte2, byte byte3){
  for (int thisCard = 0; thisCard< MAX_NO_CARDS; thisCard++){
    byte value = EEPROM.read(thisCard*5);
    if (value != byte(0)){                    //it is a valid tag
      //see if all four bytes are the same as the ones we're looking for
      if(byte0 == EEPROM.read(thisCard*5+1) && byte1 == EEPROM.read(thisCard*5+2)
        && byte2 == EEPROM.read(thisCard*5+3) && byte3 == EEPROM.read(thisCard*5+4)) {
        return(thisCard);
      }
    }
  }
  // if you don't find the tag, return 200;
  return(200);
}

This method reads a tag from the EEPROM and saves it in the byte array variable called tag[]:

int getTag(){
  byte count = 0;
  byte valid = 0;
  byte byteFromReader = 0;

  Wire.beginTransmission(0x42);
  Wire.send(0x01);           // Length
  Wire.send(0x82);           // Seek for tags
  Wire.send(0x83);           // Checksum
  Wire.endTransmission();

  delay(100);
  Wire.requestFrom(0x42, 8); // get data (8 bytes) from reader

  count = 0;                 // keeps track of which byte it is in the response from the reader
  valid = 0;                 // used to indicate that there is a tag there
  while(Wire.available())  { // while data is coming from the reader
    byteFromReader = Wire.receive();
    // no RFID found: reader sends character 2:
    if ((count == 0) && (byteFromReader == 2)) {
      return(0);
    }
    if ((count == 0) && (byteFromReader== 6)) {
      //if reader sends 6, the tag serial number is coming:
      valid = 1;
    }
    count++;

    if ((valid == 1) && (count > 3) && (count < 8)) {
      // strip out the header bytes  :
      tag[count-4] = byteFromReader;
    }
    // all four bytes received: tag serial number complete:
    if ((valid == 1) && (count == 8)) {
      return(1);
    }
  }
}

This method prompts the reader into seek mode and reads new tags.  However, if new serial data is received while the reader is waiting, it breaks out of the method and returns to the main loop.  If no serial data is received, it reads an incoming tag and saves it to EEPROM. It also blinks the success LED when a tag is read and saved successfully. If the EEPROM memory is full, it blinks the failure LED:

void seekNewTag() {
  Serial.println("Waiting for card");
  while(getTag() == 0){
    // wait for tag
    if (millis() - toggleTime > 1000) {
      toggle(waitingLED);
      toggleTime  = millis();
    }
    // unless you get a byte of serial data,
    if (Serial. available()) {
      // break out of the while loop
      // and out of the seekNewTag() method:
      return;
    }
  }

  // look it up in the database:
  int tagToCheck = lookupTag(tag[0], tag[1], tag[2], tag[3]);      

  if (tagToCheck != 200){
    Serial.println("That tag is already stored");
    printOneTag(tagToCheck);
  }
  else {
    int emptyTagLocation = findEmptyTag();
    if (emptyTagLocation != 200){
      writeTag(emptyTagLocation, tag[0], tag[1], tag[2], tag[3]);
      Serial.println("That tag is new");
      printOneTag(emptyTagLocation);
      blink(successLED, 100, 1);
    }
    else {
      Serial.println("Maximum number of cards stored");
      blink(failureLED, 50, 10);
    }
  }
}

The following method looks up a tag in the EEPROM database and deletes it from the database. It’s called when you send the Arduino a d.

void seekAndDeleteTag() {
  Serial.println("Deleting the next card");
  Serial.println("Waiting for card");
  while(getTag() == 0){
    // do nothing; wait for tag
    // unless you get a byte of serial data,
    if (Serial. available()) {
      // break out of the while loop
      // and out of the method:
      return;
    }
  }
  int tagToDelete = lookupTag(tag[0], tag[1], tag[2], tag[3]);
  if (tagToDelete == 200){
    Serial.print("That tag is not stored");
  }
  else {
    deleteTag(tagToDelete);
  }
}

The final two methods are used to blink the LEDs:

void toggle(int thisLED) {
  toggleState = !toggleState;
  digitalWrite(thisLED, toggleState);
}

void blink(int thisLED, int interval, int count) {
  for (int i = 0; i < count; i++) {
    digitalWrite(thisLED, HIGH);
    delay(interval/2);
    digitalWrite(thisLED, LOW);
    delay(interval/2);
  }
}

That’s the whole program.  Once you’ve entered it, upload it to your Arduino and try reading tags with the serial monitor open. Try also sending some of the serial commands to see them in action.  Then move on to programming the Processing interface.

Here’s the Arduino code in its entirety:

arduino_rfid_reader_0001

That’s it!  Once you’ve tested it out in the serial monitor, go on to the RFID to Web tutorial, which shows you how to make a Processing sketch for a GUI that interfaces with this reader, uploads tags to a PHP script and retrieves data from a database.

One Reply to “Arduino-based RFID reader”

Comments are closed.