RFID读写器的工作原理
RFID的数据采集以读写器为主导,RFID读写器是一种通过无线通信,实现对标签识别和内存数据的读出和写入操作的装置。
读写器又称为阅读器或读头(Reader)、查询器(Interrogator)、读出装置(Reading Device)、
扫描器(Scanner)、通信器(Communicator)、编程/编码器(Programmer)等等。
读写器工作原理
RFID读写器的基本原理是利用射频信号与空间耦合传输特性,使电子标签与阅读器的耦合元件在射频耦合通道内进行能量传递、数据交换、实现对标识对象的自动识别。
发生在读写器和电子标签之间的射频信号耦合有两种类型。
1、电感耦合(Inductive Coupling):变压器原理模型。
依据电磁感应定律,射频信号通过空间高频交变磁场实现耦合。电感耦合方式一般发生在中、低频工作的近距离射频识别系统中。
典型的工作频率有125KHz、134KHz和13.56MHz。识读距离一般小于1m,典型识读距离为10~20cm,也称为近场耦合。
2、电磁反向散射耦合(Backscatter Coupling):雷达原理模型。
依据电磁波的空间传播定律,电磁波碰到目标后反射,同时携带回目标信息。
电磁反向散射耦合方式一般发生在高频、微波工作的远距离射频识别系统、典型的工作频率有433MHz,915MHz,2.45GHz,5.8GHz。
识别作用距离大于1m,典型作用距离为3~10m,也称为远场耦合。
典型的阅读器包含有射频模块(发送器和接收器)、控制模块以及阅读器天线。
射频模块的主要功能是产生高频发射能量:对发射信号进行调制,并传输给标签;接收并解调来自标签的射频信号。
读写控制模块也成为控制系统,主要具有以下功能:
* 与应用系统软件进行通信,并执行从应用系统软件发来的动作指令;
* 控制与标签的通信过程;
* 信号的编码与解码;
* 执行防碰撞算法;
* 对读写器和标签之间传送的数据进行加密和解密;
* 进行读写器和标签之间的身份验证。
RFID读写器根据使用的结构和技术不同,可以分为只读和读/写装置,阅读器通常包含基础中间件软件,是RFID系统信息控制和处理中心。
阅读器通过电感或电磁耦合给标签提供能量和时序,一般采用半双工通信方式进行数据交换。
在实际应用中,可以通过Ethernet(以太网)或WLAN(无线局域网)等实现对物体识别的数据采集,处理及远程传送等管理功能。
Parallax RFID Card Reader - USB + Serial
- Reads 125 kHz Tags, EM41000 Family
- Power requirements: 5 VDC (powered by USB port)
- Communication: 2400 bps serial through virtual COM port
- Dimensions: 2.45" W x 3.25" H (62.2mm W x 82.5mm)
- Operating temp range: +32°F to +158°F (0°C to +70°C)
Phidgets RFID Quick Start Kit
The PhidgetRFID Read-Write reads RFID tags that are brought in close proximity to the reader and returns the tag identification number. Writing data to T5577 tags is also supported. RFID (radio frequency identification) systems use data strings stored inside RFID tags to uniquely identify people or objects when their tags are scanned by an RFID reader. These types of systems are found in many applications such as passport protection, animal identification, inventory control systems, and secure access control systems.
RFID Reader
The PhidgetRFID Read-Write supports reading and writing in 3 protocols; EM4100, ISO11785 FDX-B, and PhidgetTag. The PhidgetTag protocol simply stores up to 24 ASCII characters to the tag, eliminating the necessity for a table of corresponding tag numbers and names in your program. Phidgets sells EM4100 read-only tags that can be read with either of our RFID readers, and writable tags which can be written with the 1024 using any protocol. Any 3rd-party EM4100 or ISO11785 tags can be read.
Because passive tags require a strong RF field to operate, their effective range is limited to an area in close proximity to the RFID reader. The distance over which the RFID tag is usable is affected by such things as the tag shape and size, materials being used in the area near the reader, and the orientation of the reader and tag in respect to each other and in their operating environment. The smaller a tag, the closer it must be to the reader to operate.
The 1024 has two digital outputs, labeled "+5V" and "LED". These work the same as any other Phidgets Inc. digital output, except that the "+5V" output has a higher current rating. You can use these outputs to have an LED or buzzer to indicate when a tag read has occured.
Demo: Cloning a Verichip Yourself
Download
I have:
- schematics as PDF
- BOM, with Digikey part numbers
- board artwork (Gerbers and drill file)
- PIC source code (assembly for MPASM / IHEX binary)
Theory of Operation
Please refer to the schematic.
It's important to remember that I designed this device for absolute minimum parts count and cost.
I therefore made some fairly disgusting tradeoffs. A lot of what I did is terrible design practice.
I had originally intended to use the PIC with an ER (external resistor) oscillator.
It's okay for the clock frequency to drift within a few percent, since the tag always synchronizes itself off the reader's clock.
It therefore seems like there's no reason to spend money on a crystal. This turns out not to be true, though.
The phase noise of the ER oscillator is terrible.
I 'scoped the CLKOUT pin, with the oscillator running at 10 MHz (so CLKOUT is around 10/4 = 2.5 MHz),
and triggered off an edge of that square wave.
Using the delayed timebase, I looked at an edge about 100 us after the trigger point; it jitters over more than a period!
The frequency-selective antenna turns this phase noise into amplitude modulation, which raises the noise floor to a hopeless level.
I therefore cannot use the ER oscillator.
The INTRC oscillator is somewhat better, but does not divide well to produce 134 kHz (125 kHz or 143 kHz, large error).
This means that we need to use a crystal or a resonator; either will give good enough phase noise, and a resonator's cheaper.
I chose a 10 MHz ceramic resonator, for thirty-five cents.
The Verichip is designed to be read at 134 kHz, so we will divide our instruction clock by 19 to produce a 132 kHz carrier to transmit.
This isn't quite right, but it will be close enough.
When we are reading a legitimate Verichip (i.e., pretending to be a reader, to clone someone's implant),
the tag will derive its timing from the carrier that we transmit.
That means that it doesn't matter if we're a little bit off, because the tag will be off by that same amount.
When we are being read by a legitimate reader (i.e., pretending to be a tag, to pretend to be someone whose tag we've already cloned),
we will derive our timing from the carrier that the reader transmits, which we measure through R9.
In `read' mode, we transmit the unmodulated carrier that powers the tag.
This means that we need a high-power output buffer to drive the antenna.
This is all very low-frequency, so a couple of general-purpose transistors (Q1, Q2) will do the job fine.
They are configured as emitter followers here, to buffer the carrier output by the PIC's GPIO pin.
The GPIO pin can only source or sink +/- 20 mA; the transistors that we are using are rated for 200 mA, although we won't run them quite that high.
The information-bearing signal returned from the tag appears in the voltage across C1,
but we first have to separate it out from the carrier that we are using to power the tag.
We do this with a peak detector, followed by a passive filter (D1, C3-5, R5-8).
This produces a signal that we interpret using the PIC's comparator.
I do this in a somewhat ugly way. The signal from the antenna is AC-coupled so that it has a mean of zero volts (with respect to ground).
I apply this to one input of the PIC's comparator; the other input of that comparator goes to ground, through the VREF module.
This means that I am applying an input voltage below Vss to the PIC, which is outside the recommended operation conditions.
It works very well, though. The only problem is if the signal from the tag gets very strong,
because the protection diodes will clamp it asymmetrically about ground, and move the decision point.
It's difficult to couple to the tag's antenna well enough for this to be a problem, so I'm not very worried.
Since I don't know very much about the structure of the tag's ID,
it's difficult for me to come up with a good metric to determine whether I've read a valid ID.
There is presumably a CRC or something, but I haven't bothered trying to figure it out;
I have only a very small number of tags to test against, so it would be difficult for me to test any theory that I might come up with.
Instead I just read the ID several times, and verify that it is the same each time.
The current firmware reads the ID once, and then checks it three times.
This is a parameter that you can play with; more verifications gives increased confidence in the ID,
but also makes it more likely that we will reject a valid read.
In `simulate' mode, we listen for a carrier from a reader, and change the load across the antenna in such a way as to transmit our ID.
We can change the load across the antenna by either driving or tri-stating RB3.
When we drive RB3 low, we short-circuit the coil through Q2.
When we tri-state RB3, it cannot supply any base current for either Q1 or Q2,
so no collector current flows, so the coil is open-circuited.
The only trick is that we must listen for the legitimate reader's incident carrier,
because that is what gives us our sense of time.
We do this through R9, once again using the PIC's comparator.
The resistor is necessary because the voltage at the antenna might be much larger than the PIC's Vdd = 4.5 V;
without the resistor, a very large current would flow through the protection diodes on that input pin and destroy the microcontroller.
R9 limits that current to a safe amount.
Some current does still flow through the protection diodes, though;
if R9 gets too small then we risk putting the PIC into latchup, which would be relatively bad.
Also, comparator 1 stops working if too much current flows into the substrate from RA0.
This is well outside the manufacturer's recommended operating conditions.
Without R6, the current through the coil would drop to zero when we tri-stated RB3,
which means that the current through C3 would drop to zero,
which means that the voltage across it would drop to zero and we would lose our sense of time.
As long as some current always flows, this isn't a problem.
The device has no on/off switch; this is achieved in software.
The PIC can be put to sleep (clock oscillator stopped, wake up on interrupt), dropping the micro's power consumption to almost nothing.
The LEDs must be turned off, and the coil must be driven low
(since the input buffer for RB3 might draw class A current if we float it, and R6 will draw current if we drive it high).
The PIC's comparators and VREF module should be turned off; o
therwise they burn about a hundred microamps.
With the software given below, battery standby life should be on the order of the shelf life of the cells.
Demo
To steal someone's Verichip: Press and release the white button.
The white light will turn on while we try to get a read. Hold the antenna very close to the bearer's arm;
if you know the orientation of the implanted tag, then try to hold your antenna parallel.
The green light will blink to indicate a successful read, and the cloner will exit `read mode.'
Press the green button to exit `read' mode without a successful read.
To replay the ID to a reader: hold the antenna close to the Verichip reader.
Press and hold the green button until the door opens (or they bring you a drink, or they let you in to the army base, etc.).
The green light will turn on for as long as you hold down the green button.
The cloner looks like the cloned Verichip only while the green switch is depressed;
the reader won't see you unless you're holding it down.
If you've got an ID that you would like to hold on to, then press the white button and then the green button,
and then release both (in either order). This will save the most recently-read ID to the PIC's non-volatile (EEPROM) memory.
The ID stored in EEPROM is loaded at power-on reset, so you can later recall this ID by removing and reinserting the batteries.
By default, the stored ID is Annalee Newitz's, number 47063.
The easiest way to archive an acquired ID (for later use, or to email to a friend, or whatever) is
to read out the PIC's EEPROM, using the in-circuit programming connector.
This can be saved as an IHEX file, or in any other format that your programming software supports.
When it comes time to reuse that ID, just program it into the PIC's EEPROM,
and it will be the first ID in memory after power-on reset.
The read range, when stealing someone's Verichip, is about an inch.
This isn't great, but probably enough to work with.
The official Verichip reader gives about four inches of read range, on axis and with fresh batteries.
I could presumably do as well or better, but that would require more than one IC,
and would therefore not be quite so cheap, or easy to build.
If anyone from Verichip makes an issue of this,
then I will design a `low-frequency range extension' board for my proxmark3, and see how far I can go.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; An ultra-simple Verichip cloner: we can behave either as a reader (to ;; get the ID of a legitimate tag), or as a simulated tag (to replay the ;; stored ID to a legitimate reader). ;; ;; The hardware is relatively stupid; I've made every compromise I could ;; think of to get the parts count down. ;; ;; This code is for MPASM 5.05, probably works with other versions though. ;; ;; Jonathan Westhues, Sep 2006 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #include <p16f628.inc> radix dec ;; leave BODEN off to get the standby battery life up (want a few years, ;; comparable to shelf life of the alkaline AAA cells) __config _CP_OFF & _HS_OSC & _PWRTE_ON & _WDT_OFF & _BODEN_OFF & _LVP_OFF ;; GPIO pin assignments on PORTA #define PORTA_READER_OUTPUT 0 #define PORTA_CARRIER_SENSE 1 ;; GPIO pin assignments on PORTB #define PORTB_LED_GREEN 0 #define PORTB_LED_WHITE 1 #define PORTB_DIVIDER_POWER 2 #define PORTB_COIL_DRIVER 3 #define PORTB_SWITCH_WHITE 4 #define PORTB_SWITCH_GREEN 5 ;; Convenience macros for btfss/btfsc; I find these quicker to read. ifset macro port, bit btfsc port, bit endm ifclear macro port, bit btfss port, bit endm ;; Wrapper macros to manipulate the two LEDs on the board. Used only for ;; user interface, nothing special. WhiteLedOn macro bcf PORTB, PORTB_LED_WHITE endm WhiteLedOff macro bsf PORTB, PORTB_LED_WHITE endm GreenLedOn macro bcf PORTB, PORTB_LED_GREEN endm GreenLedOff macro bsf PORTB, PORTB_LED_GREEN endm ;; Macros for time delays (cycle-counted busy waits). These are only ;; approximate. DebounceWait macro label movlw 60 movwf milliCount label Wait1Millisecond decfsz milliCount, f goto label endm Wait1Millisecond macro clrf microCount goto $ + 1 goto $ + 1 goto $ + 1 decfsz microCount, f goto $ - 4 endm ;; Wrappers macros to manipulate the PWM peripheral (Timer2/CCP), used to ;; divide the micro clock down to produce the transmitted carrier in ;; `reader' mode. We will use (10 MHz)/(4*(18+1)) = 132 kHz, for ~2% ;; error vs. desired 134 kHz, good enough. TurnOnPwmPeripheral macro banksel PR2 movlw 18 movwf PR2 ^ 0x80 banksel CCPR1L movlw 9 ; Pwm duty cycle 50% movwf CCPR1L movlw 0x0c ; Pwm mode, MSBs clear movwf CCP1CON bsf T2CON, 2 ; T2 on endm TurnOffPwmPeripheral macro clrf CCP1CON bcf T2CON, 2 ; T2 off endm ;; Variables in Bank 0. cblock 0x20 microCount bitCount cardId:64 milliCount cycleCount temp iterCount endc org 0 Reset goto Init ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; We get an interrupt whenever the user presses or releases a button with ;; interrupts on. The interrupt handler looks at the state of the pushbuttons, ;; and from this it jumps to the correct operating mode. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 4 Isr bcf STATUS, RP0 bcf INTCON, RBIF DebounceWait l12 ifclear PORTB, PORTB_SWITCH_GREEN goto switchGreenPressed ifclear PORTB, PORTB_SWITCH_WHITE goto switchWhitePressed ;; so neither is pressed, so go to sleep DebounceWait l6 goto SleepNSpin ;; White switch is for read mode. That mode is latched, and then exited with ;; a press of the green switch, so we must wait until they release the ;; white switch before entering that code. switchWhitePressed WhiteLedOn DebounceWait l0 awaitReleaseWhite ifclear PORTB, PORTB_SWITCH_GREEN goto bothSwitchesPressed ifclear PORTB, PORTB_SWITCH_WHITE goto awaitReleaseWhite DebounceWait l1 bcf INTCON, RBIF bsf INTCON, GIE ;; can break us out by pressing any other button goto GetIdFromCard ;; Green switch is for replay mode; that mode stays only as long as the ;; switch is held for, so jump straight in to that routine and let the edge ;; when the switch is released take us out. switchGreenPressed WhiteLedOff GreenLedOn DebounceWait l2 bcf INTCON, RBIF bsf INTCON, GIE goto TransmitCardId ;; Both switches means `load sample ID', in this case Annalee's. Then we ;; just wait for the button to be released, and go back to sleep. bothSwitchesPressed WhiteLedOn GreenLedOn DebounceWait l3 awaitReleaseBoth ifclear PORTB, PORTB_SWITCH_WHITE goto awaitReleaseBoth ifclear PORTB, PORTB_SWITCH_GREEN goto awaitReleaseBoth DebounceWait l4 goto writeIdToEeprom ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Where we end up after power-on. Load either a fixed ID from program ;; memory, or the ID that we have stored in EEPROM, and then go to sleep. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Init ;; load the stored ID from flash, or the one from the table if there's ;; none in flash bsf STATUS, RP0 clrf EEADR ^ 0x80 bsf EECON1 ^ 0x80, RD movf EEDATA ^ 0x80, w bcf STATUS, RP0 xorlw 0xff ifset STATUS, Z goto loadFixedId ; and this jumps to SleepNSpin when it's done ;; so there's a valid ID in there, somewhere; load it from flash movlw cardId movwf FSR movlw 64 movwf bitCount bsf STATUS, RP0 clrf EEADR ^ 0x80 bcf STATUS, RP0 loadIdFromFlash bsf STATUS, RP0 bsf EECON1 ^ 0x80, RD movf EEDATA ^ 0x80, w incf EEADR ^ 0x80, f bcf STATUS, RP0 movwf INDF incf FSR, f decfsz bitCount, f goto loadIdFromFlash ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Go to sleep, and wait for an interrupt to wake us up. First we should power ;; down anything that might waste current, though. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SleepNSpin ; Turn on the pull-ups, which we need to operate the switches. banksel OPTION_REG bcf OPTION_REG ^ 0x80, NOT_RBPU ; Switches are inputs, all others outputs on PORTB. banksel TRISB movlw 0x30 movwf TRISB ^ 0x80 ; RA0 and RA1 are used as comparators, rest are unused, so drive them ; as outputs to avoid class A current in input buffers. movlw 0x03 movwf TRISA ^ 0x80 banksel PORTB clrf PORTA ; Configure comparators as off (since they draw 30 uA each, rather a ; lot of current). movlw 0x00 movwf CMCON ; Configure voltage reference as off (since it also draws current) banksel VRCON movlw 0x00 movwf VRCON ^ 0x80 banksel PORTB ; Drive LEDs HIGH (off), coil driver LOW (don't waste current in R6), ; voltage divider LOW (don't waste current in that), programming pins ; (which are N/C in normal operation) LOW. movlw 0x03 movwf PORTB TurnOffPwmPeripheral ; ISR forces us out, so must turn on interrupts bsf INTCON, RBIE bcf INTCON, RBIF bsf INTCON, GIE asleep sleep goto asleep ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Routines to transmit an ID; basically we count incident carrier cycles ;; using the comparator on PORTA_CARRIER_SENSE, and from that we determine ;; our timing as we clock out the stored ID over and over. ;; ;; This routine does not return; it exits only as a result of an interrupt. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TransmitCardId ; Set up Vref to ground, since the sensed carrier comes in AC-coupled ; about that point. banksel VRCON movlw 0xa0 movwf VRCON ^ 0x80 ; Both comparators on, -inputs to RA0/RA1, +inputs to Vreg module banksel CMCON movlw 0x02 movwf CMCON ; Set the pin that drives the emitter followers LOW. The loop will touch ; only TRISB, not PORTB, so that pin will alternate between low impedance ; to ground and tri-state. bcf PORTB, PORTB_COIL_DRIVER spinTx movlw 64 movwf bitCount movlw cardId movwf FSR replayNibble variable i = 0 while i < 4 ;; First, we wait for 16 incident carrier cycles movlw 16 movwf cycleCount ifset CMCON, 7 goto $ - 1 ifclear CMCON, 7 goto $ - 1 decfsz cycleCount, f goto $ - 5 ;; Then, we set the coil driver pin according to the ID stored in memory. bsf STATUS, RP0 bsf TRISB ^ 0x80, PORTB_COIL_DRIVER ifclear INDF, i bcf TRISB ^ 0x80, PORTB_COIL_DRIVER bcf STATUS, RP0 variable i = i + 1 endw incf FSR, f decfsz bitCount, f goto replayNibble goto spinTx ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Routines to read the ID. We apply a ~134 kHz square wave to the coil driver ;; gates, using the PWM module to save ourselves the pain of counting ;; cycles. Then we wait for an edge after a long period of time, and we ;; assume that we just sync'd on the ID so we receive it. ;; ;; This is prone to error, though, especially at startup, so we read the ID ;; again and compare it to the one that we recorded. If they don't match then ;; we throw the stored ID away and start over. ;; ;; This routine will return if it thinks that it has read an ID, or exit ;; due to an interrupt. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GetIdFromCard ; Set up Vref to Vdd/2, since the sensed carrier comes in AC-coupled ; about that point. banksel VRCON movlw 0xa0 movwf VRCON ^ 0x80 ; Both comparators on, -inputs to RA0/RA1, +inputs to Vreg module banksel CMCON movlw 0x02 movwf CMCON ; Let us minimize the substrate current injected through R9, by driving ; that pin as an output. banksel TRISA bcf TRISA ^ 0x80, 1 banksel PORTB bcf PORTB, PORTB_COIL_DRIVER TurnOnPwmPeripheral ; give the oscillator some time to settle (tuned circuit) DebounceWait l8 spinRx ; First, wait for an edge (any edge; we will replay the ID cyclically, ; so it doesn't matter where in the ID we get bit-sync). awaitEdgeHigh ifclear CMCON, 6 goto awaitEdgeHigh awaitEdgeLow ifset CMCON, 6 goto awaitEdgeLow ; Now, delay a little while so that we will sample the bit at the centre ; of the bit time, not at the edge. movlw 60 movwf microCount spinToMiddle decfsz microCount, f goto spinToMiddle ; Set up the area of memory in which we will store the ID. movlw cardId movwf FSR movlw 64 movwf bitCount nextBit ; This loop is unrolled, to four iterations per jump; this is because ; we want to store 256 bits in 64 bytes, so we must use four iterations variable i = 0 while i < 4 bcf INDF, i ifset CMCON, 6 bsf INDF, i GreenLedOn GreenLedOff movlw 98 movwf microCount decfsz microCount, f goto $ - 1 goto $ + 1 variable i = i + 1 endw goto $ + 1 goto $ + 1 incf FSR, f decfsz bitCount, f goto nextBit movlw 3 movwf iterCount checkIdManyTimes ; Now we have the ID; but since we don't know how to check the CRC or ; anything like that, we need some way to determine whether we've ; received a valid signal, or just noise. Do this by receiving the ID ; a second time. movlw cardId movwf FSR movlw 64 movwf bitCount nextBitCheck ; This loop is unrolled, to four iterations per jump; this is because ; we have stored 256 bits in 64 bytes. Check each bit to see that it ; is the same that we received last time. variable i = 0 while i < 4 movlw 0 ifset CMCON, 6 movlw (1<<i) xorwf INDF, w movwf temp ifset temp, i goto spinRx nop movlw 97 movwf microCount decfsz microCount, f goto $ - 1 goto $ + 1 variable i = i + 1 endw goto $ + 1 goto $ + 1 incf FSR, f decfsz bitCount, f goto nextBitCheck decfsz iterCount, f goto checkIdManyTimes ; We made it this far, so we got the same thing each time. For a further ; paranoia check, make sure that the signal could plausibly be the ; Manchester-type modulation that the Verichip uses. movlw cardId movwf FSR movlw 64 movwf bitCount manchesterCheck movf INDF, w andlw 0x0f movwf temp ; so now temp = (cardId[64-bitCount] & 0x0f) ; sixteen possibilities, of which six can never happen: ; 0000 0001 0111 1000 1110 1111 movlw 0x00 xorwf temp, w ifset STATUS, Z goto spinRx movlw 0x01 xorwf temp, w ifset STATUS, Z goto spinRx movlw 0x07 xorwf temp, w ifset STATUS, Z goto spinRx movlw 0x08 xorwf temp, w ifset STATUS, Z goto spinRx movlw 0x0e xorwf temp, w ifset STATUS, Z goto spinRx movlw 0x0f xorwf temp, w ifset STATUS, Z goto spinRx decfsz bitCount, f goto manchesterCheck ; If we made it this far, then the consistency check passed. Leave the ; ID stored in memory, blink the green LED to indicate success, and ; go back to sleep. WhiteLedOff movlw 5 movwf bitCount blink GreenLedOn DebounceWait l7 DebounceWait l11 GreenLedOff DebounceWait l9 DebounceWait l10 decfsz bitCount, f goto blink ; If the two IDs agree, then it's probably a valid read, so we leave ; it stored in RAM and go to sleep. goto SleepNSpin ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; In case someone has a Verichip reader but no chip to clone, they can ;; still do a demo by replaying a previously-cloned chip's ID. I read this ;; one with a proxmark3, and used `vchdemod clone' to store that in a ;; .tr file. A perl script converts the .tr file into a table that can be ;; included inline here. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; loadFixedId ;; this is #included in my build process, but it seemed easier to distribute ;; just one file so I've cut-and-paste inlined it here ;; {{BEGIN CUT-AND-PASTED MATERIAL ; Autogenerated include by tr2asm.pl, from in.tr movlw 3 movwf (cardId+0) movlw 3 movwf (cardId+1) movlw 3 movwf (cardId+2) movlw 3 movwf (cardId+3) movlw 3 movwf (cardId+4) movlw 11 movwf (cardId+5) movlw 10 movwf (cardId+6) movlw 10 movwf (cardId+7) movlw 10 movwf (cardId+8) movlw 2 movwf (cardId+9) movlw 5 movwf (cardId+10) movlw 5 movwf (cardId+11) movlw 5 movwf (cardId+12) movlw 13 movwf (cardId+13) movlw 12 movwf (cardId+14) movlw 12 movwf (cardId+15) movlw 12 movwf (cardId+16) movlw 4 movwf (cardId+17) movlw 3 movwf (cardId+18) movlw 11 movwf (cardId+19) movlw 10 movwf (cardId+20) movlw 4 movwf (cardId+21) movlw 13 movwf (cardId+22) movlw 4 movwf (cardId+23) movlw 5 movwf (cardId+24) movlw 5 movwf (cardId+25) movlw 5 movwf (cardId+26) movlw 13 movwf (cardId+27) movlw 10 movwf (cardId+28) movlw 10 movwf (cardId+29) movlw 10 movwf (cardId+30) movlw 10 movwf (cardId+31) movlw 4 movwf (cardId+32) movlw 5 movwf (cardId+33) movlw 5 movwf (cardId+34) movlw 5 movwf (cardId+35) movlw 13 movwf (cardId+36) movlw 10 movwf (cardId+37) movlw 10 movwf (cardId+38) movlw 10 movwf (cardId+39) movlw 10 movwf (cardId+40) movlw 10 movwf (cardId+41) movlw 12 movwf (cardId+42) movlw 12 movwf (cardId+43) movlw 2 movwf (cardId+44) movlw 13 movwf (cardId+45) movlw 12 movwf (cardId+46) movlw 12 movwf (cardId+47) movlw 4 movwf (cardId+48) movlw 3 movwf (cardId+49) movlw 13 movwf (cardId+50) movlw 4 movwf (cardId+51) movlw 5 movwf (cardId+52) movlw 5 movwf (cardId+53) movlw 5 movwf (cardId+54) movlw 13 movwf (cardId+55) movlw 10 movwf (cardId+56) movlw 10 movwf (cardId+57) movlw 10 movwf (cardId+58) movlw 10 movwf (cardId+59) movlw 4 movwf (cardId+60) movlw 5 movwf (cardId+61) movlw 5 movwf (cardId+62) movlw 5 movwf (cardId+63) ;; END CUT-AND-PASTED MATERIAL}} goto SleepNSpin ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Write the ID from RAM to flash; presumably this is one that we wish to ;; hold on to. That means that this ID will be loaded by default on power- ;; on reset, so we can always get it back, even if we clone other tags ;; later. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; writeIdToEeprom movlw 64 movwf bitCount movlw cardId movwf FSR bsf STATUS, RP0 clrf EEADR ^ 0x80 bcf STATUS, RP0 writeByteToEeprom ; load the byte to be written into W movf INDF, w bsf STATUS, RP0 ; write W to EEADR movwf EEDATA ^ 0x80 bsf EECON1 ^ 0x80, WREN movlw 0x55 movwf EECON2 ^ 0x80 movlw 0xaa movwf EECON2 ^ 0x80 bsf EECON1 ^ 0x80, WR ifset EECON1 ^ 0x80, WR goto $ - 1 ; increment write position in EEPROM incf EEADR ^ 0x80, f bcf STATUS, RP0 ; increment read position in RAM incf FSR, f decfsz bitCount, f goto writeByteToEeprom bsf STATUS, RP0 bcf EECON1 ^ 0x80, WREN bcf STATUS, RP0 goto SleepNSpin end
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.
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.
ProxClone - Long Range Reader / Cloner
Long Range Reading
The ability to be able to read a 125Khz access card or keyfob at a large distance involves numerous factors including reader antenna size, radiated power, receiver sensitivity and the like. The reader cloner shown on the previous web page has the benefit of simplicity and low cost but is only capable of reading an RFID tag from a distance of approximately 3-4 inches. The ability to read a tag from much greater distances requires much more complicated receiver circuitry and the ability to generate a higher power RF magnetic field.
Design Concept
Rather than designing a long range reader from scratch, my long range cloner project leverages the use of a commercial off-the-shelf long-range reader that I have modified to be able to write a T5567 Read/Write card with the information that is output by the commercial reader. The reader that I selected for my project is the 5375AGN00 MaxiProx reader that is manufactured by HID. The reader has a typical read range of up to 24 inches for a standard ProxCard II access card. The reader outputs the card information via a standard Wiegand interface following each read of a valid tag. The wiegand data stream is then sent to a custom circuit board that I have physically installed into unused space within the MaxiProx reader. The custom circuit board utilizes a Parallax SX28 8-bit microcontroller to decode, display and store the data that is sent by the reader.
Commercial readers do not output all of the binary information that is transmitted by the tag. Information such as the card number, the facility code and parity bits are transmitted by the reader whereas the front end preamble and detailed card format information are verified by the reader but not output as part of the wiegand data stream. However, this missing information can be captured (and displayed) by my other custom reader/cloner device and once known, this "fixed" information can be combined with the commercial readers output data to generate the necessary programming sequence to create a clone card.
The top photo below shows the finished long range reader/cloner. The second photo shows the internal layout which includes the stock commercial reader circuitry (on right) along with my added circuitry and LCD display (on left) and a 12x AA rechargeable battery pack to allow the unit to be completely portable. The readers high power antenna 12"x12" is used to read the tags while the smaller 2" diameter antenna is used to write the T5567 R/W cards.
Operation
Since the modified reader has its own local power source it is completely portable and can easily be used in the field. When the unit gets within a couple of feet of a valid HID (or HID compatible) card the reader's LED will blink and it will begin outputing the wiegand data stream. The microcontroller will decode the readers wiegand data stream and drive the 2x16 LCD with information that includes the card format (bit length) and the hex value of the 26-37 bits of wiegand data that was received. The wiegand data is further decoded to determine the decimal value of the cards facility code and card number which is then displayed on the second line of the LCD display. The card number is usually the same as is physically printed on the backside of the card. If the operator desires to make a copy of the card that was read, he simply brings a T5567 R/W card near the smaller antenna and then presses the "write" button. In less than a second a duplicate card is created.
Note: The long read range of the unit combined with it's portability makes it fairly simple to covertly read and copy a card. The unit can easily be concealed in a briefcase or backpack. The simple act of walking by someone in a hallway or being in near vicinity (e.g. elevator) is sufficient to read and clone a persons card making this type of cards use questionable for high security applications.
A schematic of the hardware design is shown below. The entire assembly (reader & custom circuit board) requires less than 200ma of current to operate, thus allowing at least 10 hours of operation before the batteries must be recharged. The microcontroller code was written in assembly language and uses less than 2K bytes of program memory to support all functionality.
RFID Reader wrapup
I realized I never posted any images of my RFID reader in action, so here's a few of how it looks today in action.
This first view is of my workbench with scope, logic analyzer, reader & tag, and a few of my homemade tags.
Here's a closeup of the reader itself. The USB cable and antenna are the only connections required. The LED flashes every time the code recognizes the "triple-0" tag header sequence.
I have my 4 scope probes hooked up to the following signals from bottom to top.
First is a closeup view of just a couple of bits from the signal, showing the bits modulated with the 125khz carrier.
4) This is the antenna input signal after passing through the detector diode, low-pass filter, and decoupling capacitor.
3) The noisy line near the top of the antenna signal is the threshold voltage, which is adjusted manually to a couple hundred mV via a simple potentiometer / voltage divider. These two signals are fed into the AVR's built-in analog comparator to produce interrupts whenever the antenna signal rises above the threshold.
1) This the raw manchester-encoded signal from the tag as output by the detector code. It's composed of sequences of 5 possible signals, "short low", "double low", "triple low (this is the start-flag)", "short high", and "double high". You can see the how the trace echoes the signal in the antenna, delayed by 1/2 the length of a "short", which is how long the timer takes to detect the end of a high or low.
2) The topmost trace is what I use for triggering the scope, it's always low except when the tag header (000) is detected.
The next view is zoomed out a bit, showing the trigger pulse on channel 2, and the first 8 or 9 bits of the signal on channel 1.
Here's the two logic signals as seen via the Saleae Logic, and underneath are the (slightly obscured) decoded tag values output to the serial port.
Here's the schematic:
And, last but not least, the code itself (This is Arduino code for Teensy 2.0)
#include <avr/interrupt.h> #include <avr/io.h> #include <stdint.h> #include <util/delay.h> #define CARRIERPOS_PIN 10 #define CARRIERPOS_REG PORTC6 #define CARRIERNEG_PIN 9 #define CARRIERPOS_REG PORTC7 #define LED_PIN 11 #define LED_PORT PORTD #define LED_REG PORTD6 // AIN0 = PE6 = threshold // ADC0 = PF0 = antenna - detect #define DEBUG_PORT PORTB #define DEBUG_REG PORTB3 #define DEBUG2_REG PORTD0 #define LED_ON() (LED_PORT |= _BV(LED_REG)) #define LED_OFF() (LED_PORT &= ~_BV(LED_REG)) #define TOGGLE_LED() (LED_PORT ^= _BV(LED_REG)) #define DEBUG_ON() (DEBUG_PORT |= _BV(DEBUG_REG)) #define DEBUG_OFF() (DEBUG_PORT &= ~_BV(DEBUG_REG)) #define DEBUG2_ON() (PORTD |= _BV(PORTD0)) #define DEBUG2_OFF() (PORTD &= ~_BV(PORTD0)) #define TOGGLE_DEBUG2() (PORTD ^= _BV(DEBUG2_REG)) #define TOGGLE_DEBUG() (PORTB ^= _BV(DEBUG_REG)) void initTimer4( ) { // pwm with dead time generator TCCR4A = _BV( PWM4A ) | _BV( COM4A0 ); // phase & frequency correct pwm TCCR4D = _BV( WGM40 ); TCCR4E = _BV( ENHC4 ); //duty cycle for oscillator toggle OCR4A = 64; //OCR4B = 64; // 125k would be no prescaler, period = 128 OCR4C = 64; // no prescaler (CK/1) TCCR4B = _BV(CS40); } void initTimer1( ) { TCCR1A = 0; TCCR1B &= ~_BV( WGM13 ); // NORMAL mode TCCR1B |= _BV( ICNC1 ); // enable noise cancellation TCCR1B &= ~_BV( ICES1 ); // falling edge detect TCNT1 = 0; OCR1A = 200; // upper bound of single-bit length // input capture interrupt triggered by analog comparator // enable Timer/Countern Output Compare A Match interrupt TIMSK1 |= _BV( ICIE1 ) | _BV( OCIE1A ); TCCR1B = _BV( CS11 ); // PS = 8 } void initAnalogComparator( ) { // AIN0 = PE6 = detect // ADC0 = PF0 = threshold // disable adc ADCSRA &= ~_BV( ADEN ); // enable analog comparator mux ADCSRB |= _BV( ACME ); // clear analog comparator interrupt flag ACSR |= _BV( ACI ) | _BV( ACIS1 ) | _BV( ACIC ); // interrupt on falling edge ACSR &= ~_BV( ACIS0 ) & ~_BV( ACBG ); // enable analog comparator input capture, AIN0 connected to AC+, ADC0 connected to AC- input ADMUX &= ~_BV( MUX2 ) & ~_BV( MUX1 ) & ~_BV( MUX0 ); // disable digital input buffer DIDR1 |= _BV( AIN0D ); } #define BUFSIZE 256 #define wrapI() if (i > BUFSIZE) {i=0;} #define write1() { {wrapI(); buf[i++] = 1; }; } #define write0() { {wrapI(); buf[i++] = 0; }; } // #define wrapT() {if (t > BUFSIZE) {t=0;}} // #define writeT(n) {wrapT(); timebuf[t++] = n; } // stores raw signal, still manchester encoded (01 is a 0, 10 is a 1) volatile int buf[ BUFSIZE ]; // stores the actual decoded bits volatile int decoded[ 45 ]; //stores timings between up-edges volatile uint16_t timebuf[BUFSIZE]; volatile int i = 0; // volatile int t = 0; volatile int bitcount = 0; volatile int logging = 0; volatile int msglen = 0; volatile int lastlen = 0; volatile int isReading = 0; volatile int isSync; void countbits( ) { isReading = 1; if ( bitcount > 3 && bitcount < 7 ) { write1( ); } else if ( bitcount > 8 && bitcount < 11 ) { msglen++; write1( ); write1( ); } else if ( bitcount > 12 ) { // triple long high as part of the header write1( ); write1( ); write1( ); DEBUG2_OFF(); LED_OFF(); if ( isSync != 1 ) { isSync = 0; } else { i = 0; // t=0; msglen = 0; isSync = 2; } } bitcount = 0; } // this timer will fire and turn off the debug pin if a capture doesn't happen within 2/8T. ISR( TIMER1_COMPA_vect ) { if ( bitcount > 0 ) { countbits( ); DEBUG_OFF(); // turn off the pin as soon as possible at the beginning of low periods. }LED_OFF(); } ISR( TIMER1_CAPT_vect ) { if ( 1 ) { uint16_t timer1val = ICR1; TCNT1 = 0; // log timing infos separately from bit values // wrapT(); // writeT( timer1val ); if ( timer1val > 100 && timer1val < 200 ) { if ( bitcount == 0 ) { DEBUG_ON(); } bitcount++; } else { if ( timer1val > 800 && timer1val < 1150 ) { msglen++; // DEBUG_OFF(); // countbits(); write0(); } else if ( timer1val > 1600 && timer1val < 1900 ) { msglen++; write0(); write0(); } else if ( timer1val > 2400 && timer1val < 2700 ) { // header bits, triple long low lastlen = msglen; write0(); write0(); write0(); // sync pulse isSync = 1; LED_ON(); DEBUG2_ON(); } } } // TOGGLE_LED(); // TOGGLE_DEBUG(); } void setup( ) { memset( (void*) buf, sizeof(uint8_t) * BUFSIZE, 0 ); memset((void*)timebuf, sizeof(uint8_t)*BUFSIZE, 0); cli( ); // debug pin out / OC4B pin out, OCOA (PB7) output DDRB = _BV( PORTB3 ) | _BV( PORTB7 ); DDRD = ~_BV( PORTD4 ) | _BV( PORTD0 ) | _BV( PORTD6 ); // enable pull-up resistor PORTD = _BV( PORTD4 ); // 125khz carrier oscillator pins 8&9, OC4A/~OC4A pinMode(CARRIERPOS_PIN, OUTPUT); pinMode( CARRIERNEG_PIN, OUTPUT ); // threshold voltage out pinMode( 12, OUTPUT ); LED_OFF(); // input ddr regs, enable pull-up resistors DDRE = ~_BV( DDE6 ); PORTE = ~_BV( PORTE6 ); DDRF = ~_BV( DDF0 ); PORTF = ~_BV( PORTF0 ); // initTimer0( ); initTimer1( ); initTimer4( ); initAnalogComparator( ); sei( ); analogWrite( 12, 6 ); Serial.begin( 57600 ); } int manchesterToBit( int m1, int m2 ) { if ( m1 && ~m2 ) return 1; if ( ~m1 && m2 ) return 0; } // write out the rfid's digits to the serial port void decodeManchesterMessage( ) { int inputIndex; int tmp1, tmp2; for ( int a = 0; a < 45; a++ ) { inputIndex = ( 2 * a ) % BUFSIZE; tmp1 = buf[ inputIndex ]; tmp2 = buf[ inputIndex + 1 ]; decoded[ a ] = manchesterToBit( tmp1, tmp2 ); } } void decodeHID( ) { // decoded buffer should be filled at this point with the decoded bits of the 45 bit message. // this method will convert the 20, 8, and 16-bit sections into their respective decimal values // and print them to the Serial port. long mfgCode = 0; int siteCode = 0; long cardCode = 0; int z = 0; for ( ; z < 20; z++ ) { Serial.print( z ); // Serial.print( ":" ); mfgCode |= decoded[ z ] << ( 19 - z ); // Serial.print( decoded[ z ] ); // Serial.print( "," ); // Serial.print( decoded[ z ] << ( 19 - z ) ); // Serial.print( "," ); // Serial.print( mfgCode ); // Serial.print( "." ); } Serial.println( ); for ( ; z < 29; z++ ) { siteCode |= decoded[ z ] << ( 28 - z ); } for ( ; z < 44; z++ ) { cardCode |= decoded[ z ] << ( 43 - z ); } Serial.println( ); Serial.print( mfgCode ); Serial.print( ":" ); Serial.print( siteCode ); Serial.print( ":" ); Serial.println( cardCode ); } void printbuf( ) { isReading = 0; logging = 1; decodeManchesterMessage( ); Serial.println( ); for ( int r = 0; r < lastlen; r++ ) { Serial.print( decoded[ r ] ); // Serial.print( "," ); } decodeHID( ); logging = 0; } void loop( ) { if ( isReading && lastlen == 45 ) { printbuf( ); } _delay_ms( 1000 ); }
AVR RFID reader
I've been taking a break from Cricut coding to do some more work on the RFID stuff.
I decided that before I try to fix my (not working) AVR-RFID card, I should probably have my own reader.
And why buy a reader when I really want to understand how it all works?
I'm following the spirit of the instructions posted at this forum by Scanlime
(http://forums.parallax.com/showthread.php?105889-World-s-simplest-RFID-reader).
I have two (real & working) sample cards of the right type to use as guinea pigs.
Instead of a propeller, I'm using a Teensy 2.0 AVR32U4 in "phase & frequency correct" pwm mode,
using the dead time generator to create a 125khz differential signal between pins 11 & 12.
Since 11 also happens to be the Teensy's LED pin, it also ends up pulsing brightly at 125khz.
The Teensy is a really great little device that I found on the hack shelf at the ATX Hackerspace,
it's very compact with built-in high speed usb2, based on the ATMEGA32U4. (datasheet )
I have not yet implemented the detector circuit (that's next).
Following scanlime's directions, I wound 30 or so turns of #26 magnet wire around a spray painted wooden block" (the green wire from the Radio Shack magnet wire assortment); then a 1nf capacitor in series to the AVR's output pins, running the program below, and finally the 'scope probe up to the L/C junction to see what was up.
Good news, it was oscillating at 125khz, but the wave was not smooth, more rounded with some dips in the curve.
(no screenshot of the funky waveform, sorry)
I swapped out the capacitor a few times on my breadboard and eventually settled on 3.2nf giving the most "wavelike" curve I could manage (as seen above). 21.8v peak to peak Im pretty sure not 218.. The shape of the waveform looks to me like I may need just a little more capacitance to be quite right, but im hopeful that it wont really matter anyway.
I'll get an inductance meter reading on the reader antenna next Monday night just for curiosity sake.
(sorry no photo of the antenna yet)
Here is my current arduino code for the 125khz differential oscillator. There's not much there but those register settings are so crucial to getting it all working! I really need to learn how to do in-circuit debugging.
#include <avr/interrupt.h> #include <avr/io.h> void setup() { cli(); pinMode(11, OUTPUT); pinMode(12, OUTPUT); // phase & frequency correct pwm mode 9 // PWM4A enables PWM mode based on comparator OCR4A TCCR4C = _BV(PWM4D) | _BV(COM4D0); // no prescaler TCCR4B = _BV(CS40); // enable dead time TCCR4D = _BV(WGM40); // 1024 prescaler //TCCR1B = _BV(WGM13) | _BV(CS12) | _BV(CS10); // 125k would be no prescaler, period = 128 OCR4C = 128; OCR4D = 64; } void loop() { while(1) { delay(10); } } sei(); }
After a few more days of hacking on this I'm a lot closer than I was.
At first, my avr-rfid antenna was oscillating strongly enough for the processor to run and start trying to send the code; but I couldn't see any induced signal in the reader (transmitter)'s antenna.
Finally I decided to pay attention to the schematic note "tune for 125khz resonance with C1" (http://scanlime.org/2008/08/simplest-rfid-reader/) and start playing with the reader coil windings. Previously I had been trying to tune the capacitor to the coil instead of fixing the capacitor at the spec (1nf) and tuning the coil to resonate properly.
I realized in the process I had only about 19 turns of wire in my coil instead of 30+ like are called for, and just not enough inductance. I ended up adding about 10 or 11 extra turns to around 30 like I should have done to start with, sanding off a little of the insulation every couple turns to recheck the oscillation.
I also dispensed with the single-layer wrapping of the coil wires, and instead cut "V" grooves about 1/4" down from the corners of the wood block. Now the wire is more of just a tight bundle instead of thin and wide like a ribbon.
Anyhow, after retuning my reader coil to the maximum voltage at 125khz with the 1nf capacitor, I hooked it all back up to the Teensy and VOILA I now see a pretty good variation in the reader coil when the card is near, meaning BITS!. Not only that, but i also see the same types of variation when I try my Hackerspace badge and my work badge, so I'm definitely getting closer!
I have not yet started implementing the detector code, which I'll have to port from Scanlime's original propeller assembly.. Looking at the 'scope signal I admit I still can't quite tell a 1 from a 0 though, only that there's SOME kind of deliberate modulation going on
In the photo attached, the upper (yellow) signal is the oscillation in the AVR-RFID tag. It looks to me like the first few and last few pulses are 1 cycle shorter than the ones in the middle.
The lower (blue) trace is the oscillation in the reader coil. When there's no tag near, the peaks go away and the signal is a nice flat 125khz sine wave. Once the tag gets close enough to run and start pulsing (1 inch or so), the wave pattern appears superimposed on it.
It looks to me like the valleys go just a little lower each time the tag pulse is wider....
The Teensy is working a little harder now, generating:
- 125khz differential 5v signal to oscillate the reader antenna
- pwm signal at 1000hz through a matched RC low pass filter to give a smooth but variable analog threshold voltage. This voltage is automatically tuned (in scanlime's implementation), along with the actual reader frequency for peak performance.
-still TODO: pulse counting on the detector circuit signal to start trying to detect some actual bits.
After a 6-week hiatus, I finally picked the RFID reader project back up. My ultimate goals have evolved a long way, but I'll reveal those later.
For now, I'm still working on the part where I need to detect the incoming signal.
Using the Teensy 2.0:
-I'm using timer4 in phase & frequency correct mode, clk/1 prescaler, with a 128 cycle period. Using the dead-band generator at 50% duty cycle, I'm getting a differential signal output to pins OC4A and ~OC4A, and am using this to drive my antenna.
-Like everyone else before me, I pass the antenna signal through a signal diode, high-pass-filter and decoupling capacitor.
The last thing I did before I stopped working on this previously, was to connect the signal to the ADC and sample as fast as possible, and light the LED if the voltage was above a hardcoded threshold value (about 0.5v I think).
Although this does successfully "work", and starts to light up when a card is within about 3" of the reader, it also has lots of false positives and lights up when you just mess with the antenna in any way.
I constantly go back to Beth Scott's "simplest RFID reader?" forum post, as well as the Microchip 125khz RFID system design guide and AVR 32U4 datasheet for ideas and inspiration.
My latest new attempt is using an analog threshold voltage, the analog comparator, and the timer1 input capture interrupt & edge detector. The idea is, timer1 constantly counts from 0 to 0xffff with prescaler set to CLK/64 (250khz). The TOP value could be adjusted to match the bit length of the RFID protocol to be read (*2 since we're running at double the frequency).
Reading the datasheet, I decided to try connecting AIN+ to the threshold voltage, and AIN- to the antenna signal, and enable the Timer1 Input capture feature. I set the edge detector to trigger the input capture interrupt on a Falling edge, which I figured would result when the AIN- voltage drops below the AIN+ voltage.
The input capture interrupt starts with the current value of Timer1 in the 16-bit input capture register. I figure all I need to do is store the value of the timer each time the interrupt fires. A string of 1's or other "lead-in" for an actual card value will help me determine the "beginning" or "0 offset" value of timer1.
For now, I just am trying to still flash the light whenever the interrupt fires, but my first attempt at 1am didn't result in any visible result. As is always the case, I'm sure I've just missed some essential register setting and will need to review all the init code against the datasheet again, as well as trying to get my bus pirate logic analyzer mode working again so I can get a better debug view at the pin level.
Just for grins here's my code at the moment.
#include <avr/interrupt.h> #include <avr/io.h> volatile uint16_t pdval; #define CARRIERPOS_PIN 10 #define CARRIERPOS_REG PORTC6 #define CARRIERNEG_PIN 9 #define CARRIERPOS_REG PORTC7 #define LED_PIN 11 #define LED_PORT PORTD #define LED_REG PORTD6 #define THRESHOLD_INPUT_PIN 24 #define THRESHOLD_INPUT_REG AIN0 // aka PE6 #define THRESHOLD_INPUT_PORT PORTE #define ANTENNA_INPUT_PIN 20 #define ANTENNA_INPUT_REG PF1 // goes to AIN- via ADMUX #define ANTENNA_INPUT_PORT PORTF #define LED_ON() (LED_PORT |= _BV(LED_REG)) #define LED_OFF() (LED_PORT &= ~_BV(LED_REG)) // Inline assembly: The nop = do nothing for one clock cycle. #define nop() __asm__ __volatile__("nop") /** Timer4 is used in Phase/Frequency correct mode to generate a 125khz differential output signal used to stimulate the reader antenna. */ void initTimer4() { // pwm with dead time generator TCCR4A = _BV(PWM4A) | _BV(COM4A0); // no prescaler (CK/1) TCCR4B = _BV(CS40); // phase & frequency correct pwm TCCR4D = _BV(WGM40); TCCR4E = _BV(ENHC4); //duty cycle for oscillator toggle OCR4A = 64; // 125k would be no prescaler, period = 128 OCR4C = 64; } /** we'll try to use the input capture feature of Timer1 to determine -when- each 1 occurred. We'll be listening for 1's, and as soon as we recognize a start sequence, we'll reset Timer1 to 0. It will increment at 125khz along with Timer4 which is generating the differential antenna output signal. Each time the ADC interrupt fires to indicate the antenna signal on AIN- exceeding the threshold voltage on AIN+, we will trigger the input capture and take note of the value of Timer1. This will give us our the cycle number in which it occurred relative to the start sequence. This would be a Falling Output Edge (I think) since AIN- is exceeding AIN+ when the input signal rises above the threshold value. */ void initTimer1() { // CTC mode 4; TOP = OCR1A, enable input capture noise canceler, 4 cycle delay // input capture set to falling edge select (0), CLK/1 prescaler TCCR1B = (1<<ICNC1) | (1<<WGM12) | (1<<CS10); // 65535 cycles to overflow at CLK/1 prescaler, maximum possible bit offset OCR1A = 0xFFFF; // enable input capture interrupt on analog comparator TIMSK1 |= (1<<ICIE1); // clear the input capture flag after enabling interrupts to avoid a false interrupt call TIFR1 |= (1<<ICF1); } void initAnalogComparator() { // turn off ADEN bit to disable ADC ADCSRA &= (0<<ADEN); // connect ADC MUX to comparator AIN- ADCSRB |= (1<<ACME); // enable input capture function of Timer1. comparatur ACSR = (1<ACIC); // note: ICIE1 must be set to 1 in TIMSK1 to enable Timer1 Input Capture Interrupt } // each time the input capture interrupt is triggered, a falling edge was detected // by the analog comparator. store the value in ISR(TIMER1_CAPT_vect) { // TODO: read ICR1L first, then ICR1H. to actually read the byte // for now we'll just turn the LED on for 8 cycles and then turn it back off. LED_ON(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); nop(); LED_OFF(); } void setup() { cli(); // 125khz carrier oscillator pins 8&9, OC4A/~OC4A pinMode(CARRIERPOS_PIN, OUTPUT); pinMode(CARRIERNEG_PIN, OUTPUT); pinMode(ANTENNA_INPUT_PIN, INPUT); pinMode(THRESHOLD_INPUT_PIN, INPUT); // led pinMode(LED_PIN, OUTPUT); // timer4 generates the 125k differential carrier signal initTimer4(); // timer1 counts to 65536 over and over again to timestamp the falling edges // it might be reset to 0 when we recognize a data header (series of 1s)... initTimer1(); // input capture interrupt on Timer1 is triggered each time // the antenna signal exceeds the threshold value, giving a falling edge on the // comparator. for now, the LED is flashed every time the interrupt is triggered. initAnalogComparator(); // enable global interrupts() sei(); } void loop() { while(1) { } }
After wrestling and backing up a bit and rewriting a good part of it, I finally got the analog comparator and input capture scheme working.
Timer1 counts continuously while the antenna signal is being generated.
The analog comparator is configured to trigger the Timer 1 Input Capture interrupt whenever the signal detector voltage crosses an analog threshold voltage.
Whenever there is a rising edge (input signal rises above threshold), I check to see if timer1 has exceeded a certain value or not. If there's been no rising edge within a certain length of time, we either have a "1" bit, OR we have no card at all.
"No card" is handled by only allowing a minimum AND maximum "low time" (I.e. 1's can only be SO long).
The picture below shows the modulated antenna signal in blue, and the detected 1-bits in yellow. Now I'm finally able to get started on actually storing 0's and 1's.
Since the input capture interrupt is called twice on every cycle, we need to know if it has been longer than about 5 or 6 complete cycles since the last FALLING edge.
Looking at the Microchip spec now, I realize I didn't QUITE implement it properly.
According to this picture, I should actually be timing the duration of consecutive cycles above AND below the threshold. I was only timing the length of the LOW portion. I'm not certain whether it really matters or not, time will tell.
Anyhow, this should be "good enough" for now, the next thing I'll work on is actually storing the consecutive bits and looking for a header of some kind to determine the beginning of the string. I also planned to implement some error correction by requiring several consecutive matching reads before deciding that we've actually detected the signal correctly and completely
100000001000000001000110001100111011
0000000000000000
100011 001100111011000000 1000000000100 11000110011101100000001000000000100011000110011101100000001000000000
100011000110011101100000001000000000100011000110011101100000001000000000100011000110011101100000001000000000
10001100 01100111 01100000 00100000 00001000 11000110 01110110 00000010 00000000 10001100 01100111 01100000 00100000 0000
100011000110011101100000001000000000100011000110011101100000001000000000100011000110011101100000001000000000
100011000110011101100000001000000000100011000110011101100000001000000000100011000110011101100000001000000000
100011000110011101100000001000000000100011000110011101100000001000000000100011000110011101100000001000000000
100011000110011101100000001000000000100011000110011101100000001000000000100011000110011101100000001000000000
100011000110011101100000001000000000100011000110011101100000001000000000100011000110011101100000001000000000
100011000110011 0110000000100000000 1000110001 00111011000000 1000000000100011000110011101100000001000000001
00011000110011 0110000000100000000 1000110011 00111011000000 10000000001 0011000110011 0110000000100000000
100011000100111 011000000010000000001000110011 0011101100000001000000000100011000110011101100000001000000000
100011000100111 01100000001000000000100011000110011101100000001000000000100011000110011101100000001000000000
1000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000100011001110110000000100000000010001100011001110110000000100000000100110001100111011000000100000000010001100011001111100000001000000001000110001101110110000001000000000100010001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001000110001100111011000000010000000001
This is what my reader is writing to the serial port now.
As before, the analog comparator is connected between the analog threshold voltage and the antenna signal.
The input capture interrupt function now toggles the LED and debug pins, and writes a 0 or 1 to the serial port, if the time between the last falling and rising edge from the analog comparator output occurred in one of two ranges of time. i.e, 250-1100 ticks right now for a "short", and 1101-3499 ticks for a "long".
Based on my previous reading I think I have to actually be timing how long since the last rising edge* - the catch is that the "high part" of the signal is actually constantly toggling at 250khz (every rising and falling analog comparator edge), so I have to be able to ignore the very short-duration low periods to properly time the "high part". Maybe if I just check the low period duration and don't reset the timer if it's too short, effectively filtering out the higher frequency component of the signal.
I've had a great time trying to view the signal on my analog scope. I connected one probe to the raw, rectified, a/c coupled antenna signal, and the other probe to the debug pin on the Teensy.
Using the coarse and fine timebase controls on the scope, triggering on the debug pin, I can zoom out far enough to see the entire signal from the card. Since the signal is constantly repeating, I can get a static and unique "image" of each card's bitstream.
Playing with the potentiometer on the threshold voltage, I discovered that the ideal threshold voltage is directly related to how far away the card is. The further the card, the lower the threshold voltage.
The "depth of field" of the "best" threshold value is surprisingly low as well. Holding the card at a constant distance from the reader, I turn the knob back and forth, watching as the bits in the "bit picture" on the scope eerily flicker in and out of view, locked horizontally, but flickering more and less depending on the error rate.
Sorry, I thought I took a cellphone picture of it but I must have not hit "save" before closing the phone. I hate when I do that.
The best, most rock-solid signal is obtained (not surprisingly) when the card is laying right on the wood of the reader coil. At the best threshold setting, it reads the card only up to about a half inch from the wood.
However, by re-tuning the threshold voltage for various card-to-reader distances, I can manage to successfully lock onto the signal from up to maybe 4 inches away.
Clearly, a dynamic threshold voltage tuner seems like a worthwhile upgrade for maximum read-range flexibility.
Something else interesting that I noticed, the cards are nearly "one-sided." That is, when I flip the card over, I can only barely see the signal, probably not well enough to actually read it. My work badge is a different type of HID card and doesn't seem to have that behavior. I'll have to see if the reader on the hackerspace door does the same thing.
Back to the bits. I was really eager to see what my serial bitstream looked like, if it was repeating, the length, and so on.
Opening the log file in my text editor, I played with trying to line up sections of the text to find a repeating pattern, and its length.
Sure enough, I discovered one on each of my two hackerspace cards! But, oddly enough, the number of bits doesn't seem to match up with the expected number.
I've realized that I don't really know what encoding is being used.
From Scanlime's blog, I read that "... Cesar Fernandez described the HID card format ... The 45-bit code"... ; but in my text file, the two visually identical cards appear to give slightly different-length repeating bitstreams. (36 & 38 bits)
00000000 10001100011010000100100000001
00000000010001100011010000100100000001
00000000 10001100011010000100100000001
00000000010001100011010000100100000001
00000000 1000110001101000 100100000001
00000000 10001100011010000100100000001
0000000001000110001101000 100100000001
00000000 10001100011010000100100000001
0000000001000110001101000 100100000001
0000000001000110001101000 100100000001
00000000010001100011010000100100000001
00000000 1000110001101000 100100000001
00000000010001100011010000100100000001
Lining up the repeating rows, and adding spaces where there were missing bits, I realized that the errors nearly all occur in the same places in the signal, and so I must have a slight misalignment in my time window lengths between 0's and 1's. I'll probably have to increase the timer1 prescaler from /8 to /1 to increase the resolution my cycle counter, and create a bigger difference in values between "short" vs "long".
If the card is using around 4 "low antenna cycles" to mean short, and around 8 to mean long, I had expected that a /8 timer would give me plenty of resolution (1 timer tick per 8 16mhz clock cycles, 16 per 125khz antenna cycle, 64 timer ticks per "4 low antenna cycle" 0, and 128 ticks per 1.
However, in practice, my window sizes are currently set at 4 and 10 times larger than this, based on just experimentation and observation of the output. I think what this means is that the encoding is not quite what I'm picking up, and I'm actually reliably missing 9 or 10 bits of the true signal.
Here's my latest code.
#include <avr/interrupt.h> #include <avr/io.h> #define CARRIERPOS_PIN 10 #define CARRIERPOS_REG PORTC6 #define CARRIERNEG_PIN 9 #define CARRIERPOS_REG PORTC7 #define LED_PIN 11 #define LED_PORT PORTD #define LED_REG PORTD6 // AIN0 = PB6 = detect // ADC0 = PF0 = threshold #define DEBUG_PORT PORTB #define DEBUG_REG PORTB3 #define LED_ON() (LED_PORT |= _BV(LED_REG)) #define LED_OFF() (LED_PORT &= ~_BV(LED_REG)) #define TOGGLE_LED() (LED_PORT ^= _BV(LED_REG)) #define DEBUG_ON() (DEBUG_PORT |= _BV(DEBUG_REG)) #define DEBUG_OFF() (DEBUG_PORT &= ~_BV(DEBUG_REG)) #define TOGGLE_DEBUG() (PORTB ^= _BV(DEBUG_REG)) void initTimer4() { // pwm with dead time generator TCCR4A = _BV(PWM4A) | _BV(COM4A0); // no prescaler (CK/1) TCCR4B = _BV(CS40); // phase & frequency correct pwm TCCR4D = _BV(WGM40); TCCR4E = _BV(ENHC4); //duty cycle for oscillator toggle OCR4A = 64; // 125k would be no prescaler, period = 128 OCR4C = 64; } void initTimer1() { TCCR1A = 0; // ctc mode 4 TCCR1B |= _BV(WGM12); // CTC mode 4 //TCCR1B &= ~_BV(ICNC1) | ~_BV(ICES1); // edge detect falling, disable noise canceler TCCR1B &= ~_BV(ICES1); TCCR1B |= _BV(ICNC1); OCR1A = 255; // TOP // 64 is about 8 cycles TCNT1 = 0; TCCR1B = _BV(CS11); // PS = 1 } void initAnalogComparator() { // AIN0 = PE6 = detect // ADC0 = PF0 = threshold // input capture timer1 //ACSR = _BV(ACIC); // disable adc ADCSRA &= ~_BV(ADEN); // enable analog comparator mux ADCSRB |= _BV(ACME); // clear analog comparator interrupt flag ACSR |= _BV(ACI); ACSR &= ~_BV(ACIS1) & ~_BV(ACIS0) & ~_BV(ACBG); // enable analog comparator input capture on output toggle, AIN0 connected to AC+ ACSR |= _BV(ACIC); TIMSK1 |= _BV(ICIE1); // ADC0 connected to AC- input ADMUX &= ~_BV(MUX2) & ~_BV(MUX1) & ~_BV(MUX0); // disable digital input buffer DIDR1 |= _BV(AIN0D); // input ddr regs, enable pull-up resistors DDRE &= ~_BV(DDE6); PINE &= ~_BV(PORTE6); DDRF &= ~_BV(DDF0); PINF &= ~_BV(PORTF0); } ISR(TIMER1_CAPT_vect) { uint16_t timer1val = ICR1; if (bit_is_set(ACSR, ACO)) { // change edge to descending TCCR1B &= ~_BV(ICES1); } else { // long for 1, short for 0 if (timer1val > 1100 && timer1val 250) { LED_ON(); DEBUG_ON(); LED_OFF(); DEBUG_OFF(); Serial.print("0"); } else { LED_OFF(); DEBUG_OFF(); } // if the signal was below threshold for longer than 8 125khz cycles, it's a 1 // change edge to rising TCCR1B |= _BV(ICES1); } // the pulse timer TCNT1 = 0; // clear the interrupt flag after changing edge TIFR1 |= _BV(ICF1); } void setup() { cli(); initTimer4(); initTimer1(); initAnalogComparator(); // debug pin out / toggle on analog comparator isr & timer0 DDRB |= _BV(PORTB3) | _BV(PORTB7); PINB |= _BV(PINB7) | _BV(PINB7); // 125khz carrier oscillator pins 8&9, OC4A/~OC4A pinMode(CARRIERPOS_PIN, OUTPUT); pinMode(CARRIERNEG_PIN, OUTPUT); // led pinMode(LED_PIN, OUTPUT); sei(); Serial.begin(57600); } void loop() { }
RFIDuino
RFIDuino is an Arduino shield for reading and writing 13.56 MHz RFID
tags. It consists of a 65x65mm circuit board, which plugs upside-down onto a
standard Arduino (NG, Diecimila, Duemilanove, or BlueTooth).
The board has a build-in antenna and contains all the circuitry to read and write 13.56 MHz ISO-14443A (Mifare) RFID tags. It is connected to the Arduino by I2C, using the Wire library.
The production version will be available in my web shop from early April.
Sniffer Nano
Sniffer Nano v2.0 is a 125K RFID card reader module. It’s very small but multifunctional, and can be added to your project conveniently.
We have completed the final debugging of the Sniffer Nano v2.0. It works well, the first version firmware will not include the ID store and protocol switch , but still retained the buad rate setting function so you can change the UART baud rate by serial command.
As normal use, you just need RX TX and INT pins for data communication.Besides,there are 6 reserve I/O pins for extension .We can use these I/O for drivering LED ,buzzer or relay, even to control a RF module for wireless communication.In the public version firmware will not include these etension function ,but you can modify the code to achieve it.Also you can connect us for help to customize a special version for you.
In order to allow more users to more easily understand the code and participate in, we make our firmware base on Arduino library. All the code will open source, so user can modify the code by themselves for meeting their special requirements.Of cause, we will keep improving the firmware and publish them. Using the Arduino bootloader, user can easily update their module via serial ports by Arduino IDE.
Basic features
- Smaller than many other module
- Low power consumption for embedded systems
- Support setting Protocol and Baud by serial command
- Soldering package compatible with both SMD and DIP
- Standard 100mil spacing package, can be directly used in bread board
- Interrupt evoking trigger
- Open source, based on Arduino. Support update firmware
Specifications
- Frequency: EM4100 protocal (125khz Read-only)
- Baud rate:4800-115200 bps
- Power:5VDC (±5%)
- Current Consumption:<50mA
- Reading Distance: 0-30 mm (Different manufacturers and shape of the card ,difference distance.)
- extensin I/O:6
- Indicators:Power
- Operating temperature:-10℃~+60℃
Sniffer Nano Firmware v1.1 AND Door Controller
The Sniffer Nano Firmware is update to v1.1, and it’s patched by Flemming Frandsen.
Flemming Frandsen is a Danish designer who bought our product,
he found that the Sniffer Nano source code is not easy to read and written in a completely different style than what he was used to,
so this guy do a great job to modify the code and make some significant improvement:
- Cleaned up the code a bit
- Converted to using a bit array to save 7/8 of the RAM
Download the Source code here:
SnifferNano Firmware v1.1 (2.1 KiB, 534 hits)
- ATmega168 (easier to get than ATmega328, but with less EEPROM)
- Ethernet controller (ENC28J60) + RJ45 transformer
- 8 KB IIC EEPROM
- 12V / 3 A switch mode buck converter (passive PoE 15-30 V)
- 3.3V / 800 mA linear regulator (fed by 12V)
- 2x 10A relay outputs with an indicator LED
- 3x RJ45 connector (with LEDs) for Keyboard, RFID and lock
- Protection Diodes to GND and 3v3 ,all GPIO pins brought out to the RJ45 connector
- Standard 6 pin ICSP header
- 6 pin FTDI header
- 6 pin serial IIC + power header
You can find the RFID which basis on Sniffer Nano, but Flemming Frandsen made some change on it to get a better reading effect :
- The DC-blocking C3 capacitor has been changed to 100nF.
- The amplifier got a higher gain and a 1 nF capacitor in the
- Inverting feedback to cut down on the carrier wave.
The Schmitt trigger was changed out completely to a traditional coupling, but with an automatic threshold voltage via an RC filter.
Download the whole schematic of this door controller here:
Door Controller schematic (115.5 KiB, 479 hits)
The Sniffer Nano v2.0 is finished and will in stock next week, we are making the datasheet for it.
The firmware now for Sniffer Nano v2.0 just support UART output.
To update the firmware, you can get the Wiegand output or even IIC output.
Not just 125Khz, We found that the 134.2Khz RFID protocol is very similar to 125KHz,
so just update the firmware , you can get a 134.2Khz reader like RDM660!
RDM660 is designed for reading code from 134.2KHz card or tags .
It support HTS-H32、HTS-48、HTS-56 type card and tag , compatible with ISO11784/85 protocol.
It’s similar to RDM630 125kHz RFID reader module , nearly the same pins and the same output protocol,
user just need to read the UART serial port that you can get the card / tag ID.
The 134.2kHz is widely used in pet tags , so if you want to make some project with reading out the tag information form the pet , that the RMD660 is you best choice.
Specification and Parameter
- Decode IC : EM4095
- Standard : SO11784/85
- Support : HTS-H32、HTS-48、HTS-56
- Frequency : 134.2KHz
- Baud rate : 115200bit/s
- Power : DC5V(5%)
- Current : <100mA
- Operation distance : >60mm( Depend on Card/Tag shape, manufacturer)
- Size : 39*19*9 mm(DIP28)
We are trying to make the Sniffer Nano V2.0 compatible with both 125kHz and 134.2kHz , but it seems that it’s a hard job to decode the different protocol in the case of you don’t know the using protocol first..May be we could release 2 version firmware for Sniffer Nano V2.0 , then it can be used in 125k or 134.2kHz.
RDM630 New Version And Sniffer Nano V2.0
The 125KHz RFID reader module – RDM630 had been sold out and we purchased a new batch form our supplier.
When get these modules , we are surprised to find that it’s not the same as the old one !
Working principle of Sniffer Nano
We will publish the source code after tidying up the code .To better understand the code, undetstand the working principle of RFID reader is necessary. So we decide to introduce the workflow of our module first.
Sniffer Nano is the device which can transmit the 125KHz RF signal to the tag/card for power supply and receive the signal which reflects from the tag/card. Because the reflect signal is coded, reader receives the RF signal and decodes to valid information.
First, we need the Sniffer Nano to output a 125KHz RF signal, so we use the MCU Timer and PWM pin to export a 125KHz square wave.
Of cause this signal can not direct to the coil , it will access the antenna before a power amplifier circuit. The waveform as below:
These 125KHz square waves will drive the antenna which a air core coil. According to the theory of air core transformer, the 125KHz square wave will be transmitted to the tag/card. To be a load for reader, with the power supply, the tag receives the energy comes from the reader, and is going to be active. Then the tag changes itself from a state to the other state. Seeing from the reader, the load seems to be changed, also the signal pass the antenna will be different – this is the load modulation.Tag information contained in these changes of signals.
The signal with information is analog , so it can not be recognized by MCU.
we need to use a detection circuit which include diode and LC filter to extract the valid information ,and change to digital signal.
When the MCU receive these digital signal, it will catch and save into a buffer ,then decode them according to the EM4100 protocol.
The next processing is all by the software burn in the MCU. We will go on to introduce these contents after we release the source code.
Sniffer Nano Source Code v1.0
There are many C code about using a MCU to work as a card reader, their basic working principle is the same .
First , all the reader need to output 125KHz signal for the coil , here we use the ARV timer PWM output a 125KHz square wave:
void InitialTimer(void) { DDRD=0; DDRD|=0x20; TCCR0A = 0x12; TIMSK0 = 0x00; OCR0B = 0x03; OCR0A = 0x03; TCNT0=0; TCCR1B = 0x00; TIMSK1|= 0x20; TCNT1H = 0x0FF; TCNT1L = 0xF8; TCCR1A = 0x00; ACSR|=0x04; TCCR1B = 0xC5; TCCR0B = 0x02; }
Next, the signal passed the external circuit and come back to another pin , so we need to catch this signal back for decode. It’s easy to use the capture interrupt and timer to catch these data.
ISR(TIMER1_CAPT_vect) { if(u8_state==0) { StopTimer CleanTimer if(Time<200) { u8_site = 0; u8_FirstTime=1; } if(u8_catchend==0) { if(u8_trgger==1) { SetBit if(Time<=4) { SetBit } TriggerEgde_Low } else { CleanBit if(Time<=4) { CleanBit } TriggerEgde_High } } } }
finally, decode these data in the receive buffer by the EM4100 protocol.
void decode(void) { unsigned char i,j,k; unsigned char temp = 0; unsigned char start_data[21]={ 1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1 }; unsigned char row_parity = 0; unsigned char id_code[11] = { 0,0,0,0,0,0,0,0,0,0,0 }; for(i=0;i<200;i++) { for(j=0;j<20;j++) { if(u8_DataArray[i+j]!=start_data[j]) { break; } } if(j==20) { i += 20; for(k = 0;k<11;k++) { row_parity = 0; temp = 0; for(j=0;j<5;j++) { temp<= 1; if((u8_DataArray[i] == 0)&&(u8_DataArray[i+1] == 1)) { temp |= 0x01; if(j<4) row_parity += 1; } else if((u8_DataArray[i] == 1)&&(u8_DataArray[i+1] == 0)) temp<= 0xfe; else { u8_state=0; u8_site=0; return; } i+=2; } id_code[k] = (temp&&1); temp<= 0x01; row_parity %= 2; if(k<10) { if(row_parity != temp) { u8_state=0; u8_site=0; return; } } else { if(temp!=0) { u8_state=0; u8_site=0; return; } } } if (k==11) { for(j = 2;j<10;j++) { u64_rfid += (((unsigned long)(id_code[j]))&&(4 * (9 - j))); } u8_state=Process; return; } } } u8_state=0; u8_site=0; }
The capture and the decode part is basic on the source code that written by Rainbow Chen.
We rewrite the main loop to FSM ,and rebuild the code base on Arduino , for user can easy update the code via serial port by Arduino IDE.
The source code include a .pde C file and a .h header file . You can download the project here:
SnifferNano Source code v1.0 (1.9 KiB, 451 hits)
We open source the code and hope you can join us to improve it .
If you have any suggestion or doubts , please don’t hesitate to tell us ,leaving your comment or sending us a E-mail:
#define Idle 0 #define ReveiveCOM 1 #define Action 2 #define Waiting 0 #define Decoding 1 #define Process 2 #define Time ICR1 #define StopTimer {TCCR1B = 0;} #define CleanTimer {TCNT1 = 0;} #define TriggerEgde_High { TCCR1B=0xC5; u8_trgger=1;} #define TriggerEgde_Low { TCCR1B=0x85; u8_trgger=0;} #define SetBit {u8_DataArray[u8_site]=0; u8_site++; if(u8_site>=255) u8_catchend=1;} #define CleanBit {u8_DataArray[u8_site]=1; u8_site++; if(u8_site>=255) u8_catchend=1;} /******************** Sniffer Nano ************************* This is the first vesion source code of Sniffer Nano.It' basic on the source code that written by Rainbow Chen. It implements the use of a microcontroller to read the RFID Tag and output the card ID . iteadstudio.com 7/30/2010 ************************************************************/ #include "EEPROM.h" #include "RFID.h" unsigned char u8_state=0; unsigned char u8_site=0; unsigned char u8_catchend=0; unsigned char u8_trgger=1; unsigned char u8_DataArray[256]; unsigned char u8_FirstTime=0; unsigned char u8_Comstate=0; unsigned char u8_value; unsigned long u64_rfid = 0; unsigned char u8_command[5]; unsigned char p=0; ISR(TIMER1_CAPT_vect) { StopTimer CleanTimer if(u8_state==Waiting) { if(Time>500) { u8_site = 0; u8_FirstTime=1; } if(u8_catchend==0) { if(u8_trgger==1) { SetBit if(Time>=3) { SetBit } TriggerEgde_Low } else { CleanBit if(Time>=3) { CleanBit } TriggerEgde_High } } } } void setup() { InitialSystem(); InitialTimer(); Serial.println("Sniffer Nano F/W v1.0"); } void loop() { switch(u8_state) { case Waiting: if(u8_catchend==1) { u8_catchend=0; if(u8_FirstTime==1) { u8_state=Decoding; } } switch(u8_Comstate) { case Idle: if(Serial.available()>0) u8_Comstate=ReveiveCOM; break; case ReveiveCOM: u8_command[p]=Serial.read(); if((p==0)&&(u8_command[p]=='A')) { p++; u8_Comstate=Idle; } else if((p==1)&&(u8_command[p]=='T')) { p++; u8_Comstate=Idle; } else if((p==2)&&(u8_command[p]=='+')) { p++; u8_Comstate=Idle; } else if((p==3)&&(u8_command[p]=='B')) { p++; u8_Comstate=Idle; } else if(p==4) { u8_Comstate=Action; } else { p=0 ; u8_Comstate=Idle; } break; case Action: u8_value=u8_command[4]; if(u8_value<0x03) { EEPROM.write(0, u8_value); Serial.print("OK"); } else { EEPROM.write(0, 0x04); Serial.print("ERROR"); } p=0; u8_Comstate=Idle; break; default: u8_Comstate=Idle; p=0; break; } break; case Decoding: decode(); break; case Process: OutputData(); u8_FirstTime=0; break; default: u8_state=0; break; } } void decode(void) { unsigned char i,j,k; unsigned char temp = 0; unsigned char start_data[21]={ 1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1 }; unsigned char row_parity = 0; unsigned char id_code[11] = { 0,0,0,0,0,0,0,0,0,0,0 }; for(i=0;i<200;i++) { for(j=0;j<20;j++) { if(u8_DataArray[i+j]!=start_data[j]) { break; } } if(j==20) { i += 20; for(k = 0;k < 11;k++) { row_parity = 0; temp = 0; for(j=0;j<5;j++) { temp <<= 1; if((u8_DataArray[i] == 0) && (u8_DataArray[i+1] == 1)) { temp |= 0x01; if(j < 4) row_parity += 1; } else if((u8_DataArray[i] == 1) && (u8_DataArray[i+1] == 0)) temp &= 0xfe; else { u8_state=Waiting; u8_site=0; return; } i+=2; } id_code[k] = (temp >> 1); temp &= 0x01; row_parity %= 2; if(k<10) { if(row_parity != temp) { u8_state=Waiting; u8_site=0; return; } } else { if(temp!=0) { u8_state=Waiting; u8_site=0; return; } } } if (k==11) { for(j = 2;j < 10;j++) { u64_rfid += (((unsigned long)(id_code[j])) << (4 * (9 - j))); } u8_state=Process; return; } } } u8_state=Waiting; u8_site=0; } void OutputData(void) { digitalWrite(2,LOW); Serial.println(u64_rfid); u8_FirstTime=0; u64_rfid=0; u8_site=0; u8_state=Waiting; digitalWrite(2,HIGH); } void InitialSystem(void) { pinMode(2,OUTPUT); digitalWrite(2,HIGH); u8_value=EEPROM.read(0); if(u8_value==0x03) Serial.begin(4800); else if(u8_value==0x04) Serial.begin(9600); else if(u8_value==0x05) Serial.begin(115200); else Serial.begin(9600); } void InitialTimer(void) { DDRD=0; DDRD|=0x20; TCCR0A = 0x12; TIMSK0 = 0x00; OCR0B = 0x03; OCR0A = 0x03; TCNT0=0; TCCR1B = 0x00; TIMSK1|= 0x20; TCNT1H = 0x0FF; TCNT1L = 0xF8; TCCR1A = 0x00; ACSR|=0x04; TCCR1B = 0xC5; TCCR0B = 0x02; }
The 125 KHz RFIG Tag Reader
Published on: 21 May 2013
Designed by: Vassilis Serasidis on 18 August 2012
Updated on: 12 June 2014 by Vassilis Serasidis.
Programming language: C
IDE: AVRstudio 6
Target MCU: ATtiny13 (internal 9.6 MHz oscillator
ATtiny85 (internal 8 MHz oscillator).
Tag frequency:125 kHz
Power supply voltage: +5V DC.
Output data:Serial 9600 bps 8N1. It outputs the 10-digit Tag serial number.
Introduction
This RFID reader works with 125 kHz tags in credit card size shape cards and with 125 kHz key fobs (picture 1). The EM4100 protocol is used. When you approach an RFID Tag close enough (4-5 cm) to the reader's coil (L1) the reader will read the 10-digit unique ID of the Tag and transmit it as ASCII characters trough the serial output with 2400 bits per second.
The circuit includes a buzzer that beeps when a Tag is read successfully.
Description
I will try to explain with simple words how the RFID works.
The ATtiny13 uses the PWM function to produce an 125 kHz square 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).
The RFID reader provides energy to the transponder (Tag) by creating an electromagnetic field. The energy transmission between the RFID reader and the Tag is the same as transformers convert the voltage from the 220V AC power network to 12V AC, based on the magnetic field that creates the primary coil. In our case the primary coil is the RFID reader and the secondary coil is the RFID tag. The only difference is that on RFID circuits there is no iron core between the two coils (one coil is on the reader side and the other coil is in to the RFID tag). The D1 ,C3 and R5 components constitute an AM signal demodulator (AM = Amplitude Modulation) .
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.
Constructing the coil
The coil has 120 mm diameter and 58 turns. Just in case leave more copper wire for additional 2-3 turns (60-61 turns total).
To achieve maximum distance between the RFID Tag and reader (between Tag and reader's antenna-coil) you need to calibrate the coil.
If you connect an oscilloscope to the point that C2, D1 and L1 are connected you will see the red marked spot of noising on the left picture.
That is a sign that L1 must be calibrated.
How can you calibrate the L1 ?
Power on the RFID reader and:
1. After you connect an oscilloscope probe to the C2, D1 and L1 connection point try slowly to remove or add more cooper wire (more or less turns) to the coil until the noise will be eliminated. or
2. If you don't have an oscilloscope then try to move your RFID tag close to the L1 until the tag will be recognized by the reader.
If your Tag will be recognized to the distance of 2 cm from the L1, try to add more turns (more cooper wire) to L1 to see if this time
your Tag will be recognized from longer distance (3 cm for example).
Try the same by removing turns (cooper wire) from the L1. Finally you will achieve the maximum range between your Tags and the L1.
I made the (L1) 120 mm diameter and 58 turns but afterward I wanted to make it in smaller size.
So, I folded the coil to the half to make it as a "figure eight" (the shape seems like the 8 number ) and I did again the calibration.
That's why the coil L1 on the pictures seems smaller than 120 mm.
The L1 on the picture has 60 mm diameter and almost 116 turns.
Programming the ATtiny13.
You have to set the ATtiny13 fuses to: High Fuse: 0x1F and Low Fuse: 0x7A .These settings set the ATtiny13 to work with the internal 9.6 MHz oscillator. The System clock divided by 8 option is disabled.
The firmware v1.00 is 1024 bytes and it occupies the 100% of the Flash program memory of ATtiny13.
Maybe a migration to any other 8-pin AVR such as ATtiny85 it would be a good idea if you want to add more functions to the source code.
Migration from ATtiny13 (1 kB flash memory) to ATtiny85 (8 kB flash memory) has been made on 13 February 2014 with firmware v2.00
Programming the ATtiny85.
You have to set the ATtiny85 fuses to: High Fuse: 0xDD and Low Fuse: 0xE2 .These settings set the ATtiny85 to work with the internal 8 MHz oscillator. The System clock divided by 8 option is disabled.
History:
(v2.01) 12 June 2014
* Oscillator frequency selection has been made automatic. The only you have to do is to select the micro-controller type from AVR studio 6 (ATtiny13 or ATtiny13A or ATtiny85) and re-compile the source code.
* The baud rate has been changed from 2400 bps to 9600 bps. Of course you can change it back to 2400 bps if you want.
(v2.00) 13 February 2014
The source code has been written from the scratch. The hex file is produced for ATtiny85 that has bigger flash memory and the same pinout with ATtiny13.
(v1.00) 18 August 2012
The initial firmware version of 125 kHz RFID reader.
http://www.serasidis.gr/circuits/RFID_reader/125kHz_RFID_reader.rar
Reference: - EM4100 Protocol description
Mivo- RFID based mobile payment system
By Harshvardhan Chamria (hc448) and Harold Beyel (heb47)
We used our ECE 4760 final project : Proximity Security System
as a platform to develop a proof of concept for Mivo.
Mivo is a low-cost, stripped down mobile payment system.
Our prototype combines Radio Frequency Identification (RFID), Security Pin Authentication and Ethernet Data Transfer
to provide a prototype for a low-cost secure payment system that has the potential to change lives in rural areas of developing countries.
High Level Design
We envisioned that the tasks of a generic technological payment system are:
- Identification
- Authentication
- Database access and update
Identification:
After considerable contemplation and debate we decided that we would use RFID (Radio Frequency Identification) for Identification. We selected RFID because this standard has a fairly short-range connection, a small connection-setup time (both of which make it slightly tougher to hack than some other standards) and also because we knew that our Cornell Cards have RFID tags embedded within them. Thus, we would be able to use these cards for identification instead of investing in RFID tags.
Authentication:
We decided to use 4-digit security pin for Authentication because we wanted a system that is quick and convenient while being secure. We had also considered adding a Fingerprint based authentication; however, due to the time constraint of 5 weeks we decided that we would add this feature later.
Database access and update:
Now for the Database access, while we would eventually want to transfer data over airwaves (in order to exploit the existing telecommunication infrastructure and avoid the hassle of setting up Ethernet connections), for the purpose of this project we decided to use Ethernet based data transfer. We took this decision because neither of us is familiar with 2G or 3G standards and implementing this in 5 weeks would have been a stretch.
Logical Structure
A state machine controls how the entire system works. We decided to use this topology because we were going to use a state machine (from Bruces keypad code) to debounce a button-press on the keypad and extending this structure to control the various sub-systems of our project seemed a logical next step.
RFID
Before we start a detailed explanation of our implementation we will take a step back and talk a little about the technology in general.
Excerpts from the microID 125kHz RFID System Guide:
RFID Introduction:
Radio Frequency Identification (RFID) systems use radio frequency to identify, locate and track people, assets and animals. Passive RFID systems are composed of three components – a reader (interrogator), passive tag and host computer. The tag is composed of an antenna coil and a silicon chip that includes basic modulation circuitry and non-volatile memory. The tag is energized by a time-varying electromagnetic radio frequency (RF) wave that is transmitted by the reader. This RF signal is called a carrier signal. When the RF field passes through an antenna coil, there is an AC voltage generated across the coil. This voltage is rectified to result in a DC voltage for the device operation. The device becomes functional when the DC voltage reaches a certain level. The information stored in the device is transmitted back to the reader. This is often called backscattering. By detecting the backscattering signal, the information stored in the device can be fully identified.
RFID Tag:
Tag consists of a silicon device and antenna circuit. The purpose of the antenna circuit is to induce an energizing signal and to send a modulated RF signal. The read range of tag largely depends upon the antenna circuit and size. The antenna circuit of tag is made of LC resonant circuit or E-field dipole antenna, depending on the carrier frequency. The LC resonant circuit is used for the frequency of less than 100 MHz. In this frequency band, the communication between the reader and tag takes place with magnetic coupling between the two antennas through the magnetic field. The antenna utilizing the inductive coupling is often called magnetic dipole antenna. The antenna circuit must be designed such a way to maximize the magnetic coupling between them.
Modulation Protocol:
The passive RFID tag uses backscattering of the carrier frequency for sending data from the tag to reader. The amplitude of backscattering signal is modulated with modulation data of the tag device. The modulation data can be encoded in the form of ASK (NRZ or Manchester), FSK or PSK. Therefore, the modulation signal from the tag is Amplitude-Amplitude, Amplitude-FSK and Amplitude-PSK.
Carrier:
Carrier is the transmitting radio frequency of reader (interrogator). This RF carrier provides energy to the tag device, and is used to detect modulation data from the tag using backscattering. In read/write device, the carrier is also used to deliver interrogator’s command and data to the tag.
Frequency Shift Keying:
This form of modulation uses two different frequencies for data transfer; the most common FSK mode is FC/8/10 (FC is the carrier frequency). In other words, a ‘0’ is transmitted as an amplitude-modulated clock cycle with period corresponding to the carrier frequency divided by 8, and a ‘1’ is transmitted as an amplitude-modulated clock cycle period corresponding to the carrier frequency divided by 10. The amplitude modulation of the carrier thus switches from FC/8 to FC/10 corresponding to 0’s and 1’s in the bitstream, and the reader has only to count cycles between the peak-detected clock edges to decode the data. FSK allows for a simple reader design, provides very strong noise immunity, but suffers from a lower data rate than some other forms of data modulation.
Now, we were really lucky for a couple of reasons. (1) Cornell has had RFID tags embedded in each of the Cornell ID cards since Fall of 2003 so we could simply use these tags (albeit these tags would not be embedded in our cell-phones, this would be OK for now), (2) Craig and Ricardo (from Spring 2006) had already done all the research on Cornell’s RFID tags (we are extremely grateful to them for their comprehensive report).
Salient Points:
- The tags operated on a 125 [kHz] carrier frequency.
- The modulation method used was FSK (Frequency Shift Keying).
This information was extremely crucial because we saved a lot of time on research and could concentrate on building a robust system instead!
Thus, now we knew that 0’s corresponded to a (125/8) 15.625[kHz] frequency and 1’s corresponded to a (125/10) 12.5[kHz] frequency.
Before delving into the specifics of the individual parts of the circuit here’s a breakdown of the top-level tasks (referenced from the microID 125kHz RFID System Guide):
-
Transmission:
The transmitting section contains circuitry for a carrier signal (125 kHz), power amplifiers, and a tuned antenna coil.
The 125 kHz carrier signal is typically generated by passing a 125 [kHz] square-wave signal from the Mega16 through an RF choke.
The signal is amplified before it is fed into the antenna tuning circuit.
A complementary power amplifier circuit is typically used to boost the transmitting signal level.
An antenna impedance tuning circuit consisting of capacitors is used to maximize the signal level at the carrier frequency.
This tuning circuit is also needed to form an exact LC resonant circuit for the carrier signal.
The tuning compensates the variations in the component values and the perturbation of coil inductance due to environment effect. -
Reception:
The receiving section consists of an antenna coil, demodulator, filters, amplifiers, and microcontroller.
The FSK reader needs two steps for a full recovery of the data.
The first step is demodulating the backscattering signal, and the second step is detecting the frequency (or period) of the demodulation signal.
The demodulation is accomplished by detecting the envelope of the carrier signal.
A half-wave capacitor-filtered rectifier circuit is used for the demodulation process.
A diode detects the peak voltage of the backscattering signal.
The voltage is then fed into an RC charging/discharging circuit.
The RC time constant must be small enough to allow the voltage across C to fall fast enough to keep in step with the envelope.
However, the time constant must not be so small as to introduce excessive ripple.
The demodulated signal must then pass through a filter and signal shaping circuit before it is fed to the microcontroller.
The microcontroller performs data decoding after this.
RFID Hardware:
Note: We adopted the ATMega32 schematic from Spring 2008 as it fit in better with our design. However, the pin-configuration and external hardware setup are identical in the ATMega32 and ATMega16 chips.
Explanation:
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).
Our 125[kHz] carrier wave
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 1nF 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).
RFID tag datastream to MCU
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:
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:
http://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/s2006/cjr37/Website/index.htm
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.
The figures below show the behaviour of the circuit when operating correctly:
Comparator Output with Reset Pulse
Comparator Output with Data Output
A Close-Up of our Reset Pulse Reveals That it is now 350 ns; 100 ns Over the Minimum for the Decade Counter
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:
- Wait until we receive a start sequence (extra long logic high)
- On next interrupt, decode the first Manchester bit: delay by a very short amount to ensure steady state, read the logic level.
- 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.
- 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)
- Repeat steps 2 and 3 until we read an end sequence (extra long logic low)
- 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!
Code Listings:
Main Program Files:
state_machine.c
ethlib.c
rfid.c
References
MicroID 125[kHz] Reference Guide
Ricardo and Craig’s website
Bryan Bryce’s website
WIZ812MJ datasheet
ATMega16 datasheet
// Header file for rfid unsigned long RFID( ); ISR( INT1_vect ); void rfid_initialize( void ); void uninit( void ); unsigned long bin2dec( char *bin ); #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include "rfid.h" //#define RF_IN PINA0 #define NUM_SAMPLES 100 #define NUM_CHECKS 20 #define T1 25 #define T2 55 #define T3 80 /* Global Variable Definitions */ char samples[ NUM_SAMPLES ]; volatile char started = 0; volatile int sample_index = 0; volatile int checksum = 0; volatile int lastchecksum = 0; char num_ones = 0; char num_zeros = 0; volatile char data_ready = 0; char correct_check_count = 0; /* ===Begin Functions=== */ /* Main execution function */ /*int main(void){ initialize(); while(1){ RFID(); } }*/ unsigned long RFID( ) { GICR = 1 << INT1; // Keep going until we get a valid data stream while ( correct_check_count < NUM_CHECKS ) { while ( !data_ready ) { } // When we have a full set of data, do checks // First time checking should always be right if ( correct_check_count == 0 ) { lastchecksum = checksum; } if ( checksum == lastchecksum ) { correct_check_count++; } else { correct_check_count = 0; } lastchecksum = checksum; data_ready = 0; //printf("%d", correct_check_count); } GICR = 0 << INT1; correct_check_count = 0; int i = 0; do { i++; //printf("%c", samples[i++]); samples[ i - 1 ] = samples[ i - 1 ] + 48; }while ( samples[ i ] != 2 ); samples[ i ] = '