everhack Stuff I've been messing with, or just thinking about.

21Oct/112

RFID reader, part 5 – 1’s and 0’s, but what do they mean?

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()
{
}

Filed under: Uncategorized 2 Comments
18Oct/110

RFID Reader, part 4, detecting bits! (well, 1’s at least)

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.

 

 

 

Filed under: Uncategorized No Comments
6Oct/113

RFID Reader, part 3

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)
 {
 }
}
Filed under: Uncategorized 3 Comments