loading

Turn your Arduino into a Magnetic Card Reader!

Featured
Picture of Turn your Arduino into a Magnetic Card Reader!
magcard-2.jpg
magcard-reader-top.jpg
Everyone has used a magnetic card reader, I believe. I mean, who carries cash these days? They're not difficult to get your hands on, either, and during a trip to my favorite local electronics shop, I found a bin full of these guys. So....of course, I picked one up and brought it home to see what sort of stuff I could do with it and an AVR.

This instructable will show you how to connect a Magtek magnetic card reader to an AVR or Arduino/clone and read data from the first track of card. Buckle your seats; magnetic card readers have a high bit rate!
 
Remove these adsRemove these ads by Signing Up

Step 1: The Equipment List

Picture of The Equipment List
magcard-ra-header.jpg
Here are a few things you'll need to get started.
  • Magnetic card reader (Mine is a Magetk 90mm dual-head reader. $5.00)
  • AVR, Arduino, or clone (ATmega328p ~ $4.30 from Mouser.com
  • solderless breadboard
  • some wire
  • maybe a header if you like that sorta thing.
  • something to read your serial port. I use AVR Terminal from BattleDroids.net

That's all you should need to get started. Depending on the magcard reader you end up getting, you may have to modify these instructions, and most assuredly the code, to work with your specific reader. However, the code I've written should get you pretty far, I hope.

Step 2: Self-clocking Magnetic Card Readers

Picture of Self-clocking Magnetic Card Readers
magcard-reader2.jpg
magcard-setup1.jpg
magcard-setup-final.jpg
Magnetic card readers are "self-clocking," meaning that they provide a clock called a strobe, against which the connected microcontroller can sync. This is a boon. It means you don't have to worry about looking for a clocking signal and timing the signal to center directly on the clock pulse, and no bothersome oscillating into the sweet spot of the clock signal. This make sense when you think about card swipes: everyone swipes at a different pace, some slower, some faster than others. Self-clocking allows even my sweet grandma the ability to use her card without breaking her wrist. Reminds me of having to change the setting for her that determines how much time is valid between clicks to register a double-click....

This card reader's data is valid 1.0 us before the strobe is put onto the line, so there's no worry about delaying to get yourself into the "bit time." For a dual head reader such as the one I'm using, there are two data tracks available to read. In this 'ible, I'm going to show reading from the primary first track to get you started.

There are five connections you will need to make (four if you don't mind giving up more fine tuned control for fewer I/O ports being used). Check out the picture below. The red wire goes to +5V while the black wire goes to ground. The green wire is /CARD_PRESENT; the yellow wire is /STROBE, and the white wire is /DATA1. The forward slash ( / ) means that the data is inverted. A low signal (ie 0) is read as a one, or high. The other connectors are brown for /STROBE2 and orange for /DATA2. We won't be using these.

If you want, you can forget about /CARD_PRESENT. This data line goes low after about 17 head flux rotations to indicate that a card is present (instead of, say, random noise causing your reader to send bogus data) and is used to validate that the data you're getting is card data and not junk. You can skip this connection if you check for the start sentinel on the data stream. More on that later.

As you can see below, I used a right angle male header connected to a bread board and connected my reader to that. I connected /STROBE to PIND2 (digital pin 2 on an Arduino), /CARD_PRESENT to PIND3 (for illustration purposes), and /DATA1 to PIND4. Make sure you enable pullups on these pins so your pins don't float. I also traded out my Arduino for a Bare Bones AVR because I like the way it fits into the breadboard.

Step 3: Magnetic Card Basics

Picture of Magnetic Card Basics
The primary functions that you'll need to do to read a magnetic card are:
1. Detect when the card has been swiped
2. Read the stream of data
3. Detect when the card has gone
4. Process the data
5. Display the data

First, I'll introduce you to some magnetic card basics that you'll need to know when you start writing your own code.

Magnetic Card Standards

Magnetic cards are standardized by the ISO in the following documents:

7810 Physical characteristics of credit card size document
7811-1 Embossing
7811-2 Magnetic stripe - low coercivity
7811-3 Location of embossed characters
7811-4 Location of tracks 1 & 2
7811-5 Location of track 3
7811-6 Magnetic stripe - high coercivity
7813 Financial transaction cards

As you can see, financial cards are specified in a separate document and often have different formats than, say, your grocery card or international calling card. You will have to program for these differences. I just had a credit card and insurance card handy, so I programmed for these types (which both happen to be format B).

Card Formats

There are several different formats for magnetic cards. Format A and B are common, with B being the most common I've seen, and which is supported in this code. Formats C through M are reserved by the ISO, I believe, while N through ?? are reserved for institutional custom use.

Track 1
For financial cards, the first track is recorded at 210 bits per inch and is the first 0.110" of the card from the top. The data is encoded as "card data" as 7-bits per character. That's 6-bits for the character and a bit for parity. There are ~ 79 alphanumeric characters on track 1.
The physical ordering is backwards. That is, data is  but it's written backwards on the card (and hence, will be read by your firmware) as . The parity is odd.

The card data format looks like this:
[SS] [FC] [Primary Account #] [FS] [Name] [FS] [Additional data] [FS][ES][LRC]where:
SS Start sentinel
FC Format code
FS Field separator
ES End sentinel
LRC Longitudinal Redundancy Check character

Track one SS = '%', FC = one of the formats (going to be B a lot of times), FS is often '', ES is '?' and the LRC character is commonly '<' although it's not specified in the standards. Besides being written on the card backward, the data has an odd parity bit and is 0x20 from ASCII. We'll handle this when we process the data.

Track 2 
Track two is 0.110" wide and starts 0.110 from the top of the card. It's recording density is 75 bits per inch. The data is 5-bits per character and consists of around 40 numeric symbols only. You shouldn't encounter any letters on this track.

The card data format should follow this structure:
[SS] [primary account #] [FS] [additional data | discretionary data] [ES] [LRC]
The SS for track two is the semicolon: ';' and the FS is '='

With this holy knowledge under your belt, continue on to the next steps to see code implementing the procedure outlined above.

Step 4: Detect When a Card is Swiped

Picture of Detect When a Card is Swiped
1. Detect when a card has been swiped
Formally, one would check the /CARD_PRESENT pin to see if it's dropped low. Fortunately, this isn't really necessary. We'll check for valid card later. Alternately, you could read your strobe pin to see when strobes have been put onto the pin, however, this will net you lots of clocking zero's. The reader will send about 60-70 leading zero's to let you know that data is about to be presented. However, we're going to use the nature of binary data to determine when to start recording bits.

The start sentinel (SS) for track one is the percentage sign (%). It's binary value is 0010 0101 which means it will be stored (and read) as 1010 001 (it's 7-bits so the 8th bit isn't transmitted). Now, the astute reader will notice that even though the data is backwards it doesn't match the binary ASCII value. That's because it's 0x20 off of hex. The % symbol is 0x25 and 0100 0101 is 0x05. Card data has 0x20 subtracted from the value. That one hanging out there in the high nibble is the odd parity bit. It's put there so that there are an odd number of "1"s in the value.

So because we know that a valid card will always start with this start sentinel, and because the parity bit is a 1, then when we detect the first HIGH to LOW transition on the data pin, then we know we have just started to receive the start sentinel from a card. Now, this isn't always going to be true, and a foolproof plan would be to check the /CARD_PRESENT card to see if it's gone LOW in addition.

The simplest way to detect the start of the SS, is to create an external interrupt triggered on the falling edge of the /STROBE. The data is valid 1.0 us before the falling edge, so when you've sampled the falling edge, then you know you can read the /DATA1 pin and get a valid value. Here's the code to create your external interrupt triggered on a falling edge.

voidInitInterrupt(void){    // Setup interrupt    BSET(EIMSK,INT0);     // external interrupt mask    BSET(EICRA,ISC01);    // falling edge    BCLR(EICRA,ISC00);    // falling edge    BSET(SREG,7);         // I-bit in SREG}

In my common.h that I include in all my programs, the definitions of BSET and BCLR can be found. Refer to that file should you have any questions about how to set bits. Now, when the interrupt is triggered, we want to sample the /DATA1 (in my code defined as CARD_DATA) and set a bit in a general purpose IO register. If we're on the 7th bit, save off the register as a character in our global buffer. I use a GPIOR0 register because it's spiffy fast access. The pseudo code is something like this:

Stop 16-bit timer    Clear timer    If DATA is LOW        Set BIT=1 in REGISTER        Decrement BIT        Set flag so we don't skip any more 0's    else DATA is HIGH        Set BIT=0 in REGISTER         Decrement BIT    If BIT is 0        Add byte to buffer        Increment index        Reset BIT

If you are asking yourself why decrement instead of increment, remember that the data is backwards, so instead of recording the bits as we get them from LSB to MSB, we save them from MSB to LSB so we don't have to reverse the bits later when processing the data. If you really wanted, you could also add 0x20 hex here, but since it's about 5us on these strobes, I'm keeping the processing in this interrupt service routine to a minimum.

ISR(INT0_vect){	StopTimer();	ClearTimer();	if ( !BCHK(PIND,CARD_DATA1) )	// inverse low = 1	{		BSET(GPIOR0,bit);		--bit;		bDataPresent = 1;	} else if (bDataPresent) {		BCLR(GPIOR0,bit);		--bit;	}	if (bit < 0) {		buff[idx] = (char)GPIOR0;		++idx;		bit = 6;	}			StartTimer();}

If you're wondering what the timing business is about, that's covered in the step in determining when the card has left the reader.

Step 5: Read the Stream of Data

Read the stream of data


Well, I've already shown you how to read the data, as it's part of the Interrupt Service Routine for our falling edge external interrupt. An alternative method would be to set a flag in the ISR, and in the main loop poll the flag and read the data that way, but I believe the way I've presented it is cleaner. Be your own judge and write yours however your MCU will allow it.

That being said, let's move on to finding out how to detect when the card pulls an Elvis and has left the building.

Step 6: Detect the Card Leaving the Reader

Picture of Detect the Card Leaving the Reader

Detect when a card has gone


Formally, one would sample the /CARD_PRESENT pin to see if it's gone HIGH again, but we don't need no steenkin' /CARD_PRESENT taking up another I/O port. This is where those timers come in.

Every time the interrupt is called because we've detected a falling edge on /STROBE, we stop a timer, clear the timer value and start reading. When we've finished reading we start the timer again. Repeat ad nauseum, or until the timer reaches a certain value. That means that the last interrupt has been called and no more data has come in, so we assume that's it and start processing the data we've collected.

For timers, we use TIMER1, ie the 16-bit timer. I'm using a 16 Mhz resonator externally to my AVR. If you're using an arduino, then you probably are, too. So, I've chosen a prescaler value of 1024 which means every (16,000,000 / 1024) times the timer will increment. That is to say, it will 'tick' 15,625 times a second. The /CARD_PRESENT will go HIGH indicating the card has left the reader about 150ms after the last data bit. Knowing this, I just decided to check about every 1/4 of a second. That would look something like this:
    ( ((F_CPU) / PRESCALER) / 4 )
which turns out to be around 3900. So, when the timer counter TCNT1 reaches 3900, then I know it's been about 300ms and I can pretty safely conclude that the card has left the reader. Easy.

#define PRESCALER	     1024#define CHECK_TIME     ( (F_CPU / PRESCALER) / 4 )		// 250 ms#define StartTimer()     BSET(TCCR1B,CS10), BSET(TCCR1B,CS12)	// 1024 prescaler#define StopTimer()      BCLR(TCCR1B,CS10), BCLR(TCCR1B,CS12)#define ClearTimer()     (TCNT1 = 0)

You've seen in the ISR where the timer is started, stopped, and cleared on each interrupt. Now, in the main loop we just check to see if the timer counter has reached our target value, and if so, start the data processing.

for (;;){	if( TCNT1 >= CHECK_TIME)	{			
StopTimer();		
ClearTimer();		
ProcessData();		
ReadData();					
idx = 0;		
bit = 6;		
bDataPresent = 0;		
memset(&buff,0,MAX_BUFF_SZ1);	
}	
}

Now it's safe to process the data.

Step 7: Process the Data

Picture of Process the Data

Process the data


The processing phase consists of:
  • checking for a valid SS
  • checking parity
  • converting to ASCII
  • checking for a valid ES
  • checking LRC

Here, I don't bother with checking parity, as I just set that bit to zero. I also don't calculate the LRC for this little tutorial. That would be something that a more fully realized firmware might want to do.

Here's the code to process the data doing the above steps (sans the previously mentioned). Find it in the image below. It's commented and pretty self-explanatory. A special note on parity and ASCII:
I simply clear the parity bit (7th bit...ie a 1 with 6 zeros behind it) and to convert from "card data" you must add 0x20 to the value. That's about it.

Step 8: Display the Data

Picture of Display the Data
ReadData.jpg

Display the data

The display goes to a terminal program I wrote specifically for connecting to an AVR via RS232 or USB. The program is called AVR Terminal. The ReadData() method is pretty ugly and you're encouraged to find a cleaner solution than the one I came up with.

There's also an output of the function in AVR Terminal. The output is first of a health insurance card, and the second is of a VISA card. Click the [i] in the upper left corner of the picture and choose original or large image to see it better.

Step 9: Code Download and Wrapup

In this instructable I've discussed some basics of magnetic card readers and shown you some code to get you started in the right direction in reading data from magnetic cards. There's plenty more work that could be done, such as reading and decoding the 2nd track, calculating the LRC and calculating the odd parity on each byte.

The full source code is available for download below. It was written in AVR Studio 4.17.

I hope you enjoyed this instructable and, as always, I look forward to any comments or suggestions that you may have.

Happy coding and AVR'ing!
1-40 of 54Next »
Awesome work on this. You explained all the steps in a way that takes something that is compli cated and kept it interresting all the way through. I didn't know much about what was on a stripe and now I have at least a basic idea . Thanks for the education!
Build_it_Bob
nevdull (author)  Build_it_Bob4 years ago
Thanks for the kind words! I'm glad you were able to learn something from it and I appreciate your comments!
Cheers!
-gian

Hi,

What is the format of the data you get off the card reader? I mean, do you the information about the former cardholder (name for example) or you get in another format?

Thank you in advance.

I am doing penetrating for ATM machines. I wonder if any one of you have experience creating smart card reader to encryption , or data between EPP readers and ATM PCs, smart card readers to read tr1, tr2 and tr3 data.

I am newbie in hardware hacking. kindly guide me

carlosd311 months ago

I'm building a vending machine using all the electronics arduino, the machine has a bill acceptor and coin acceptor my interest is to add a device to receive pto also with credit / debit card. I just do not know where to start can help me?

KellyCx11 year ago

Since the Arduino pictured above has a USB port, could I be able to use a USB card reader as opposed to a serial one?

eduke1 year ago
Hi,

Where is the link to the code? I do not see it anywhere ...
gep301 year ago
how could i go about finding what type of format a student id at a college will fall under.
mic_mic_mic2 years ago
thanks for the prompt response. I hoping to get my head into a queuing system project and part of it involves simulating a bank ATM card reader to get user data and relay it to a server. I'm still looking around for the perfect magnetic reader. Price is a major factor due to budget constraints on school projects.

The data should essentially identify the unique ATM card/debit card holder on swapping.

Any tips on what i should focus on to get it up and running?

I tried replying but there seems to be a problem with where the captcha is
nevdull (author)  mic_mic_mic2 years ago
heya. I would focus on reading card format B on a single track. You could use an option like this one at ebay but you'd need an AVR that understood USB (ATmega32U4, AT90USB*, etc). There's another one at ebay ($9) using RS232 so you could interface it with the arduino's USART either through a DB9 or stripping the serial cable from the reader and running the pins directly into the arduino's headers.

Also, I re-edited page 3 and 4 (4 is still a little FUBAR with formatting) as I just found out that MAJOR important mag card format information wasn't being formatted and displayed. That should be fixed now and you should have more card format information and how to read it.

Good luck!

mic_mic_mic2 years ago
Very helpful. One question, are you sure it was 5$? I've scoured the net and cant seem to find it for that price. he cheapest i've seen is around 50$. Could you post a link where you got it for the friendly price?
nevdull (author)  mic_mic_mic2 years ago
Well I wish I could share the price with you out on the intertubes but I picked up a handful of these gems at my local electronic hobby shop. And sure enough, they were $5. However, I was back there just last weekend and noticed they still had a small box full of them but they were now priced at $10. But you're right, these devices do tend to be a little pricey. The USB type more so than the straight serial versions.

Good luck!
GJ W00T You certainly dont "fake the funk " brother
nevdull (author)  wherewillwhy2 years ago
Would faking the funk be committing a funkadelic felony?

Hope you enjoyed this or my other instructables! Thanks! Always glad to get your comments.

Cheers!
Gian
Krayzi993 years ago
Where the heck did you get $5.00 as the price? It's more like $5(0).00!
nevdull (author)  Krayzi993 years ago
I got my magnetic card reader from a local electronics shop where they were all in a bin for $5/ea. Where you find yours will obviously dictate the ultimate price of your project, but I have seen them on ebay from a few dollars and up (though you may pay more in shipping unless its free). I would try ebay first. You might get lucky with a good deal here and there on these items. Of course, if you're buying everything from scratch, including the arduino, it'll cost more, but this 'ible was targeted more toward current arduino/avr owners who just wanted to play around with a free/cheap mag card reader they could get their hands on.

Try your luck on ebay looking for a mag card reader in a price range that's workable for you. It could be there.

Good luck!
SpiralEdge4 years ago
I'm new to electronics and got lost at the part where the coding begins. How did you connect the card reader to the computer in order to read data from it?
nevdull (author)  SpiralEdge4 years ago
I connected the arduino through wires connecting the serial interface of the magnetic card.
pdrocb nevdull3 years ago
Hello! Thanks fo your post!

I have an Arduino... but normally you have a .pde extension in the .zip i dont see any. Can you please help me, how can I use your code into Arduino.

Thank you!

Another question did you use a Magtek MiniWedge Stripe with RS232 or usb or keyboard?
nevdull (author)  pdrocb3 years ago
This code isn't from the Arduino IDE nor does it compile into an arduino PDE file. This is normal code written in C that can run on a wide range of ATmega devices, including the Arduino. However, my guess is, you will require a programmer to download it into flash just like with the other atmegas. Sorry for any confusion there.

Re: your other question. My magcard uses a serial protocol that I describe in this instructable. It's pretty standard but doesn't use USB. I had the Arduiono redirect output to a windows terminal via RS232.

Good luck!
CoolKoon4 years ago
Could you by any chance get one with a similar price that could write these cards as well? :P I could really make use of one :P
Identity Theft anyone? Just kidding, ha ha ha.
Nah, I hate magnetic cards and would love to develop a way to introduce some false information on them (in a BOFH way) :D
nevdull (author)  CoolKoon4 years ago
Well I agree, in that it would be kinda geeky fun to have mag cards that were written with the info you wanted (and or encrypted) for testing on home egress, maybe a small sub-zoned area inside your house protected by mag card
hintss nevdull4 years ago
I like iButtons for that.
Yeah, but I'd need a writer for that as well. I suppose yours can't do that, can it?
nevdull (author)  CoolKoon4 years ago
No, my mag card device is a reader-only. Check ebay for some reader/writer combos, although for raw bang-for-your-buck, 125khz RFID reader/writers are only dollars (for the chip, you'd have to build the circuit). Tough decision to make if you're really settled on magnetic cards.
I'm not really settled on mag cards, but haven't seen RFID writers either. I could make use of one too :P
nevdull (author)  CoolKoon4 years ago
You'll find RFID writers on ebay again, and at futurlec.com, i believe.  But why buy one when you can build one with the Atmel U2270B RFID basestation in a chip for a coupla bucks.  Yum yum.


Thanks for the idea! I even managed to find such chip at a local supplier. I'll see whether I can make it into something useful ;)
nevdull (author)  CoolKoon4 years ago
awesome! Good going, let me know how it turns out!
awesome to hack my dad his new credit card!
jcouch12274 years ago
Anyone know what that red thing is called, a header? the thing in the second picture above. thanks
nevdull (author)  jcouch12274 years ago
It's a right-angle 7-pin male header, to be precise. :)
Cheers!
-gian
akrolik4 years ago
Is there a link as to where a $5.00 card reader can be purchased?
nevdull (author)  akrolik4 years ago
I got mine from a local electronic shop here for $5 each. I don't know if they have an online store, but their website is:

http://jbsaundersco.com/

Good luck!
bonez134 years ago
Sweet, being looking for project like this...
nevdull (author) 4 years ago
Flash Animation
limerick4 years ago
must say though that in general, rfid is more secure in a sense, than keypad codes as with a card, you have to have a physical credential versus a keypad code which an be obtained in several ways from 'shoulder surfing' to someone
telling another, to some dufus writing it down and misplacing it and it falls into the wrong hands.
needtobuild5 years ago
Pretty sweet !
It would be cool to make a door lock for a house with this =)
1-40 of 54Next »