DIY FSK RFID Reader

简介:

This page describes the construction of an RFID reader using only an Arduino (Nano 3.0 was tested, but others may work), a hand-wound wire coil, and some assorted low cost common components.

Credits

The hardware and software designs for this project are based in part on the ideas, code and schematics posted by Micah Dowty here and Asher Glick here.

Background

RFID readers are devices sold by companies such as Parallax to read RFID tags with embedded identification circuits (we focus here on passive tags, activated by the reader's transmitted RF energy). The design presented here shows how to wind a simple wire loop by hand (or create an equivalent printed circuit spiral version), connect it to an Arduino (or its chip), add a few low cost common components and create your own RFID reader. To make it more interesting (i.e. challenging), we will focus on the FSK class of RFID tags, which are fairly common among the 125kHz devices, but for some reason are not supported by the Parallax kits.

Micah Dowty has shown a design for an FSK/ASK RFID reader built around a Parallax Propeller device. His code, which is in assembly language, implements an ingenious (but complex) algorithm to create a dynamically variable analog bias voltage, which is used to pull the weak RFID signal into range, so it can be discriminated into binary signals by the Propeller's digital input circuitry. He also dynamically tweaks the transmit/receive RF frequency to keep the antenna's tank circuit in peak resonance for optimal signal to noise. There are three problems with his approach: first, the passive detection circuit lacks amplification, which makes it very sensitive to noise and therefore raises reliability issues. Second, the design is based on the Propeller chip, and if you are a fan of the Arduino and/or associated Atmel AVR chips, it leaves you out. And third, the dynamic slewing of frequencies and bias voltage is overly complicated, making it hard to debug. His general concept is attractive, however: use a microcontroller chip and wind your own wire loop to create, with some simple components and appropriate code, a complete DIY RFID reader.

Asher Glick has presented a solution for reading and decoding FSK RFID tags using the Arduino/AVR family (which he calls AVRFID), which is good except it apparently requires obtaining and modifying an existing Parallax RFID reader device (which natively only supports ASK).

Our goal here is to present a simple solution for reading FSK tags which addresses the above shortcomings: make it robust and reliable for real-world noise environments, base it on the Arduino, and build the RFID reader ourselves using a few simple low-cost parts, rather than buying and/or modifying one.

Circuit

Arduino DIY FSK-RFID circuit diagram:

The circuit diagram above was derived from the "World's Simplest RFID Reader" design posted by Micah Dowty. Based on the Parallax Propeller, Micah's approach was to use passive components only, without amplification, in order to achieve the ultimate in simplicity. The lack of amplification, however, results in a weak signal, potentially less than 2V PTP. This signal is then biased by an analog level produced by the Propeller, to try to maintain the signal's DC level near the discrimination point of the Propeller's binary-digital input circuitry. His code attempts to dynamically calculate that optimal midpoint level, and feed it into the circuit using a filtered PWM DAC output. Since the signal is weak, it can be distorted by interference and noise, which results in reduced reliability. The circuit presented here includes (as Micah suggests in his documentation) one active component: a common low-cost LM234 quad-opamp IC (or equivalent). This addition provides several significant advantages, at a negligible cost. First, the signal is amplified (using one of the four opamps on the IC package) to a more noise-immune level (of 2-3 volts PTP). Second, the DC level of the signal is maintained at exactly Vcc/2 using another opamp on the IC, which eliminates the need for the DC propping code in the Arduino. Third, having the signal amplifier in place allows another low-pass RC filter stage (another capacitor and resistor), which makes the final discriminated digital signal cleaner and more reliable. The end result is a more robust detected signal with improved noise immunity.

As a quick review of the circuit, the loop is made of a toroidally-wound #22-30 magnet wire (we used an empty roll of Scotch 3.25" I.D. packing tape as former), and can be remoted from the circuit if needed, via coaxial cable. The inductor L1 and capacitance C1 should be matched to resonate at around 125 kHz. When driven at its resonant frequency by the Arduino's 0-5 volt square wave signal, the center point of the resonator (which connects to D1's cathode) will have a fairly pure voltage sine wave, of about 30V PTP. When coupled to an RFID tag, the pure sine wave RF will fluctuate visibly as the tag opens and closes its own loop antenna to repeatedly transmit its code. This modulation is then detected from the RF envelope by D1, C2 and R1, which produce a negative bias voltage with the small detected coded signal, e.g. about 11 RF cycles per coded cycle. The coded cycles are of two different wave lengths (or frequencies), which represent streams of logic ones and zeros, and they need to arrive at the Arduino chip as binary levels which can be timed reasonably accurately so as to reliably tell the difference between the two distinct frequencies.

The relatively large capacitor C3 decouples the negative bias voltage from the signal, and is followed by a low-pass RC filter stage (R2 and C4) which attenuates some of the residual RF spikes from the lower frequency coded RFID signal. Capacitor C5 decouples the resulting signal and presents it to the amplification stage, implemented by the LM324 opamp, IC1. The latter amplifies the weak signal from about .15V to about 3V PTP (depending of the ratio of R4 to R3), and places it on top of a Vcc/2 bias voltage, about 2.5V in the arduino's case. This signal is then fed into one of the digital input ports on the Arduino (which also includes some helpful hysteresis), and is discriminated by the internal comparator into a square wave of ones and zeroes.

Software

The Arduino sketch, derived from the code posted by Asher Glick, uses a single timer channel in the Arduino (using the Timer1 library) for both RF signal generation as well as timing clock to count the width of each input signal wave. There are two distinct cycle lengths in the detected input signal, "long" and "short", corresponding to logical ones and zeroes, respectively. A binary stream of stretches of repeated ones and zeroes is assembled, and then decimated into the original coded bits on the RFID tag, after decoding the Manchester encoding.

Here is the actual code:

       1        

       2        

       3        

       4        

       5        

       6        

       7        

       8        

       9        

       10        

       11        

       12        

       13        

       14        

       15        

       16        

       17        

       18        

       19        

       20        

       21        

       22        

       23        

       24        

       25        

       26        

       27        

       28        

       29        

       30        

       31        

       32        

       33        

       34        

       35        

       36        

       37        

       38        

       39        

       40        

       41        

       42        

       43        

       44        

       45        

       46        

       47        

       48        

       49        

       50        

       51        

       52        

       53        

       54        

       55        

       56        

       57        

       58        

       59        

       60        

       61        

       62        

       63        

       64        

       65        

       66        

       67        

       68        

       69        

       70        

       71        

       72        

       73        

       74        

       75        

       76        

       77        

       78        

       79        

       80        

       81        

       82        

       83        

       84        

       85        

       86        

       87        

       88        

       89        

       90        

       91        

       92        

       93        

       94        

       95        

       96        

       97        

       98        

       99        

       100        

       101        

       102        

       103        

       104        

       105        

       106        

       107        

       108        

       109        

       110        

       111        

       112        

       113        

       114        

       115        

       116        

       117        

       118        

       119        

       120        

       121        

       122        

       123        

       124        

       125        

       126        

       127        

       128        

       129        

       130        

       131        

       132        

       133        

       134        

       135        

       136        

       137        

       138        

       139        

       140        

       141        

       142        

       143        

       144        

       145        

       146        

       147        

       148        

       149        

       150        

       151        

       152        

       153        

       154        

       155        

       156        

       157        

       158        

       159        

       160        

       161        

       162        

       163        

       164        

       165        

       166        

       167        

       168        

       169        

       170        

       171        

       172        

       173        

       174        

       175        

       176        

       177        

       178        

       179        

       180        

       181        

       182        

       183        

       184        

       185        

       186        

       187        

       188        

       189        

       190        

       191        

       192        

       193        

       194        

       195        

       196        

       197        

       198        

       199        

       200        

       201        

       202        

       203        

       204        

       205        

       206        

       207        

       208        

       209        

       210        

       211        

       212        

       213        

       214        

       215        

       216        

       217        

       218        

       219        

       220        

       221        

       222        

       223        

       224        

       225        

       226        

       227        

       228        

       229        

       230        

       231        

       232        

       233        

       234        

       235        

       236        

       237        

       238        

       239        

       240        

       241        

       242        

       243        

       244        

       245        

       246        

       247        

       248        

       249        

       250        

       251        

       252        

       253        

       254        

       255        

       256        

       257        

       258        

       259        

       260        

       261        

       262        

       263        

       264        

       265        

       266        

       267        

       268        

       269        

       270        

       271        

       272        

       273        

       274        

       275        

       276        

       277        

       278        

       279        

       280        

       281        

/*  Arduino program for DIY FSK RFID Reader

 *  See description and circuit diagram at http://playground.arduino.cc/Main/DIYRFIDReader

 *  Tested on Arduino Nano and several FSK RFID tags

 *  Hardware/Software design is based on and derived from:

 *  Arduino/Timer1 library example

 *  June 2008 | jesse dot tane at gmail dot com

 *  AsherGlick: / AVRFID https://github.com/AsherGlick/AVRFID

 *  Micah Dowty:

 *  http://forums.parallax.com/showthread.php?105889-World-s-simplest-RFID-reader

 *

 *  Copyright (C) 2011 by edude/Arduino Forum

  

 This program is free software: you can redistribute it and/or modify

 it under the terms of the GNU General Public License as published by

 the Free Software Foundation, either version 3 of the License, or

 (at your option) any later version.

  

 This program is distributed in the hope that it will be useful,

 but WITHOUT ANY WARRANTY; without even the implied warranty of

 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

 GNU General Public License for more details.

  

 You should have received a copy of the GNU General Public License

 along with this program.  If not, see .

  

 */

  

#include "TimerOne.h"

  

int ledPin = 13; // LED connected to digital pin 13

int inPin = 7;   // sensing digital pin 7

int val;

int bitlenctr = 0;

int curState = 0;

  

#define maxBuf 1000 //reduce to 100 or so for debugging

#define debug  0

  

char raw[maxBuf];

  

int index = 0;

int bufnum = 0;

#define   redLED 12

#define   grnLED 11

  

void setup()

{

 Serial.begin(9600);

 Timer1.initialize(7);  // initialize timer1, and set the frequency; this drives both the LC tank as well as the pulse timing clock

 // note: modify this as needed to achieve resonance and good match with the desired tags

 // the argument value is in microseconds per RF cycle, so 8us will yield RF of 125kHz, 7us --> 143kHz, etc.

  

 Timer1.pwm(9, 512);           // setup pwm on pin 9, 50% duty cycle

 Timer1.attachInterrupt(callback);  // attaches callback() as a timer overflow interrupt, once per RF cycle

  

 pinMode(ledPin, OUTPUT);      // sets the digital pin 13 as output for scope monitoring

 pinMode(inPin, INPUT);      // sets the digital pin 7 as input to sense receiver input signal

 pinMode(grnLED, OUTPUT);

 pinMode(redLED, OUTPUT);

 digitalWrite(grnLED, 0);

 digitalWrite(redLED, 1);

}

  

void callback()

{

 val = digitalRead(inPin);

 digitalWrite(ledPin, val); // for monitoring

 bitlenctr++;

 if(val != curState) {

   // got a transition

   curState = val;

   if(val == 1) {

     // got a start of cycle (low to high transition)

     if(index < maxBuf) {

       raw[index++] = bitlenctr;

     }

     bitlenctr = 1;

   }

 }

}

  

void loop()

{

 if(index >= maxBuf) {

  

   Serial.print("got buf num: ");

   Serial.println(bufnum);

  

   if(debug) {

     for(int i = 0; i < maxBuf;

     i++) {

         Serial.print((int)raw[i]);

       Serial.print("/");

     }

     Serial.println("///raw data");

     delay(2000);

   }

  

   // analyze this buffer

   // first convert pulse durations into raw bits

   int tot1 = 0;

   int tot0 = 0;

   int tote = 0;

   int totp = 0;

   raw[0] = 0;

   for(int i = 1; i < maxBuf; i++) {

     int v = raw[i];

     if(v == 4) {

       raw[i] = 0;

       tot0++;

     }

     else if(v == 5) {

       raw[i] = raw[i - 1];

       totp++;

     }

     else if(v == 6 || v == 7) {

       raw[i] = 1;

       tot1++;

     }

     else {

       raw[i] = 101; // error code

       tote++;

     }

   }

  

   // next, search for a "start tag" of 15 high bits in a row

   int samecnt = 0;

   int start = -1;

   int lastv = 0;

   for(int i = 0; i < maxBuf; i++) {

     if(raw[i] == lastv) {

       // inside one same bit pattern, keep scanning

       samecnt++;

     }

     else {

       // got new bit pattern

       if(samecnt >= 15 && lastv == 1) {

         // got a start tag prefix, record index and exit

         start = i;

         break;

       }

       // either group of 0s, or fewer than 15 1s, so not a valid tag, keep scanning

       samecnt = 1;

       lastv = raw[i];

     }

   }

  

   // if a valid prefix tag was found, process the buffer

   if(start > 0 && start < (maxBuf - 5*90)) { //adjust to allow room for full dataset past start point

     process_buf(start);

   }

   else {

     Serial.println("no valid data found in buffer");

   }

   if(debug) {

     for(int i = 0; i < maxBuf;

       i++) {

         Serial.print((int)raw[i]);

       Serial.print("/");

     }

     Serial.print("///\nbuffer stats: zeroes:");

     Serial.print(tot0);

     Serial.print("/ones:");

     Serial.print(tot1);

     Serial.print("/prevs:");

     Serial.print(totp);

     Serial.print("/errs:");

     Serial.println(tote);

     delay(1000);

   }

  

   // start new buffer, reset all parameters

   bufnum++;

   curState = 0;

   index = 0;

 }

 else {

   delay(5);

 }

}

  

// process an input buffer with a valid start tag

// start argument is index to first 0 bit past prefix tag of 15+ ones

void process_buf(int start) {

 // first convert multi bit codes (11111100000...) into manchester bit codes

 int lastv = 0;

 int samecnt = 0;

 char manch[91];

 char final[45];

 int manchindex = 0;

  

 Serial.println("got a valid prefix, processing data buffer...");

 for(int i = start + 1; i < maxBuf && manchindex < 90; i++) {

   if(raw[i] == lastv) {

     samecnt++;

   }

   else {

     // got a new bit value, process the last group

     if(samecnt >= 3 && samecnt <= 8) {

       manch[manchindex++] = lastv;

     }

     else if(samecnt >= 9 && samecnt <= 14) {

       // assume a double bit, so record as two separate bits

       manch[manchindex++] = lastv;

       manch[manchindex++] = lastv;

     }

     else if(samecnt >= 15 && lastv == 0) {

       Serial.println("got end tag");

       // got an end tag, exit

       break;

     }

     else {

       // last bit group was either too long or too short

       Serial.print("****got bad bit pattern in buffer, count: ");

       Serial.print(samecnt);

       Serial.print(", value: ");

       Serial.println(lastv);

       err_flash(3);

       return;

     }

     samecnt = 1;

     lastv = raw[i];

   } //new bit pattern

 }

  

 Serial.println("converting manchester code to binary...");

 // got manchester version, convert to final bits

 for(int i = 0, findex = 0; i < 90; i += 2, findex++) {

   if(manch[i] == 1 && manch[i+1] == 0) {

     final[findex] = 1;

   }

   else if(manch[i] == 0 && manch[i+1] == 1) {

     final[findex] = 0;

   }

   else {

     // invalid manchester code, exit

     Serial.println("****got invalid manchester code");

     err_flash(3);

     return;

   }

 }

  

 // convert bits 28 thru 28+16 into a 16 bit integer

 int code = 0;

 int par = 0;

 for(int i = 28, k = 15; i < 28+16; i++, k--) {

   code |= (int)final[i] << k;

 }

 int paritybit = final[28+16];

 for(int i = 0; i < 45; i++) {

   par ^= final[i];

 }

  

 if(par) {

   Serial.print("got valid code: ");

   Serial.println((unsigned int)code);

   // do something here with the detected code...

   //

   //

   digitalWrite(redLED, 0);

   digitalWrite(grnLED, 1);

   delay(2000);

   digitalWrite(grnLED, 0);

   digitalWrite(redLED, 1);

 }

 else {

   Serial.println("****parity error for retrieved code");

   err_flash(3);

 }

}

  

// flash red for duration seconds

void err_flash(int duration) {

 return;

 for(int i = 0; i < duration*10; i++) {

   digitalWrite(redLED, 0);

   delay(50);

   digitalWrite(redLED, 1);

   delay(50);

 }

}

Status

The device and transceiver antenna have been built and tested on multiple FSK RFID tags of various kinds, in breadboard and soldered perfboard versions, connected to remote and local probes. When the probe is properly tuned, the device can reliably detect FSK RFID tags within a range of 0 to at least 2 inches from the coil, although it may be possible that this can be extended with larger coil sizes and/or other optimizations. The circuit has also been simulated on Spice, as described below.

Spice simulation

LTspiceIV simulated waveforms of FSK RFID reader plus transponder tag:

As seen in the LTspiceIV screenshot above, the circuit (with a passive virtual ground reference - see note below) was simulated on a computer, and the results confirmed the essential design, closely replicating the waveforms actually seen on the oscilloscope. The RFID transponder tag was simulated as a coupled transformer winding with a resonantly tuned capacitor, shunted to ground by a square-wave signal. The RFID tag's ground is connected to the main circuit's ground for simulation purposes. The inductive coupling between the two "transformer windings" is a variable which can be changed in LTspice, and was varied for testing between 1 and 0.01 (0.015 is shown in the waveforms above), equivalent to having the RFID tag positioned at different distances from the reader coil.

Notes

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

References

原帖连接:http://playground.arduino.cc/Main/DIYRFIDReader

本文转自 K1two2 博客园博客,原文链接:http://www.cnblogs.com/k1two2/p/5653439.html  ,如需转载请自行联系原作者


相关文章
|
8月前
|
芯片
基于Micropython利用ESP32-C3墨水屏电子时钟方法
基于Micropython利用ESP32-C3墨水屏电子时钟方法
423 0
|
8月前
【Simulink】基于FCS-MPC的单相并网逆变器电流控制(Matlab Function)
【Simulink】基于FCS-MPC的单相并网逆变器电流控制(Matlab Function)
|
8月前
【Simulink】基于FCS-MPC的三相并网逆变器电流控制(Matlab Function)
【Simulink】基于FCS-MPC的三相并网逆变器电流控制(Matlab Function)
|
8月前
|
算法
【Simulink】基于无差拍的单相L型滤波并网逆变器电流控制
【Simulink】基于无差拍的单相L型滤波并网逆变器电流控制
|
8月前
【Simulink】基于无差拍的三相L型滤波并网逆变器电流控制
【Simulink】基于无差拍的三相L型滤波并网逆变器电流控制
|
8月前
【Simulink】基于FCS-MPC的带阻感负载的三相逆变器电流控制(Matlab Function)
【Simulink】基于FCS-MPC的带阻感负载的三相逆变器电流控制(Matlab Function)
|
8月前
|
传感器 机器人
|
算法 新能源
基于电流控制的并网逆变器(Simulink)
基于电流控制的并网逆变器(Simulink)
161 0
|
算法 C语言
MLX90640 红外热成像仪测温模块开发笔记(五)
MLX90640 的 32*24=768 像素虽然比以往的 8*8 或者 16*8 像素提高了很多,但若直接用这些像素还是不能很好的形成热像图,为了使用这些像素点平滑成像就需要对其进行插值,使用更多的像素来绘制图像。 看了一些别人的算法,感觉主要就是多项式插值,仅是插值方法的组合方式不同。
MLX90640 红外热成像仪测温模块开发笔记(五)
|
传感器 存储 编解码
MLX90640 红外热成像仪测温传感器模块开发笔记(七)
Red Eye Camera(以下简称“IDF-x” 或“设备” )是基于红外阵列高精度温度传感器以及先进软件算法的非接触式热成像仪器,可对视场范围内任何物体进行红外成像,成像分辨率512*384 像素,温度灵敏度 0.1℃,绝对精度±1.5℃,刷新频率最高 64Hz。自带存储和实时时钟,具备数据实时输出显示、拍照存储功能,数字接口包括 UART 和 USB,可直接连接计算机和 Android 手机,配合上位机软件或者手机 APP 程序,使用十分方便。
MLX90640 红外热成像仪测温传感器模块开发笔记(七)