Program Flow: The basic structure of this program is to build a buffer (basically the live storage of information, what I originally thought would require a dedicated memory block), read the digital input much faster than it changes (if I read a 4khz-8khz signal at a rate of 125khz, I should get 30-15 reads before each change in state), then record the time between each change in state. So I’d get 30, 30, 60, 30, 30, 60 for L H L L H L H H: if that doesn’t make sense, look at manchester encoding again (in the middle of the page.). From there I convert them back into binary. Finally, I reconstruct the data into the EM4100 format, check the parity bits for data integrity, and display the resulting card number in the serial monitor.
The Program
Initialization
As a reminder, this code is based off of the DIY RFID reader from Arduino. I’ve left the copyright under edude/Arduino Forum because I’ve only technically modified it. If anyone has any more insight into the correct way of doing this please let me know! The GNU general public license can be found here.
Credit:
/* 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 * November 2011 | Modified by Ryan Baker for use with the parallax ASK modulated, manchester encoded, 125khz RFID tag | rbaker dot rpb at gmail dot com * Copyright (C) 2011 by edude/Arduino ForumThis 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 <http://www.gnu.org/licenses/>.*/
So to start our initialization,
#include "TimerOne.h"int ledPin = 13; // LED connected to digital pin 13 int inPin = 7; // sensing digital pin 7 int outPin = 9; int val; int bitlenctr = 0; int curState = 0;#define maxBuf 200 //reduce to 100 or so for debugging #define streamBuf 400 // should be 2*maxBuf #define debug 0int raw[maxBuf]; int rawZ[maxBuf]; bool binStream[streamBuf];int index = 0; int indexZ = 0; int bufnum = 0;
First thing to note is that we need to download the timer1 library if you haven’t already. This will let us set our clock speed (without having to manually change the prescalars on the PWM). We initialize variables and constants (which I’ll describe later if necessary) for input/output/counting/etc. The important points here: each constant takes precious memory on one’s microcontroller, so making something like raw or rawZ (our buffers) large is not going to be helpful. Also, Arduino input/output pins are not created equal, so be careful if you choose to change them.
setup()
void setup() { Serial.begin(9600); Timer1.initialize(8); // 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(outPin, 512); // setup pwm on pin 9, 50% duty cycle Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt, once per RF cyclepinMode(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 }
The serial communication is used for the serial display showing you the tag id. You can use whatever baud you want. Then we initialize our timer with the number of microseconds per period (see comments in code). As my tag is 125khz, I have 8. After, we output a pwm to drive the LC circuit. 512 indicates a duty cycle of 0.5 (50%) with 1023 is the highest at a duty cycle of 1 (100%). This gives a square wave. Luckily only the fundamental frequency of the square actually goes through the LC circuit (it’s the only resonant frequency) and so the circuit still works as before.
Next we have the attachInterrupt() function, which is super cool. You can create your own function (in this case, callback, described next) which runs every RF cycle. So remember I said we check the signal every period and record times between changes? This function does that.
Lastly, we set our digital pins to be inputs/outputs and the pins specified.
callback()
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; } if(index > 1 && val == 0){ // got transition to low if(indexZ < maxBuf) { rawZ[indexZ++] = bitlenctr; } bitlenctr = 1; } } }
And I just found out WordPress doesn’t maintain tabs from Arduino, so I need to manually add those as spaces… anyway! We initialized our bitlenctr as zero and every time this callback runs (once per clock period) we add one to it (I’m guessing the name stands for bit length counter). We also initialized an int curState as zero previously and compare it with val, the value of our input pin. If they’re not the same, we need to do stuff. One thing we need to do is change curVal into being val, so that when this callback runs again it doesn’t fire off again (we only want it to do stuff when the state changes). Then we record the value into either raw or rawZ depending on if the input is 1 or 0 (Z stands for 0). This is how we keep track of the times between state changes (so often this number is around 30 or 60). Finally, we reset our bitlenctr counter to 1. For anyone reading this, I know the code isn’t particularly optimized, so you’ve been warned!
I’m going to skip loop() for now as it is longest and instead focus on the only other function, process_buf. Note that process_buf() should be placed at the end of the source, not after callback!
process_buf()
// 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) { int curHByte; int parityTerminus[4]; bool failed = false; bool willFlipIfFail = true; int sumParity; char theHex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; char theCode[10]; while(failed^willFlipIfFail){ failed=false; parityTerminus[0]=0; parityTerminus[1]=0; parityTerminus[2]=0; parityTerminus[3]=0; // Loop through theCode to get our final RFID code for(int i = 0; i < 10; i++){ curHByte=0; sumParity=0; //Serial.println(" "); // Loop through the bits corresponding to a single character for( int j = 0; j < 4; j++){ if(!willFlipIfFail){ binStream[start+j+i*5]=!binStream[start+j+i*5]; } curHByte = curHByte+round(pow(2,(3-j)))*(int)binStream[start+j+i*5]; parityTerminus[j] = parityTerminus[j]+(int)binStream[start+j+i*5]; sumParity = sumParity+(int)binStream[start+j+i*5]; //Serial.print(binStream[start+j+i*5]); } //Serial.print(binStream[start+4+i*5]); //Serial.print(" and "); //Serial.print(sumParity%2); if(sumParity%2!=binStream[start+4+i*5]){ failed = true; } // Convert binary to hex theCode[i]=theHex[curHByte]; //Serial.print(" "); //Serial.print(theCode[i]); } //Serial.print("\n"); for( int i = 0; i < 4; i++){ //Serial.print(parityTerminus[i]%2); //Serial.print(" and "); //Serial.print(binStream[start+50+i]); //Serial.print("\n"); if(parityTerminus[i]%2!=binStream[start+50+i]){ failed = true; } } willFlipIfFail=!willFlipIfFail; } if(failed){ Serial.println("\nParity failed, abort.\n"); } else{ Serial.println("\nSuccess! Your tag ID is: "); for(int i=0;i<10;i++){ Serial.print(theCode[i]); } } Serial.println("\n"); }
First thing to note, you’ll see many lines of code commented. This was for a lazy/manual form of debugging problems (and there were many). You can uncomment them or just get rid of them and write your own if you have problems. This is when explicitly knowing the technology like we’ve been trying to do is really important because you can output various strings of bits or times or whatever and check them. If you’ve done little electronics/coding debugging, this may be difficult.
Ok, so this function is for processing a set of binary numbers to check for a valid tag. We’ll have already found the start of the tag (the first set of at least 9 ones) and so this function sorts the data according to the EM4100 protocol, checks data integrity, and outputs the tag id. If you were making a security lock or something, all you would need to do from here is check that tag against a pre-made list of tags (strcmp, be careful though, it may output 0 when true? You’ll need to double check yourself) and if there was a match, output the signal to another pin to unlock whatever.
To start, we initialize variables, an important one being theHex. When we get some number, say 1011, we convert it to a decimal number curHByte (current half byte), in this case 11, then use that as an index to theHex to get the corresponding hex character, in this case B. We record these sets of hex chars into theCode.
The while loop is here to do two checks on the data. It XORs ‘failed’ with ‘willFlipIfFail,’ which is opaque, I know, but I wasn’t able to reliably check if the binary was right-side-up or upside-down, that is, if it had ones for zeros or vice versa, so I check both cases! I start by assuming the binary stream I have is right, and if the data check fails (setting failed=true), I flip all the bits (then setting willFlipIfFail to its opposite [this is important for the XOR, don’t set to false] so I don’t do this again) and recheck the data. I then do a loop through the ten characters I intend to pull from the binary (hence the i loop from 1 to 10). We remember that there are 4 bits representing the hex, so we convert those to decimal in our j loop. Remember our last 4 parity bits of the tag though? They need to count up the rows for their parity. Thus we need to keep track of those parity bits (parityTerminus) all of the time. We also need to sum the bits in our current j loop (that is, along columns) for checking their parity. If that binary sum (integer sum modulo 2) doesn’t equal the corresponding parity bit, we’ve failed. Otherwise, write it into the code. Once this i loop is done, check the parityTerminus numbers to see if they also pass. If all have passed, failed should be false and we exit the loop. Depending on if you failed or succeeded you get a different message. Failed obviously just tells you it didn’t work, succeed and you read out the tag.
Now finally, the microcontroller’s main program loop()
loop()
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); for(int i = 0; i < maxBuf;i++) { Serial.print((int)rawZ[i]); Serial.print("/"); } Serial.println("///rawZ 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; rawZ[0] = 0; for(int i = 1; i < maxBuf; i++) { int v = raw[i]; int vZ = rawZ[i]; // Check length of high bits if(v > 10 && v < 22) { raw[i] = 1; tot0++; } else if(v > 21 && v < 27) { raw[i] = raw[i - 1]; totp++; } else if(v > 26 && v < 50) { raw[i] = 2; tot1++; } else { raw[i] = 101; // error code tote++; } // Check length of low bits if(vZ > 10 && vZ < 22) { rawZ[i] = 1; tot0++; } else if(vZ > 21 && vZ < 27) { rawZ[i] = rawZ[i - 1]; totp++; } else if(vZ > 26 && vZ < 50) { rawZ[i] = 2; tot1++; } else { rawZ[i] = 101; // error code tote++; } } // Test for whether starting bit is 1 or 0 int hStart=0, lStart=0; for(int i = 0; i < maxBuf ; i++){ if(raw[i]==2){ hStart=i; break; } } for(int i = 0; i < maxBuf ; i++){ if(rawZ[i]==2){ lStart=i; break; } } int curBit; if(hStart<lStart){ curBit=1; } else{ curBit=0; } // Convert time lengths to binary int binIndex = 1; for(int i = 3; i < maxBuf; i++) { if(raw[i]==1&&rawZ[i-2]==1){ binStream[binIndex]=curBit; // Serial.print((int)binStream[binIndex]); binIndex++; } else if(raw[i]==2&&rawZ[i-2]==1){ binStream[binIndex]=curBit; // Serial.print((int)binStream[binIndex]); binIndex++; curBit=1-curBit; binStream[binIndex]=curBit; // Serial.print((int)binStream[binIndex]); binIndex++; } else if(raw[i]==1&&rawZ[i-2]==2){ curBit=1-curBit; binStream[binIndex]=curBit; // Serial.print((int)binStream[binIndex]); binIndex++; } else if(raw[i]==2&&rawZ[i-2]==2){ curBit=1-curBit; binStream[binIndex]=curBit; // Serial.print((int)binStream[binIndex]); binIndex++; curBit=1-curBit; binStream[binIndex]=curBit; // Serial.print((int)binStream[binIndex]); binIndex++; } else{ Serial.println("\nErrors in binary stream (a few of these messages are fine): "); Serial.print(raw[i]); Serial.println(" "); Serial.print(rawZ[i-2]); } } delay(2000);// next, search for a "start tag" of 9 high bits in a row int samecnt = 0; int start = -1; bool lastv = 0; for(int i = 0; i < streamBuf; i++) { if((binStream[i] == lastv)&&(binStream[i] == 1)) { // inside one same bit pattern, keep scanning samecnt++; // Serial.print(samecnt); } else { // fewer than 9 1s, so not a valid tag, keep scanning samecnt = 1; lastv = binStream[i]; } // got new bit pattern if(samecnt >= 9) { // got a start tag prefix, record index and exit start = i; // Serial.println("\nFound the ones!\n"); break; } }// if a valid prefix tag was found, process the buffer if(start > 0 && start < (streamBuf - 64)) { //adjust to allow room for full dataset past start point process_buf(start+1); } 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: ones: "); for(int i = 0; i < maxBuf; i++) { Serial.print((int)rawZ[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; indexZ = 0; } else { delay(5); } }
This is big but not that difficult to understand. We process the data if the buffer has been filled, otherwise we simply continue calling the callback to fill the buffer (this goes on in the background). The first debug check simply outputs to the serial monitor if you chose to do so earlier. The first i loop simply threshold the “imperfect” times (15 will sometimes be 14 or 16, or 30 might be 28 or 31,etc) to a value 1 or 2, representing the lengths of the highs or lows in a discrete way. After that, there’s a section in the code which tries to determine if our stream started on a one or zero, which can help but still isn’t robust. Any errors in the first few bits can ruin this, and sometimes they do. Then our next for loop we convert the times into the original binary. This is particularly tricky because a 2 in our time vectors adds half a period to our binary vector, which can sometimes result in adding two values to the binary stream… but sometimes not! So the binary stream needs a separate index from i. Our result also depends on the combination of lows and highs. So we can have a pair (raw[i], rawZ[i-2]) (the minus two is because of the strange way I indexed them to start with) {(1,1), (2,1), (1,2), (2,2)}, which correspond to adding {(curBit), (curBit, 1-curBit),(1-curBit),(1-curBit,curBit)} (be careful with (2,2) as the second bit added is the same bit that it starts with, which in the code progresses as 1-curBit and then 1-curBit again to get to the previous state), respectively, to the binary stream. Not intuitive by any measure, but it works! We delay just for stability, then we do a simply check for at least 9 ones (or zeros). If it passes, we call process_buf at the index which corresponds to the start of the 9 ones, and if not, continue down the loop. If debugging is on, it shows more parameters. Lastly, we reinitialize the buffer indices. The else statement at the very end is from the very first if statement where we checked it we had filled the buffer. If we didn’t, we waited a little with this delay and then rechecked (with callback() running in the background).
And that’s it! After running this I was able to identify my tag as 08003D8D7B, which is what we found in my very first post. Now that I’ve finished this part of the project, I’m going to update my original goals now that I have a greater understanding of the technology:
- To gain a minimal, procedural understanding of how to read RFID tags (Day 1).
- To gain a quantitative understanding of the science behind reading RFID tags and to make my own original design for reading them.
- Final Project: To design and construct a device capable of reading a tag on a different system where the parameters are unknown (a tag not provided by parallax and whose parameters are different than the one I currently have).
These goals are not actually different than before, they’re just reframed in a more cohesive manner. The first two are now finished and now I need to figure out how to read a card another card of mine. Testing it quickly on my current setup revealed that it may not even be a 125khz (or any LF) device but may be a 13.56 MHz device instead. I’m guessing this because when I place this card near the coil it didn’t even change the amplitude. Yet there is something strange in that it responds as I’m moving to or from the coil… so I’m not sure. Anyway, I don’t think I have any more free days this year to pursue this project, but I hope to report on something interesting by early next year. Until then, I have one or two other interesting, unrelated projects to report on before the year’s end, so stay tuned. Thanks for reading!
Edit: There are hints that my other card really is 13.56Mhz. I made a resonant circuit at ~1MHz, used a function generator to generate the signal, used an oscilloscope to monitor the resulting modulation, and the card changed the waveform somewhat when brought near the inductor. So now to get into 13.6MHz…
Hi.
Before receive the correctly tag, I am receiving many time the mesage:
“Errors in binary stream (a few of these messages are fine):”
Why I am receiving this?
The error you get is in the code around line 225. The previous section (starting around line 127) takes recorded delta-time stamps of up and down events (raw and rawZ) and converts them into a number 1 or 2 depending on a variety of circumstances (determined via some protocol). If you look at that section, you see the following (note that I am overwriting the raw value just to save microprocessor memory)(the following bounds are in units of clock cycles):
10 < raw[i] raw[i] = 1
21 < raw[i] raw[i] = previous number, raw[i – 1]
26 < raw[i] raw[i] = 2
anything else -> raw[i] = 101, an error code
You are getting the error code. Therefore, the signals you are getting are either too fast (less than 10 clock cycles) or too slow (greater than 50 clock cycles). I’m not sure WHY you are getting massively different times in your clock cycles. Investigate the following:
Are you looking at a 13.56 MHz tag? This project is for ~125 kHz tags and can’t deal with them.
Are you picking up random noise?
Is your tag actually using the EM4100 protocol? If not, the delta-times wouldn’t necessarily be the same. And if you don’t know, then you need to debug it, learn the times you ARE getting, then investigate a variety of different protocols (in my next post, “day 7,” check out another protocol I found, the HID Corporate 1000 series protocol).
Are you using a tag with ASK modulation? In the next post, “day 7,” I go over how to deal with FSK and PSK modulation.
If you have access to an oscilloscope, check the timing between pulses. If not, put “Serial.print((int)v[i]);” in each if statement in lines 131-147, and “Serial.print((int)vZ[i]);” in each if statement in lines 149-165. You may be able to read out how fast your signal is that way.
Good luck!
Many, many thank in help me. I am really desperate in fix my problem.
I am using a card of 125khz but I don’t know the model. The card information tag is 0006535350 099,47286.
Now, I will verify in osciloscope and send to you the image in your e-mail: comment-reply@wordpress.com
The most interst is taht after some time with the erros, the cad is read.
Again,
Thank you helping me!!!
Hi.
My reply in red below:
I am not using your circuit because I have a other that was working with a other microcontroller but I donât know what has inside the microcontroller.
Samuel Oliveira.
Skype: soliveirabrasil
Hi I am here again. Now with some idea where is the problem.
Every time when I turn on the circuit with the card close of the bobine, I not receve the mesage error (Errors in binary stream (a few of these messages are fine):).
Looking the input signal arraving to the microcontroler after the RFID circuit, if the RFID card is not close of the bobine is coming a noise with spikes.
Do you think what I can eliminate this problem (using the software or some hardware)?
Do you think that is possible some error in bobine calc?
Thanks,
Samuel Oliveira.