HACKMIAMI

Magnetic Stripe Card Reader!

December 21st, 2008 | By: jp

Below we have a magnetic card reader made at the HACKMIAMI labs. It reads track 2 found on most magnetic striped cards (i.e. credit cards, drivers licenses, and student ids). Something interesting to point out, while testing the equipment with an old student ID card from a local university we found out it holds the person’s social security number on the card. The SSN use to be the student ID number. I wouldn’t be surprised if other universities did the same. The magnetic card reader was made using a Sanguino (a beefy Arduino clone), an LCD found on SparkFun, and magnetic card reader from All Electronics.

Magnetic Card Reader

Things to Keep in Mind

The +5V and ground is provided by the sanguino. The LEDs are just for DEBUGGING and are not necessary. The LCD4Bit.h file used is modified so ports 18 – 23 on the sanguino are used for the LCD. You can download the LCD4bit here. An arduino or most other clones can be used for this project.

Schematic

Code

/*
 *   MAGNETIC STRIPE CARD READER with LCD ver. 0.2
 *   BY JP! ... email: jp @ hackmiami [dot] org
 *              web: http://www.hackmiami.org/
 *    
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see .
 *
 */
 
#include <LCD4bit.h>;
 
LCD4Bit lcd = LCD4Bit(2);
int incomingByte = 0;
int charMax = 16;
int charCount = 0;
char ch[] = "                ";
 
boolean printToLCD = false;
 
int DATA = 1, CLOCK = 11, CARD_IN = 2, ENDSTOP = 10;
volatile int state = LOW;
 
int buf[255];
char actual_buf[255];
 
int i = 0;
 
void setup() {
 
  Serial.begin(9600);
 
  lcd.init(); //initialize the LCD
 
  // SET PINS AS INPUT
  pinMode(CARD_IN, INPUT);
  pinMode(CLOCK, INPUT);
  pinMode(DATA, INPUT);
  pinMode(ENDSTOP, INPUT);
 
  // SET PINS AS OUTPUT
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(0, OUTPUT);
 
  attachInterrupt(2, stateToHigh, FALLING); // Wait for card to start being pulled out, interupt 0 is digital pin 10
  attachInterrupt(0, stateToLow, FALLING); // Wait for card to be completely out, interupt 2 is digital pin 2
 
  Serial.println("Ready... ");
  lcd.printIn("Ready...");
}
 
void loop() {
 
  digitalWrite(7, digitalRead(ENDSTOP)); // ENDSTOP LED for debugging
  digitalWrite(6, digitalRead(CARD_IN)); // CARD_IN LED for debugging
 
  // IF YOU HAVE SOMETHING TO PRINT TO LCD, PRINT IT
  if (printToLCD) { 
     lcd.clear();
 
     int j = 1;
     while(actual_buf[j] != '=' && j <= 16) {
        lcd.print(actual_buf[j]);
        j++;
     }
     lcd.commandWrite(0xC0);
     lcd.printIn("HACKMIAMI.ORG");
 
     printToLCD = false;
  }
 
}
 
// MAKE STATE GO HIGH AND START COLLECTING DATA
void stateToHigh() {
  digitalWrite(0, HIGH);
  state = HIGH;
  Serial.print("STATE: HIGH\n");
 
  // The data is synced to a clock. So everytime the clock rises there is data to be collected. 
  attachInterrupt(1, getData, RISING); // interupt 1 is digital pin 11
}
 
// SET STATE TO LOW, no longer need to collect data so detach interrupt
void stateToLow() {
 
  detachInterrupt(1);
 
  Serial.print("\nSTATE: LOW\n");
 
  state = LOW;
  digitalWrite(0, LOW);
 
  Serial.print("\n");
  complement();
//  reverse();
  readBuf();
  Serial.print("\n");
 
  i = 0;
 
}
 
// DECODE WHAT IS WRITTEN ON THE CARD
void readBuf() {
 
  char values[] = {'0', '1', '2', '3', '4', '5', '6', '7',
                   '8', '9', ':', ';', '<', '=', '>', '?'};
 
  int index = 0, val, k = 0, binbuf = 0, parity = 0, parity_error = false;
 
  Serial.print("RAW: ");
 
  for (int j = 0; j < i; j++) {
     Serial.print(buf[j]);
  }
 
  Serial.print("\n\n");
 
  // FIND START SENTINEL
  for (int j = 0; j < i - 5; j++) {
    if (buf[j] == 1 && buf[j + 1] == 1 && buf[j + 2] == 0 && buf[j + 3] == 1 && buf[j + 4] == 0) {
      k = j;
      break;
    }
  }
 
  int p = 0;
  for (int j = k; j < i; j++) {
 
    if ((j - k) % 5 == 4) {
 
      actual_buf[p] = values[binbuf];
      binbuf = 0;
 
      if ((parity % 2) == buf[j]) {
        parity_error = true;
      }
 
      parity = 0;
      p++;
    }
    else {
      binbuf += buf[j] * (int) ceil(pow(2, (j - k) % 5));
      parity += buf[j];
    }
 
  }
 
  for (int j = 0; j < p; j++) {
     Serial.print(actual_buf[j]);
  }
 
  Serial.print("\n");
  printToLCD = true;
 
  if (parity_error) {
    Serial.print("\nPARITY ERROR!");
  }
}
 
