RFID Reader 线路图收集

 

 

This 125 kHz RFID reader

http://www.serasidis.gr/circuits/RFID_reader/125kHz_RFID_reader.htm

http://www.serasidis.gr/circuits/RFID_reader/images/125kHz_RFID_reader_schem.GIF

I will try to explain with simple words how the RFID works. The ATtiny13 uses the PWM function to produce an 125 kHzsquare wave signal.

This signal comes out from PB0 pin. On the falling edge of the PB0 (Logic '0'), the T1 does not conduct.

So the L1 is energized from R1 (100 ohm) with +5V.

When PB0 pin rises (Logic '1') the T1 conducts and one side of L1 goes to GND.

The L1 goes in parallel with C2 creating an LC oscillator.

These transitions of L1 to logic '1' and logic '0' are made 125000 times in one second (125 kHz).

 

Data communication between Tag and reader. 

How an RFID Tag communicates with the reader?

The idea is simple, but very clever!

When a Tag wants to send a logic '0' to the reader it puts a "load" to its power supply line to request more power from the reader.

That will make a small voltage drop on the RFID reader side.

That voltage level is logic '0' (picture 4).

Simultaneously, as long as the reader transmits the 125 kHz signal it reads the voltage of the transmitted signal trough the filters D1, C3 and R5, C1.

When the Tag drops the voltage as we said before, the reader reads this voltage drop as logic '0'.

When the Tag doesn't require any additional power, it doesn't make a voltage drop.

That is logic '1' (picture 3).

The 'Ones' or 'Zeros' length depends on the serial transmission data rate.

For example, for 125 kHz carrier frequency we don't have 125000 bits per second data transmission!

The data transmission from Tag to the reader varies from 500 bits per second up to 8000 bits per second. 

The RFID tag content EM4100

The 125kHz RFID tag transmits 64 bits.

  1. The first 9 bits are the start communication bits ( always '1' ).  
  2. The next 4 bits are the Low Significant Bits of the customer ID(D00,...,D03).
  3. The next 1 bit (P0) is the Even parity bit of the previous 4 bits .
  4. The next 4 bits are the High Significant Bits of the customer ID (D04,...,D07).
  5. The next 1 bit (P1) is the Even parity bit of the previous 4 bits.
  6. The next 4 bits are first part of the 32-bit Tag's serial number (D08,...,D11).
  7. ...
  8. The PC0 bit is the Even parity bit of bits D00, D04, D08, D12, D16, D20, D24, D28, D32 and D36 (the bits on the same column).
  9. The PC1, PC2, PC3 bits represent the parity bits of the next 3 columns.

The data verification is been done from ATtiny13 by calculating the Even parity bit of each line and each column with the parity bits that had been received form the RFID Tag transmitted data.

Simple RFID Reader Module Design

http://freshengineer.com/blog/simple-rfid-reader-module-design/ 

I first started by creating a simple, non-filtered, non-processed reader.

I’ve used a coil of about 1mH for both sides.

Since my chosen frequency was 125 KHz, my capacitor should be 1.62nF according to the following equation; I picked 1n5 standard value.

So this configuration is probably one of the simplest forms of an RFID reader-tag pair:

 

L1 is driven via a low-impedance 125 KHz oscillator, can be a sine or a square wave

since the LC circuit will filter out the unwanted harmonics that are presented in a square wave.

If the Q of the inductor is high, then a voltage that is greater than the oscillator’s output is going to be present in the “Out”.

I’ve seen 100 Vpp when I fed the LC circuit with 5Vpp!

So, the “Out” waveform at the top of the C1 is a sine wave of a 125 KHz frequency.

Now, the fun thing begins when we put the tag near the reader.

L2, C2 pair picks up the 125KHz waveform via L2. So, if you scope C2, you will see 125 KHz sine wave.

Now, if you scope “Out”, you will see that Vpp at C1 will drop when we close the switch SW1.

That is because we load L1′s magnetic field via L2.

Now, push the button like you are sending a Morse code and watch the “Output” waveform on the scope. Aha, modulation!

Simple! That is how real RFID passive tags work.

However, instead of sending Morse code, they modulate the signal with their specific modulation scheme.

I am going to work with EM4100 protocol since it is widely used.

Okay, let’s bring some real circuitry here.

http://freshengineer.com/Documents/RFID_Reader/KiCad/Outputs/Schematic.pdf

 

OK, L1 and C6 are our main guys.

They are the components that are mentioned before as “L1″ and “C1″ in Figure 1.

The circuitry on the left side of L1 is used to drive this LC circuit, and right side of C6 is used to read the changes in the signal.

C1 AC couples the clock signal of 125KHz to the circuit.

R1 and R2 biases the transistor Q1.

R4 limits its base current.

Q1 drives the input of push-pull follower formed by Q2 and Q3.

A push-pull follower will drive the signal at low output impedance.

D2 and D3 prevents distortions at the cross-overs from zero level.

Now, our signal at “TP1″ is something like this, with no processing and modulation:

We are going to use an “envelope detector” formed by D4, C8 and R13.

After the recovery, this is how our “modulated” signal looks like:

 

Of course, these measurements are made with the tag almost touching the reader.

If we move the tag away about 5 cm from the reader, we may not be able to see the signal even with the oscilloscope.

So, we have to filter and amplify this signal and make it ready to be processed by a microcontroller later on.

As you can see above, the signal we are dealing with is an AC signal.

To deal with AC signals with the OP-AMPs, you need either a dual supply which goes to negative (for example -12V, +12V),

or you need a virtual ground.

We are going to assume that half point of our supply voltage is ground.

So, if we are using a 5V single supply, our half point is +2.5V. If +2.5V is ground, then +5V is our new +2.5V and 0V is our new -2.5V.

There you have it, a dual supply.

We need the output impedance of this supply low, so we use an OP-AMP to buffer the +2.5V point

which is high impedance due to R15 and R16, and we get a low impedance output as shown:

OK, now that we have solved that problem, let’s go back to our filter design.

We have a square wave at certain frequency that we want to boost.

While boosting the desired frequency we want to kill the other frequencies.

But we see a bump there; square wave.

A square wave is a signal that includes lots of harmonics (theoretically; infinity) of its actual frequency.

These harmonics are hidden in the rise and fall waves, sharper the rise and fall, more the harmonics count.

So, that means, if you low pass filter a square wave -that is not letting higher frequencies to pass a filter,

you delete those harmonics and remember, those harmonics are in rise and fall times.

Thus, you end up with a sine wave.

We do not want that, that’s why we are going to let these frequencies pass as the way they are, however we are going to boost the original frequency.

To do this, we have a filter design like follows:

“SignalOut” is our input coming from the envelope detector.

C2 and R3 form a high pass filter to AC couple the input, and D1 protects the non-inverting input of the U1:A from over-voltage.

You may say that it is not needed as the capacitor C2 will not allow any DC voltage through, you are correct.

But only in steady state, if the capacitor is discharged, then it will let DC until it is charged.

By the way, think +2.5V point as a “ground” point, since it is a virtual ground.

C5 and R10 AC couples the output from U1:A in case of any DC offset.

Then, this signal is filtered again, resulting in more amplification.

Here is a graph showing the transfer characteristics of these filters:

 

 

Here is the waveform at the output node, pin 7 of U1:

Yay! We have a filtered, clean output!

But not so fast, because we need logic output.

This is done easily by a comparator.

Normally, OP-AMP comparators compare the input with a reference voltage, generally half the supply voltage.

However, this may not work well if the rise and fall times of the input waveform is not in symmetry or close enough.

Let’s demonstrate that with a reference voltage of half the supply:

The input signal has a loooong fall time.

It should fall down at 3ms point ideally, since this is a recovered, however badly distorted ~43% duty cycle square wave - well at least let’s assume.

See how the output waveform is a ~56% duty cycle square wave. We do not want that.

What you have to do is simple, compare the input signal with its average.

How do you find a signals average? That is simple too - put it into a low pass filter, and here is the output:

Now let’s look at our case and apply:

 Let’s look at R14 and C10, we have selected them so that we have a good averaging (should I say weighted?) level for both 1KHz and 2KHz outputs we will have.

This is the final output, isn’t it great:

 

Finished PCB:

I am going to cover the digital section, that is the decoding part of this signal, in an another post.

One little hint; it is Manchester coding!

Until then, feel free to comment and share. 

DIY FSK RFID Reader

http://playground.arduino.cc/Main/DIYRFIDReader

http://playground.arduino.cc/uploads/Main/FSK-RFID-reader-v2.png

This page describes the construction of an RFID reader using only an Arduino (Nano 3.0 was tested, but others may work),

a hand-wound wire coil, and some assorted low cost common components.

Credits

The hardware and software designs for this project are based in part on the ideas, code and schematics

posted by Micah Dowty here : SIMPLEST RFID READER

and Asher Glick here : AsherGlick AVRFID

Background

RFID readers are devices sold by companies such as Parallax to read RFID tags with embedded identification circuits

