Magnetic Stripe Card Reader!

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 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;
   }
}