// GET DATA AND ADD TO BUFFER
void getData() {
  buf[i] = digitalRead(DATA);
  i++;
}
 
// FLIP THE ORDER IN WHICH THE DATA WAS READ
void reverse() {
  int buff[255];
  int c = 0;
  for (int j = i - 1; j >= 0; j--) {
    buff[c] = buf[j];
    c++;
  }
 
  for (int j = 0; j < i; j++) {
     buf[j] = buff[j];
  }
 
}
// INVERT THE 1's TO 0's AND VICE VERSA
void complement() {
   for (int j = 0; j < i; j++) {
      (buf[j] == 1) ? buf[j] = 0 : buf[j] = 1;
   }
}

17 Responses to “Magnetic Stripe Card Reader!”

  1. Ashish Anand says:

    Hey,

    I’m using your GPL code for an article in a hacking magazine. Hope its okay!

  2. jp says:

    Go right ahead! Just mention HackMiami in the magazine as well. Let me know when it is published.

  3. benc says:

    Hello,

    Thanks for this great tutorial!
    I am interesting in making a personnal card-reader but I don’t find any MR read head. Does anybody know where I can buy it?
    Thanks

  4. jp says:

    I bought the magnetic card reader at allelectronics.com for $4 american. It’s the best price I’ve found.

  5. rad says:

    I tried to modif the code for the Arduino Diecimila and using an Omron reader and all I get as a read are characters. here is the code below. Thanks for help

    ==============================================================================
    /*
    * MAGNETIC STRIPE CARD READER with LCD ver. 0.2
    * BY JP! … email: jp @ hackmiami [dot] org
    * web: http://www.hackmiami.org/
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU General Public License as published by
    * the Free Software Foundation, either version 3 of the License, or
    * (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU General Public License for more details.
    *
    * You should have received a copy of the GNU General Public License
    * along with this program. If not, see .
    *
    */

    /*#include ;

    LCD4Bit lcd = LCD4Bit(2);
    int incomingByte = 0;
    int charMax = 16;
    int charCount = 0;
    char ch[] = ” “;

    boolean printToLCD = false;
    */

    int DATA =3, CLOCK = 11, CARD_IN = 2;
    volatile int state = LOW;

    int buf[255];
    char actual_buf[255];

    int i = 0;

    void setup() {

    Serial.begin(9600);

    //lcd.init(); //initialize the LCD

    // SET PINS AS INPUT
    pinMode(CARD_IN, INPUT);
    pinMode(CLOCK, INPUT);
    pinMode(DATA, INPUT);
    //pinMode(ENDSTOP, INPUT);

    // SET PINS AS OUTPUT
    pinMode(6, OUTPUT);
    pinMode(7, OUTPUT);
    pinMode(0, OUTPUT);

    attachInterrupt(1, stateToHigh, FALLING); // Wait for card to start being pulled out, interupt 0 is digital pin 10
    attachInterrupt(0, stateToLow, FALLING); // Wait for card to be completely out, interupt 2 is digital pin 2

    Serial.println(“Ready… “);
    //lcd.printIn(“Ready…”);
    }

    void loop() {

    //digitalWrite(7, digitalRead(ENDSTOP)); // ENDSTOP LED for debugging
    //digitalWrite(6, digitalRead(CARD_IN)); // CARD_IN LED for debugging

    // IF YOU HAVE SOMETHING TO PRINT TO LCD, PRINT IT
    /* if (printToLCD) {
    lcd.clear();

    int j = 1;
    while(actual_buf[j] != ‘=’ && j <= 16) {
    lcd.print(actual_buf[j]);
    j++;
    }
    lcd.commandWrite(0xC0);
    lcd.printIn(“HACKMIAMI.ORG”);

    printToLCD = false;
    }
    */
    }

    // MAKE STATE GO HIGH AND START COLLECTING DATA
    void stateToHigh() {
    digitalWrite(3, HIGH);
    state = HIGH;
    Serial.print(“STATE: HIGH\n”);

    // The data is synced to a clock. So everytime the clock rises there is data to be collected.
    attachInterrupt(1, getData, RISING); // interupt 1 is digital pin 11
    }

    // SET STATE TO LOW, no longer need to collect data so detach interrupt
    void stateToLow() {

    detachInterrupt(1);

    Serial.print(“\nSTATE: LOW\n”);

    state = LOW;
    digitalWrite(3, LOW);

    Serial.print(“\n”);
    complement();
    // reverse();
    readBuf();
    Serial.print(“\n”);

    i = 0;

    }

    // DECODE WHAT IS WRITTEN ON THE CARD
    void readBuf() {

    char values[] = {‘0′, ‘1′, ‘2′, ‘3′, ‘4′, ‘5′, ‘6′, ‘7′,
    ‘8′, ‘9′, ‘:’, ‘;’, ”, ‘?’};

    int index = 0, val, k = 0, binbuf = 0, parity = 0, parity_error = false;

    Serial.print(“RAW: “);

    for (int j = 0; j < i; j++) {
    Serial.print(buf[j]);
    }

    Serial.print(“\n\n”);

    // FIND START SENTINEL
    for (int j = 0; j < i – 5; j++) {
    if (buf[j] == 1 && buf[j + 1] == 1 && buf[j + 2] == 0 && buf[j + 3] == 1 && buf[j + 4] == 0) {
    k = j;
    break;
    }
    }

    int p = 0;
    for (int j = k; j < i; j++) {

    if ((j – k) % 5 == 4) {

    actual_buf[p] = values[binbuf];
    binbuf = 0;

    if ((parity % 2) == buf[j]) {
    parity_error = true;
    }

    parity = 0;
    p++;
    }
    else {
    binbuf += buf[j] * (int) ceil(pow(2, (j – k) % 5));
    parity += buf[j];
    }

    }

    for (int j = 0; j = 0; j–) {
    buff[c] = buf[j];
    c++;
    }

    for (int j = 0; j < i; j++) {
    buf[j] = buff[j];
    }

    }
    // INVERT THE 1’s TO 0’s AND VICE VERSA
    void complement() {
    for (int j = 0; j < i; j++) {
    (buf[j] == 1) ? buf[j] = 0 : buf[j] = 1;
    }
    }
    ============================================================================

  6. Rad says:

    I tried to tweak the code for Arduino Diecimila with no luck. Any help will be appreciated.

  7. jp says:

    Sorry for the late reply. Which Omron reader are you using?

  8. [...] sketch which takes web data and prints it on a thermal printer. I found a great tutorial for reading a magstripe reader with a Sanguino, so I’ll either adapt the code for Arudino use or just use a Sanguino. I haven’t [...]

  9. mantra says:

    Does the magnetic stripe card reader used in the project have a TTL interface?

  10. Jaime says:

    Hello i need to make a card reader whit Arduino Duemilanove (2009) and a Omron V3B-4K is it possible to use your code or need’s to be mod and to what pins i connect the reader

    TKS

  11. jp says:

    There is some modification to be done in order to get it to work with that Arduino and card reader. But it is definitely possible.

    I plan to revisit this project and make a non breadboard version of it.

  12. Jaime says:

    Can you give-me some direction to start pls the idea that i have is like this:
    have a LCD saying (Please Pass Card) (Invalid Card){When the card of a non authorized worker try to use the machine} (Machine ON) (Out of Service)
    I thinking to use the card reader a LCD and a relay to start the machine.

    My Arduino is going to arrive in 1 day to start the tests if you can point me in the right direction i will be very happy because I’m only starting to learn who to use this now. I have seen that the part of LCD and relay is not very difficult but card reader i think that is not so easy…

    TKS

    Jaime Branco

  13. spatialguru says:

    Any tips on getting custom card blanks and a writer? It can’t be _that_ hard can it? ;)

  14. sarun says:

    the above program will not work for arduino decimila or duemilanove because the program uses 3 interrupts which both the arduino’s don’t have [they have only 2 interrupts]

  15. nitroboozter says:

    hey can you please check if the connections in the diagram and the program’s pin connection are all correct.i have an ARDUINO MEGA and i tried connecting all the pins in the sanguino to the respective pins in the mega i am using a OMRON 3S4YR-SBR4N-50 manual half insert reader the connections i made are correct i checked it two three times

    OMRON PIN DETAIL FROM DATASHEET
    not connected — pin1
    read data (output) — pin2
    read clock (output) — pin3
    card load (output) — pin4
    card detection rear(output) — pin5
    card detection rear(input) — pin6
    card detection front(output) — pin7
    card detection front(input) — pin8
    vin — pin9
    ground — pin10

    ARDUINO MEGA PIN DETAIL
    pin1 — connected to omron pin2
    pin2 (interrupt 0)– connected to omron pin5
    pin3 (interrupt 1)– connected to omron pin3
    pin21(interrupt 2)– connected to omron pin4

    i have not connected any lcd and have removed all the codings for the lcd..when i’ve hooked it up the pc and check for the data using serial monitor all i get is Raw: and 2-3 line of spaces when i insert and remove a card.please tell me what i’m doing wrong???

  16. nitroboozter says:

    hey i have also changed the pin numbers in the program too

Leave a Reply