(we focus here on passive tags, activated by the reader's transmitted RF energy).

The design presented here shows how to wind a simple wire loop by hand

(or create an equivalent printed circuit spiral version), connect it to an Arduino (or its chip),

add a few low cost common components and create your own RFID reader.

To make it more interesting (i.e. challenging), we will focus on the FSK class of RFID tags,

which are fairly common among the 125kHz devices, but for some reason are not supported by the Parallax kits.

 

Micah Dowty has shown [ World's simplest RFID reader ]  a design for an FSK/ASK RFID reader built around a Parallax Propeller device.

His code, which is in assembly language, implements an ingenious (but complex) algorithm to create a dynamically variable analog bias voltage,

which is used to pull the weak RFID signal into range, so it can be discriminated into binary signals by the Propeller's digital input circuitry.

He also dynamically tweaks the transmit/receive RF frequency to keep the antenna's tank circuit in peak resonance for optimal signal to noise.

All capacitances are in picofarads. C1 and C3 should be 1000 pF, not 1000 nF. Likewise, C2 is 2200 pF.

 

There are three problems with his approach:

first, the passive detection circuit lacks amplification, which makes it very sensitive to noise and therefore raises reliability issues.

Second, the design is based on the Propeller chip, and if you are a fan of the Arduino and/or associated Atmel AVR chips, it leaves you out.

And third, the dynamic slewing of frequencies and bias voltage is overly complicated, making it hard to debug.

His general concept is attractive, however: use a microcontroller chip and wind your own wire loop to create,

with some simple components and appropriate code, a complete DIY RFID reader.

Asher Glick has presented a solution [ AsherGlick AVRFID ] for reading and decoding FSK RFID tags

using the Arduino/AVR family (which he calls AVRFID),

which is good except it apparently requires obtaining and modifying an existing Parallax RFID reader device

(which natively only supports ASK).

Our goal here is to present a simple solution for reading FSK tags which addresses the above shortcomings:

make it robust and reliable for real-world noise environments, base it on the Arduino,

and build the RFID reader ourselves using a few simple low-cost parts, rather than buying and/or modifying one.

The circuit diagram above was derived from the "World's Simplest RFID Reader" design posted by Micah Dowty.

Based on the Parallax Propeller, Micah's approach was to use passive components only, without amplification,

in order to achieve the ultimate in simplicity.

The lack of amplification, however, results in a weak signal, potentially less than 2V PTP.

This signal is then biased by an analog level produced by the Propeller,

to try to maintain the signal's DC level near the discrimination point of the Propeller's binary-digital input circuitry.

His code attempts to dynamically calculate that optimal midpoint level, and feed it into the circuit using a filtered PWM DAC output.

Since the signal is weak, it can be distorted by interference and noise, which results in reduced reliability.

The circuit presented here includes (as Micah suggests in his documentation) one active component:

a common low-cost LM234quad-opamp IC (or equivalent).

This addition provides several significant advantages, at a negligible cost.

First, the signal is amplified (using one of the four opamps on the IC package) to a more noise-immune level (of 2-3 volts PTP).

Second, the DC level of the signal is maintained at exactly Vcc/2 using another opamp on the IC,
which eliminates the need for the DC propping code in the Arduino.

Third, having the signal amplifier in place allows another low-pass RC filter stage (another capacitor and resistor),
which makes the final discriminated digital signal cleaner and more reliable.
The end result is a more robust detected signal with improved noise immunity.

As a quick review of the circuit, the loop is made of a toroidally-wound #22-30 magnet wire

(we used an empty roll of Scotch 3.25" I.D. packing tape as former),

and can be remoted from the circuit if needed, via coaxial cable.

The inductor L1 and capacitance C1 should be matched to resonate at around 125 kHz.

When driven at its resonant frequency by the Arduino's 0-5 volt square wave signal,

the center point of the resonator (which connects to D1's cathode) will have a fairly pure voltage sine wave, of about 30V PTP.

When coupled to an RFID tag, the pure sine wave RF will fluctuate visibly as the tag opens and closes its own loop antenna to repeatedly transmit its code.

This modulation is then detected from the RF envelope by D1, C2 and R1,

which produce a negative bias voltage with the small detected coded signal, e.g. about 11 RF cycles per coded cycle.

The coded cycles are of two different wave lengths (or frequencies), which represent streams of logic ones and zeros,

and they need to arrive at the Arduino chip as binary levels which can be timed reasonably accurately

so as to reliably tell the difference between the two distinct frequencies.

The relatively large capacitor C3 decouples the negative bias voltage from the signal,

and is followed by a low-pass RC filter stage (R2 and C4) which attenuates some of the residual RF spikes

from the lower frequency coded RFID signal.

Capacitor C5 decouples the resulting signal and presents it to the amplification stage, implemented by the LM324opamp, IC1.

The latter amplifies the weak signal from about .15V to about 3V PTP (depending of the ratio of R4 to R3),

and places it on top of a Vcc/2 bias voltage, about 2.5V in the arduino's case.

This signal is then fed into one of the digital input ports on the Arduino (which also includes some helpful hysteresis),

and is discriminated by the internal comparator into a square wave of ones and zeroes.

Software

The Arduino sketch, derived from the code posted by Asher Glick, [ https://github.com/AsherGlick/AVRFID ]

uses a single timer channel in the Arduino (using the Timer1 library) for both RF signal generation

as well as timing clock to count the width of each input signal wave.

  1  /***************************************************************************** 
  2  |         This program was written by Asher Glick aglick@tetrakai.com         | 
  3  |             This program is currently under the GNU GPL licence             |
  4  *****************************************************************************/
  5 
  6 /****************** CHIP SETTINGS ******************
  7 | This program was designed to run on an ATMEGA328  |
  8 | chip running with an external clock at 8MHz       |
  9 ***************************************************/
 10 
 11 /********** FUSE SETTINGS **********
 12 |   Low Fuse 0xE2                   |
 13 |  High Fuse 0xD9                   |       +- AVRDUDE COMMANDS -+
 14 | Extra Fuse 0x07                   |       | -U lfuse:w:0xe0:m  |
 15 |                                   |       | -U hfuse:w:0xd9:m  |
 16 | These fuse calculations are       |       | -U efuse:w:0xff:m  |
 17 | based off of the usbtiny AVR      |       +--------------------+
 18 | programmer. Other programmers     |
 19 | may have a different fuse number  |
 20 ***********************************/
 21 
 22 /************************** AVRDUDE command for 8MHz ************************** 
 23 | sudo avrdude -p m328p -c usbtiny -U flash:w:myproject.hex                    |
 24 |                       -U lfuse:w:0xE2:m -U hfuse:w:0xD9:m -U efuse:w:0x07:m  |
 25 |                                                                              |
 26 | NOTE: when messing with fuses, do this at your own risk. These fuses for the |
 27 |        ATMEGA328P (ATMEGA328) worked for me, however if they do not work for |
 28 |        you, it is not my fault                                               |
 29 | NOTE: '-c usbtiny' is incorrect if you are using a different programmer      |
 30 ******************************************************************************/
 31 
 32 /******************************* CUSTOM SETTINGS ******************************
 33 | Settings that can be changed, comment or uncomment these #define settings to |
 34 | make the AVRFID code do different things
 35 ******************************************************************************/
 36 
 37 //#define Binary_Tag_Output         // Outputs the Read tag in binary over serial
 38 #define Hexadecimal_Tag_Output    // Outputs the read tag in Hexadecimal over serial
 39 //#define Decimal_Tag_Output        // Outputs the read tag in decimal
 40 
 41 #define Manufacturer_ID_Output    // The output will contain the Manufacturer ID (NOT IMPLEMENTED)
 42 #define Site_Code_Output          // The output will contain the Site Code       (NOT IMPLEMENTED)
 43 #define Unique_Id_Output          // The output will contain the Unique ID
 44 
 45 //#define Split_Tags_With '-'       // The character to split tags pieces with
 46 
 47 //#define Whitelist_Enabled         // When a tag is read it will be compaired 
 48                                   // against a whitelist and one of two functions
 49                                   // will be run depending on if the id matches
 50                                  
 51                                  
 52                                  // some conststents
 53                                   
 54 // These values may need to be changed depending on the servo that you are using
 55 #define SERVO_OPEN 575    // open signal value for the servo
 56 #define SERVO_CLOSE 1000  // close signal value for the servo
 57 
 58 
 59 //20-bit manufacturer code,
 60 //8-bit site code
 61 //16-bit unique id
 62 
 63 #define MANUFACTURER_ID_OFFSET 0
 64 #define MANUFACTURER_ID_LENGTH 20
 65 
 66 #define SITE_CODE_OFFSET 20
 67 #define SITE_CODE_LENGTH 8
 68      
 69 #define UNIQUE_ID_OFFSET 28
 70 #define UNIQUE_ID_LENGTH 16
 71 
 72 
 73 
 74 // these settings are used internally by the program to optimize the settings above
 75 #ifndef serialOut
 76   #define serialOut
 77 #endif
 78 
 79 
 80 /// Begin the includes
 81 
 82 #include <avr/io.h>
 83 #include <avr/interrupt.h>
 84 #include <stdlib.h>
 85 
 86 #define ARRAYSIZE 900   // Number of RF points to collect each time
 87 
 88 char * begin;           // points to the bigining of the array
 89 int * names;            // array of valid ID numbers
 90 int namesize;           // size of array of valid ID numbers
 91 volatile int iter;      // the iterator for the placement of count in the array
 92 volatile int count;     // counts 125kHz pulses
 93 volatile int lastpulse; // last value of DEMOD_OUT
 94 volatile int on;        // stores the value of DEMOD_OUT in the interrupt
 95 
 96 /********************************* ADD NAMES *********************************
 97 | This function add allocates the ammount of memory that will be needed to    |
 98 | store the list of names, and adds all the saved names to the allocated      |
 99 | memory for use later in the program                                         |
100 *****************************************************************************/
101 void addNames(void) {
102   namesize = 2; // number of IDs in the access list
103   names = malloc (sizeof(int) * namesize);
104   // change or add more IDs after this point
105   names [0] = 12345;
106   names [1] = 56101;
107 }
108 
109 /******************************* INT0 INTERRUPT *******************************
110 | This ISR(INT0_vect) is the interrupt function for INT0. This function is the |
111 | function that is run each time the 125kHz pulse goes HIGH.                   |
112 | 1) If this pulse is in a new wave then put the count of the last wave into   |
113 |     the array                                                                |
114 | 2) Add one to the count (count stores the number of 125kHz pulses in each    |
115 |     wave                                                                     |
116 ******************************************************************************/
117 ISR(INT0_vect) {
118   //Save the value of DEMOD_OUT to prevent re-reading on the same group
119   on =(PINB & 0x01);
120   // if wave is rising (end of the last wave)
121   if (on == 1 && lastpulse == 0 ) {
122     // write the data to the array and reset the cound
123     begin[iter] = count; 
124     count = 0;
125     iter = iter + 1;
126   }
127   count = count + 1;
128   lastpulse = on;
129 }
130 
131 /************************************ WAIT ************************************
132 | A generic wait function                                                      |
133 ******************************************************************************/
134 void wait (unsigned long time) {
135   long i;
136   for (i = 0; i < time; i++) {
137     asm volatile ("nop");
138   }
139 }
140 
141   //////////////////////////////////////////////////////////////////////////////
142  //////////////////////////// SERIAL COMMUNICATION ////////////////////////////
143 //////////////////////////////////////////////////////////////////////////////
144 
145 /******************************** USART CONFIG ********************************
146 | USART_Init(void) initilizes the USART feature, this function needs to be run |
147 | before any USART functions are used, this function configures the BAUD rate  |
148 | for the USART and enables the format for transmission                        |
149 ******************************************************************************/
150 #define FOSC 8000000 // Clock Speed of the procesor
151 #define BAUD 19200    // Baud rate (to change the BAUD rate change this variable
152 #define MYUBRR FOSC/16/BAUD-1 // calculate the number the processor needs
153 void USART_Init(void) {
154   unsigned int ubrr = MYUBRR;
155   /*Set baud rate */
156   UBRR0H = (unsigned char)(ubrr>>8);
157   UBRR0L = (unsigned char)ubrr;
158   /*Enable receiver and transmitter */
159   UCSR0B = (1<<RXEN0)|(1<<TXEN0);
160   /* Set frame format: 8data, 2stop bit */
161   UCSR0C = (1<<USBS0)|(3<<UCSZ00);
162 }
163 
164 /******************************* USART_Transmit *******************************
165 | The USART_Transmit(int) function allows you to send numbers to USART serial  |
166 | This function only handles numbers up to two digits. If there is one digit   |
167 | the message contains a space, then the digit converted to ascii. If there    |
168 | are two digits then the message is the first digit followed by the seccond   |
169 | If the input is negative then the function will output a newline character   |
170 ******************************************************************************/
171 void USART_Transmit(char input )
172 {
173   while ( !( UCSR0A & (1<<UDRE0)) );
174   // Put the value into the regester to send
175   UDR0 = input;
176 }
177   //////////////////////////////////////////////////////////////////////////////
178  ////////////////////////// BASE CONVERSION FUNCTIONS /////////////////////////
179 //////////////////////////////////////////////////////////////////////////////
180 char binaryTohex (int four, int three, int two, int one) {
181   int value = (one << 0) + (two << 1) + (three << 2) + (four << 3);
182   if (value > 9) return 'A' + value - 10;
183   return '0' + value;
184 }
185 
186 /*********************** GET HEX ARRAY FROM BINARY ARRAY **********************
187 |
188 ******************************************************************************/
189 int * getHexFromBinary (int * array, int length, int * result) {
190   int i;
191   int resultLength = (length+3)/4; // +3 so that the resulting number gets rounded up
192   // 4 / 4 = 1       [correct]
193   // 7 / 4 = 1 (4+3) [still correct]
194   // 5 / 4 = 1       [not rounded up]
195   // 8 / 4 = 2 (5+3) [correct]
196   
197   for (i = 0; i < resultLength; i++) {
198     result[i*4] = (array[i+0] << 0)
199                 + (array[i+1] << 1)
200                 + (array[i+2] << 2)
201                 + (array[i+3] << 3);
202   }
203   return result;
204 }
205 /*************************** GET DECIMAL FROM BINARY **************************
206 | This function will take in a binary input and return an intiger with the     |
207 | corrisponding value, assumed as decimal                                      |
208 ******************************************************************************/
209 int getDecimalFromBinary (int * array, int length) {  
210   int result = 0;
211   int i;
212   for (i = 0; i < length; i++) {
213     result = result<<1;
214     result += array[i]&0x01;
215   }
216   return result;
217 }
218 
219 
220 
221 void recurseDecimal (unsigned int val) {
222   if (val > 0 ) {
223     recurseDecimal(val/10);
224     USART_Transmit('0'+val%10);
225   }
226   return;
227 }
228 
229 void printDecimal (int array[45]) {
230   #ifdef Manufacturer_ID_Output
231   int manufacturerId = getDecimalFromBinary( array + MANUFACTURER_ID_OFFSET,MANUFACTURER_ID_LENGTH);
232   manufacturerId = getDecimalFromBinary( array + MANUFACTURER_ID_OFFSET,MANUFACTURER_ID_LENGTH);
233   recurseDecimal(manufacturerId);
234   #endif
235   
236   #ifdef Split_Tags_With
237     USART_Transmit(Split_Tags_With);
238   #endif
239   
240   #ifdef Site_Code_Output
241   
242   int siteCode = getDecimalFromBinary( array + SITE_CODE_OFFSET,SITE_CODE_LENGTH);
243   recurseDecimal(siteCode);
244   #endif
245 
246   #ifdef Split_Tags_With
247     USART_Transmit(Split_Tags_With);
248   #endif
249 
250   #ifdef Unique_Id_Output
251   int lastId = getDecimalFromBinary( array + UNIQUE_ID_OFFSET,UNIQUE_ID_LENGTH);
252   recurseDecimal(lastId);
253   #endif
254   
255   USART_Transmit('
');
256   USART_Transmit('
');
257 }
258 void printHexadecimal (int array[45]) {
259   int i;
260   #ifdef Manufacturer_ID_Output
261   for (i = MANUFACTURER_ID_OFFSET; i < MANUFACTURER_ID_OFFSET+MANUFACTURER_ID_LENGTH; i+=4) {
262     USART_Transmit(binaryTohex(array[i],array[i+1],array[i+2],array[i+3]));
263   }
264   #endif
265   
266   #ifdef Split_Tags_With
267     USART_Transmit(Split_Tags_With);
268   #endif
269   
270   #ifdef Site_Code_Output
271   for (i = SITE_CODE_OFFSET; i < SITE_CODE_OFFSET+SITE_CODE_LENGTH; i+=4) {
272     USART_Transmit(binaryTohex(array[i],array[i+1],array[i+2],array[i+3]));
273   }
274   #endif
275 
276   #ifdef Split_Tags_With
277     USART_Transmit(Split_Tags_With);
278   #endif
279 
280   #ifdef Unique_Id_Output
281   for (i = UNIQUE_ID_OFFSET; i < UNIQUE_ID_OFFSET+UNIQUE_ID_LENGTH; i+=4) {
282     USART_Transmit(binaryTohex(array[i],array[i+1],array[i+2],array[i+3]));
283   }
284   #endif
285   USART_Transmit('
');
286   USART_Transmit('
');
287 }
288 
289 
290 
291 void printBinary (int array[45]) {
292   int i;
293   #ifdef Manufacturer_ID_Output
294   for (i = MANUFACTURER_ID_OFFSET; i < MANUFACTURER_ID_OFFSET+MANUFACTURER_ID_LENGTH; i++) {
295     USART_Transmit('0'+array[i]);
296   }
297   #endif
298   
299   #ifdef Split_Tags_With
300     USART_Transmit(Split_Tags_With);
301   #endif
302   
303   #ifdef Site_Code_Output
304   for (i = SITE_CODE_OFFSET; i < SITE_CODE_OFFSET+SITE_CODE_LENGTH; i++) {
305     USART_Transmit('0'+array[i]);
306   }
307   #endif
308 
309   #ifdef Split_Tags_With
310     USART_Transmit(Split_Tags_With);
311   #endif
312 
313   #ifdef Unique_Id_Output
314   for (i = UNIQUE_ID_OFFSET; i < UNIQUE_ID_OFFSET+UNIQUE_ID_LENGTH; i++) {
315     USART_Transmit('0'+array[i]);
316   }
317   #endif
318   USART_Transmit('
');
319   USART_Transmit('
');
320 }
321 
322 
323 
324 
325 /********************************* Search Tag *********************************
326 | This function searches for a tag in the list of tags stored in the flash     |
327 | memory, if the tag is found then the function returns 1 (true) if the tag    |
328 | is not found then the function returns 0 (false)                             |
329 ******************************************************************************/
330 int searchTag (int tag) {
331   int i;
332   for (i = 0; i < namesize; i++) {
333     if (tag == names[i]) {
334       return 1;
335     }
336   }
337   return 0;
338 }
339 
340 
341 
342 
343 void whiteListSuccess () {
344   PORTB |= 0x04;
345   // open the door
346   OCR1A = 10000 - SERVO_OPEN;
347   {
348     unsigned long i;
349     for (i = 0; i < 2500000; i++) {
350       if (!((PINB & (1<<7))>>7)) {
351         break;
352       }
353     }
354   }
355   //close the door
356   OCR1A = 10000 - SERVO_CLOSE;
357   {
358     unsigned long i;
359     for (i = 0; i < 500000; i++) {
360       asm volatile ("nop");
361     }
362   }
363   OCR1A = 0;
364   wait (5000);
365 }
366 void whiteListFailure () {
367   PORTB |= 0x08;
368   wait (5000);
369 }
370 
371 
372 
373   //////////////////////////////////////////////////////////////////////////////
374  ///////////////////////////// ANALYSIS FUNCTIONS /////////////////////////////
375 //////////////////////////////////////////////////////////////////////////////
376 
377 /************************* CONVERT RAW DATA TO BINARY *************************
378 | Converts the raw 'pulse per wave' count (5,6,or 7) to binary data (0, or 1)  |
379 ******************************************************************************/
380 void convertRawDataToBinary (char * buffer) {
381   int i;
382   for (i = 1; i < ARRAYSIZE; i++) {
383     if (buffer[i] == 5) {
384       buffer[i] = 0;
385     }
386     else if (buffer[i] == 7) {
387       buffer[i] = 1;
388     }
389     else if (buffer[i] == 6) {
390        buffer[i] = buffer[i-1];
391     }
392     else {
393       buffer[i] = -2;
394     }
395   }
396 }
397 
398 /******************************* FIND START TAG *******************************
399 | This function goes through the buffer and tries to find a group of fifteen   |
400 | or more 1's in a row. This sigifies the start tag. If you took the fifteen   |
401 | ones in multibit they would come out to be '111' in single-bit               |
402 ******************************************************************************/
403 int findStartTag (char * buffer) {
404   int i;
405   int inARow = 0;
406   int lastVal = 0;
407   for (i = 0; i < ARRAYSIZE; i++) {
408     if (buffer [i] == lastVal) {
409       inARow++;
410     }
411     else {
412       // End of the group of bits with the same value
413       if (inARow >= 15 && lastVal == 1) {
414         // Start tag found
415         break;
416       }
417       // group of bits was not a start tag, search next tag
418       inARow = 1;
419       lastVal = buffer[i];
420     }
421   }
422   return i;
423 }
424 
425 /************************ PARSE MULTIBIT TO SINGLE BIT ************************
426 | This function takes in the start tag and starts parsing the multi-bit code   |
427 | to produce the single bit result in the outputBuffer array the resulting     |
428 | code is single bit manchester code                                           |
429 ******************************************************************************/
430 void parseMultiBitToSingleBit (char * buffer, int startOffset, int outputBuffer[]) {
431   int i = startOffset; // the offset value of the start tag
432   int lastVal = 0; // what was the value of the last bit
433   int inARow = 0; // how many identical bits are in a row// this may need to be 1 but seems to work fine
434   int resultArray_index = 0;
435   for (;i < ARRAYSIZE; i++) {
436     if (buffer [i] == lastVal) {
437       inARow++;
438     }
439     else {
440       // End of the group of bits with the same value
441       if (inARow >= 4 && inARow <= 8) {
442         // there are between 4 and 8 bits of the same value in a row
443         // Add one bit to the resulting array
444         outputBuffer[resultArray_index] = lastVal;
445         resultArray_index += 1;
446       }
447       else if (inARow >= 9 && inARow <= 14) {
448         // there are between 9 and 14 bits of the same value in a row
449         // Add two bits to the resulting array
450         outputBuffer[resultArray_index] = lastVal;
451         outputBuffer[resultArray_index+1] = lastVal;
452         resultArray_index += 2;
453       }
454       else if (inARow >= 15 && lastVal == 0) {
455         // there are more then 15 identical bits in a row, and they are 0s
456         // this is an end tag
457         break;
458       }
459       // group of bits was not the end tag, continue parsing data
460       inARow = 1;
461       lastVal = buffer[i];
462       if (resultArray_index >= 90) {
463         //return;
464       }
465     }
466   }
467 }
468 
469 /******************************* Analize Input *******************************
470 | analizeInput(void) parses through the global variable and gets the 45 bit   |
471 | id tag.                                                                     |
472 | 1) Converts raw pulse per wave count (5,6,7) to binary data (0,1)           |
473 | 2) Finds a start tag in the code                                            |
474 | 3) Parses the data from multibit code (11111000000000000111111111100000) to |
475 |     singlebit manchester code (100110) untill it finds an end tag           |
476 | 4) Converts manchester code (100110) to binary code (010)                   |
477 *****************************************************************************/
478 void analizeInput (void) {
479   int i;                // Generic for loop 'i' counter
480   int resultArray[90];  // Parsed Bit code in manchester
481   int finalArray[45];   //Parsed Bit Code out of manchester
482   int finalArray_index = 0;
483   
484   // Initilize the arrays so that any errors or unchanged values show up as 2s
485   for (i = 0; i < 90; i ++) { resultArray[i] = 2; }
486   for (i = 0; i < 45; i++)  { finalArray[i] = 2;  }
487   
488   // Convert raw data to binary
489   convertRawDataToBinary (begin);
490     
491   // Find Start Tag
492   int startOffset = findStartTag(begin);
493   PORTB |= 0x10; // turn an led on on pin B5)
494   
495   // Parse multibit data to single bit data
496   parseMultiBitToSingleBit(begin, startOffset, resultArray);
497   
498   // Error checking, see if there are any unset elements of the array
499   for (i = 0; i < 88; i++) { // ignore the parody bit ([88] and [89])
500     if (resultArray[i] == 2) {
501       return;
502     }
503   }
504   //------------------------------------------
505   // MANCHESTER DECODING
506   //------------------------------------------
507   for (i = 0; i < 88; i+=2) { // ignore the parody bit ([88][89])
508     if (resultArray[i] == 1 && resultArray[i+1] == 0) {
509       finalArray[finalArray_index] = 1;
510     }
511     else if (resultArray[i] == 0 && resultArray[i+1] == 1) {
512       finalArray[finalArray_index] = 0;
513     }
514     else {
515       // The read code is not in manchester, ignore this read tag and try again
516       // free the allocated memory and end the function
517       return;
518     }
519     finalArray_index++;
520   }
521   
522   #ifdef Binary_Tag_Output         // Outputs the Read tag in binary over serial
523     printBinary (finalArray);
524   #endif
525     
526   #ifdef Hexadecimal_Tag_Output    // Outputs the read tag in Hexadecimal over serial
527     printHexadecimal (finalArray);
528   #endif
529     
530   #ifdef Decimal_Tag_Output
531     printDecimal (finalArray);
532   #endif
533   
534   
535   #ifdef Whitelist_Enabled
536   if (searchTag(getDecimalFromBinary(finalArray+UNIQUE_ID_OFFSET,UNIQUE_ID_LENGTH))){
537     whiteListSuccess ();
538   }
539   else {
540     whiteListFailure();
541   }
542   #endif
543 }
544 
545 /******************************* MAIN FUNCTION *******************************
546 | This is the main function, it initilized the variabls and then waits for    |
547 | interrupt to fill the buffer before analizing the gathered data             |
548 *****************************************************************************/
549 int main (void) {
550   int i = 0;
551 
552   //------------------------------------------
553   // VARIABLE INITLILIZATION
554   //------------------------------------------
555 
556   // Load the list of valid ID tags
557   addNames(); 
558   
559   //==========> PIN INITILIZATION <==========//
560   DDRD = 0x00; // 00000000 configure output on port D
561   DDRB = 0x1E; // 00011100 configure output on port B
562   
563   //=========> SERVO INITILIZATION <=========//
564   ICR1 = 10000;// TOP count for the PWM TIMER
565   
566   // Set on match, clear on TOP
567   TCCR1A  = ((1 << COM1A1) | (1 << COM1A0));
568   TCCR1B  = ((1 << CS11) | (1 << WGM13));
569   
570   // Move the servo to close Position
571   OCR1A = 10000 - SERVO_CLOSE;
572   {
573     unsigned long j;
574     for (j = 0; j < 500000; j++) {
575       asm volatile ("nop");
576     }
577   }
578   // Set servo to idle
579   OCR1A = 0;
580   
581   // USART INITILIZATION
582   USART_Init();
583   
584   //========> VARIABLE INITILIZATION <=======//
585   count = 0;
586   begin = malloc (sizeof(char)*ARRAYSIZE);
587   iter = 0;
588   for (i = 0; i < ARRAYSIZE; i ++) {
589     begin[i] = 0;
590   }
591   
592   //=======> INTERRUPT INITILAIZATION <======//
593   sei ();       // enable global interrupts
594   EICRA = 0x03; // configure interupt INT0
595   EIMSK = 0x01; // enabe interrupt INT0
596   
597   //------------------------------------------
598   // MAIN LOOP
599   //------------------------------------------
600   while (1) {
601     sei(); //enable interrupts
602     
603     while (1) { // while the card is being read
604       if (iter >= ARRAYSIZE) { // if the buffer is full
605         cli(); // disable interrupts
606         break; // continue to analize the buffer
607       }
608     }  
609     
610     PORTB &= ~0x1C;
611     
612     //analize the array of input
613     analizeInput ();
614     
615     //reset the saved values to prevent errors when reading another card
616     count = 0;
617     iter = 0;
618     for (i = 0; i < ARRAYSIZE; i ++) {
619       begin[i] = 0;
620     }
621   }
622 }
View Code

 

There are two distinct cycle lengths in the detected input signal, "long" and "short",

corresponding to logical ones and zeroes, respectively.

A binary stream of stretches of repeated ones and zeroes is assembled,

and then decimated into the original coded bits on the RFID tag, after decoding the Manchester encoding.

Here is the actual code:

 1 /*  Arduino program for DIY FSK RFID Reader
  2  *  See description and circuit diagram at http://playground.arduino.cc/Main/DIYRFIDReader
  3  *  Tested on Arduino Nano and several FSK RFID tags
  4  *  Hardware/Software design is based on and derived from:
  5  *  Arduino/Timer1 library example
  6  *  June 2008 | jesse dot tane at gmail dot com
  7  *  AsherGlick: / AVRFID https://github.com/AsherGlick/AVRFID
  8  *  Micah Dowty: 
  9  *  http://forums.parallax.com/showthread.php?105889-World-s-simplest-RFID-reader
 10  *
 11  *  Copyright (C) 2011 by edude/Arduino Forum
 12 
 13  This program is free software: you can redistribute it and/or modify
 14  it under the terms of the GNU General Public License as published by
 15  the Free Software Foundation, either version 3 of the License, or
 16  (at your option) any later version.
 17 
 18  This program is distributed in the hope that it will be useful,
 19  but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  GNU General Public License for more details.
 22 
 23  You should have received a copy of the GNU General Public License
 24  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 25 
 26  */
 27 
 28 #include "TimerOne.h"
 29 
 30 int ledPin = 13; // LED connected to digital pin 13
 31 int inPin = 7;   // sensing digital pin 7
 32 int val;
 33 int bitlenctr = 0;
 34 int curState = 0;
 35 
 36 #define maxBuf 1000 //reduce to 100 or so for debugging
 37 #define debug  0
 38 
 39 char raw[maxBuf];
 40 
 41 int index = 0;
 42 int bufnum = 0;
 43 #define   redLED 12
 44 #define   grnLED 11 
 45 
 46 void setup()
 47 {
 48   Serial.begin(9600);
 49   Timer1.initialize(7);  // initialize timer1, and set the frequency; this drives both the LC tank as well as the pulse timing clock
 50   // note: modify this as needed to achieve resonance and good match with the desired tags
 51   // the argument value is in microseconds per RF cycle, so 8us will yield RF of 125kHz, 7us --> 143kHz, etc.
 52 
 53   Timer1.pwm(9, 512);           // setup pwm on pin 9, 50% duty cycle
 54   Timer1.attachInterrupt(callback);  // attaches callback() as a timer overflow interrupt, once per RF cycle
 55 
 56   pinMode(ledPin, OUTPUT);      // sets the digital pin 13 as output for scope monitoring
 57   pinMode(inPin, INPUT);      // sets the digital pin 7 as input to sense receiver input signal
 58   pinMode(grnLED, OUTPUT);
 59   pinMode(redLED, OUTPUT);
 60   digitalWrite(grnLED, 0);
 61   digitalWrite(redLED, 1);
 62 }
 63 
 64 void callback()
 65 {
 66   val = digitalRead(inPin);
 67   digitalWrite(ledPin, val); // for monitoring
 68   bitlenctr++;
 69   if(val != curState) {
 70     // got a transition
 71     curState = val;
 72     if(val == 1) {
 73       // got a start of cycle (low to high transition)
 74       if(index < maxBuf) {
 75         raw[index++] = bitlenctr;
 76       }
 77       bitlenctr = 1;
 78     }
 79   }  
 80 }
 81 
 82 void loop()
 83 {  
 84   if(index >= maxBuf) {
 85 
 86     Serial.print("got buf num: ");
 87     Serial.println(bufnum);
 88 
 89     if(debug) {
 90       for(int i = 0; i < maxBuf;
 91       i++) {
 92           Serial.print((int)raw[i]);
 93         Serial.print("/");
 94       }
 95       Serial.println("///raw data");
 96       delay(2000);
 97     }
 98 
 99     // analyze this buffer
100     // first convert pulse durations into raw bits
101     int tot1 = 0;
102     int tot0 = 0;
103     int tote = 0;
104     int totp = 0;
105     raw[0] = 0;
106     for(int i = 1; i < maxBuf; i++) {
107       int v = raw[i];
108       if(v == 4) {
109         raw[i] = 0;
110         tot0++;
111       } 
112       else if(v == 5) {
113         raw[i] = raw[i - 1];
114         totp++;
115       } 
116       else if(v == 6 || v == 7) {
117         raw[i] = 1;
118         tot1++;
119       } 
120       else {
121         raw[i] = 101; // error code
122         tote++;
123       }  
124     }   
125 
126     // next, search for a "start tag" of 15 high bits in a row
127     int samecnt = 0;
128     int start = -1;
129     int lastv = 0;
130     for(int i = 0; i < maxBuf; i++) {
131       if(raw[i] == lastv) {
132         // inside one same bit pattern, keep scanning
133         samecnt++;
134       } 
135       else {
136         // got new bit pattern
137         if(samecnt >= 15 && lastv == 1) {
138           // got a start tag prefix, record index and exit
139           start = i;
140           break;
141         }
142         // either group of 0s, or fewer than 15 1s, so not a valid tag, keep scanning
143         samecnt = 1;
144         lastv = raw[i];
145       }  
146     }
147 
148     // if a valid prefix tag was found, process the buffer
149     if(start > 0 && start < (maxBuf - 5*90)) { //adjust to allow room for full dataset past start point
150       process_buf(start);
151     } 
152     else {
153       Serial.println("no valid data found in buffer");
154     }
155     if(debug) {
156       for(int i = 0; i < maxBuf;
157         i++) {
158           Serial.print((int)raw[i]);
159         Serial.print("/");
160       }
161       Serial.print("///
buffer stats: zeroes:");
162       Serial.print(tot0); 
163       Serial.print("/ones:"); 
164       Serial.print(tot1);
165       Serial.print("/prevs:"); 
166       Serial.print(totp);
167       Serial.print("/errs:");
168       Serial.println(tote);  
169       delay(1000);
170     }
171 
172     // start new buffer, reset all parameters
173     bufnum++;
174     curState = 0;
175     index = 0;
176   } 
177   else {
178     delay(5);
179   }  
180 }
181 
182 // process an input buffer with a valid start tag
183 // start argument is index to first 0 bit past prefix tag of 15+ ones
184 void process_buf(int start) {
185   // first convert multi bit codes (11111100000...) into manchester bit codes
186   int lastv = 0;
187   int samecnt = 0;
188   char manch[91];
189   char final[45];
190   int manchindex = 0;
191 
192   Serial.println("got a valid prefix, processing data buffer...");
193   for(int i = start + 1; i < maxBuf && manchindex < 90; i++) {
194     if(raw[i] == lastv) {
195       samecnt++;
196     } 
197     else {
198       // got a new bit value, process the last group
199       if(samecnt >= 3 && samecnt <= 8) {
200         manch[manchindex++] = lastv;
201       } 
202       else if(samecnt >= 9 && samecnt <= 14) {
203         // assume a double bit, so record as two separate bits
204         manch[manchindex++] = lastv;
205         manch[manchindex++] = lastv;
206       } 
207       else if(samecnt >= 15 && lastv == 0) {
208         Serial.println("got end tag");
209         // got an end tag, exit
210         break;
211       } 
212       else {
213         // last bit group was either too long or too short
214         Serial.print("****got bad bit pattern in buffer, count: ");
215         Serial.print(samecnt);
216         Serial.print(", value: ");
217         Serial.println(lastv);
218         err_flash(3);
219         return;
220       }  
221       samecnt = 1;
222       lastv = raw[i];
223     } //new bit pattern
224   }
225 
226   Serial.println("converting manchester code to binary...");
227   // got manchester version, convert to final bits
228   for(int i = 0, findex = 0; i < 90; i += 2, findex++) {
229     if(manch[i] == 1 && manch[i+1] == 0) {
230       final[findex] = 1;
231     } 
232     else if(manch[i] == 0 && manch[i+1] == 1) {
233       final[findex] = 0;
234     } 
235     else {
236       // invalid manchester code, exit
237       Serial.println("****got invalid manchester code");
238       err_flash(3);
239       return;
240     }
241   }
242 
243   // convert bits 28 thru 28+16 into a 16 bit integer
244   int code = 0;
245   int par = 0;
246   for(int i = 28, k = 15; i < 28+16; i++, k--) {
247     code |= (int)final[i] << k;
248   }
249   int paritybit = final[28+16];
250   for(int i = 0; i < 45; i++) {
251     par ^= final[i];
252   }
253 
254   if(par) {  
255     Serial.print("got valid code: ");
256     Serial.println((unsigned int)code);
257     // do something here with the detected code...
258     // 
259     //
260     digitalWrite(redLED, 0);
261     digitalWrite(grnLED, 1);
262     delay(2000);
263     digitalWrite(grnLED, 0);
264     digitalWrite(redLED, 1);
265   } 
266   else {
267     Serial.println("****parity error for retrieved code");
268     err_flash(3);
269   }  
270 }
271 
272 // flash red for duration seconds
273 void err_flash(int duration) {
274   return;
275   for(int i = 0; i < duration*10; i++) {
276     digitalWrite(redLED, 0);
277     delay(50);
278     digitalWrite(redLED, 1);
279     delay(50);
280   } 
281 }
View Code

Status

The device and transceiver antenna have been built and tested on multiple FSK RFID tags of various kinds,

in breadboard and soldered perfboard versions, connected to remote and local probes.

When the probe is properly tuned, the device can reliably detect FSK RFID tags within a range of 0 to at least 2 inches from the coil,

although it may be possible that this can be extended with larger coil sizes and/or other optimizations.

The circuit has also been simulated on Spice, as described below.

As seen in the LTspiceIV screenshot above, the circuit (with a passive virtual ground reference - see note below)

was simulated on a computer, and the results confirmed the essential design,

closely replicating the waveforms actually seen on the oscilloscope.

The RFID transponder tag was simulated as a coupled transformer winding with a resonantly tuned capacitor,

shunted to ground by a square-wave signal.

The RFID tag's ground is connected to the main circuit's ground for simulation purposes.

The inductive coupling between the two "transformer windings" is a variable which can be changed in LTspice,

and was varied for testing between 1 and 0.01 (0.015 is shown in the waveforms above),

equivalent to having the RFID tag positioned at different distances from the reader coil.

Notes

The Vcc/2 virtual ground voltage for IC1's non-inverting input
can also be taken directly from the midpoint of the 100K voltage divider resistors,
bypassing the second opamp.
In such a case, the divider's midpoint should be connected to pin3 of IC1 via a 1M resistor.

Arduino RFID reader and pin current sourcing

http://electronics.stackexchange.com/questions/82374/arduino-rfid-reader-and-pin-current-sourcing

I'm thinking about building the DIY RFID reader described here

http://playground.arduino.cc/Main/DIYRFIDReader, with an arduino uno.

For the LC filter, I have a 330uH inductor at home

(and I'm using a 4.7nF capacitor for C1 instead of the 7nF shown on the schematic, to get at 125khz resonant frequency)

and also I'm using 1N4148 diodes instead of 1N914 (I couldn't find them on stock at the local electronics shop).

I did the simulation of the circuit in LT Spice (schematic is quite similar to what the original author has)

and it shows we that the current draw from the arduino pin D9 goes up to 400 mA,

from what I know the AVR can't source more than 50 mA per pin.

Here's the LTSpice circuit: 

And the simulation result: 

My question is: what will happen in reality when building the circuit,

will the pin source less current than the simulation and it will simply work

or I run the risk of damaging the pin by attempting to source that much current

(perhaps not instant damage, but long term) ?

If there is risk for damaging the pin, what solutions do I have to prevent it?

Would a simple current limiting resistor do?

Would it be better to use a push-pull configuration like the one below ?

Firstly, the 330uH inductor you already have is almost certainly not suitable

unless it is an air cored inductor wound on some kind of inert magnetic material

or an open ended ferrite rod of reasonably low permeability.

Check what it says on the circuit you linked to.

This coil has to transmit and receive so it needs to be likely a non-bought-in (and likely hand-wound) part.

D9 excessive current in simulation - you need to current limit the top circuit because it's driving a series resonant coil

and capacitor and this will act like a short circuit at resonance. Maybe try 100 ohms or a bit less.

In your alternative oscillator circuit you've got Q3 upside down in your simulation of the RFID transmitter -

that will cause excessive base currents through Q3 and may easily account for the excessive current from IO D9.

Your tag circuit might also benefit from a 100 ohm in series with the collector of Q1 too.

What does the waveform look like when the tag isn't responding - does it still show amplitude modulation issues? 

You are right, the original circuit suggests a hand-wound part, I thought I could use the 330uH filter inductor ...

It's not clear to me exactly what the difference would be between the inductor I have and a hand-wound inductor.

Would a 220 ohm resistor in series with D9 (not shown in the original circuit) +

the hand-wound inductor (as described in the original circuit) work?

As for the alternative circuit, you are right about Q3 (I did the picture in a hurry, next time I'll pay more attention).

SIMPLEST RFID READER?

http://scanlime.org/2008/08/simplest-rfid-reader/

That’s a Propeller microcontroller board with a few resistors and capacitors on it.

Just add a coil of wire, and you have an RFID reader.

Here’s a picture of it scanning my corporate ID badge,

and displaying the badge’s 512 bits of content on a portable TV screen:

I’ve been interested in building an RFID reader for a crazy project a friend of mine is working on

(automated beer dispensing system  and it was an excuse to do some analog tinkering,

so I had a go at building an RFID reader with the Propeller.

Why not use Parallax’s fine RFID reader accessory?

I wanted to be able to read any off-the-shelf card, including common proximity ID badges.

I just got it working. I’d love to be proved wrong,

but as far as I know this is the world’s simplest RFID reader design

All capacitances are in picofarads. C1 and C3 should be 1000 pF, not 1000 nF. Likewise, C2 is 2200 pF.

Yep.. just a propeller and a few passive components.

To understand this circuit, it’s helpful to know how RFID works first.

Here’s the 10-second RFID primer for those who aren’t already familiar with it:

RFID tags work via magnetic fields, like a transformer.

A coil in the reader generates a magnetic carrier wave, which is picked up by a coil in the tag or card.

This carrier wave powers the card, and provides it with a clock reference.

To send data, the card varies the amount of current it draws from the reader’s field,

attenuating the carrier wave slightly.

This attenuation is usually used to send some kind of modulated signal.

The card I’ve been testing with uses a 125 kHz carrier wave,

and FSK (Frequency Shift Keyed) modulation.

Zeroes and ones are attenuation waveforms with different frequencies.

So, step one: generating a carrier wave with the propeller.

I use a counter, naturally, and I generate a differential-drive signal on two pins.

This gives me 125 kHz at about 6.6V peak-to-peak.

I use an LC tank tuned near 125 kHz to amplify this and shape it into a sine wave.

Here’s my carrier wave, measured at the junction between L1 and C1.

Note the scale- it’s about 20 volts peak-to-peak! (Also notice that my tuning isn’t quite perfect. Sadness and despair!)

When you bring a proximity card near the reader coil, you can see the attenuation waveform overlaying the carrier wave.

This is at the same voltage scale, measured at the same point, but I zoomed out on the X axis so you can see the pattern clearly:

To detect this signal, I use D1, C2, and R1 as a peak detector and low-pass filter,

to remove the majority of the high-voltage carrier wave.

This is then AC coupled via C3.

At this point, the left side of C3 has a “floating” low-voltage waveform

that I can position anywhere I want, by changing the bias on C3.

To generate this bias voltage, I use CTRB in DUTY mode.

I’ll explain why I call this pin “threshold” later.

R4 and C4 are a low-pass filter give me an analog output voltage from CTRB.

This signal is usually DC- it’s used for calibration.

R3 “pulls” the floating waveform toward the analog value output by CTRB.

Now I take advantage of the Propeller’s Vdd/2 input threshold, and use the “input” pin as a comparator.

(R2 is just to limit high-frequency noise.)

Now I can use the CTRB voltage to adjust the detection threshold- how much attenuation causes “input” to read 0 vs 1.

Now I have a digital signal, but it’s still really noisy.

I use an assembly-language cog to reject as much noise as possible,

and time the resulting FSK pulses.

This image shows the analog signal at the junction between R2 and C3, along with the digital pulse detector’s output.

The amount of time between pulses signifies a “1″ or “0″ bit.

In this case, the detection threshold is at 9 carrier wave cycles.

Less than 9, it’s a 0. More than 9, it’s a 1 bit.

End result: I can display the 512 bits of data from my corporate proximity badge. Neat.

I’d include more details on the actual data I’ve captured and the protocol,

but so far I’ve only been able to test this with a couple ID badges from the office and I’d rather not share those bits 

I have a Parallax RFID starter kit on order, so I’ll let you know if I can read any of those tags successfully.

Source code is attached, but be warned it’s hugely messy.

Update: The latest code and schematics are now have a home in the Object Exchange:

http://obex.parallax.com/object/549.

1 {                                   
  2 
  3 rfid-lf - Minimalist software-only reader for low-frequency RFID tags
  4 ─────────────────────────────────────────────────────────────────────
  5 
  6 I. Supported Tags
  7 
  8   Tested with the EM4102 compatible RFID tags sold by Parallax, and with
  9   HID proximity cards typically issued for door security.
 10 
 11   These are two fairly typical ASK and FSK tags, respectively. To support
 12   other RFID protocols, it should be possible to use one of these two
 13   decoders as a starting point.
 14 
 15 I. Theory of operation
 16 
 17   The Propeller itself generates the 125 kHz carrier wave, which excites
 18   an LC tank. The inductor in this tank is an antenna coil, which becomes
 19   electromagnetically coupled to the RFID tag when it comes into range.
 20   This carrier powers the RFID tag. The RFID sends back data by modulating
 21   the amplitude of this carrier. We detect this modulation via a peak
 22   detector and low-pass filter (D1, C2, R1). The detected signal is AC
 23   coupled via C3, and an adjustable bias is applied via R3 from a PWM signal
 24   which is filtered by R4 and C4. This signal is fed directly to an input
 25   pin via R2 (which limits high-frequency noise) where the pin itself acts
 26   as an analog comparator.
 27 
 28   The waveform which exits the peak detector is a sequence of narrow spikes
 29   at each oscillation of the carrier wave. Each of these spikes occurs when
 30   C2 charges quickly via D1, then discharges slowly via R1. We can use this
 31   to fashion a simple A/D converter by having the Propeller use a counter to
 32   measure the duty cycle of this waveform. The higher the carrier amplitude,
 33   the more charge was stored in C2, and the longer the pulse will be.
 34 
 35   At this point, we have a digital representation of the baseband RF signal
 36   from the RFID tags. This signal is then put through a bank of demodulators,
 37   filters, and protocol decoders.
 38   
 39   We include multiple closed-loop control systems in order to keep the reader
 40   calibrated. We adjust the Threshold (THR) voltage dynamically in order to
 41   keep the duty cycle at a reasonable level so we don't over-saturate our
 42   A/D converter. We also automatically tweak the carrier frequency in order
 43   to keep the LC tank operating at its resonant peak.
 44 
 45   This object requires two cogs, mostly just because it needs three counter
 46   units: One to generate the carrier, one to measure incoming pulses, and one
 47   to generate the threshold voltage.
 48 
 49 II. Schematic
 50 
 51                 (P5) IN º─┐    (P1) C+ ©──┐    
 52                           │               │    
 53                        R2 ¼               ¶ L1 
 54                R4         │  C3       D1  │    
 55    (P3) THR ©──½¾──┳──½¾──┻──«──┳──┳──¦§──┫    
 56                    │  R3        │  │      │    
 57                 C4¬­®        R1 ¼ ¬­®C2  ¬­®C1 
 58                                        │    
 59                                           │    
 60                                (P0) C- ©──┘    
 61                    
 62   C1     1.5 nF
 63   C2     2.2 nF
 64   C3     1 nF
 65   C4     2.2 nF
 66   R1     1 MΩ
 67   R2     270 Ω
 68   R4     100 kΩ
 69   R3     220 kΩ
 70   D1     Some garden variety sigal diode from my junk drawer.
 71   L1     About 1 mH. Tune for 125 kHz resonance for C1.
 72  
 73   Optional parts:
 74 
 75    - An amplifier for the carrier wave. Ideally it would be a very high
 76      slew rate op-amp or buffer which could convert the 3.3v signal from
 77      the Prop into a higher-voltage square wave for exciting the LC tank.
 78   
 79      I've tried a MAX233A, but it was unsuccessful due to the low slew rate
 80      which caused excessive harmonic distortion. The best option is probably
 81      a high voltage H-bridge, but I haven't tested this yet.
 82 
 83    - An external comparator for the input value. This makes the circuit
 84      easier to debug, and it helps if you're in a noisy electrical environment.
 85 
 86      Hook the inverting input up to a voltage divider that generates a Vdd/2
 87      reference, and the non-inverting input up to the junction between C3 and R3.
 88      I've tested this with a MAX473 op-amp.
 89         
 90 III. License
 91        
 92  ┌───────────────────────────────────┐
 93  │ Copyright (c) 2008 Micah Dowty    │               
 94  │ See end of file for terms of use. │
 95  └───────────────────────────────────┘
 96 
 97 }
 98 
 99 CON
100   CARRIER_HZ         = 125_000      ' Initial carrier frequency
101   DEFAULT_THRESHOLD  = $80000000    ' Initial comparator frequency
102   THRESHOLD_GAIN     = 7            ' Log2 gain for threshold control loop
103   CARRIER_RATE       = 7            ' Inverse Log2 rate for carrier frequency control loop
104   CARRIER_GAIN       = 300          ' Gain for carrier frequency control loop  
105 
106   SHIELD2_PIN        = 6            ' Optional, driven to ground to shield INPUT.
107   INPUT_PIN          = 5            ' Input signal
108   SHIELD1_PIN        = 4            ' Optional, driven to ground to shield INPUT.
109   THRESHOLD_PIN      = 3            ' PWM output for detection threshold
110   DEBUG_PIN          = 2            ' Optional, for oscilloscope debugging
111   CARRIER_POS_PIN    = 1            ' Carrier wave pin
112   CARRIER_NEG_PIN    = 0            ' Carrier wave pin
113 
114   ' Code formats.
115   ' The low 16 bits indicate length, in longs.
116   ' Other bits are used to make each format unique.
117 
118   FORMAT_EM4102      = $0001_0002   ' Two longs: 8-bit manufacturer code, 32-bit unique ID.
119   FORMAT_HID         = $0002_0002   ' HID 128 KHz prox cards. 45-bit code.
120   
121 VAR
122   byte cog1, cog2
123   long format_buf
124   long shared_pulse_len
125   long em_buffer[2]
126   long hid_buffer[2]
127   
128 PUB start | period, okay
129 
130   ' Fundamental timing parameters: Default carrier wave
131   ' drive frequency, and the period of the carrier in clock
132   ' cycles.
133 
134   init_frqa := fraction(CARRIER_HZ, clkfreq)
135   period := clkfreq / CARRIER_HZ
136 
137   ' Derived timing parameters
138   
139   pulse_target := period / 3      ' What is our 'center' pulse width?
140   next_hyst := pulse_target
141   hyst_constant := period / 100   ' Amount of pulse hysteresis
142 
143   ' Output buffers
144   em_buf_ptr := @em_buffer
145   hid_buf_ptr := @hid_buffer
146   format_ptr := @format_buf
147   pulse_ptr1 := @shared_pulse_len
148   pulse_ptr2 := @shared_pulse_len
149   format_buf~
150   
151   cog1 := cognew(@cog1_entry, 0) + 1
152   okay := cog2 := cognew(@cog2_entry, 0) + 1
153 
154   
155 PUB stop
156   if cog2
157     cogstop(cog1~ - 1)
158     cogstop(cog2~ - 1)
159   
160 PUB read(buffer) : format
161   '' Read an RFID code, if one is available.
162   ''
163   '' If a code is available, it is copied into 'buffer', and we return
164   '' a FORMAT_* constant that identifies the code's format. If no code
165   '' is available, returns zero.
166   ''
167   '' The format code's low 16 bits indicate the length of the received
168   '' RFID code, in longs. The other format bits are used to uniquely
169   '' identify each format.
170   ''
171   '' The buffer must be at least 16 longs, to hold the largest code format.
172 
173   format := format_buf
174 
175   if format == FORMAT_EM4102
176     longmove(buffer, @em_buffer, constant(FORMAT_EM4102 & $FFFF))
177 
178   if format == FORMAT_HID
179     longmove(buffer, @hid_buffer, constant(FORMAT_HID & $FFFF))
180 
181   format_buf~
182   
183 PRI fraction(a, b) : f
184   a <<= 1
185   repeat 32
186     f <<= 1
187     if a => b
188       a -= b
189       f++
190     a <<= 1
191 
192 DAT
193 
194 '==============================================================================
195 ' Cog 1: Pulse timing
196 '==============================================================================
197 
198                         org
199 
200 cog1_entry
201 
202                         '
203                         ' Measure a low pulse.
204                         '
205                         ' The length of this pulse is proportional to the amplitude of the carrier.
206                         ' To measure it robustly, we'll measure the average duty cycle rather than
207                         ' looking at actual rising or falling edges.
208                         '
209                         ' We could easily do this in cog2, but we're out of counters there. Measure
210                         ' the pulse length using this cog's CTRA, and send it back to cog2 synchronously.
211                         '                        
212 
213                         mov     ctra, :ctra_value
214                         mov     frqa, #1
215 
216 :loop                   mov     t0, phsa
217                         wrlong  t0, pulse_ptr1
218 :wait                   rdlong  t0, pulse_ptr1 wz
219               if_z      jmp     #:loop
220                         jmp     #:wait
221 
222 :ctra_value             long    (%01100 << 26) | INPUT_PIN
223 pulse_ptr1              long    0
224 t0                      res     1
225                         
226                         fit
227 
228 
229 DAT
230 
231 '==============================================================================
232 ' Cog 2: Control loops and protocol decoding
233 '==============================================================================
234 
235                         '======================================================
236                         ' Initialization
237                         '======================================================
238 
239                         org
240 
241 cog2_entry              mov     dira, init_dira
242                         mov     ctra, init_ctra         ' CTRA generates the carrier wave
243                         mov     frqa, init_frqa
244                         mov     ctrb, init_ctrb         ' CTRB generates a pulse threshold bias
245                         mov     frqb, init_frqb
246 
247 
248                         '======================================================
249                         ' Main A/D loop
250                         '======================================================
251 
252 mainLoop
253                         '
254                         ' Synchronize each loop iteration with a rising edge on
255                         ' the carrier wave. To avoid races when reading pulse_ptr,
256                         ' we should ideally synchronize at a point 180 degrees
257                         ' out of phase with the negative pulse from our analog peak
258                         ' detector.
259                         '
260 
261                         mov     prev_pulse, cur_pulse   ' Remember previous pulse info 
262 
263 :high                   test    h80000000, phsa wz
264               if_z      jmp     #:high
265 :low                    test    h80000000, phsa wz      
266               if_nz     jmp     #:low
267 
268                         rdlong  cur_pulse, pulse_ptr2   ' Fetch pulse count from cog1
269                         wrlong  zero, pulse_ptr2
270                         
271                         mov     pulse_len, cur_pulse    ' Measure length of pulse
272                         sub     pulse_len, prev_pulse
273                         
274                         add     pulse_count, #1         ' Global pulse counter, used below
275 
276                         '
277                         ' Adjust the comparator threshold in order to achieve our pulse_target,
278                         ' using a linear proportional control loop.
279                         '
280 
281                         mov     r0, pulse_target
282                         sub     r0, pulse_len
283                         shl     r0, #THRESHOLD_GAIN
284                         sub     frqb, r0
285 
286                         '
287                         ' We also want to dynamically tweak the carrier frequency, in order
288                         ' to hit the resonance of our LC tank as closely as possible. The
289                         ' value of frqb is actually a filtered representation of our overall
290                         ' inverse carrier amplitude, so we want to adjust frqa in order to
291                         ' minimize frqb.
292                         '
293                         ' Since we can't adjust frqa drastically while the RFID reader is
294                         ' operating, we'll make one small adjustment at a time, and decide
295                         ' whether or not it was an improvement. This process eventually converges
296                         ' on the correct resonant frequency, so it should be enough to keep our
297                         ' circuit tuned as the analog components fluctuate slightly in value
298                         ' due to temperature variations.
299                         '
300                         ' This algorithm is divided into four phases, sequenced using two
301                         ' bits from the pulse_count counter:
302                         '
303                         '   0. Store a reference frqb value, and increase frqa
304                         '   1. Test the frqb value. If it wasn't an improvement, decrease frqa
305                         '   2. Store a reference frqb value, and decrease frqa
306                         '   3. Test the frqb value. If it wasn't an improvement, increase frqa
307                         '
308 
309                         test    pulse_count, carrier_mask wz
310         if_nz           jmp     #:skip_frqa
311                         test    pulse_count, carrier_bit0 wz
312                         test    pulse_count, carrier_bit1 wc
313                         negc    r0, #CARRIER_GAIN
314         if_nz           mov     prev_frqb, frqb
315         if_nz           add     frqa, r0
316         if_z            cmp     prev_frqb, frqb wc
317         if_z_and_c      sub     frqa, r0        
318 :skip_frqa
319 
320                         '
321                         ' That takes care of all our automatic calibration tasks.. now to
322                         ' receive some actual bits. Since our pulse length is proportional
323                         ' to the amount of carrier attenuation, our demodulated bits (or
324                         ' baseband FSK signal) are determined by the amount of pulse width
325                         ' excursion from our center position.
326                         '
327                         ' We don't need to measure the center, since we're actively balancing
328                         ' our pulses around pulse_target. A simple bit detector would just
329                         ' compare pulse_len to pulse_target. We go one step further, and
330                         ' include a little hysteresis.
331                         '
332 
333                         cmp     next_hyst, pulse_len wc    
334                         muxc    outa, #|<DEBUG_PIN         ' Output demodulated bit to debug pin
335 
336                         mov     next_hyst, pulse_target    ' Update hysteresis for the next bit
337                         sumc    next_hyst, hyst_constant
338 
339               if_nc     add     baseband_s32, #1
340               if_nc     add     baseband_s256, #1
341                         rcl     baseband_reg+0, #1 wc      ' Store in our baseband shift register
342               if_nc     sub     baseband_s32, #1           '   ... and keep a running total of the bits.              
343                         rcl     baseband_reg+1, #1 wc
344                         rcl     baseband_reg+2, #1 wc
345                         rcl     baseband_reg+3, #1 wc
346                         rcl     baseband_reg+4, #1 wc
347                         rcl     baseband_reg+5, #1 wc
348                         rcl     baseband_reg+6, #1 wc
349                         rcl     baseband_reg+7, #1 wc
350               if_nc     sub     baseband_s256, #1              
351 
352                         '
353                         ' Our work here is done. Give each card-specific protocol decoder
354                         ' a chance to find actual ones and zeroes.
355                         '
356                         
357                         call    #rx_hid
358                         call    #rx_em4102
359 
360                         jmp     #mainLoop
361 
362 
363                         '======================================================
364                         ' EM4102 Decoder 
365                         '======================================================
366 
367                         ' The EM4102 chip actually supports multiple clock rates
368                         ' and multiple encoding schemes: Manchester, Biphase, and PSK.
369                         ' Their "standard" scheme, and the one Parallax uses, is
370                         ' ASK with Manchester encoding at 32 clocks per code (64
371                         ' clocks per bit). Our support for this format is hardcoded.
372                         '
373                         ' The EM4102's data packet consists of 40 payload bits (an 8-bit
374                         ' manufacturer ID and 32-bit serial number), 9 header bits, 1 stop
375                         ' bit, and 14 parity bits. This is a total of 64 bits. These bits
376                         ' are manchester encoded into 128 baseband bits. 
377                         '
378                         ' We could decode this the traditional way- do clock/data recovery
379                         ' on the Manchester signal using a DPLL, look for the header, do
380                         ' manchester decoding on the rest of the packet, etc. But this is
381                         ' software, and we can throw memory and CPU at the problem in order
382                         ' to get a more noise-resistant decoding.
383                         '
384                         ' A packet in its entirety is 4096 clocks. This is 128 manchester
385                         ' codes by 32 clocks per code. We can treat this as 32 possible phases
386                         ' and 128 possible code alignments. In fact, it's a more convenient
387                         ' to treat it as 64 possible phases and 64 possible bits. We get the
388                         ' same result, and it's less data to move around.
389                         '
390                         ' To save memory, we'll decimate the signal and examine it only every
391                         ' other carrier cycle. This gives us only 32 possible phases.
392                         '
393                         ' Every time a code arrives, we shift it into a 64-bit shift register.
394                         ' We have 32 total shift registers, which we cycle through. Every time
395                         ' we shift in a new bit, we examine the entire shift register and test
396                         ' whether it's a valid packet.
397                         
398 rx_em4102
399                         test    pulse_count, #1 wz      ' Decimate x2 (Opposite phase from the HID decoder)
400               if_nz     jmp     #rx_em4102_ret
401 
402                         ' Low pass filter with automatic gain control. Look at an average of the last
403                         ' 32 bits, and correct our duty cycle to 50% by picking a threshold based on
404                         ' the average of the last 256 bits.
405 
406                         mov     r0, baseband_s256
407                         shr     r0, #3
408                         cmp     baseband_s32, r0 wc
409 
410 :shift1                 rcl     em_bits+1, #1 wc        ' Shift in the new filtered bit
411 :shift2                 rcl     em_bits+0, #1
412 :load1                  mov     em_shift+0, em_bits+0   ' And save a copy in a static location
413 :load2                  mov     em_shift+1, em_bits+1
414 
415                         add     :shift1, dest_2         ' Increment em_bits pointers
416                         add     :shift2, dest_2
417                         add     :load1, #2
418                         add     :load2, #2
419                         cmp     :shift1, em_shift1_end wz
420               if_z      sub     :shift1, dest_64        ' Wrap around     
421               if_z      sub     :shift2, dest_64
422               if_z      sub     :load1, #64     
423               if_z      sub     :load2, #64    
424                         
425                         rdlong  r0, format_ptr wz       ' Make sure the output buffer is available
426               if_nz     jmp     #rx_em4102_ret
427               
428                         '
429                         ' At this point, the encoded packet should have the following format:
430                         ' (Even bits in the manchester code are not shown.)
431                         '
432                         '   bits+0:  11111111_1ddddPdd_ddPddddP_ddddPddd                        
433                         '   bits+1:  dPddddPd_dddPdddd_PddddPdd_ddPPPPP0
434                         '
435                         ' Where 'd' is a data bit and 'P' is a parity bit.
436                         '
437 
438                         mov     r0, em_shift+0          ' Look for the header of nine "1" bits
439                         shr     r0, #32-9
440                         cmp     r0, #$1FF wz
441               if_nz     jmp     #rx_em4102_ret
442 
443                         rcr     em_shift+1, #1 nr,wc    ' Look for a footer of one "0"
444               if_c      jmp     #rx_em4102_ret
445 
446                         ' Looking good so far. Now loop over the 10 data rows...
447 
448                         mov     em_decoded, #0
449                         mov     em_decoded+1, #0
450                         mov     em_parity, #0
451                         mov     r0, #10
452 :row
453                         mov     r2, em_shift+0          ' Extract the next row's 5 bits
454                         shr     r2, #18
455                         and     r2, #%11111 wc          ' Check row parity
456               if_c      jmp     #rx_em4102_ret
457 
458                         mov     r1, em_decoded+1        ' 64-bit left shift by 4
459                         shl     em_decoded+1, #4
460                         shl     em_decoded+0, #4
461                         shr     r1, #32-4
462                         or      em_decoded+0, r1 
463                         
464                         shr     r2, #1                  ' Drop row parity bit
465                         xor     em_parity, r2           ' Update column parity
466                         or      em_decoded+1, r2        ' Store 4 decoded bits
467 
468                         mov     r1, em_shift+1          ' 64-bit left shift by 5
469                         shl     em_shift+1, #5
470                         shl     em_shift+0, #5
471                         shr     r1, #32-5
472                         or      em_shift+0, r1 
473 
474                         djnz    r0, #:row
475 
476                         mov     r2, em_shift+0          ' Extract the next 4 bits
477                         shr     r2, #19
478                         and     r2, #%1111
479                         xor     em_parity, r2 wc        ' Test column parity
480               if_c      jmp     #rx_em4102_ret
481 
482                         mov     r0, em_buf_ptr          ' Write the result
483                         wrlong  em_decoded+0, em_buf_ptr
484                         add     r0, #4
485                         wrlong  em_decoded+1, r0
486                         wrlong  c_format_em, format_ptr
487                         
488 rx_em4102_ret           ret
489 
490 em_shift1_end           rcl     em_bits+1+64, #1 wc     ' This is ":shift1" above, after we've
491                                                         ' passed the end of the em_bits array.
492 
493 
494                         '======================================================
495                         ' HID Decoder
496                         '======================================================
497 
498                         ' This is a data decoder for HID's 125 kHz access control cards.
499                         ' I don't have any actual documentation from HID- this is all
500                         ' gleaned from public documentation of other RFID systems, and
501                         ' from my own reverse engineering.
502                         '
503                         ' These cards use a FSK scheme, which appears to be identical
504                         ' to the one used by the Microchip MCRF200. Zeroes and ones are
505                         ' nominally encoded by attenuation pulses of 8 and 10 cycles,
506                         ' respectively. Bits appear to last 50 RF cycles, which is one
507                         ' of the configurable bit lengths for the MCRF200.
508                         '
509                         ' See Figure 2-2 of the Microchip microID 125 kHz RFID System Design Guide:
510                         '   http://ww1.microchip.com/downloads/en/devicedoc/51115F.pdf
511                         '
512                         ' So, to decode this signal we use a similar low-pass filter and shift
513                         ' register technique as the one we used above in the EM4102 decoder,
514                         ' but only after doing an FSK detection stage.
515                         '
516                         ' After FSK detection and low-pass filtering, the signal appears to
517                         ' be a repeating pattern of 96 of these 50-cycle bits. Each repetition
518                         ' begins with the special sequence '000111'. The packet data is manchester
519                         ' encoded, so runs of three zeroes or ones cannot occur during the packet body.
520                         ' This means there should be 45 bits of actual packet data after manchester
521                         ' decoding.
522                         '
523                         ' XXX: There is almost certainly another level of encoding present,
524                         '      since HID claims that these cards have either a 26-bit or 34-bit
525                         '      code. It's likely that there is an error correcting/detecting code
526                         '      embedded within the 45-bit signal we're receiving here.
527 
528 rx_hid
529                         ' This is the FSK decoder. We shift zeroes into hid_fsk_reg on every
530                         ' carrier wave cycle. When we detect a stable rising edge on the baseband,
531                         ' the whole hid_fsk_reg is inverted. At this point, the most recent FSK
532                         ' cycle will be all ones, and the previous cycle will be all zeroes.
533                         '
534                         ' On each stable rising edge, we can detect a 1 or a 0 by tapping the
535                         ' hid_fsk_reg. The location of the tap determines our threshold frequency.
536                         ' This detected bit is latched until the next rising edge.
537 
538                         shl     hid_fsk_reg, #1         ' Advance the FSK detection shifter
539 
540                         mov     r0, baseband_reg        ' Detect rising edges
541                         and     r0, #%1111
542                         cmp     r0, #%0001 wz           ' If Z=1, it's an edge.
543 
544               if_z      xor     hid_fsk_reg, hFFFFFFFF  ' Invert on every edge
545 
546               if_nz     rcr     hid_lp_reg+0, #1 nr,wc  ' Repeat the last bit, or tap a new bit.
547               if_z      test    hid_fsk_reg, hid_fsk_mask wc
548 
549                         ' Now the detected FSK bit is in the carry flag. Feed it into
550                         ' a 32-bit-wide low pass filter, which is close enough to our
551                         ' bit length of 50 cycles. We correct the 32-bit filter's DC bias
552                         ' using a 256-bit filter. This helps us maintain an even duty cycle
553                         ' in the received manchester bits.
554 
555               if_nc     add     hid_lp_s32, #1
556               if_nc     add     hid_lp_s256, #1
557                         rcl     hid_lp_reg+0, #1 wc
558               if_nc     sub     hid_lp_s32, #1              
559                         rcl     hid_lp_reg+1, #1 wc
560                         rcl     hid_lp_reg+2, #1 wc
561                         rcl     hid_lp_reg+3, #1 wc
562                         rcl     hid_lp_reg+4, #1 wc
563                         rcl     hid_lp_reg+5, #1 wc
564                         rcl     hid_lp_reg+6, #1 wc
565                         rcl     hid_lp_reg+7, #1 wc
566               if_nc     sub     hid_lp_s256, #1              
567 
568                         mov     r0, hid_lp_s256
569                         shr     r0, #3
570                         cmp     hid_lp_s32, r0 wc
571                         
572                         ' Now the carry flag holds a filtered version of the post-FSK-detection
573                         ' signal. These are manchester-encoded bits. We need to detect the header,
574                         ' validate the manchester encoding, and extract the actual data bits
575                         ' from the received signal. This is a typical data/clock recovery problem.
576                         ' We could use a PLL, but when doing this in software it's easier and less
577                         ' error-prone to just brute-force it, and try to detect a signal at all
578                         ' possible phases.
579                         '
580                         ' At this point there are 100 possible phases, and we need enough room to
581                         ' store 51 bits. (45 data bits + 3 bits each from 2 headers) We'll use a
582                         ' 64-bit shift register for each phase.
583                         '
584                         ' To cut down on the amount of memory required, we'll decimate the signal
585                         ' by processing on only every other carrier cycle. This means we only have
586                         ' 50 possible phases.
587 
588                         test    pulse_count, #1 wz      ' Decimate x2 (Opposite phase from the EM4102 decoder)
589               if_z      jmp     #rx_hid_ret
590 
591 :shift1                 rcl     hid_bits+1, #1 wc       ' Shift in the new filtered bit
592 :shift2                 rcl     hid_bits+0, #1
593 :load1                  mov     hid_shift+0, hid_bits+0 ' And save a copy in a static location
594 :load2                  mov     hid_shift+1, hid_bits+1
595 
596                         add     :shift1, dest_2         ' Increment hid_bits pointers
597                         add     :shift2, dest_2
598                         add     :load1, #2
599                         add     :load2, #2
600                         cmp     :shift1, hid_shift1_end wz
601               if_z      sub     :shift1, dest_100       ' Wrap around     
602               if_z      sub     :shift2, dest_100
603               if_z      sub     :load1, #100     
604               if_z      sub     :load2, #100     
605 
606                         ' Also save a 180-degree phase shifted version of hid_shift,
607                         ' for doing manchester validation and header detection.
608                         ' The bits in hid_shiftp are delayed by 1/2 bit relative to
609                         ' hid_shift.                        
610 
611 :load3                  mov     hid_shiftp+0, hid_bits+0+50
612 :load4                  mov     hid_shiftp+1, hid_bits+1+50
613                         add     :load3, #2
614                         add     :load4, #2
615                         cmp     :load3, hid_load3_end wz
616               if_z      sub     :load3, #100     
617               if_z      sub     :load4, #100
618 
619                         or      dira, #|<7
620                         test    hid_shift+1, #1 wz
621                         muxz    outa, #|<7
622 
623                         rdlong  r0, format_ptr wz       ' Make sure the output buffer is available
624               if_nz     jmp     #rx_hid_ret
625 
626                         '
627                         ' Ok. At this point, we can validate the contents of hid_shift and
628                         ' hid_shiftp, and extract the packet data if possible. hid_shiftp
629                         ' has our actual packet data, and em_shift has the complement of that
630                         ' data. The header sequence of "000111" turns into "001" in hid_shift
631                         ' and "011" in hid_shiftp.
632                         '
633                         ' To provide further validation, we look for both the current packet's
634                         ' header and the next packet's header. So, the data bits are actually
635                         ' in bits 3 through 47.
636                         '
637 
638                         mov     r0, hid_shiftp+0        ' Check 1st header at -180°
639                         and     r0, hid_s_mask
640                         cmp     r0, hid_sp_check wz
641               if_z      mov     r0, hid_shift+0         ' Check 1st header at 0°
642               if_z      and     r0, hid_s_mask
643               if_z      cmp     r0, hid_s_check wz
644 
645               if_z      mov     r0, hid_shiftp+1        ' Check 2nd header at -180°
646               if_z      and     r0, #%111
647               if_z      cmp     r0, #%001 wz
648               if_z      mov     r0, hid_shift+1         ' Check 2nd header at 0°
649               if_z      and     r0, #%111
650               if_z      cmp     r0, #%011 wz
651 
652               if_z      mov     r0, hid_shiftp+0        ' Check Manchester encoding
653               if_z      xor     r0, hid_shift+0         '   (high long)
654               if_z      xor     r0, hFFFFFFFF
655               if_z      and     r0, hid_data_mask wz              
656               if_z      mov     r0, hid_shiftp+1        '   (low long)
657               if_z      xor     r0, hid_shift+1
658               if_z      xor     r0, hFFFFFFFF
659               if_z      andn    r0, #%111 wz
660 
661               if_nz     jmp     #rx_hid_ret             ' Exit on any decoding error
662 
663                         ' Hooray, we have a correct-looking packet. Justify and
664                         ' trim the 45 bits of data, and store it.
665                         
666 
667                         mov     r0, hid_shiftp+0                         
668                         mov     r1, hid_shiftp+1
669 
670                         and     r0, hid_data_mask       ' Cut off 1st header and unused bits   
671 
672                         shr     r0, #1 wc               ' Cut off 2nd header
673                         rcr     r1, #1 wc                         
674                         shr     r0, #1 wc
675                         rcr     r1, #1 wc                         
676                         shr     r0, #1 wc
677                         rcr     r1, #1 wc
678 
679                         mov     r2, hid_buf_ptr         ' Write the result
680                         wrlong  r0, r2
681                         add     r2, #4
682                         wrlong  r1, r2
683                         wrlong  c_format_hid, format_ptr
684                         
685 rx_hid_ret              ret
686 
687 hid_shift1_end          rcl     hid_bits+1+100, #1 wc   ' This is ":shift1" above, after we've
688                                                         ' passed the end of the hid_bits array.
689                         
690 hid_load3_end           mov     hid_shiftP+0, hid_bits+0+100
691 
692 
693 '------------------------------------------------------------------------------
694 ' Initialized Data
695 '------------------------------------------------------------------------------
696 
697 hFFFFFFFF     long      $FFFFFFFF
698 h80000000     long      $80000000
699 h7FFF0000     long      $7FFF0000
700 hFFFF         long      $FFFF
701 zero          long      0
702 c_format_em   long      FORMAT_EM4102
703 c_format_hid  long      FORMAT_HID
704 
705 dest_2        long      2 << 9
706 dest_100      long      100 << 9
707 dest_64       long      64 << 9
708 input_mask2   long      |<INPUT_PIN
709 
710 init_dira     long      |<CARRIER_POS_PIN | |<CARRIER_NEG_PIN | |<THRESHOLD_PIN | |<DEBUG_PIN | |<SHIELD1_PIN | |<SHIELD2_PIN
711 init_frqa     long      0
712 init_frqb     long      DEFAULT_THRESHOLD
713 init_ctra     long      (%00101 << 26) | (CARRIER_POS_PIN << 9) | CARRIER_NEG_PIN 
714 init_ctrb     long      (%00110 << 26) | THRESHOLD_PIN
715 
716 carrier_mask  long      |<CARRIER_RATE - 1
717 carrier_bit0  long      |<(CARRIER_RATE + 0)
718 carrier_bit1  long      |<(CARRIER_RATE + 1)
719 
720 pulse_target  long      0
721 next_hyst     long      0
722 hyst_constant long      0
723 
724 baseband_reg  long      0,0,0,0,0,0,0,0
725 baseband_s32  long      32      ' Number of zeroes in last 32 baseband bits
726 baseband_s256 long      256     ' Number of zeroes in last 256 baseband bits
727 
728 em_buf_ptr    long      0
729 hid_buf_ptr   long      0
730 format_ptr    long      0
731 pulse_ptr2    long      0
732 
733 hid_header    long      |<16 - 1
734 hid_max_bits  long      512
735 hid_fsk_mask  long      |<9
736 
737 hid_lp_reg    long      0,0,0,0,0,0,0,0
738 hid_lp_s32    long      32      ' Number of zeroes in last 32 FSK bits
739 hid_lp_s256   long      256     ' Number of zeroes in last 256 FSK bits
740 
741 hid_s_mask    long      %111 << (3 + 45 - 32)
742 hid_s_check   long      %011 << (3 + 45 - 32)
743 hid_sp_check  long      %001 << (3 + 45 - 32)
744 hid_data_mask long      (1 << (3 + 45 - 32)) - 1
745 
746 
747 '------------------------------------------------------------------------------
748 ' Uninitialized Data
749 '------------------------------------------------------------------------------
750 
751 r0            res       1
752 r1            res       1
753 r2            res       1
754 
755 cur_pulse     res       1
756 prev_pulse    res       1
757 pulse_len     res       1
758 
759 pulse_count   res       1
760 prev_frqb     res       1
761 
762 hid_bit_len   res       1
763 hid_bit_count res       1
764 hid_fsk_reg   res       1
765 hid_reg       res       1
766 
767 em_bits       res       64      ' 64-bit shift register for each of the 32 phases
768 em_shift      res       2       ' Just the current shift register
769 em_decoded    res       2       ' Decoded 40-bit EM4102 packet       
770 em_parity     res       1       ' Column parity accumulator
771 
772 hid_bits      res       100     ' 64-bit shift register for each of the 50 phases
773 hid_shift     res       2       ' Just the current shift register
774 hid_shiftp    res       2       ' 180 degrees out of phase with hid_shift
775 hid_decoded   res       2       ' Decoded 45-bit HID packet
776 
777                         fit
View Code

125 kHz RFID Reader

http://www.changpuak.ch/electronics/RFID-READER-125kHz.php

A small circuit to read RFID badges operating at 125 kHz (EM4102)
This circuit is used here : Coffee Tally Sheet • Kaffee-Strichliste

Reading RFID-badges operating at 125 kHz is very easy. You just have to put a carrier (125 kHz) into the air and have the badge do the work.

The badge will do the ASK (Amplitude Shift Keying) and you only have to monitor the envelope of the carrier.

This can be done by a simple diode rectifier. 

The carrier is generated by a 74HC4060 and a 4 MHz crystal.

Using "Q4" delivers the desired 125 kHz signal.

The AM Demodulator is build of a AA113 (for historic reasons) and R5, C5.

The demodulator is followed by a limiting amplifier.

The voltage across the coil is about 14 Vpp. 

 

The envelope of the carrier, when a tag / badge is brought close to the coil ...

 

ProxClone - Proximity Card Reader / Cloner

http://proxclone.com/reader_cloner.html

Reader / Cloner Overview

The picture below is of my prototype combination card reader and cloner.

The unit is self contained and does not require the use of a PC or other external equipment to operate.

Operation is simple and straightforward.

Simply hold a card near the antenna and the unit reads and decodes the information from the card.

The information is then formatted and displayed on a 4x20 character LCD.

If the operator wishes to make a copy of the card he simply brings a T5557/5567 Read/Write card near the antenna

and presses the "write" button.

The LED flashes and in less than a second the R/W card has been programmed with the information that was read from the original.

Voila !! - A clone card.

The cost to build the device was minimal (approx. $30) including the LCD display and circuit board.

The design fit on a single sided circuit board that I etched myself.

The PWB was made to be the same size as the LCD so that they could be plugged together as a single assembly.

A detailed description of my design concept is included below.

Background

I initially began this activity by trying to build a simple card reader that could be used

to obtain all of the information that was transmitted by the card during a simple read operation.

Most commercial card readers do not output all of the data that is read.

Information such as the header and card format are never transmitted as part of the readers normal output stream.

Knowing this information is critical for being able to replicate a cards operation.

As a result, I set out to build my own custom reader.

The Design

A document that I found to be invaluable during my learning process was

Microchip's 125 Khz RFID Sysem Design Guide which can be found on their website.

 RFID System Design Guide.

Their FSK reference design circuit was basically what I used for my design.

I made a couple of small modifications to simplify the design and to allow the use of a Parallax SX28 microcontroller instead of the PIC. 

A photo of my initial "reader-only" design (without write capability) is shown below:

After studying the datasheet for the T5557/5567 IC (used in many vendors access cards),

I soon realized that the reader circuit would only have to be modified slightly in order to also be able to function as an RFID writer.

To function as a writer the design simply needed to be able to modulate the 125Khz RF carrier using On/Off Keying (OOK) modulation

since this is how the T55x7 chips are programmed.

I modified the design to accomplish this by basically giving the microcontroller the ability to control the 125 Khz divide counter reset signal.

An extra push button was also added to an unused GPIO input on the microcontroller.

A schematic of my reader circuit (modified to become a writer) is shown below:

The reader/writer circuit design can be broken down into four main components.

1) The clocking circuit, which generates a 4 Mhz clock for the microcontroller and a 125Khz carrier signal for the RFID interface.

2) The RF front end consisting of a tuned LC resonator and an AM peak detector

3) A series of low pass and band pass filters to extract the 12.5Khz and 15.6Khz FSK signals.

4) The SX28 microcontroller which performs the following functions:
   - LCD initialization
   - Decoding and storage of the FSK data from the op amp filter output.
   - Parsing and formatting of the card data.
   - Driving the LCD display.
   - Programming the clone card by modulating the 125 Khz carrier (per the T55x7 datasheet).

My updated "write-capable" version of the Reader/Writer assembly along with a commercial 44780 4x20 LCD

and 125Khz loop antenna is shown in the photo below.

The completed unit was installed onto a piece of acryllic plastic in lieu of trying to find an off-the-shelf plastic box

that everything would fit in and still look halfway decent.

I have tested the unit with numerous card types including 26-bit, 34-bit, 36-bit, 37-bit and the 35-bit Corporate 1000 formats.

The cloner was able to duplicate all of them without any difficulty.

In all cases, the vendors own commercially available readers were unable to distinguish between the original and the clone cards.

 

 

Final Design Project -
RFID Proximity Security System

 

https://instruct1.cit.cornell.edu/Courses/ee476/FinalProjects/s2006/cjr37/Website/index.htm

Introduction and Motivations:

For our final project, we designed and built (and exhaustively tested) an RFID-based proximity security system

for use with Cornell Identification cards, which have been RFID-embedded since fall of 2003.

The idea for this project was sort of spawned from our general interest in RFID technologies

and the near-simultaneous occurance of Lab 2 (Keypad Security System)

and the antiquated lock system at our fraternity house breaking.

 

 

At the highest level, our device uses an antenna coil to power the RFID tag embedded in our Cornell ID's

and read the induced response from the card.

This response is then filtered and manipulated into useful data and interpreted by the Atmel Mega32 microcontroller

which runs the actual security program. In addition to interactions with the ID cards, the system is in contact

with an administrator computer via a serial communications link and hyperterm.

The security system can store up to 20 45-bit codes which are derived from communications with each unique RFID tag.

If a card is read and it is not in the code database, a red LED flashes for 3 seconds.

Likewise, if the code can be found in the database, a green LED lights for 3 seconds.

From hyperterm, the administrator has the power to add codes, delete codes, list all codes,

"unlock" the door (the equivalent of the green LED flashing),

and initialize routines which allow codes to be added to the database by gathering data from the reader itself.

Educational topics explored in this lab include (but are not limited to)

passive filter design, active filter design, amplification circuits, RF antenna design, digital logic, serial communications, RFID theory,

pin interrupts, timer interrupts, and soldering.

In short, for this project we used elements of basically every introductory level ECE course we have taken.

Since we are dealing with such a complicated topic, on the hardware side of things we tried to rely as much as we could on proven circuit designs.

This would enable us to focus more on getting our system working well as a whole rather than spending countless hours debugging small parts of our project.

For this, the Microchip® microID 125 KHz Reference Guide (see citations section) proved to be an invaluable resource for both theory and results.

High Level Hardware Design:

Before we start with actual circuit design, it is neccessary to understand the principals behind the technology

that this project has set out to harness; passive RFID communications.

Passive RFID tags work in such a way that they are actually powered by an external signal,

which, in most cases is the carrier signal from the tag reader circuit.

These tags are fairly simple and are comprised of merely an L-C antenna (similar to the one shown in the block diagram below)

and the circuitry neccessary to modulate this carrier signal once powered on.

The reader and tag communicate using magnetic coupling since their respective antennas can sense changes in magnetic field,

which is observed as a change in voltage in the reader circuit.

The Cornell ID cards we use in this project were developed by HID®; specifically the HID DuoProx II cards.

These are useful because they have both embedded RFID as will as a magnetic strip, while much of campus is starting

to switch over to proximity entry systems, many current systems (including the dining halls) are still swipe-operated.

From looking at their website, it was difficult to determine much information about the card's operation,

asside from the fact that it operates at a 125 KHz carrier frequency and it could have a tag size anywhere between 26 and 128 bits long.

After many hours of research we discovered that the modulation type used in the cards is Frequency Shift Keying (FSK),

one of the more common ways used in RFID.

FSK modulates the signal by essencially multiplying a lower amplitude, lower frequency signal with the carrier signal, creating an AM-like effect;

the lower frequency enveloping the carrier frequency.

To switch between a "1" and a "0", the tag switches the modulating frequency.

The two frequencies used by our cards were 12.5 KHz (125 KHz/10) and 15.625 KHz (125 KHz/8), which correspond to 1 and 0 respectively.

The modulation produces an effect that looks similar to the figure below:

Figure 1: Simulation of FSK Modulation With Modulation Frequencies of 12.5 and 15.625 KHz and Carrier Frequency of 125 KHz

The job of the reader circuit is to provide the 125 KHz carrier frequency, transmit that to the tag, and detect the magnetic coupling from the tag,

which should look like the figure above.

In order to interpret this data, the carrier frequency must be removed, and the enveloping frequencies must be magnified into something measureable.

The block diagram/flow chart for our reader circuit can be found in the figure below:

Although each individual part of the circuit and program will be described in detail later,

the general idea for circuit operation is as such:

The Mega32 provides a timer-driven 125 KHz square wave for our carrier frequency.

This is then sent through an RF choke, which is essentially a passive low-pass filter with steep drop-off

to knock out the upper harmonics and leave us with only a sine wave.

The sine wave is then amplified using an emmitter follower PNP transistor and a half bridge to maximize current.

Since our resonant circuit is a series L-C circuit, maximum resonance is achieved at minimum impedance,

so it is very important that we provide adequate current amplification as to not overdrive our microcontroller.

To help reduce the strain (and ramp up the current more) further,

the square wave output from the MCU is put through parallel inverters.

On the recieving end, the signal is first half-wave rectified, since the negative part of the signal doesn't really make a difference,

and is then fed through a half-wave R-C filter to help knock out most of the 125 KHz carrier and detect the envelope signal.

This signal is then bandpass filtered using a series a Twin-T active bandpass filters,

and lowpass filtered with an active Butterworth filter to further decrease gain in frequencies outside of the 10-20 KHz area

and increase gain of the envelope signals such that it saturates the op-amps of the filters.

As a final stage the signal is put through a comparator and resistive divider to produce a nice square wave at logic levels.

Some D-flip flops and a decade counter are used to extract data from the modulating square waves.

Which are fed into the MCU and processed.

Hardware and Software Tradeoffs:

There are many ways to design a proximity card reader in terms of tradeoffs between hardware and software.

In most cases, software is cheaper because you don't need to purchase any parts but at the same time you are costing the MCU processing time.

Using more hardware will obviously increase the cost of the design but ultimately may alleviate painfully tedious optimizations

that would have been necessary had you used code to replace a component or device.

One of the first tradeoffs we encountered was whether to use the Mega32 or a separate counter to generate the 125 kHz carrier frequency.

The microID 125 kHz RFID System Design Guide suggested using a 14-stage binary counter to divide the clock from the crystal to 125 kHz.

However, since the Mega32 has built-in hardware timers that can output to one of the pins, there was no need to use a counter.

Another tradeoff we encountered was whether to use DSP or hardware to analyze the signal on the antenna.

Recall that this signal is the carrier signal and the magnetically coupled response from the card superimposed onto each other.

Using DSP, we could sample at the Nyquist frequency and compute the FFT of the signal

to find what frequencies are present in the response and from there decode the response.

If we were to use DSP, we would have to sample at greater than 250 kHz

meaning there would only be 64 cycles between samples to compute the FFT.

This imposed a huge constraint on the rest of our security system so we decided to implement the most of the decoding in hardware.

Specific Circuit Elements:

Transmit Stage: RF Choke and Power Amplifier:

The circuit of Figure 3 below is an RF choke followed by a current buffer and half-bridge amplifier.

The RF choke is used to filter out most, if not all of the upper harmonic frequencies found in the square wave output from the MCU,

leaving the fundamental frequency, 125 KHz, as a sine wave to be amplified.

The square wave generator seen in the figure below is, in actuality, the output from the MCU and a set of inverters to ramp up the current.

Diodes are used in the half bridge to help reduce crossover distortion caused from differing points of either transistor in the half bridge turning on and off.

In our design we used the 2N3904 and 2N3906 NPN and PNP BJT transistors from the lab since they were cheap and convenient.

In order to get better amplifier gain, and thus increase read range of our circuit, we could have used power MOSFETS instead for the half-bridge,

but we found the BJT's gave us a mostly acceptable level of gain, especially once the circuit was tuned.

 

Resonant Antenna Circuit:

While this portion of our circuit is only comprised of two components, it is also arguably the most important hardware element;

if it performs poorly then our security system performs poorly.

Because this design was recommended for proximity solutions from the Microchip® guide,

we decided to go with a series L-C resonant curcuit as opposed to one where the resistor and inductive antenna were in parallel.

Because of this, at maximum resonance we also observe maximum current.

In order to determine values for the inductance and capacitance needed, we used the equation: 

where f0 is the resonant frequency (in Hertz), L is inductance (in Henries) and C is capacitance (in Farads).

Since f = 125 KHz and we had plenty of 1 nF ceramic capacitors in the lab, we settled on an inductance of 1.62 mH

To construct an antenna with the neccessary inductance we used coils of laquered copper wire, since it works well and is fairly compact.

In our final construction revision, we used a rectangular-shaped antenna coil since it fit well with the design.

The figure above shows the coil as circular, which is what we used for most of our preliminary testing

before we actually put the unit together.

Both antennas operated roughly the same as each other, although the rectangular coiled one resonates more.

Inductance for the rectangular coil is determined by the following equation: 

where L is in microHenries, x and y are the width/length of the coil (in cm), h is the height of the coil (in cm),

b is the width across the conducting part of the coil (in cm) and N is the number of turns.

In our case, x=3.6cm, y=13.8 cm, h=1 cm, and we estimated b=.3 cm.

Using the equation, we calculated the coil to need approximately 90 turns.

It turned out this was a pretty good estimate.

After constructing the coil, we proceeded to tune it by removing coils until we saw the highest resonant voltage from our carrier frequency,

which was at roughly 88 turns.

Oscilloscope results from this circuit, with both just the carrier frequency, and with the modulated signal from the RFID tag can be seen below.

Half-Wave Rectify and Filtering:

This portion of the circuit is devoted to separating out the carrier frequency from the modulating envelope,

since its really only the envelope that has the data we care about.

The first stage is half-wave rectifying the signal to make things simpler and then filtering it slightly with an R-C filter.

As is the norm for filtering AC signals in this manner there is some 125 KHz ripple,

but choosing good values we could make the enveloping frequencies stand out from the ripple.

For this we chose R=390 KOhms and C= 2.2 nF.

Scope readings are shown in the figure below.

Note that the peaks are the ripple, and the whole signal seems to oscillate at 15.625 KHz.

You can tell this because there are 8 125 KHz ripple peaks per oscillation of the envelope.

At a 12.5 KHz envelope, there would be 10 ripples per oscillation.

Once signal leaves this stage, it passes through a capacitor to knock out the DC offset and into the next set of filters;

a pair of active Twin-T filters and an active Butterworth filter with the TL084 OpAmp as the gain element.

The circuit diagram for this is in the figure below:

Figure 8: Circuit Diagram for the Filter Stage. Signal Comes in From the Right and Exits out the Butterworth on the Left

As can be seen from the Bode Plot in the figure below, the first filter mostly isolates the pass band (10-20 KHz),

with roughly unity gain for all frequencies outside the pass band.

The second filter further accentuates gain in the pass-band while slightly reducing the magnitude of frequencies outside the pass band.

After this, the signal goes through a massive Butterworth Low-Pass filter to drastically increase gain of lower frequencies already in the pass band
and virtually eliminate the higher frequencies, including the 125 KHz carrier signal.

Once out of the filters, the signal is then put through a TL084-based comparator and a resistive divider to generate a nice square wave at logic levels.

The 12.5 KHz and 15.625 KHz frequencies come out of the filters beautifully.

When no card is present, the system reports a 28.75 KHz wave

which represents the highest frequency to come out of the filters with enough gain to saturate the opamps.

Figure 10: Protoboard With Our Filter Circuit (left) and 28.75 KHz idle Frequency From the Filter Output

Figure 11: Envelope Frequencies After Filtering and Reduction to Logic Levels. 12.5 KHz is on Left While 15.625 KHz is on Right

Data Creation Stage:

Technically, from the output of the comparator we should have been able to read and interpret data from the card using a timer interrupt.

We quickly realized, however, that by doing this we would cripple the functionality of our system.

In order to accurately measure the frequency of the incoming data stream we would realistically need to sample at 125 KHz,

which means that, with a clock rate of 16 MHz we would have 128 clock cycles to compute everything before the next sampling interrupt fired.

This would have been extremely difficult to implement.

Looking for an alternate method to obtain data, we found a brilliant design in the Microchip reference guide,

which makes use of flip-flops and a decade (Johnson) counter. This circuit can be seen in the figure below:

The way this works is as follows:

The comparator output serves as the clock for the first D flip-flop, which also takes logic 1 as its D value.

On the rising edge of the comparator clock, Q is immediately set to 1.

However, simulataneously ~Q goes low and clears the flip-flop.

This creates an extremely short pulse which serves as a reset for the decade counter and clock for the second flip-flop.

The decade counter is just a 1-hot counter which takes a 125 KHz clock.

With every rising edge of this clock, the counter outputs the next pin to logic 1;

so typical output would look like (if one were looking at output pins 0-9 of the counter) 1000000000 0100000000 00100000000 etc.

However, this counter is being reset with every rising edge of the comparator output.

Thus, since we've already determined that 125 KHz/10 = 12.5 KHz is to be our frequency that represents logical 1,

all we have to do is check for the output on pin9 to know whether or not we see that frequency.

If the system is operating at either one of the other possible frequencies, the counter will be reset before pin9 can go active.

The pin9 output serves as input to the second flip-flop and also to the clock inhibitor, which keeps the 9th pin high until the counter is reset.

Because of this set-up, the Q output of the second flip-flop will remain logical 1 so long as modulating frequency is 12.5 KHz

and will drop down to 0 if its anything else.

Theoretically, this circuit should work perfectly.

However, experimentally it did not, and thus required a small modification.

The 100 KOhm resistor on the first flip-flop serves to lengthen the time it takes for the ~Q signal to get to CLEAR.

Since all transistors have some amount of natural capacitance, this forms an RC circuit of sorts with a set RC time constant for the signal to rise or fall.

As it turns out, this time was too short for the decade counter.

The original design from the reference guide specified only a 10KOhm resistor between ~Q and CLEAR.

With the 10 KOhm resitor, pulse widths for the reset pulse were a mere 50 ns long, while the counter required at least 250 ns.

This caused some very eratic behaviour.

After many hours of debugging, we finally pinpointed the problem and replaced the resistor with the 100 KOhm resistor

which increased the pulse width long enough for the counter to correctly operate.

The figures below show the behaviour of the circuit when operating correctly:

Figure 13: Comparator Output with Reset Pulse (left). Comparator Output with Data Output (right)

Figure 14: A Close-Up of our Reset Pulse Reveals That it is now 350 ns; 100 ns Over the Minimum for the Decade Counter

Software Design and Program Details: 

Initialize:

This function initializes the various interrupts, timers, input-output,

and global variables utilized in the program. The following are all initialized or setup here:

  • Timer2 is used to toggle OC2 to generate a 125 kHz square wave.
  • Port A is the output of the digital counter/flip-flop circuit.
  • Port C is the LED output.
  • Timer0 is used to count milliseconds for keeping track of time
    and controlling the timing on scheduled tasks and timer0 interrupt is enabled.
  • Enable the transmitter and receiver in the USART and set the baud rate to 9600.
  • Initialize flags, counts, buffers, and other state variables.
  • External interrupt 2 is enabled and set to trigger on the rising edge.
  • Set interrupt bit.
  • Hold and prompt for the date and time before entering the while(1) loop.

USART Receive Interrupt

This interrupt handles any typed characters received from the terminal through the RS232 connection
and stores it into a buffer.

It also echoes the character to the terminal. Once a carriage return is detected, the receive-ready flag is set indicating
that a line command has been entered by the user via the terminal.

USART Transmit Interrupt

This interrupt handles transmitting characters to the terminal. It simply loops through the transmit buffer until the last character is sent and then is ready for another transmission.

Timer0 Compare Match Interrupt

This interrupt serves as a timer to control the scheduling of different timers.

It decrements the timers for timing how long the door is unlocked, timing how long a second lasts, and for checking the receive-ready flag.

External Interrupt2

This interrupt samples the data (output of the counter/flip-flop circuit). It is triggered by the rising edge of the output of the comparator. The output of the comparator is approximately a 29 kHz square wave if there is no modulation, and a 15.625 kHz or 12.5 kHz square wave if there is modulation. When the output of the comparator is 15.625 kHz the data is 0 and when the output is 12.5 kHz the data is 1. The point of this interrupt is to detect how many bits are in a 0 or 1 pulse of the data. Thus every time this interrupt fires, we increment a counter and add the sample to an array.

Main Method

In main, there are 3 major modes of operation.

The mode of operation at startup is normal;

however the modes can be changed through the terminal.

 

In normal mode, the reader waits for External Interrupt2 to finish reading a response from the card.

It does this in about a thousand executions of the interrupt.

When enough bits have been sampled, we turn off the External Interrupt2.

When first analyzing the response from the card, we noticed periodicity every 540 bits.

Thus, to guarantee that our sample window captures a full continuous 540 bit cycle,

we sampled 1080 bits before turning off the interrupt.

An example of a 1080 bit response looked something like this:

001111100000011111000000111111000000000000111111111100000011111000000000000111111000000111110000001111100000011111100000 11111100000011111111110000000000001111110000011111100000011111111110000000000001111110000011111111111000000111110000001111
00000000000011111100000011111111110000000000001111110000011111100000000000000000011111111111111110000011111100000011111000000 1111100000011111100000111111000000111110000001111111111000000000000111111111110000001111100000000000011111100000111111000000
111110000001111111111000000111111000001111110000001111100000011111000000111111000000000000111111111100000011111000000000000
1111110000001111100000011111000000111111000001111110000001111111111000000000000111111000001111110000001111111111000000000000
11111100000111111111110000001111100000011111000000000000111111000000111111111100000000000011111100000111111000000000000000000 11111111111111110000011111100000011111000000111110000001111110000011111100000011111000000111111111100000000000011111111111000000
1111100000000000011111100000111111000000111110000001111111111000000111111000001111110000

There is a long sequence of 1's and 0's that stand out;

we used these as references to identify the start and end of the 540 bit response.  

To extract the 540 bit response we wrote a function to detect a sequence of 15 to 18 1's.

This function loops until it finds the start sequence and stores it in a global variable and calculates the end sequence.

There is also something noticeable about the 540 bit sequence.

There are 1's and 0's in groups of 5, 6, 10, 11, or 12 excluding the start and stop sequence.

Since 10, 11, and 12 can be made from combinations of 5 and 6,

we hypothesized that maybe these longer groups are combinations of two groups of 5 or 6.

With this in mind, we wondered whether a group of 5 or 6 bits possibly represents a single bit.

This would make sense because the card cannot perfectly transition from one modulated frequency to another without some transition.

Thus a group of 10, 11, or 12 represents two bits.

Thus the reduced (90 bit) version of the 540 bit response excluding the start and stop sequence is extracted

by detecting sequences of bits and replacing them with a single or a double bit:

010101010101011001101001010101101010101010011010010101010101100101011001011010100101100101

From this code, it is fairly obvious that the reduced sequence is encoded in Manchester code.

If you split up the 90 bit response into pairs of bits, there are transitions within each pair:

01 01 01 01 01 01 01 10 01 10 10 01 01 01 01 10 10 10 10 10 10 01 10 10 01 01 01 01 01 01 10 01 01 01 10 01 01 10 10 10 01 01 10 01 01

A transition from low to high corresponds to a 1 and a transition from high to low corresponds to a 0.

Since two bits correspond to a single bit, the Manchester decoded response has half the bandwidth and is thus only 45 bits long:

111111101001111000000100111111011101100011011

We believe this code is the raw data stored on the card.

We have not been able to decode this further to find it's relation to Cornell student ID number

but it is not necessary since this number is unique to each card.

After decoding the initial 540 bit response to this 45 bit code, we store this data and sampled again.

To prevent false reads, we keep sampling until we successfully read 3 consecutive identical codes.

If we get 3 consecutive identical codes, we compare this code to the code bank (where all the authorized codes are stored).

The code bank is stored in EEPROM due to limited space in SRAM.

If the 3 consecutive identical codes match a code in the code bank, a green LED is lit for 3 seconds to signify that the door is unlocked.

If the code does not match the code bank, a red LED is lit to signify that the door is not opened and that the code is not authorized.

A statement is also printed to the administrative terminal providing the card code,

whether the card was accepted, and the time at which the event occurred.

 

The other mode is called remote operation.

In this mode, the admin has a choice of remotely adding a code to a specific code bank position

or remotely adding any number of codes (bound between 1 and 20 inclusive).

When adding a code to a specific code bank position, we turn on External Interrupt2 and read the card response.

Just like in normal mode, we find the start code, reduce the sequence, and Manchester decode the sequence.

We do this until we read 5 consecutive identical codes and then store it into the specified position in the code bank.

When adding a specific quantity of codes, we first search through the status of the code bank and find the first unused position.

Then we go into remote add by position mode and we add the code at the first unused position.

We do this until either the specified quantity of codes are stored or until the code bank is full.

After either of these modes finishes executing, the reader goes back to its normal mode,

but now with the new stored codes in the code bank.

The end of the main loop serves as a scheduler that checks the timers for certain tasks and executes the task.

It executes the function to check the receive-ready flag, turns off the LED's after 3 seconds,

and executes the counter that keeps track of time and date.

Check Receive Ready

The last major part of the security system is the administrative interface through the terminal. This function checks to se if a command has been received from the admin. It checks the command and executes the corresponding actions. The commands that are implemented are:

  • a <pos> <code>        – add a code
  • l                                   – list all codes
  • d <pos>                      – delete a code
  • u                                  – unlock door
  • rp <pos>                    – remote add (positional)
  • ra <amt>                   – remote add (amount)

Helper Functions

The rest of the functions are simple, self-explanatory, helper functions that accomplish simple tasks such as keeping track of the time and date, storing codes into the code bank, verifying that a code matches a code in the code bank, comparing two codes, copying a code, and completing or starting a receive or transmit using the USART. 

Results of Design:

Certain aspects of the proximity security system performed equal to what we initially expected at the start of the project.

The maximum range of the proximity card reader is about 1.5 to 2.0 inches from the antenna coil.

According to the microID 125 kHz RFID System Design Guide, different dimensions of reader and tag coils will affect the maximum read distance.

For example, a 3 x 6 inch reader antenna and a 0.5 inch diameter card antenna will have a maximum read distance of about 1.5 inches.

For a 1.0 inch diameter card antenna the read distance increases to 4 inches.

Since we do not know the exact dimensions of the antenna inside of our Cornell University identification cards,

we do not know whether we are performing below or above ideal expectations,

however, the current read range is sufficient for our purposes.

The approximate read time for most cards is about 1 to 3 seconds once the card is within the maximum read distance.

The latency is due to the redundant code check.

Once the card is in range and we start receiving data, we read the data 3 consecutive times.

If the code ever differs, then we try to read another 3 consecutive times.

Thus, if the card is near the maximum range or has poor modulation consistency then it will take longer to read.

The proximity security system is fairly accurate as we expected.

There are little to no false positives, although sometimes there are false negatives.

If the card is held around the maximum range, we occasionally receive incorrect data.

However, if the card is within an inch of the coil, the data can be read with virtually no errors.

Although important, safety was a minor concern when designing a proximity security system.

Since there is no contact between the user and the reader, there is no danger of harming the individual through direct contact.

The 125 kHz signal transmitted from the reader is also harmless but may cause interference with other devices operating at the same frequency.

From our time in lab however, we noticed almost no interference from other people's designs.

Our goal for this project was to build a security system that used our current Cornell University identification cards.

Thus by completing that goal, we believe we have built a fully operational concept/prototype of an ideal proximity security system for our fraternity house.

This project is very usable by anyone wanting a hands-free front door security system while utilizing a card that is necessary to have with you anyway.

 

1 /*     Authors: Ricardo Goto and Craig Ross
  2     Project: RFID Security system
  3 */    
  4 
  5 #include <Mega32.h> 
  6 #include <stdio.h>
  7 #include <delay.h>
  8 
  9 /* Define Constants */
 10 #define NUM_BITS 1080
 11 #define NUM_CODES 20
 12 #define RED 0b11111110
 13 #define GREEN 0b11111101
 14 #define OFF 0b11111111
 15 #define CHECK_RECEIVE_TIME 50
 16 #define NORMAL 0
 17 #define REMOTEPOS 1
 18 #define REMOTEAMT 2
 19 
 20 /* Define input pin from RFID circuit */
 21 #define RFIDIN PINA.0
 22 #define LED PORTC
 23 
 24 
 25 /* Prototypes */
 26 void initialize(void);
 27 void gets_str(void);
 28 void puts_str(void);  
 29 void find_start_code(void);
 30 void reduce_sequence(void);
 31 void manchester_decode(void);
 32 unsigned char match_code(unsigned char *char1,unsigned char *char2);
 33 void copy_code(unsigned char *char1,unsigned char *char2);
 34 unsigned char verify_code(unsigned char char1[]);
 35 void check_receive(void);
 36 void time_counter(void);
 37 void store_into_bank(char position, char code[], char offset);
 38 
 39 /* Global Variables */                   
 40 char curr_sample;
 41 char code_count;
 42 char start_flag;
 43 int totalBits;
 44 char bit_array[NUM_BITS+1];
 45 int sample_buffer;
 46 int start_sequence;
 47 int end_sequence;
 48 char reduced_array[91];
 49 char final_code[46];
 50 char code_check[46];
 51 char check_receive_timer;
 52 int door_timer;
 53 char mode;
 54 signed char add_pos;
 55 char del_pos;
 56 char add_amt;
 57 unsigned int seconds;
 58 unsigned int minutes;
 59 unsigned int hours;
 60 unsigned int days;
 61 unsigned char r_days[4];
 62 unsigned char r_hours[3];
 63 unsigned char r_minutes[3];
 64 char no_code[46];
 65 eeprom char code_bank[NUM_CODES][46];
 66 char temp[46];
 67 eeprom char bank_status[NUM_CODES];                                
 68 char r_index, r_buffer[128], r_ready, r_char;
 69 char t_index, t_buffer[128], t_ready, t_char;
 70 
 71 /* USART recieve interrupt */
 72 interrupt[USART_RXC] void uartr(void)
 73 {
 74     r_char = UDR;
 75     UDR = r_char;
 76     if (r_char == 8) 
 77     {
 78         if(r_index != 0) r_index--;
 79     }
 80     else if (r_char != '
') r_buffer[r_index++] = r_char;
 81     else
 82     {
 83           r_buffer[r_index] = 0x00;
 84         r_ready = 1;
 85         UCSRB.7 = 0;
 86         putchar('
');
 87     }
 88 }
 89 
 90 /* USART transmit interrupt */
 91 interrupt[USART_DRE] void uartt(void)
 92 {
 93     t_char = t_buffer[++t_index];
 94     if (t_char == 0)
 95     {
 96         UCSRB.5 = 0;
 97         t_ready = 1;
 98     }
 99     else UDR = t_char;
100 }
101 
102 /* Timer0 compare match ISR */
103 interrupt [TIM0_COMP] void timer0_compare(void)
104 {
105     /* Decrement timers every millisecond */
106     if(check_receive_timer > 0) --check_receive_timer;
107     if(door_timer > 0) door_timer--;
108      if (seconds > 0) --seconds;
109 }
110 
111 /* External pin interrupt */
112 interrupt[EXT_INT2] void int2(void)
113 {
114     /* In this interrupt, we want to sample the output of the 
115        second DFF just after the rising edge of the output of
116        the comparator stage. This will tell us how the length
117        of the pulse corresponds to the number of bits. We only
118        start caring about the data when it first goes high,
119        however, the card will probably be close to the
120        maximum reading distance. Therefore, we wait until the
121        card gets closer (we assume the person is bringing the 
122        card to about 1-2 inches from the reader coil) and then 
123        we start to sample. Once we have enough samples to
124        capture a full period of the looping response, we stop
125        sampling.
126     */
127     delay_us(16);
128     curr_sample= RFIDIN;
129     if((start_flag == 0) && (curr_sample == 1)) 
130     {
131         start_flag= 1;
132         sample_buffer++;
133     }
134     else if((start_flag == 1) && (sample_buffer <= 500)) sample_buffer++;
135     else if((start_flag == 1) && (totalBits < NUM_BITS))
136     {
137         if(curr_sample == 1) bit_array[totalBits++]= 49;
138         else bit_array[totalBits++]= 48;
139     }
140 }
141 
142 
143 void main(void)
144 {
145     char i;
146     initialize();
147     while(1)
148     {
149         if((totalBits == NUM_BITS) && (door_timer == 0) && (mode == NORMAL))
150         {
151             /* Normal Operation:
152                When a full period of the looping card response is
153                captured, we want to decode the response. We execute
154                the following steps until we get 3 identical codes 
155                in a row.
156                - turn off the external pin interrupt since we are 
157                  not going to be reading anything in this window of 
158                  time
159                - look for a start code and take only data
160                - reduce the bit sequence to 90 bits
161                - manchester decode to 45 bits
162                If we get 3 identical codes, then we compare that code
163                with the code bank to see if the code is currently
164                allowed access to the facility. If so, we blink a green
165                LED (open the door) for 3 seconds. If not, then we
166                blink a red LED (do not open the door) which also lasts
167                for 3 seconds.
168             */
169             GICR= 0b00000000;
170             find_start_code();
171             reduce_sequence();
172             manchester_decode();
173             if(code_count == 0)
174             {   
175                 copy_code(code_check,final_code);
176                 code_count= 1;
177             }
178             else if(code_count == 1)
179             {
180                 if(match_code(code_check,final_code)) code_count= 2;
181                 else code_count= 0;
182             }
183             else if(code_count == 2)
184             {
185                 if(match_code(code_check,final_code))
186                 {
187                     if(verify_code(final_code))
188                     {
189                         sprintf(t_buffer,"
Card ID: %s
Access Granted at %03d:%02d:%02d

",final_code,days,hours,minutes);
190                         puts_str();
191                         while(t_ready == 0){};
192                         LED= GREEN;
193                         door_timer= 3000;
194                     }
195                     else
196                     {
197                         sprintf(t_buffer,"
Card ID: %s
Access Denied at %03d:%02d:%02d

",final_code,days,hours,minutes);
198                         puts_str();
199                         while(t_ready == 0){};
200                         LED= RED;
201                         door_timer= 3000;
202                     }
203                 }
204                 code_count= 0;
205             }
206             /* Make sure to reset the interrupt variables
207                and turn on the external interrupt.
208             */
209             totalBits= 0;
210             start_flag= 0;
211             sample_buffer= 0;
212             GICR= 0b00100000;
213         }
214         else if(mode == REMOTEAMT)
215         {   
216             /* Remote Quantity Add Operation:
217                If we are in this mode of operation, this segment of code
218                makes it so that we loop in Remote Positional Add until
219                the amount of codes have been added. Our algorithm searches
220                the code bank status to find open positions and uses Remote
221                Positional Add to add the card ID.
222             */
223             add_pos= -1;
224             for(i= 0; i<NUM_CODES; i++)
225             {
226                 if(bank_status[i]== 0)
227                 {
228                     add_pos= i;
229                     bank_status[i]= 1;
230                     break;
231                 }
232             }
233             if(add_pos != -1)
234             {
235                 mode= REMOTEPOS;
236                 printf("Please place card in front of reader
");
237                 delay_ms(500);
238                 LED= GREEN & RED;
239                 delay_ms(200);
240                 LED= OFF;
241                 delay_ms(300);
242                 LED= GREEN & RED;
243                 delay_ms(200);
244                 LED= OFF; 
245                 add_amt--;
246             }
247             else
248             {
249                 printf("Code bank is full, please delete unwanted codes and re-run
");
250                 add_amt= 0;
251                 mode= NORMAL;
252             }
253         }    
254         else if((totalBits == NUM_BITS) && (mode == REMOTEPOS))
255         {
256             /* Remote Positional Add Operation:
257                Similar to normal mode except we are storing the
258                next card into the code bank. To prevent a bad code
259                from getting into the code bank, we add even more
260                error protection by having to read the same code 5
261                times in a row before it is accepted into the code
262                bank. 
263             */
264             GICR= 0b00000000;
265             find_start_code();
266             reduce_sequence();
267             manchester_decode();
268             if(code_count < 5)
269             {
270                 if(code_count == 0) 
271                 {
272                     copy_code(code_check,final_code);
273                     code_count++;
274                 }
275                 else
276                 {
277                     if(match_code(code_check,final_code)) code_count++;
278                     else code_count= 0;
279                 }
280             }
281             if(code_count == 5)
282             {
283                 store_into_bank(add_pos,final_code,0);
284                 sprintf(t_buffer,"Code added to position %d
",add_pos);
285                 puts_str();
286                 LED= GREEN;
287                 delay_ms(200);
288                 LED= RED;
289                 delay_ms(200);
290                 LED= GREEN & RED;
291                 delay_ms(500);
292                 LED= OFF; 
293                 code_count= 0;
294                 if(add_amt > 0)mode= REMOTEAMT;    
295                 else mode= NORMAL;
296             }
297             totalBits= 0;
298             start_flag= 0;
299             sample_buffer= 0;
300             GICR= 0b00100000;      
301         }
302         
303         /* Check timers for timed tasks */
304         if(check_receive_timer == 0) check_receive();
305         if((door_timer == 0) && ((LED == GREEN) || (LED == RED))) LED= OFF;
306          if(seconds == 0) time_counter();
307     }
308 }
309 
310 /* Function: Check Receive
311    This function checks the r_ready flag every 50 milliseconds. If
312    there is a requested command in the r_buffer then we process the
313    command and execute the corresponding code.
314 */
315 void check_receive(void)
316 {
317     char i,j;
318     check_receive_timer= CHECK_RECEIVE_TIME;
319     if(r_ready == 1)
320     {
321         /* Add input code at specific position */
322         if(r_buffer[0] == 'a')
323         {
324             if(r_buffer[3] == ' ')
325             {
326                 add_pos= r_buffer[2]-48;
327                 if((add_pos >= 0) && (add_pos <= 9))
328                 {
329                     store_into_bank(add_pos,r_buffer,4);
330                     bank_status[add_pos]= 1;
331                     printf("Code added at position %d
",add_pos);
332                 }
333                 else printf("Invalid code position
");
334             }
335             else if(r_buffer[4] == ' ')
336             {  
337                 add_pos= (r_buffer[2]-48)*10+(r_buffer[3]-48);
338                 if((add_pos >=0) && (add_pos < NUM_CODES))
339                 {
340                     store_into_bank(add_pos,r_buffer,4);
341                     bank_status[add_pos]= 1;
342                     printf("Code added at position %d
",add_pos);
343                 }
344                 else printf("Invalid code position
");
345             }
346             else printf("Incorrect syntax
");
347         }
348         /* List all codes and corresponding status */
349         else if(r_buffer[0] == 'l')
350         {
351             putsf("Code Bank:
");
352             for(i= 0; i < NUM_CODES; i++)
353             {
354                 for(j= 0; j < 45; j++)
355                 {
356                     temp[j]= code_bank[i][j];
357                 }
358                 printf("%02d: %d - %s
",i,bank_status[i],temp);
359             }
360         }
361         /* Delete code at specific position */
362         else if(r_buffer[0] == 'd')
363         {
364             if(r_buffer[3] == '')
365             {
366                 del_pos= r_buffer[2]-48;
367                 if((del_pos >= 0) && (del_pos <= 9))
368                 {
369                     store_into_bank(del_pos,no_code,0);
370                     bank_status[del_pos]= 0;
371                     printf("Code deleted at position %d
",del_pos);
372                 }
373                 else printf("Invalid code position
");
374             }
375             else if(r_buffer[4] == '')
376             {  
377                 del_pos= (r_buffer[2]-48)*10+(r_buffer[3]-48);
378                 if((del_pos >=0) && (del_pos < NUM_CODES))
379                 {
380                     store_into_bank(del_pos,no_code,0);
381                     bank_status[del_pos]= 0;
382                     printf("Code deleted at position %d
",del_pos);
383                 }
384                 else printf("Invalid code position
");
385             }
386             else printf("Incorrect syntax
");
387         }
388         /* Unlock door for 3 seconds */
389         else if(r_buffer[0] == 'u')
390         {
391             LED= GREEN;
392             door_timer= 3000;      
393         }
394         /* Enable remote positional add mode */
395         else if((r_buffer[0] == 'r') && (r_buffer[1] == 'p'))
396         {
397             
398             if(r_buffer[4] == '') 
399             {
400                 add_pos= r_buffer[3]-48;
401                 if((add_pos >= 0) && (add_pos <= 9))
402                 {
403                     bank_status[add_pos]= 1;
404                     printf("Please place card in front of reader
");
405                     LED= GREEN & RED;
406                     delay_ms(400);
407                     LED= OFF;
408                     delay_ms(400);
409                     LED= GREEN & RED;
410                     delay_ms(400);
411                     LED= OFF; 
412                     mode= REMOTEPOS;
413                 }
414                 else printf("Invalid code position
"); 
415             }
416             else if(r_buffer[5] == '')
417             {
418                 add_pos= (r_buffer[3]-48)*10+(r_buffer[4]-48);
419                 if((add_pos >=0) && (add_pos < NUM_CODES))
420                 {
421                     bank_status[add_pos]= 1;
422                     printf("Please place card in front of reader
");
423                     LED= GREEN & RED;
424                     delay_ms(400);
425                     LED= OFF;
426                     delay_ms(400);
427                     LED= GREEN & RED;
428                     delay_ms(400);
429                     LED= OFF; 
430                     mode= REMOTEPOS;
431                 }
432                 else printf("Invalid code position
");  
433             }
434             else printf("Incorrect syntax
");
435         }
436         /* Enable remote quantity add mode */
437         else if((r_buffer[0] == 'r') && (r_buffer[1] == 'a'))
438         {
439             if(r_buffer[4] == '') 
440             {   
441                 add_amt= r_buffer[3]-48;
442                 if((add_amt >= 1) && (add_amt <= 9)) mode= REMOTEAMT;
443                 else printf("Invalid add quantity
");
444             }
445             else if(r_buffer[5] == '')
446             {   
447                 add_amt= (r_buffer[3]-48)*10+(r_buffer[4]-48);
448                 if((add_amt >=1) && (add_amt <= NUM_CODES)) mode= REMOTEAMT;
449                 else printf("Invalid add quantity
");
450             }
451             else printf("Incorrect syntax
");
452         }
453         /* Help */
454         else if(r_buffer[0] == 'h')
455         {
456             putsf("Commands:
");
457             putsf("a  - add a code -                 syntax: a <pos> <code>
");
458             putsf("l  - list all codes -             syntax: l
");
459             putsf("d  - delete a code -              syntax: d <pos>
");
460             putsf("u  - unlock door -                syntax: u
");
461             putsf("rp - remote add (positional) -    syntax: rp <pos>
");
462             putsf("ra - remote add (amount) -        syntax: ra <amt>
");
463         }
464         /* Unrecognized commands */
465         else printf("Unrecognized Command
");
466         gets_str();
467     }           
468 }
469 
470 /* Function: Time Counter
471    This function keeps track of time for reporting to
472    the hyperterm.
473 */
474 void time_counter(void)
475 {
476     seconds= 59999;
477     if(minutes < 59) minutes++;
478     else
479     {
480         minutes= 0;
481         if(hours < 23) hours++;
482         else
483         {
484             hours= 0;
485             if(days < 364) days++;
486             else days= 0;
487         }
488     }
489 }
490 
491 /* Function: Store Into Bank
492    Stores a code into code bank at a specific position starting with
493    a specific offset of code.
494 */
495 void store_into_bank(char position, char code[], char offset)
496 {
497     unsigned char i;
498     for(i= 0; i < 45; i++) code_bank[position][i]= code[i+offset];
499 }
500 
501 /* Function: Verify Code
502    This function takes a code for an argument and checks
503    to see if the code matches with any of the codes in
504    the code bank. Returns a 1 if there is a match and a 0 
505    otherwise.
506 */
507 unsigned char verify_code(unsigned char char1[])
508 {
509     unsigned char i= 0;
510     unsigned char j= 0;
511     unsigned char code_match= 0;
512     unsigned char element_match;
513     while((code_match == 0) && (i < NUM_CODES))
514     {
515         if(bank_status[i]==1)
516         {
517             element_match= 1;
518             while((element_match == 1) && (j <45))
519             {
520                 if(code_bank[i][j] != char1[j]) element_match= 0;
521                 j++;
522             }
523             code_match= element_match;
524         }
525         i++;
526     }
527     return code_match;
528 }
529 
530 /* Function: Match Code
531    This function compares two codes by comparing the 
532    elements of each array. Returns a 1 if there if
533    there is a match and a 0 otherwise.
534 */
535 unsigned char match_code(unsigned char char1[], unsigned char char2[])
536 {
537     char i= 0;
538     char correct= 1;
539     while((correct == 1) && (i < 45))
540     {
541         if(char1[i] != char2[i]) correct= 0;
542         i++;
543     }
544     return correct;
545 }
546 
547 /* Function: Copy Code
548    Copies a code from source char2 to destination char1.
549 */
550 void copy_code(unsigned char char1[], unsigned char char2[])
551 {
552     unsigned char i;
553     for(i= 0; i < 45; i++) char1[i]= char2[i];    
554 }
555 
556 /* Function: Find Start Code
557    To find the start code, we must look for a sequence of 15 to 18 1's.
558    We run through the bit stream received from the card and look for 1's.
559    Anytime there is a 0, we reset the counter. Eventually the counter 
560    will get to 15 or more and then we have detected the start sequence.
561    Since we know there are 540 bits in a period of the card response, we
562    know the end of the sequence is 539 bits later.
563 */
564 void find_start_code(void)
565 {
566     int i= 0;
567     char sequence= 0;
568     char count= 0;
569     start_sequence= 0;
570     end_sequence= 0;
571     while((i < totalBits))
572     {
573         if(bit_array[i] == 49)
574         {
575             sequence= 1;
576             count++;        
577         }
578         else
579         {
580             if((sequence == 1) && (count >= 15))
581             {
582                 start_sequence= i-count;
583                 end_sequence= start_sequence+539;
584                 i= totalBits+1;
585             }
586             sequence= 0;
587             count= 0;
588         }
589         i++;
590     }
591     if (i == totalBits) start_sequence= -1;
592 }
593             
594 /* Function: Reduce Sequence
595    Once we have the 540 bit card response, we need to remove
596    the redundancy. The code looks something similar to:
597    00000111111000000111110000000000001111110000011111...
598    We recognize 5 to 6 bits as a single bit and a stream of 
599    10, 11, or 12 bits is two bits. Thus the code above would
600    decode to 010100101. There are 90 bits in the reduced form. 
601    This function transforms the redundant bit stream to the 
602    reduced form by detecting sequences of 5/6 and 10/11/12 
603    and creating a new array.
604 */
605 void reduce_sequence(void)
606 {
607     int i;
608     signed char j= -1;
609     unsigned char count;
610     unsigned char value;
611     i= start_sequence;
612     while(i <= end_sequence)
613     {
614         count= 0;
615         value= bit_array[i];
616         if(j == 89)
617         {
618             reduced_array[j]= value;
619             i= end_sequence+1;
620         }
621         else
622         {
623             while(value == bit_array[i])
624             {
625                 count++;
626                 i++;
627             }
628             if(j == -1) j++;
629             else if((count == 5) || (count ==6)) reduced_array[j++]= value;
630             else if((count == 10) || (count == 11) || (count == 12))
631             {
632                 reduced_array[j++]= value;
633                 reduced_array[j++]= value;
634             }
635         }
636     }   
637 }
638 /* Function: Manchester Decoder
639    This function translates the reduced bit stream that
640    is Manchester coded to raw data. This final bit stream
641    is the code we used to identify different cards.
642 */
643 void manchester_decode(void)
644 {
645     unsigned char i;
646     unsigned char j= 0;
647     for(i= 0; i< 90; i+=2)
648     {
649         if((reduced_array[i] == 49) && (reduced_array[i+1] == 48)) final_code[j++]= 48;
650         else final_code[j++]= 49;
651     }
652 }
653             
654 void initialize(void)
655 {    
656     char i;
657     /* PORTD.7 generates the 125kHz square wave using timer2
658        output compare. 
659        - PORTD is set for output
660        - Waveform Generation Mode is set to "CTC"
661        - Compare Output Mode is set to "Toggle 0C2 on compare match"
662        - Output Compare Register is set to 63
663        - Clock Select is set to "No prescaling"
664        Timer2 will increment at clock speed (16 MHz) to 63 and then reset.
665        Once it reaches this value, it will toggle OC2 to generate the 0V-5V
666        125kHz square wave. 
667     */
668     DDRD= 0xff;
669     TCCR2= 0b00011001;
670     OCR2= 63;
671     
672     /* RFIDIN is PORTA.0 */
673     DDRA= 0x00;
674     
675     /* Enable LED port as output */
676     DDRC= 0xff;
677     LED= OFF;
678     
679     /* Setup Timer0
680        - Turn on timer0 ISR
681        - Waveform Generation Mode is set to "CTC"
682        - Output Compare Register is set to 249
683        - Clock Select is set to "Clock/64"
684        - Initialize the counter to 0
685        Timer0 interrupt occurs every millisecond.
686     */
687     TIMSK= 0b00000010;
688     TCCR0= 0b00001011;
689     OCR0= 249;
690     TCNT0= 0;
691     
692     
693     /* Setup the USART
694        - Enable the Transmitter and Receiver
695        - Set the Baud Rate to 9600Hz
696     */
697     UCSRB = 0x18;
698     UBRRL = 103;        
699     
700     /* Initialize variables */
701     start_flag= 0;
702     curr_sample= 0;
703     totalBits= 0;
704     sample_buffer= 0;
705     code_count= 0;
706     check_receive_timer= CHECK_RECEIVE_TIME;
707     mode= NORMAL;
708     
709     /* Initialize Code Bank Status */
710     
711     for(i= 0; i < NUM_CODES; i++)
712     {
713         if (bank_status[i] == "") bank_status[i]= 0;
714     }
715     
716     /* Enable Interrupts */
717     #asm
718         sei
719     #endasm        
720     
721     /* External pin interrupt
722        - Enable in GICR
723        - Set for rising edge
724        - Clear interrupt flag
725        - PORTB as input
726     */
727     GICR= 0b00100000;
728     MCUCSR= 0b01000000;
729     DDRB.2= 0; 
730     
731     /* Send a starting message to hypertrm */
732     putsf("
Initializing the Reader...
");
733       r_ready = 0;
734       t_ready = 1;
735       gets_str();
736       
737     /* Initialize the data and time by prompting admin */
738       putsf("Please enter time in the format: DDD:HH:MM
");
739       
740       while(r_ready == 0){};
741     r_days[0] = r_buffer[0];
742     r_days[1] = r_buffer[1];
743     r_days[2] = r_buffer[2];
744     r_days[3] = '';
745     r_hours[0] = r_buffer[4];
746     r_hours[1] = r_buffer[5];
747     r_hours[2] = '';
748     r_minutes[0] = r_buffer[7];
749     r_minutes[1] = r_buffer[8];
750     r_minutes[2] = '';
751     sscanf(r_hours,"%d",&hours);
752     sscanf(r_minutes,"%d",&minutes);
753     sscanf(r_days,"%d",&days);
754     gets_str();
755     seconds = 59999;
756     sprintf(t_buffer,"Current Date and Time: %03d:%02d:%02d
",days,hours,minutes);
757     puts_str();
758     while(t_ready == 0);
759     
760     putsf("Please type h for help
");
761 }
762 
763 /* Complete receiving from hypertrm */
764 void gets_str(void)
765 { 
766   r_ready = 0;
767   r_index = 0;
768   UCSRB.7 = 1;
769 }
770 
771 /* Begin transmitting to hypertrm */
772 void puts_str(void)
773 {
774   t_ready = 0;
775   t_index = 0;
776   putchar(t_buffer[0]);
777   UCSRB.5 = 1;
778 }
View Code

 

RFIDler - An open source Software Defined RFID Reader/Writer/Emulator

http://adamsblog.aperturelabs.com/2013/08/rfidler-open-source-software-defined.html

I've said it before and I'll say it again:

I don't understand how it works.

Not only that, but I don't want to understand, and I don't need to understand!

Well, that's not quite true - I need to understand enough to know

which bits I don't need to understand, but then that's it! Stop! Enough already!!!

RFID is, as with a lot of these technologies, mysterious by nature.

It relies on strange physical phenomena like "induction" and "electro-magnetism" and "near-fields", etc.

Yes, what we Code Monkeys like to call "Magic Moonbeams".

It's all very nasty and analoguey. I don't like it. Give me the nice binary digital, please!

So in my never ending quest to find tools that convert the scary analogue world into a nice friendly digital one,

RFID is clearly a prime candidate.

There are lots of RFID/NFC devices out there these days, and you've probably got one or two in your pocket right now -

whether it's your car keys, alarm fob, door entry card, credit card, etc.

Of course, there are endless varieties of RFID readers to access them with,

but what I'd like is something that reads them all, and meets my standard criteria: small and cheap.

To be fair, there are plenty of readers out there that seem to meet this criteria.  

You can buy a simple RFID USB reader for as little as 10-15 quid,

but you'll find that it's of limited use as it will almost certainly be dedicated to one 'standard',

and you'd therefore need dozens of them to be able to read 'everything'.

There are also tools like the Proxmark3 that are truly universal and can read pretty much anything,

but, unfortunately, these are not cheap.

However, it is certainly worth looking at the PM3 as it really is quite an amazing bit of kit -

often described as the 'Swiss Army Knife of RFID',

it is versatile enough to read pretty much any tag in the standard LF/HF frequency ranges,

so will at least be useful in giving us an idea as to what we're up against...

We'll be using it later to look at some specific examples.

So, going right to the beginning, what does an RFID tag actually do?

Well, it depends. There are basically two functions, and the rest is 'details':

Firstly, pretty much every RFID tag will IDENTIFY itself. That is function one. 

Secondly, some tags will store DATA. That is function two. 

The 'details' revolve around how it does those two things - is it blindly spitting out an ID and/or DATA,

or is there some security or other command structure built around it?

That's the simple view, and if you want the longer, more detailed explanation,

there are entire volumes written about it. The 'details' can run into hundreds of pages, so I'm not going to even start.

My goal, here, is to talk about the low level fundamental communications

that seperate the evil analogue underworld from the lovely, friendly digital fairy garden, where we all like to play.

And it all begins with our friend "induction".

At the very low level, RFID/NFC relies on the fact that if you energise a coil

and place another coil near it, the second coil will pick up some of that energy through induction.

Moreover, the two coils become magically (or magnetically, depending which world you come from)  'coupled',

so it's possible for the second coil to effect the voltage on the first, and it does this by shorting itself out.

If it does so, there will be a drop in the voltage on the first coil, and this is called "DAMPING".

That's it. In a nutshell, that's how RFID works:

the coils 'talk' to each-other by either sending energy (from the READER), or causing an energy drop (from the TAG).

In a little more detail, what happens is this (and for the purpose of this section, we'll assume the TAG is the dumbest type that just spits out an ID):

the READER energises its coil by powering it on and off repeatedly.

For a standard LF system, this will be 125,000 times per second, or 125KHz.

This is known as the 'CARRIER'.

The TAG, when placed in this field, will scavenge some power from its now inductively-coupled coil and come to life.

If the reader needs to send an extra 'wake up' (or any other)  command,

it can do so by simply switching it's CARRIER off altogether for short periods.

The TAG stores enough energy that it can keep running for long enough to interpret these gap periods,

even though it's temporarily lost its power source.

The length of the CARRIER signal between the gaps will usually signify a 0 or a 1,

so like this the READER can send binary messages.

In other words, they're talking 'ASK':

Amplitude Shift Keying.

Data is sent by shifting the amplitude of the signal.

More accurately, they're talking 'OOK':

On Off Keying.

 

A message going from the READER to the TAG is signalled by the CARRIER being ON or OFF

and the message coming back is either DAMPED or UN-DAMPED.

Things get a lot easier to understand if we visualise them, so here is a plain 125KHz CARRIER viewed from an oscilloscope:

And here is the READER sending a message to the TAG:

In this case it's using a long pulse to represent a '1' and a short a '0', so the message here is '11000', or 'START_AUTH' if you're a HITAG2.

As I mentioned, the TAG can also send messages back to the READER by shorting it's own coil and DAMPING the READER's coil.

The result looks something like this:

 

 

 

 

 

It looks quite similar to when the READER sends a message,

but you'll note that it can't reduce the CARRIER all the way down to nothing -

instead it's either a 'DAMPED' or 'UN-DAMPED' wave.

This is because it's not directly controlling the voltage on the READER's coil,

only affecting it through induction.

However, it is clearly still perfectly readable.

In this case, if we take the damping action as a '1' and non-damped as a '0', we get '1010101010010110011010',

which is, in fact, a MANCHESTER encoded bitstream.

So what has MANCHESTER got to do with anything?

Well, this is where it starts to get interesting - if you look in details at the specs for these kind of tags,

you'll find that they mention modulation schemes such as 'Manchester', 'Bi-Phase', 'FSK', 'PSK', 'NRZI', 'ASK'...

WTF? We've already established that the two devices can only do ASK,

so where does all this FSK/PSK/Manchester malarky come from???

This is where I think a lot of the confusion lies.

Once you start trying to do something other than what a particular manufacturer intended with an RFID tag,

you quickly get lost in a mire of conflicting modulation schemes and other irrelevancies.

This particularly applies to the readers as well - if I want to find a reader that gives me access to the raw data, just forget it.

Most readers want to go that little bit further and fully de-modulate the signal for me.

And to do that, they need to know exactly how the signal was modulated in the first place...

And to know that, they need to know which standard tag type they're going to read, and so on, and so on...

It's easy to see how we've ended up with this situation.

A lot of these devices were built back in the days when computing power didn't come cheap,

so the designers tried to do as much as possible in the analogue world before handing over to the digital at the last moment.

This meant building circuitry that not only handled the low-level ASK communication,

but also the secondary modulation schemes that were layered on top.

The RF world have been going through a revolution, moving to SDR, in which they're doing the same thing -

handling only the very low level analogue stuff in circuitry and

doing the rest in software on small powerful microcomputers, and it's time we did the same for RFID!

So when I asked our tame (well, nearly... he's mostly house-trained and has at least stopped biting the postman) Chip Monkey

to build me an RFID reader that only gave me the lowest level data,

he was somewhat surprised to be unable to find an existing reference circuit that exactly fit the bill.

We both thought it would be simple, but no - every circuit was tied to a further demodulation scheme - FSK, PSK, Bi-Phase, etc.,

and some of them were horrendous!

They look more like they're designed to take you to the moon, rather than read a few bits from a wibbly coil!

For example, here is Microchip's 200 page document with separate example circuits for ASK, PSK and FSK.

However, after a bit more searching, we found a 'simple' design:  'The World's Simplest RFID Reader'.  

This led to an 'improved' version, although it's still described as a 'DIY FSK RFID Reader', so possibly still not quite what we're looking for.

But hang on a minute, surely it doesn't matter what type of tag they were using it to read.

We've established that the low level communication is ALWAYS using ASK,

so the final demodulation of FSK/PSK/Manchester/Whatever is going to be handled by the microprocessor,

so we should be able to use this circuit to do any type of tag, not just FSK.

So this brings us back to the question of these 'extra' modulation schemes.

Where do they come from, and how do we deal with them?

Let's use the PM3 to take a look at the raw data we get from each type of tag...

The PM3 will act as a basic reader circuit and filter out the CARRIER, leaving only the effect of the DAMPING.

This is an ASK modulated tag:

Exactly what we would expect - a straightforward square wave created by the CARRIER either being DAMPED or UN-DAMPED.

Here is an FSK tag:

Note the two different pulse types - thin ones and fat ones, so we really are seeing different frequency pulses. Weird! 

And the strangest yet, PSK:

Now that's just crazy. What the hell is that all about?

First, it helps to understand exactly what we're looking at.

The green line is showing us the voltage on the READER coil, after the tag has done it's DAMPING (or not).

We don't really care what the scale is, just that the bottom of the screen is 0 volts or fully DAMPED

and the top of the screen is *some* volts or UN-DAMPED.

The circuit that produces this is effectively looking for the presence of a 125KHz CARRIER and raising or lowering the output line accordingly.

How it works is irrelevant. I just don't care. That is Chip Monkey's problem! :)

So now I know what the lines mean, the question is how they get to look like they do.

The first one is simple:

ASK/OOK modulation is either ON or OFF,

so we get a line at the top of the screen when we're UN-DAMPED and a line at the bottom when we're DAMPED.

If we think of what the tag's doing as creating a mask which either lets the CARRIER through or not,

this makes perfect sense. Here I've marked the image with a red blob whenever the tag is DAMPING its coil:

So far, so obvious. Let's look at the FSK signal in the same way:

So now, instead of signalling data directly by DAMPING for a 0 or a 1,

we are creating a whole new CARRIER by DAMPING for different periods and allowing short or long pulses of the original CARRIER through.

The fully DAMPED signal doesn't mean anything, it's the width and number of pulses of the UN-DAMPED signal that carries the information.

In this case, 5 fat spikes means '0' and 7 thin spikes means '1' (or 12 thin spikes means '11'), so we've got '0-1101-0'. Neat!

OK, so what about the crazy PSK guy?

Well, this is interesting because what's actually happening here is that the coil is being DAMPED relative to the frequency of the original CARRIER itself.

In this case, it's going at exactly half the speed, so it's effectively blocking half the CARRIER pulses,

and most of the time producing a signal that isn't quite strong enough to reach the top of the screen,

and doesn't have time to reach the bottom either, which gives us the chunky bits in the middle.

However, whenever there is a phase change, the resulting half-bit repetition means that the DAMPING or UN-DAMPING now lasts for a full clock cycle of the original CARRIER,

so we get a little burst of HIGH or LOW popping through, hence the spikes.

In this images I've marked the mask in pink for where we're DAMPING 50% of the time, and the phase shifts are red or black as normal.

We can see that the fully DAMPED sections line up with the LOW spikes and the UN-DAMPED with the HIGH.

It's harder to read these by eye, but basically whenever there is a phase change (i.e. a spike in either direction), the bit value changes.

If there is no phase change, the bit value stays the same.

The number of bits depends on how long the gap is between the spikes,

so if one was to overlay a grid and you knew how long a bit period was,

you could simply count off the periods between bit changes and you've got your bitstream.

 

 

If we assume we're starting with a 0, this decodes as: '01101010001111100111000100010110000111010011100101101100001'. Easy-peasy! :)

Great, so now we understand what's going on, let's see if the circuit we've found will do the job...


As it turned out, the answer was 'not quite' for two reasons:


1. It couldn't handle the 'down' spikes of PSK.


2. As soon as we started playing with it, we were hit with a flurry of feature-creep! Yes, we wanted it to do more...


For a start, why have only a reader when you could also have a writer?

Unlike some technologies, there is really no difference in the RFID world between a reader and a writer.

As long as the reader can send signals to the tag, it can send 'write' and 'data' commands,

just as easily as reading the tag's emissions. 

Well, almost as easily - it just needs to be able to switch off it's coil as well as modulating it.

Clearly that's not an issue.

And why have only a writer when you could have an emulator?

Again, the only fundamental difference between a TAG and a READER/WRITER is that one energises it's coil and the other grounds it.

Everything else works pretty much the same, so given the right hardware, we have the perfect platform for a Software Defined device...

So Chip Monkey built us one. And it was good.

In fact, it was so good that we decided that instead of just publishing the schematics and blogging about it,

we would try and give it life and free it into the world... 

Proximity Cards

http://cq.cx/prox.pl

[There is a better device, although the writeup for this one is longer. It took me a month of evenings to clone my first Flexpass, with basically no equipment. Using my latest hardware, I was able to clone a Verichip—which, like the Flexpass, is an ID-only tag with no security—with only a few hours' work.]

*              *              *    

Lots of companies use proximity cards to control physical access. An employee holds their card within a few inches of the reader; the reader receives a unique id from the card and transmits it to some central computer that tells it whether or not to open the door.

This is rather magical, considering that the tag is credit card-thin and contains no battery. The trick is the same as for RFID tags. The reader constantly transmits a rather strong carrier; the tag derives its power and clock from this carrier, kind of like a crystal radio. The tag changes how much carrier it reflects back at the reader—loosely, it makes the circuit across its antenna more like a short or more like an open—to transmit its code. The reader and the tag both have antenna coils tuned to the carrier frequency; they work like a loosely-coupled resonant transformer.

Or at least that's the theory. I couldn't find any credible documentation on the protocol used by the particular proximity cards that were available to me (Motorola's Flexpass). I did find a datasheet that claimed that they worked with a 125 kHz carrier. I wound a couple dozen turns of magnet wire on a 4" form, taped it to a reader, and 'scoped the coil. There was indeed a 125 kHz sine wave, large, a few volts peak to peak. The cards did, at least, work at 125 kHz.

A Card Reader

The next step was to build a reader. The reader must transmit a fairly powerful carrier; figure at least tens of milliamps through a coil of about a millihenry. This is easy: just blast a 125 kHz square wave into the reader coil. It's a tuned circuit so it will pick out the 125 kHz component very nicely. Then, we want to look for slight (at least 40 dB down) changes in the coil current. We do this by sampling the voltage across the tuning capacitor and feeding it into a detector circuit.

Tags can use lots of different modulation schemes—PSK, FSK, plain AM. I decided to build an AM reader, assuming that that was the most likely modulation scheme, and that if the cards used something else then I could probably do the detection in software.

 

B1 models the voltage on the reader cap when the tag is backscattering a 1/π kHz square wave. D1 and C1 form a leaky peak detector, producing a 125 kHz sawtooth whose peak is determined by the peak value of B1 during that cycle. R2 and C2 are a low pass filter, to even out the sawtooth quality of our measured amplitude. C3 and R8 AC-couple the signal down to ground; otherwise it would be hard to work with because its average value is around the amplitude of the sine wave, which will be around a hundred volts (V = IZ = IjωL = (50 mA)×j2π×(125kHz)×(1.6 mH)).

U4 buffers the signal and amplifies it a bit. U3 is a Sallen-Key low-pass stage. U1 is a comparator, to turn the measured amplitude into something that can drive a digital input. R10 and R11 add hysteresis, which later turned out to be very important.

I don't think there's anything very novel about my circuit. I realized from a Microchip app note that the key is to start with a peak detector. I later found Microchip's reference design for an ASK reader. It's fairly close to mine: peak detector, passive low-pass, AC couple it down, active low-pass, comparator.

I built the reader on two printed circuit boards. The largest one contains a PIC16F877, used to generate the 125 kHz square wave, and the circuit to drive and tune the coil. The reader coil is a ~1.6 mH inductor wound from about 140 turns of 30 gauge magnet wire on a 2.5" square form.

The smaller board implements the detector circuit shown above. Both are single-sided boards made on my milling machine.

So I placed a Flexpass card on top of my reader coil, and I watched the output. Sure enough, I got modulation; alternating positive- and negative-going pulses, unevenly spaced, like what you'd get if you took a serial bitstream and high-pass filtered it. This was very encouraging.

A Card Simulator

I designed a tag that could produce identical AM when I tested it against my reader. This was pretty boring. I used a micro and alternately tri-stated or asserted (one low, one high) two GPIOs connected to one terminal of the tuned coil, with the other antenna terminal grounded. My tag produced perfect waveforms on my reader. It did not, however, work with the Motorola readers—the door did not open.

 

So then I tried a lot of things. Eventually I took a closer look at the output of the peak detector; it was a 62.5 kHz sawtooth, not 125 kHz. The card was transmitting PSK, attenuating every other peak, but my peak detector didn't drop fast enough to follow it. The AM that I saw was a modulation artifact; I expect (and it's intuitive) that it can be shown that those sorts of amplitude variations can be obtained by bandpass-filtering a PSK signal.

A bit time is 256 μs. The tag's id is periodic over 64 bit times, 16384 μs. When there is no phase shift, the signal is a 125 kHz sinusoid slightly modulated by a 62.5 kHz sinusoid. The modulation is greatly exaggerated in the figure.

 

 

 To indicate a bit transition, the tag inserts a phase shift of π. There is always an even number of carrier cycles between phase shifts, so that if the most recent phase shift was achieved by skipping a small-amplitude cycle then the next phase shift will be achieved by skipping a large-amplitude cycle.

 

 

Since a bit time is 256 μs, phase shifts are an integer multiple of 256 μs apart. Thus, in the above picture, we could have t = 256, 512, 768, ... μs.

A code consists of 64 bit times. There are no transitions for the last 29 bits for all but one of the cards that I have tested. Possibly the one card is in a newer format, or possibly it's just weird. There appears to be some structure within the bits—if I get some of the bits wrong then the reader doesn't even beep, but if I get others wrong then the reader still beeps but the door doesn't open. I haven't had any need to figure out which are which though.

My first tag sort of disintegrated (too many flywires) so I built a new combination reader/tag. In tag mode I chose to ignore the reader's carrier: I just blast my own modulated carrier at the reader. This works perfectly well, and you'd expect it to; if we're not in phase with the reader's oscillator then we will be in a few hundred milliseconds. (The beat frequency is ~1 Hz, for a variation of a few ppm, typical for a crystal).

A Toy With Blinking Lights

The hardware to pretend to be a tag is very simple. The hardware to read a tag is not much more complicated; I could do it with a micro, a quad opamp, and a dozen passives. This meant that I could build a combination card reader/simulator in a few square inches of board space. Add a couple of lithium batteries and some nice plastics and I would have a clever and mostly useless pocket-sized toy. Of course I couldn't resist building it.

For a sense of scale, the PIC16F628 (largest IC) and the opamp are both SOICs. There aren't any unreasonably fine-pitch components on this board; the tightest are the SuperSOT FETs (bottom left), with 0.95 mm lead spacing.

The hardware is very similar to the larger reader/simulator described above. I decided not to attempt PSK demodulation; I just detect the modulation artifacts and use the hysteresis, so that I know that if the comparator output has changed state since I looked at it last then there has been a phase shift since then. This sacrifices a bit of sensitivity but my read range is small enough (by design, for reasonable battery life) that this doesn't bother me. This hardware could also be used for AM cards, if I ever came across one.

The PIC can kill power to the detector section with a couple of FET switches. This plus the PIC's sleep mode means that I can do on/off in software without ruining my battery life.

The circuit is again built on a milled PCB, with one signal layer and a ground plane. The ground plane is split into analog (GND = 0V) and digital (V- = -3V) sections. It is powered by a pair of CR2032 lithium coin cells. They determine the thickness of the device; the batteries, in a holder, are 0.217" thick.

 

 

There are no connectors on the board because I couldn't find any low-profile surface mount connectors that I liked. Instead there are test points to program the PIC and tune the coil; I actually built a test/programming fixture (with pogo pins). This is pretty easy with a CNC machine. Power and coil leads are soldered directly to the board. The blue wires are mostly test points, for debugging only. The 8 pin header connects to my PIC programmer.

 

 All the plastics were routed from sheet on my milling machine, using an 1/8" carbide straight bit intended for use in wood. Yes, the workpiece is held to the table by carpet tape; this is much cheaper than a vacuum chuck.

 

Both the top and bottom screw into the core with #2 tapping screws. I put a lot of effort into finding a local source for a methylene chloride solvent cement (so that I could weld the bottom on instead of screwing it) but I gave up after many failures. I first tried machine screws but small-diameter machine screws tend to have too many threads per inch to work in plastics. Next time I'd probably look for threaded inserts.

I left the wires long so that I can remove the the board from the plastics without desoldering anything. This is necessary to put it on the programming jig, and it helps when I'm trying to figure out whether I have a circuit-does-the-wrong-thing problem or a 125-kHz-pickup-on-everything problem.

The user interface comprises four LEDs and the two tact switches. The software can currently read a card, store a single id, transmit that id over the air to a reader, blink out that id on the LEDs, and accept a new id on the tact switches. There is also a “sniff” mode, in which the detector is active but the coil is not powered; this allows me to read a card while a legitimate reader is powering it. (The read range of the cards is limited by the tag power requirements, not by reader sensitivity; it goes up substantially when another reader is powering the card.)

Most of the software is pretty straightforward. To transmit the id, I must apply a square wave with, on average, every other cycle missing. I do this entirely in software, flipping a GPIO every 4 μs. I managed to make this work on a 4 MHz device (4 cycles between edges); it's much easier on a 20 MHz device, though. The antenna is resonant around 125 kHz, so that it effectively bandpass filters the pulse train that I generate. In the figure below, the blue trace is obtained by passing the red trace through a bandpass filter centred at twice the frequency of the pulse train:

For the reader functionality I configure the PWM module to generate the unmodulated square wave so that I can be a bit sloppier with my timing.

I sync on the word by waiting for an edge after a long idle period (though this is of course unnecessary; I only need bit sync). Then I read the word into memory. Then, I read the word ten more times, comparing it against the recorded copy each time; if they all match then I decide that it's right, else I lose sync and try again. This works reasonably well, but if the card is held just barely outside the read range then I will eventually false-sync. This is bad, because there is no other verification. Legitimate readers can false-sync with no major repercussions, since they would just fail to open the door; the user would stand there waiting until the card was read correctly.

Security Implications

I can copy a proximity card at least as easily as I can take an impression of a key. This means that it's not a very good idea to reuse visitor cards without changing the id (and that it doesn't really matter whether you get the physical card back from the guy you just fired).

More insidiously, it's quite practical to read someone's card without removing it from their wallet. A bit of deliberate clumsiness, a reader up my sleeve, and I would have little trouble cloning anyone's card. I could also exploit the fact the distance at which the cards will be powered is less than the distance at which they can be read; if another reader is exciting the card then my reader can read that card from the other side of a wall!

This means that a sniffer concealed somewhere near a legitimate reader could intercept real transactions at a significant distance. This sort of attack is particularly good because the card repeats its id over and over as long as it is in the field, so that I could use signal processing techniques to combine multiple copies of the pattern to further improve my read range. This is easy—if I sample all 64 bits of the id then I don't have to get word-sync, and if I oversample then I don't even have to get bit-sync. Even if I capture the id with a few bit errors it is still useful; I could try the captured id, then every id with a Hamming distance of 1 from the captured id (one bit flipped), then 2, and so on. One or two bit errors would take seconds; three would take minutes.

If I were willing to spend money on a four (or even two) layer board then I could build a sniffer/reader much smaller than anything shown above. If I used black Lexan (or even acrylic) for the case then the device would look less like something that an image-conscious terrorist might carry. This would make it much easier to carry out the attacks outlined above.

All of these attacks can be stopped with a challenge/response scheme. I've seen brochures for cards and readers that do this; I guess it's not just a marketing gimmick.

Hardware Notes

The coil driver consists of an N/P FET pair, with the FETs working as switches. For my initial reader I connected the drains together, like in a CMOS inverter. This drove the RLC circuit that was the coil, the tuning cap, and a current-limiting resistor. This has two flaws. First, it has a software self-destruct mode; if the input floats between the positive and negative rails then both FETs will conduct, shorting V+ to V-, and the FETs will get very, very hot. This caught me because the PIC tri-states all its GPIOs when it's being programmed in system.

There was a bit of a shoot-through problem too; the switching transients got in to everything. Smaller FETs would actually have been better, but I just put some resistance between the drains, because I needed some resistance to limit the current anyways.

My layout seems to be reasonably good. There's hundreds of millivolts of noise on most of the signals around the PIC, but noise on the detector signals is in the tens of millivolts. More bypassing might have cleaned things up further. Not placing the circuit inside the antenna might also have helped....

The detector is rather poor. When reading (or sniffing) from a large distance, it would be nice to be able to turn down the hysteresis to get some chance at a read. This is not currently possible. It's probably not worth messing with the envelope detector though; it would be better to build a proper PSK detector (by correlating and then integrating the peak detector output in hardware, or with a sharp notch filter to reject the 125 kHz component, allowing me to work only with the sidebands.).

The coin cells probably aren't a very good choice for this application considering the high peak currents—Panasonic's datasheet doesn't even mention what happens if you try to draw more than a milliamp. I don't know what would be better, though; thin batteries are hard to find.

The opamps that I use (TLC2274s) are not low-current. I knew this when I chose them but I assumed that they would only need to be powered when the coil was energized. In sniff mode this is not true; the coil is not driven, and I could even put the PIC to sleep and wake on an edge from the detector. Next time, I guess.

I was careless when I designed the power switching stuff; there were a couple of leakage paths that added almost 60 microamps of off current. I was able to fix this by depopulating a couple of components and faking out the functionality that they used to provide in software. Off current is about 4 μA. The CR2032s can deliver about 200 mAh, for a standby life comparable to the shelf life of the cells.

The presence of metallic objects inside the antenna probably does weird things. Certainly it detunes the coil a bit, and even after I tweaked it back to resonance the voltage on the coil was smaller (indicating that some of my battery life is going to eddy currents in the mounting screws).

I had a few interesting problems relating to 125 kHz pickup from the read coil. The wires from the board to the coil are quite vicious; seriously bad things happen if they rest on the analog traces.

I had a lot of trouble machining the polycarbonate until I got my feeds and speeds right. Too slow of a feed for your speed is very bad; the plastic melts, the cutter loads, friction increases and chip ejection goes to nothing, and you get thermal runaway. Once I figured that out everything went quite easily. Surface finish with an 0.015" finish pass was acceptable as machined almost everywhere. Where it wasn't I used the non-serrated edge of a hacksaw blade to clean it up.

Acrylic makes a nicer case than polycarbonate—it's more rigid and less prone to scratching. An acrylic case probably wouldn't survive a two foot drop onto concrete, though.

Future Plans

The toy described above is nice, but it could be better. I believe that I could sample the peak detector output directly (after AC-coupling it down) and do the demodulation in software. I have a very clever idea that uses the PIC's comparators and voltage reference module to do PSK detection, possibly one so clever that it works only in simulation. This would allow me to lose the opamp entirely. I could drive the coil from a few GPIOs tied together, at the cost of read range. This would get the design down to the micro plus some passives.

I'd also like to get rid of the split supply and run from a single 3 V rail. I think I'd have to do some sort of trick with multiple coils (like a transformer) to get a reasonable input impedance without an unreasonably high Q.

Alternatively, I could build a long range sniffer (better detector, one foot diameter read coil, enough current that it's just on the edge of melting, a motorcycle battery to power the thing...). This wouldn't be nearly as cool as a smaller version of my toy but it would be better for convincing people that the cards are insecure.

October 2003, Waterloo

 

Mivo- RFID based mobile payment system

Port/Pin Configuration on MCU:

Pin D.7 - 125[kHz] square wave to generate the RFID carrier frequency.
Pin D.3 - External Interrupt 1 to accept filtered data from the RFID reception circuit.

 

Transmission- RF Choke and Power Amplifier: The circuit represents a 125 [kHz] square-wave signal fed into an RF choke followed by a current buffer and half-bridge amplifier.

The RF choke is used to filter majority of the high frequency signals in the square-wave, passing through only the fundamental harmonic (sine-wave) of 125 [kHz]. We could have generated a sine-wave in the MCU and passed that directly to the power amplifier however this would have been much more taxing on the MCU (as we would have had to implement a sine-wave table, etc). Generating a square-wave on the other hand is extremely simple and uses the MCU resources to a bare minimum (please see the RFID “Software” section for details). Diodes are used here, in order to remove crossover distortion (transients due to the half-bridge transistors switching between on and off states).

All of the components in this circuit were found in the ECE 4760 lab apart from the 1 [mH] inductor, which we ordered through DigiKey (part numbers can be found in the “Parts List” section).

Reception Antenna: This is definitely one of the most crucial elements of the circuit and we made sure to follow the recommendation (by the microID guide) of using a series L-C resonant circuit, as opposed to a parallel R-C circuit. This topology ensures that maximum current flow occurs at maximum resonance and also provides for a simpler design.

The resonance frequency is given by the equation:

 

As the ECE 4760 lab had 1nF capacitors in abundance, we decided to use C = 1 [nF]. This, along with (resonant frequency) f0 = 125 [kHz] gave us a value for L ≈  1.62 [mH].
Now, for the construction of the antenna we decided to go for a rectangular shaped antenna as opposed to a circular one because from our preliminary end-product designs (i.e. aesthetic box) we knew that we would need a rectangular antenna. As far as the dimensions of this antenna are concerned, we decided that we would go with an antenna about the size of our whiteboard (as our box would have to be at least as large as the whiteboard (or solder board).

The [microID guide] came in handy here as well because we could simply use the equation for calculating the inductance of a coil. The equation for the inductance of an N-turn rectangular coil, with multilayer is:

Here: L- Inductance in [µH], {x,y}- {Length, Breadth} of coil in [cms], h- Height of coil in [cms], b- diameter of wire in [cms].

The values we used: L = 1620[µH], {x, y, h} = {4.2, 15.2, 1} [cms], b = 0.32[cms] (AWG28 lacquered copper wire from the ECE 4760 lab). Solving this equation for N gave us N ≈ 83 turns. Oscilloscope results below show the inductance coil output, both with and without RFID tags being present.

Reception Circuit: Since the capacitor (in series with the coil) is grounded, the carrier signal (125 kHz) is filtered out to ground after passing the antenna coil. The circuit provides minimum impedance at the resonance frequency. This results in maximizing the antenna current, and therefore, the magnetic field strength is maximized. 
We now need to half-wave rectify and R-C filter the received signal. The inductor, capacitor and diode (and the other bottom parts in the circuit) form a signal receiving section. The voltage drop in the antenna coil is a summation (superposition) of the transmitting signal and backscattering signal. The diode is a demodulator which detects the envelope of the backscattering signal. The diode and capacitor (2.2nF) form a half-wave capacitor-filtered rectifier circuit. The detected envelope signal is charged into the 2.2nF capacitor. The resistor provides a discharge path for the voltage charged in the capacitor. This voltage passes active filters and the pulse shaping circuitry. Finally this signal is passed through a capacitor (1nF) which knocks off the DC offset.

Filtering Circuit: Next we use active Twin-T and Butterworth filters using the TL084CN (high-speed) Op Amp. The first filter acts as an isolator with bandwidth being 10[Hz] – 20[Hz], and unity-gain for all frequencies outside the band. The second filter introduces some more gain into the pass-band. Next, the signal goes into a Butterworth filter with sharp roll-off. This filter gets rid of any high-frequency components in the signal.
Finally, the filtered signal is passed through a comparator to generate a square-wave type signal. The final result is that we get very clean 12.5[kHz] and 15.625[kHz] frequencies out of the system.
One of the major differences in our design from the previous year’s design was that we got no signal when a tag was not present near the coil. This is great because we didn’t need any hack to ignore this signal.

Oscilloscope outputs from comparator (12.5[kHz])

 

Oscilloscope outputs from comparator (15.625[kHz])

Note: We did not simulate any of these circuits simply because we were using a commercial guide (presumably thoroughly tested)

and we were lucky to have the Sp 2006 group’s simulation results.

In fact, we devoted our time to trying out different values of components in order to achieve the best performance.

For further details (such as how to increase the range, etc, please refer to the microID 125kHz RFID System Guide).

Hardware/Software Tradeoff & Data Creation: 

We debated whether we could take the filtered signals and feed them directly to the MCU or if we would need some more hardware decoding. However, in order to accurately measure the frequency of the incoming data we would have to sample at a rate of about 125[kHz] and this would take up a lot of the resources of the MCU (at a clock rate of 16[MHz] this would give us 128 cycles to do everything else). We realized this because Craig and Ricardo had explained this pretty well in their report and also because we knew that it would be much faster to do this in hardware. Now, the microID RFID guide provided a really smart way that made use of D flip flops and a decade counter. The circuit is designed as follows:

Digital Component of RFID Circuit 

The explanation for this circuit is taken from Craig and Ricardo’s webpage as they did a great job of documenting and explaining the logic:

The comparator output serves as the clock for the first D flip-flop, which also takes logic 1 as its D value.

On the rising edge of the comparator clock, Q is immediately set to 1.

However, simultaneously ~Q goes low and clears the flip-flop.

This creates an extremely short pulse which serves as a reset for the decade counter and clock for the second flip-flop.

The decade counter is just a 1-hot counter which takes a 125 KHz clock.

With every rising edge of this clock, the counter outputs the next pin to logic 1;

so typical output would look like (if one were looking at output pins 0-9 of the counter)

1000000000 0100000000 00100000000 etc.

However, this counter is being reset with every rising edge of the comparator output.

Thus, since we've already determined that 125 KHz/10 = 12.5 KHz is to be our frequency that represents logical 1,

all we have to do is check for the output on pin9 to know whether or not we see that frequency.

If the system is operating at either one of the other possible frequencies, the counter will be reset before pin9 can go active.

The pin9 output serves as input to the second flip-flop and also to the clock inhibitor, which keeps the 9th pin high until the counter is reset.

Because of this set-up, the Q output of the second flip-flop will remain logical 1 so long as modulating frequency is 12.5 KHz

and will drop down to 0 if its anything else.

Theoretically, this circuit should work perfectly.

However, experimentally it did not, and thus required a small modification.

The 100 KOhm resistor on the first flip-flop serves to lengthen the time it takes for the ~Q signal to get to CLEAR.

Since all transistors have some amount of natural capacitance, this forms an RC circuit of sorts with a set RC time constant for the signal to rise or fall.

As it turns out, this time was too short for the decade counter.

The original design from the reference guide specified only a 10KOhm resistor between ~Q and CLEAR.

With the 10[KOhm] resistor, pulse widths for the reset pulse were a mere 50 [ns] long, while the counter required at least 250 [ns].

This caused some very erratic behavior.

After many hours of debugging, we finally pinpointed the problem and replaced the resistor with the 100 [KOhm] resistor

which increased the pulse width long enough for the counter to correctly operate.

RFID Software: Once the data was converted into a digital Manchester signal by the RFID circuit, we then had to process it and convert it into a useful format to the microcontroller. For this, we decided to write a library for the RFID, called ‘rfid.c’, which dealt specifically with the task of receiving and interpreting an RFID signal. Unlike Ricardo and Craig’s design for RFID, we did not use a sampling method for Manchester decoding. Instead, we opted for a more robust method with less interrupts – a timing-based method. This method only interrupts when it has to – i.e. on a change of logic value, and then the microcontroller interprets the amount of time between interrupts in order to determine the value of the Manchester bit. To do this, we go through a sequence of events after turning on the external interrupt for any logic change:

  1. Wait until we receive a start sequence (extra long logic high)
  2. On next interrupt, decode the first Manchester bit: delay by a very short amount to ensure steady state, read the logic level.
  3. If time difference is short, save the read logic level into a data buffer only if it wasn’t recorded last time. If time difference is long, save the logic level.
  4. If anything was recorded in the previous step, add to the checksum such that it is sample number dependent (we used sample * i % 9, where i is the sample number)
  5. Repeat steps 2 and 3 until we read an end sequence (extra long logic low)
  6. Insert a ‘2’ at the end of the buffer, so we know where we stopped recording

The sample number dependent checksum was added as a feature since it is more likely to be wrong in the case of multiple bit errors. If we only added the binary values, we would have created a parity check, which may not have been sufficient in cases of a lot of noise. This allowed us to cut down on the number of redundancy checks by a factor of three, and still get the same reliable results, leading to a faster system. To better explain what we mean by ‘short’, ‘long’, and ‘extra long’: since Manchester encoding has a logic change at every data bit, the longest delay we could have would be 0110. That means that during the pair of 1s, it stays logic high for two half-cycles. The shortest delay we could have would be 0101, where the 1 stays high for only one half-cycle. For the RFID system, if we clock it at the same scale as the previous two, there is also another state, which is 111110. This is a very long time, easily detectable, and denotes the start of a data stream. Once we successfully fill the buffer, we ensure the reliability by checking it multiple times. We checked 20 times, which was way overkill, but we really wanted to minimize the chance of a wrong ID. In reality, two checks actually is sufficient for most of the time, with three checks delivering near perfect results. Since we are only reading a 44-bit value from the RFID tag, we can do the 20 checks very quickly (approximately one second in most cases). We saved each data bit as a char in a string, so then we just convert it into a long using bin2dec so it can be returned using less space than a string. One cool final note was that by doing Manchester decoding with the timing-based method, we were able to get the data by only connecting one wire for ground and one wire for data to the microcontroller!

Manchester Encoding [from Wikipedia]:

In telecommunications, Manchester code is a line code in which the encoding of each data bit has at least one transition and occupies the same time. It therefore has no DC component, and is self-clocking, which means that it may be inductively or capacitively coupled, and that a clock signal can be recovered from the encoded data. Essentially Manchester encoding works in the following way: A transition from 0 to 1 corresponds to a single “1” value and a transition from 1 to 0 corresponds to a single “0” value. Thus 01 -> 1, 10 -> 0, and the reduced bitstream is half as long as the original bitstream.

 

 

 

原文地址:https://www.cnblogs.com/shangdawei/p/3729375.html