SOFTWARE : ART : MATH: SCULPTURE : ARCHITECTURE

Arduino Credit Card Decode Code

It is messy, and not very elegant, but this code works well in decoding the bitstream coming from a credit card magnetic stripe.

The project uses a Panasonic #ZU-M2121S451 magnetic card reader available from All Electronics. There is a data sheet available that describes the timing sequence. A video will be posted shortly showing the project in action.

 
/*
 * Magnetic Stripe Reader
 * by Stephan King http://www.kingsdesign.com
 *
 * Reads a magnetic stripe.
 *
 */
 
int cld1Pin = 5;            // Card status pin
int rdtPin = 2;             // Data pin
int reading = 0;            // Reading status
volatile int buffer[400];   // Buffer for data
volatile int i = 0;         // Buffer counter
volatile int bit = 0;       // global bit
char cardData[40];          // holds card info
int charCount = 0;          // counter for info
int DEBUG = 0;
 
void setup() {
  Serial.begin(9600); 
 
  // The interrupts are key to reliable
  // reading of the clock and data feed
  attachInterrupt(0, changeBit, CHANGE);
  attachInterrupt(1, writeBit, FALLING);
}
 
void loop(){
 
  // Active when card present
  while(digitalRead(cld1Pin) == LOW){
    reading = 1;
  }     
 
  // Active when read is complete
  // Reset the buffer
  if(reading == 1) {  
 
    if (DEBUG == 1) {
      printBuffer();
    }
 
    decode();
    reading = 0;
    i = 0;
 
    int l;
    for (l = 0; l < 40; l = l + 1) {
      cardData[l] = '\n';
    }
 
    charCount = 0;
  }
}
 
// Flips the global bit
void changeBit(){
  if (bit == 0) {
    bit = 1;
  } else {
    bit = 0;
  }
}
 
// Writes the bit to the buffer
void writeBit(){
  buffer[i] = bit;
  i++;
}
 
// prints the buffer
void printBuffer(){
  int j;
  for (j = 0; j < 200; j = j + 1) {
    Serial.println(buffer[j]);
  }
}
 
int getStartSentinal(){
  int j;
  int queue[5];
  int sentinal = 0;
 
  for (j = 0; j < 400; j = j + 1) {
    queue[4] = queue[3];
    queue[3] = queue[2];
    queue[2] = queue[1];
    queue[1] = queue[0];
    queue[0] = buffer[j];
 
    if (DEBUG == 1) {
      Serial.print(queue[0]);
      Serial.print(queue[1]);
      Serial.print(queue[2]);
      Serial.print(queue[3]);
      Serial.println(queue[4]);
    }
 
    if (queue[0] == 0 & queue[1] == 1 & queue[2] == 0 & queue[3] == 1 & queue[4] == 1) {
      sentinal = j - 4;
      break;
    }
  }
 
  if (DEBUG == 1) {
    Serial.print("sentinal:");
    Serial.println(sentinal);
    Serial.println("");
  }
 
  return sentinal;
}
 
void decode() {
  int sentinal = getStartSentinal();
  int j;
  int i = 0;
  int k = 0;
  int thisByte[5];
 
  for (j = sentinal; j < 400 - sentinal; j = j + 1) {
    thisByte[i] = buffer[j];
    i++;
    if (i % 5 == 0) {
      i = 0;
      if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 0) {
        break;
      }
      printMyByte(thisByte);
    }
  }
 
  Serial.print("Stripe_Data:");
  for (k = 0; k < charCount; k = k + 1) {
    Serial.print(cardData[k]);
  }
  Serial.println("");
 
}
 
void printMyByte(int thisByte[]) {
  int i;
  for (i = 0; i < 5; i = i + 1) {
    if (DEBUG == 1) {
      Serial.print(thisByte[i]);
    }
}
    if (DEBUG == 1) {
      Serial.print("\t");
      Serial.print(decodeByte(thisByte));
      Serial.println("");
    }
 
    cardData[charCount] = decodeByte(thisByte);
    charCount ++;
}
 
char decodeByte(int thisByte[]) {
    if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 1){
      return '0';
    }
    if (thisByte[0] == 1 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 0){
      return '1';
    }
 
    if (thisByte[0] == 0 & thisByte[1] == 1 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 0){
      return '2';
    }
 
    if (thisByte[0] == 1 & thisByte[1] == 1 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 1){
      return '3';
    }
 
    if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 1 & thisByte[3] == 0 & thisByte[4] == 0){
      return '4';
    }
 
    if (thisByte[0] == 1 & thisByte[1] == 0 & thisByte[2] == 1 & thisByte[3] == 0 & thisByte[4] == 1){
      return '5';
    }
 
    if (thisByte[0] == 0 & thisByte[1] == 1 & thisByte[2] == 1 & thisByte[3] == 0 & thisByte[4] == 1){
      return '6';
    }
 
    if (thisByte[0] == 1 & thisByte[1] == 1 & thisByte[2] == 1 & thisByte[3] == 0 & thisByte[4] == 0){
      return '7';
    }
 
    if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 1 & thisByte[4] == 0){
      return '8';
    }
 
    if (thisByte[0] == 1 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 1 & thisByte[4] == 1){
      return '9';
    }
 
    if (thisByte[0] == 0 & thisByte[1] == 1 & thisByte[2] == 0 & thisByte[3] == 1 & thisByte[4] == 1){
      return ':';
    }
 
    if (thisByte[0] == 1 & thisByte[1] == 1 & thisByte[2] == 0 & thisByte[3] == 1 & thisByte[4] == 0){
      return ';';
    }
 
    if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 1 & thisByte[3] == 1 & thisByte[4] == 1){
      return '<';
    }
 
    if (thisByte[0] == 1 & thisByte[1] == 0 & thisByte[2] == 1 & thisByte[3] == 1 & thisByte[4] == 0){
      return '=';
    }
 
    if (thisByte[0] == 0 & thisByte[1] == 1 & thisByte[2] == 1 & thisByte[3] == 1 & thisByte[4] == 0){
      return '>';
    }
 
    if (thisByte[0] == 1 & thisByte[1] == 1 & thisByte[2] == 1 & thisByte[3] == 1 & thisByte[4] == 1){
      return '?';
    }
}
This entry was posted in Digital Media, Software Programming. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

