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 '?'; } }
20 Comments
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?
The code is cleaned up, and there is now a model # with a link to the data sheet. I’ll work on a video.
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.
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
Keith - Thanks for the post. I updated the links. Let me know if you have any questions with the code!
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.
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.
Pls, do you have any software or idea to reset the status for PS2 Magnetic Reader ?
Model: MCR123RP-B
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!
Another question…You are not using the Clock pin?
Another question…
Im trying to understand how do get the start sentinel…I dont see how do you do it…Help
Thanks
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..)
Thanks for the answer…
So the clock pin is on Pin 3 on the arduino?
Did you see the PDF above?
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
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
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.
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…
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…
Will the reader reads the card on both direction?
The reader cannot write, if that’s what you mean.