Friday, March 03, 2006

Magnetic Stripe Card Reader with PIC12F683

Project: Read a magnetic stripe (credit card or similar)

Background: I became interested in magstripes because my student card has one (two actually, one to get into my dorm and one to add money to, like debit), and I wanted to see what is on these stripes. There are many articles detailing how magstripes work and the data format:

Theory: We are primarily concerned with the binary coming out of a card reader, and its format.

3 signals are generated:

/CP - Card Present (active low) - goes low when swiping a card
/DATA - Data (active low) - valid on low clock pulse
/STROBE - Clock (active low)

See diagram:

Format of the data:
Bytes are 5 bits wide, 4 data bits and one parity bit.
Bit 1, the least significant bit (LSB) is sent first.
There is a 16 character set - 10 data, 3 control, and 3 framing.

"The magstripe begins with a string of zeros to permit the self-clocking feature to "sync" with the data and begin decoding. A start sentinel [11010] then tells the reformatting process where to start grouping the decoded bitstream into groups of 5 bits each. At the end of the data, an "End Sentinel" is encountered, which is followed by an "Longitudinal Redundancy Check (LRC) character. The LRC is a parity check for the sums of all b1, b2, b3, and b4 data bits of all preceding characters. The LRC character will catch the remote error that could occur if an individual character had two compensating errors in its bit pattern (which would fool the 5th-bit parity check). "

Process: In order to obtain this information, we need a way to capture the bitstream and save it. A perfect task for a small microcontroller. I decided to use the PIC12F683 because it has an internal clock of 8MHz (plenty fast enough to capture the bitstream), can operate between 2 and 5V (battery powered from button cell or AAA), has many different types of interrupts (we'll use two in particular, external interrupt and IO change interrupt), enough IO pins (6, we'll use 3 for the card reader, and perhaps more for optional LED display), its small 8 pin footprint, and finally it has enough memory (96 bytes of user RAM and 256 bytes of EEPROM). We can read in a total of about 85 bytes (the typical credit card has about 38 bytes).

Program: Source code here (written in PIC assembly). First, we must initialize the microcontroller - this entails setting up the IO pins for digital input or output, enabling inuterrupts and edge trigger, and setting the internal oscillator. Also, if using display LEDs, to blink an LED to indicate "ready to scan."
Then we enter a loop where nothing happens, we simply wait until the card is read, that is, an interrupt occurs.
We connect the STROBE to the external interrupt pin of the PIC (GP2). The CARD PRESENT line is connected to GP3, which is set up for interrupt on change (that is, when the bit goes from low-to-high or high-to-low). DATA is connected to GP0 and is simply read by the microcontroller in the interrupt service routine (ISR).

The ISR is the bulk of this program. There are two entry points - the CP interrupt on change and the STROBE external interrupt, which will occur on the falling edge of the clock (high-to-low, though, theoretically, it shouldn't matter what edge we trigger on). Since there are two entry ways, the first thing the ISR checks is to see if the CP is high or low, if it is low we ignore it and start capturing the bitstream. The STROBE will be the source for most interrupts. Because the data is read on pin 0, we mask that bit by ANDing the rest of the port with 0, effectively clearing any other 1s that might be in it. We then ADD this bit into the first user register possible (we're going to need indirect addressing) and shift the register to the left to prepare for the next bit. After 5 bits are read in, we need to take complement of the byte because the data is read in complement form (low=1, high=0). Then we increment to the next byte and the next user register. That's the general principle, but there is a complication - making sure the 5 bits in each byte correspond to the same byte that's on the card, because the leading zeros can cause misalignment. This is easily fixed by saving the bitstream starting when the first 1 is read (from the start sentinel 11010).

Finally, the last interrupt will be CP going high, indicating we are done reading all the data in from the card. At this point we write all the data from the user registers into EEPROM and optionally blink an LED indicating we are done. We have saved our data in nonvolatile memory, so power can be turned off.

Shortcomings: There are a few shortcomings with this program, mostly with the user interface. The way I have written it, a card can be scanned once or twice, or a different card can be scanned second. The power must not be turned off in between scans, or the first scan data will be overwritten. I have not fully figured out the EEPROM write, I get spurious data the second time a card is read.

Error detection - I have not programmed any error detection. I do not make use of the parity bit, or the LRC at the end of the bitstream.

Also, once we have our data, we need to present it in a form understandable by the user, most easily done with a seven segment display and a momentary push button switch to view each character. This presents a problem. A switch requires another input pin, and a seven segment display requires seven output pins, however we only have three pins to work with. The seven segment display could be driven with two pins going to a shift register, and then to the seven segment display. We would need another chip, increasing the complexity and size. Or we could use another PIC with more IO pins.

Development: The next step will be to port the program to a PIC with more IO pins, in order to have a display. I'll also get a PIC with more EEPROM, so more cards can be read. The abilities of the user interface will be expanded, to include deleting a card from memory, rescanning a card, and a seven segment LED to display one character at a time.

I welcome ideas, comments, and questions.