20 Comments

  1. Posted April 6, 2008 at 1:09 pm | Permalink

    very cool - and useful, i will link to this for future students. (ps can you fix the symbols and indentation and also put the model# of card reader in the comments.) do you have any video of a successful swipe with human-readable data?

  2. Posted April 6, 2008 at 4:48 pm | Permalink

    The code is cleaned up, and there is now a model # with a link to the data sheet. I’ll work on a video.

  3. KeithB
    Posted August 5, 2008 at 7:55 am | Permalink

    Many thanks, I just picked up the same VERY INEXPENSIVE magnetic card reader from All Electronics and was wanting to port the older Linux game port code to the Arduino. GREAT JOB!!! Thanks for sharing.

  4. KeithB
    Posted August 5, 2008 at 7:59 am | Permalink

    p.s. The AllElectronics site has revamped everything. The new link is:
    http://www.allelectronics.com/make-a-store/item/MCR-12/MAGNETIC-CARD-READER/-/1.html

  5. Posted August 5, 2008 at 1:41 pm | Permalink

    Keith - Thanks for the post. I updated the links. Let me know if you have any questions with the code!

  6. gardner
    Posted August 25, 2008 at 8:52 pm | Permalink

    Now if you would only update the software for a card encoder / writer, as that is beyond my ability ;)
    But cool.

    Unfortunately, the writers cost upwards of $100, but would be fun to toy with.

  7. Posted August 29, 2008 at 10:04 pm | Permalink

    Do you have any good resources for learning how to decode the binary buffer of data? This pde works well for cc’s, but I’m trying to work with hotel cards.

  8. sami
    Posted October 21, 2008 at 10:38 am | Permalink

    Pls, do you have any software or idea to reset the status for PS2 Magnetic Reader ?
    Model: MCR123RP-B

  9. Alexander
    Posted November 22, 2008 at 10:22 pm | Permalink

    Hello,

    Have you done any upgrade to this project?

    Take a lookt at this: http://www.parallax.com/Portals/0/Downloads/docs/cols/nv/vol8/col/nv148.pdf

    How difficult will be implement that code on the Arduino?

    Thanks!

  10. Alexander
    Posted November 23, 2008 at 8:08 pm | Permalink

    Another question…You are not using the Clock pin?

  11. Alexander
    Posted November 23, 2008 at 11:34 pm | Permalink

    Another question…

    Im trying to understand how do get the start sentinel…I dont see how do you do it…Help

    Thanks

  12. Posted November 23, 2008 at 11:49 pm | Permalink

    The start sentinal is 01011.. so I create a buffer with 5 bits and add to the buffer one bit at a time, checking the sequence each time against 01011. The code is in GetStartSentinal.

    I use the clock and the data pin, and attach them to interrupts. Without using interrupts, crucial data would be lost.

    You have to read the data sheet to understand why I attach the interrupts to “change” for data and “falling” for the clock.. (or vice versa..)

  13. Alexander
    Posted November 24, 2008 at 6:22 am | Permalink

    Thanks for the answer…

    So the clock pin is on Pin 3 on the arduino?

    Did you see the PDF above?

  14. Alexander
    Posted November 25, 2008 at 9:18 pm | Permalink

    Hello…

    Another question…why you are comparing bit per bit? Its no easier to check if the sentinel = ; ?

    Also…can you explain how the interrupt work? I check on the arduino site but I dont have it clear..

    Thanks

  15. Alexander
    Posted November 26, 2008 at 9:58 pm | Permalink

    Hello again…

    Reading at this…I think this will be better than using interrupts??? What do you think?

    http://arduino.cc/en/Reference/ShiftOut

    Please answer the posts…Thanks

  16. Posted November 29, 2008 at 11:23 am | Permalink

    Hi Alexander, I think that would be a good way to implement an interface like that used by the credit card reader itself, but I don’t think it will help you in decoding the bitstream. To do that, you have to map 0’s and 1’s to characters in a way based on the protocol of how data is organized on a magnetic stripe.

  17. Posted November 29, 2008 at 11:26 am | Permalink

    You have to compare bit by bit, or 5 bits at a time, because of the magnetic stripe ANSI protocol.

    To write a decoder, you only have access to bits coming in. The credit card does not send a “;”, it sends 01011. I have to look at the bitstream 5 bits at a time to see it it equals 01011, which in turns equals “;”.

    I’ll write about interrupts if I get the chance. It is finals time…

  18. Alexander
    Posted November 30, 2008 at 3:17 pm | Permalink

    Hello,

    No problem Stephan…Write when you have time…Im i finals to jeje..

    I think Its easier to compare the 5 bits at a time…than bit per bit…

  19. Posted June 15, 2009 at 4:17 am | Permalink

    Will the reader reads the card on both direction?

  20. Posted June 15, 2009 at 7:10 pm | Permalink

    The reader cannot write, if that’s what you mean.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>