Wiegand Interface

Hi Guys,
I would like to try and make an access control system using the Duemilanove. I was giving a HID branded Prox Reader (Product link below) which I was hoping to use. The output from the reader is in the form of Data0 and Data1 wires and the data format is 26bit Wiegand. Has anyone ever interfaced this type of reader before? Does anyone know where I could start?

Thank in advance,
Alex

http://www.hidglobal.com/technology.php?tech_cat=1&subcat_id=10&techno_id=1

well, it says that it also provides clock and data lines (assuming that im looking at the right model, there are a bunch of them). you should be able to adapt a magnetic card reader project like this http://www.instructables.com/id/Arduino-magnetic-stripe-decoder/
to read the cards and control something from it.

Has anyone ever interfaced this type of reader before?

Yes I have just done this and written a routine to gather the data in in Wiegand format. I used interrupts to the two data lines and gather the data in the interrupt service routine. I will be posting the code later on today, keep your eye on the Exhibition section of the forum.

In the mean time here is the nub of the code:-

/*
 * By Mike Cook April 2009
 * Three RFID readers outputing 26 bit Wiegand code to pins:-
 * Reader A  Pins 4 & 5
 * Reader B  Pins 6 & 7
 * Reader C  Pins 8 & 9
 * Interrupt service routine gathers Wiegand pulses (zero or one) until 26 have been recieved
 * Then a sting is sent to processing
 */
#include "pins_arduino.h"
/*
 * an extension to the interrupt support for arduino.
 * add pin change interrupts to the external interrupts, giving a way
 * for users to have interrupts drive off of any pin.
 * Refer to avr-gcc header files, arduino source and atmega datasheet.
 */

/*
 * Theory: all IO pins on Atmega168 are covered by Pin Change Interrupts.
 * The PCINT corresponding to the pin must be enabled and masked, and
 * an ISR routine provided.  Since PCINTs are per port, not per pin, the ISR
 * must use some logic to actually implement a per-pin interrupt service.
 */

/* Pin to interrupt map:
 * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
 * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
 * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
 */

volatile uint8_t *port_to_pcmask[] = {
  &PCMSK0,
  &PCMSK1,
  &PCMSK2
};

typedef void (*voidFuncPtr)(void);

volatile static voidFuncPtr PCintFunc[24] = { 
  NULL };

volatile static uint8_t PCintLast[3];

/*
 * attach an interrupt to a specific pin using pin change interrupts.
 * First version only supports CHANGE mode.
 */
 void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  uint8_t slot;
  volatile uint8_t *pcmask;

  if (mode != CHANGE) {
    return;
  }
  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }
  slot = port * 8 + (pin % 8);
  PCintFunc[slot] = userFunc;
  // set the mask
  *pcmask |= bit;
  // enable the interrupt
  PCICR |= 0x01 << port;
}

void PCdetachInterrupt(uint8_t pin) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

  // disable the mask.
  *pcmask &= ~bit;
  // if that's the last one, disable the interrupt.
  if (*pcmask == 0) {
    PCICR &= ~(0x01 << port);
  }
}

// common code for isr handler. "port" is the PCINT number.
// there isn't really a good way to back-map ports and masks to pins.
static void PCint(uint8_t port) {
  uint8_t bit;
  uint8_t curr;
  uint8_t mask;
  uint8_t pin;

  // get the pin states for the indicated port.
  curr = *portInputRegister(port+2);
  mask = curr ^ PCintLast[port];
  PCintLast[port] = curr;
  // mask is pins that have changed. screen out non pcint pins.
  if ((mask &= *port_to_pcmask[port]) == 0) {
    return;
  }
  // mask is pcint pins that have changed.
  for (uint8_t i=0; i < 8; i++) {
    bit = 0x01 << i;
    if (bit & mask) {
      pin = port * 8 + i;
      if (PCintFunc[pin] != NULL) {
        PCintFunc[pin]();
      }
    }
  }
}

SIGNAL(PCINT0_vect) {
  PCint(0);
}
SIGNAL(PCINT1_vect) {
  PCint(1);
}
SIGNAL(PCINT2_vect) {
  PCint(2);
}

// End of interrupts code and start of the reader code

volatile long reader1 = 0,reader2 = 0, reader3 = 0;
volatile int reader1Count = 0, reader2Count = 0,  reader3Count = 0;

void reader1One(void) {
  if(digitalRead(4) == LOW){
  reader1Count++;
  reader1 = reader1 << 1;
  reader1 |= 1;
  }
}

void reader1Zero(void) {
  if(digitalRead(5) == LOW){
  reader1Count++;
  reader1 = reader1 << 1;  
  }
}

void reader2One(void) {
  if(digitalRead(6) == LOW){
  reader2Count++;
  reader2 = reader2 << 1;
  reader2 |= 1;
  }
}

void reader2Zero(void) {
  if(digitalRead(7) == LOW){
  reader2Count++;
  reader2 = reader2 << 1;  
  }
}

void reader3One(void) {
  if(digitalRead(8) == LOW){
  reader3Count++;
  reader3 = reader3 << 1;
  reader3 |= 1;
  }
}

void reader3Zero(void) {
  if(digitalRead(9) == LOW){
  reader3Count++;
  reader3 = reader3 << 1;  
  }
}

void setup()
{
  Serial.begin(57000);
  // Attach pin change interrupt service routines from the Wiegand RFID readers
  PCattachInterrupt(4, reader1One, CHANGE);
  PCattachInterrupt(5, reader1Zero, CHANGE);
  PCattachInterrupt(6, reader2One, CHANGE);
  PCattachInterrupt(7, reader2Zero, CHANGE);
  PCattachInterrupt(8, reader3One, CHANGE);
  PCattachInterrupt(9, reader3Zero, CHANGE);
  delay(10);
  // the interrupt in the Atmel processor mises out the first negitave pulse as the inputs are already high,
  // so this gives a pulse to each reader input line to get the interrupts working properly.
  // Then clear out the reader variables.
  // The readers are open collector sitting normally at a one so this is OK
  for(int i = 4; i<10; i++){
  pinMode(i, OUTPUT);
   digitalWrite(i, HIGH); // enable internal pull up causing a one
  digitalWrite(i, LOW); // disable internal pull up causing zero and thus an interrupt
  pinMode(i, INPUT);
  digitalWrite(i, HIGH); // enable internal pull up
  }
  delay(10);
  // put the reader input variables to zero
  reader1 = reader2 = reader3 = 0;
  reader1Count = reader2Count =  reader3Count = 0;
  digitalWrite(13, HIGH);  // show Arduino has finished initilisation
}

void loop() {
  if(reader1Count >= 26){
//  Serial.print(" Reader 1 ");Serial.println(reader1,HEX);
  Serial.println("A");Serial.println(reader1 & 0xfffffff);
  reader1 = 0;
  reader1Count = 0;
     }
     
  if(reader2Count >= 26){
  Serial.println("B");Serial.println(reader2 & 0xfffffff);
  reader2 = 0;
  reader2Count = 0;
     }
     
 if(reader3Count >= 26){
  Serial.println("C");Serial.println(reader3 & 0xfffffff);
  reader3 = 0;
  reader3Count = 0;
     }
    
}

If you only have one reader then you can use the normal attach interrupts and use Pins 2 & 3 for the inputs. Also you could trigger on just one edge of the signal and make the ISR a bit simpler.

1 Like

Thanks heaps guys, defiantly a kick in the right direction.
I look forward to seeing your finished project Mike.
Thanks again,
Alex

I look forward to seeing your finished project

Its up on the web now at:-
http://www.thebox.myzen.co.uk/Hardware/Crazy_People.html

I've having trouble trying to get this code to just read one reader I got the system to work, I had to change the serial baud rate as the Arduino IDE does not have 57000 as one of the options.

The issue I am having is with what i think might be the buffer or what ever the read count thing is, I was told that the RFID keys are 37-bit keys yet when i try to change the read count if statement to 37 nothing much changes i have to tap the RFID tag a few times before I get numbers to show up, and some times i just get this character ¡ it might be an i but i'm not entirely sure. this is what usually prints on the serial monitor when I keep tapping my keyfob:

The forum keeps formating out my examples? why? i'm copy and pasting them from the arduino IDE is that inserting hidden characters that the forum removes?
I can't put the example in i'll try typing it manually

A
484
A
98569537
A
484
98569537

That seemed to work
Ok so yeah, that's the problem i'm encountering.

as the Arduino IDE does not have 57000 as one of the options.

Odd mine does, what version are you using. I use 11 and 15. Anyway the speed should not be an issue.

I was told that the RFID keys are 37-bit keys

No RFID Wiegand codes can be anything, 26 28 and 32 are popular, 37 is quite rare.
If you are having to present your token several times then you have set the number too big and you should reduce it. Put in a print statement to see how many you are reading in the first time you run the sketch and the put that number in.

How are you wiring it up? Is it like my example or have you modified it to use pins 2 & 3, in which case the code will have to be different. Do you have pull up resistors on the inputs Wiegand readers are normally open collector outputs.

Arduino11 has an option of 57600 but not 57000.
I have wired it up like your example.

I don't understand why that when i have if(reader1Count>=26) I would read the same string everytime it was A 31460673 but if i change it to
if(reader1Count>=28) i get numbers that don't even fit anywhere in the string from when i used 26??

Here is a link to the keyfobs i'm using
http://www.hidglobal.com/prod_detail.php?prod_id=22 I checked the email from the person who orders the keyfobs and he said they are 37bit but maybe he is misinformed

any ideas?

Ok so i found out what the keyfob is programmed to be and what the reader is actually reading and they are not the same numbers. any ideas why? The system reads 31460673, yet the keyfob is facility 240 serial 01696 why would i be getting this difference?

i get numbers that don't even fit anywhere in the string from when i used 26?

You won't, the token is not transmitting a number it is transmitting a sequence of zero and one bits. This is being collected by the program and is outputting the decimal value of what all those bits are. This is why you are getting:-

The system reads 31460673, yet the keyfob is facility 240 serial 01696 why would i be getting this difference?

31460673 is in decimal, in hex it is 1E00D41
you can then write that down in binary:-
0001 1110 0000 0000 1101 0100 0001
Now where is the parity bits in this wiegand code? It depends on how it is formatted, the most common way is that the first and the last bit is a parity bit for half the field so drop them and you get:-
001 1110 0000 0000 1101 0100 000
now the facility code is normally the first section of the code, if your code is 240 (decimal) that is F0 in hex so in binary it is 00 1111 0000
so match up your code and you will see the first part is F0:-
001 1110 0000 0000 1101 0100 000
So you have a 10 bit facility code.

This leaves the back part
0 0000 1101 0100 000 now re arrange the spaces:-
0000 0110 1010 0000
which is 06A0 in hex or 1696 in decimal which I believe is the number you have as the serial code.

And for my next trick ...... ;D

Thank you so much!! I'm a little less stressed knowing that it's reading the key fobs properly.
Now is there any easy way to have it output 2401696 or 24001696 (which every the proper format is)? or would that be to complicated for the arduino?

Hi Guys,
I have been playing around with output data from the reader. When i scan my card, i get an output of 10110110101011000001111110, which is all good, its 26bits and contains the site code and card number. Separating the site code and card number however are random bits...

Site Code Card Number
1 01101101 0 101100000111111 0

Is there anyway i can separate the incoming data into Site Code and Card Number, removing the Parity Bits?

Thanks Heaps
Alex

Separating the site code and card number however are random bits

They are not random they are parity bits that cover some of the field. You would have to analyse a few more tokens to see what bit field they cover.

Is there anyway i can separate the incoming data into Site Code and Card Number

It is a very simple case of bit manipulation.
if you have the code: 1 01101101 0 101100000111111 0

then siteCode = (rawCode >> 17) & 0xff;
serialNumber = (rawCode >> 1) & 0x7fff;

All you are doing is shifting the bit pattern down until it's lease significant bit is at the start (bit zero) then ANDing it with a mask that as 1's in the bits you want to keep and zeros where you don't want the bits.

tgregor
For your code:-
001 1110 0000 0000 1101 0100 000

siteCode = (rawCode >> 16) & 0x3ff;
serialNumber = (rawCode >> 1) & 0x3fff;

I'm having an issue..I think, The reader wasn't sending the data everytime I hooked up some leds to help debug the issues and I've noticed that about 1 in 5 times the arduino resets instead of sending the code to the computer? any ideas why this is happening.

I'm using pull-up resistors on the reader for both the one and zero inputs. I've changed it so the power for the reader is coming from a separate supply yet using common grounds.

Any ideas?

Can you post the code, I assume it is different to mine. If not have you attached anything to the unused pins (like pull up resistors)?

Mike, your code helped me out a lot with a recent project of mine. I have a HID 5355AGK00 (for tgregor) which is a Wiegand26 reader with Wiegand keypad. The keypad outputs 4 bits for each keypress. I'm having some issues now which are mostly ironed out relating to determining if the wiegand in is a keypress or is a card scan.

It sounded easy initially but I ran into some timing issues. From testing, I found that the amount of time the Serial.print command takes to output a number is significant enough that when I comment out the lines, the delay in loop() needs to be longer to allow enough time for a full read. Shall I post the code for you to pick over?

Again, thanks for posting Crazy People. It helped tremendously.

Alex

I don't have anything attached to any other pins. here is the code, it's pretty much your original code.

/*
 * By Mike Cook April 2009
 * Three RFID readers outputing 26 bit Wiegand code to pins:-
 * Reader A  Pins 4 & 5
 * Reader B  Pins 6 & 7
 * Reader C  Pins 8 & 9
 * Interrupt service routine gathers Wiegand pulses (zero or one) until 26 have been recieved
 * Then a sting is sent to processing
 */
#include "pins_arduino.h"
/*
 * an extension to the interrupt support for arduino.
 * add pin change interrupts to the external interrupts, giving a way
 * for users to have interrupts drive off of any pin.
 * Refer to avr-gcc header files, arduino source and atmega datasheet.
 */

/*
 * Theory: all IO pins on Atmega168 are covered by Pin Change Interrupts.
 * The PCINT corresponding to the pin must be enabled and masked, and
 * an ISR routine provided.  Since PCINTs are per port, not per pin, the ISR
 * must use some logic to actually implement a per-pin interrupt service.
 */

/* Pin to interrupt map:
 * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
 * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
 * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
 */

volatile uint8_t *port_to_pcmask[] = {
  &PCMSK0,
  &PCMSK1,
  &PCMSK2
};

typedef void (*voidFuncPtr)(void);

volatile static voidFuncPtr PCintFunc[24] = {
  NULL };

volatile static uint8_t PCintLast[3];

/*
 * attach an interrupt to a specific pin using pin change interrupts.
 * First version only supports CHANGE mode.
 */
 void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  uint8_t slot;
  volatile uint8_t *pcmask;

  if (mode != CHANGE) {
    return;
  }
  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  }
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }
  slot = port * 8 + (pin % 8);
  PCintFunc[slot] = userFunc;
  // set the mask
  *pcmask |= bit;
  // enable the interrupt
  PCICR |= 0x01 << port;
}

void PCdetachInterrupt(uint8_t pin) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  }
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

  // disable the mask.
  *pcmask &= ~bit;
  // if that's the last one, disable the interrupt.
  if (*pcmask == 0) {
    PCICR &= ~(0x01 << port);
  }
}

// common code for isr handler. "port" is the PCINT number.
// there isn't really a good way to back-map ports and masks to pins.
static void PCint(uint8_t port) {
  uint8_t bit;
  uint8_t curr;
  uint8_t mask;
  uint8_t pin;

  // get the pin states for the indicated port.
  curr = *portInputRegister(port+2);
  mask = curr ^ PCintLast[port];
  PCintLast[port] = curr;
  // mask is pins that have changed. screen out non pcint pins.
  if ((mask &= *port_to_pcmask[port]) == 0) {
    return;
  }
  // mask is pcint pins that have changed.
  for (uint8_t i=0; i < 8; i++) {
    bit = 0x01 << i;
    if (bit & mask) {
      pin = port * 8 + i;
      if (PCintFunc[pin] != NULL) {
        PCintFunc[pin]();
      }
    }
  }
}

SIGNAL(PCINT0_vect) {
  PCint(0);
}
SIGNAL(PCINT1_vect) {
  PCint(1);
}
SIGNAL(PCINT2_vect) {
  PCint(2);
}

// End of interrupts code and start of the reader code

volatile long reader1 = 0;
volatile int reader1Count = 0;

void reader1One(void) {
  if(digitalRead(4) == LOW){
  reader1Count++;
  reader1 = reader1 << 1;
  reader1 |= 1;
  }
}

void reader1Zero(void) {
  if(digitalRead(5) == LOW){
  reader1Count++;
  reader1 = reader1 << 1;  
  }
}
/*
void reader2One(void) {
  if(digitalRead(6) == LOW){
  reader2Count++;
  reader2 = reader2 << 1;
  reader2 |= 1;
  }
}

void reader2Zero(void) {
  if(digitalRead(7) == LOW){
  reader2Count++;
  reader2 = reader2 << 1;  
  }
}

void reader3One(void) {
  if(digitalRead(8) == LOW){
  reader3Count++;
  reader3 = reader3 << 1;
  reader3 |= 1;
  }
}

void reader3Zero(void) {
  if(digitalRead(9) == LOW){
  reader3Count++;
  reader3 = reader3 << 1;  
  }
}
*/
void setup()
{
  Serial.begin(9600);
  // Attach pin change interrupt service routines from the Wiegand RFID readers
  PCattachInterrupt(4, reader1One, CHANGE);
  PCattachInterrupt(5, reader1Zero, CHANGE);

  delay(10);
  // the interrupt in the Atmel processor mises out the first negitave pulse as the inputs are already high,
  // so this gives a pulse to each reader input line to get the interrupts working properly.
  // Then clear out the reader variables.
  // The readers are open collector sitting normally at a one so this is OK
  for(int i = 4; i<10; i++){
  pinMode(i, OUTPUT);
   digitalWrite(i, HIGH); // enable internal pull up causing a one
  digitalWrite(i, LOW); // disable internal pull up causing zero and thus an interrupt
  pinMode(i, INPUT);
  digitalWrite(i, HIGH); // enable internal pull up
  }
  delay(10);
  // put the reader input variables to zero
  reader1 = 0;
  reader1Count = 0;
  
  for(int i=0;i<=3;i++){
  digitalWrite(13, HIGH);  // show Arduino has finished initilisation
  delay(200);
  digitalWrite(13,LOW);
  delay(200);
  }
 
}

void loop() {
  if(reader1Count == 26){
   // Serial.print(" Reader1: ");Serial.println(reader1,HEX);
  //Serial.println("A");
 //int siteCode = (reader1 >> 16) & 0x3ff;
 //int serialNumber = (reader1 >> 1) & 0x3fff;
  Serial.println(reader1 & 0xfffffff);
  //Serial.println(reader1Count);
  reader1 = 0;
  reader1Count = 0;
  digitalWrite(13,HIGH);
  delay(3000);
  digitalWrite(13,LOW);
  }
}

If I wanted to change it to use the original interrupts would I just have to change all of the pin reads and that for...loop that enables the internal pull-ups? Would I then be able to use all the other pins without issues? my end goal is to be able to slap on the ethernet shield and have it authenticate the tags with a server. But that is down the road, i need to be able to consistently read the tags.

Thanks for the help

Tgregor: here is Mike's sketched chopped up and using the original Arduino interrupts. DATA0 to pin 2, DATA1 to pin 3 (these cannot be changed using the attachInterrupt class)

/* Crazy People
 * By Mike Cook April 2009
 * Three RFID readers outputing 26 bit Wiegand code to pins:-
 * Reader A (Head) Pins 2 & 3
 * Interrupt service routine gathers Wiegand pulses (zero or one) until 26 have been recieved
 * Then a sting is sent to processing
 */

volatile long reader1 = 0;
volatile int reader1Count = 0;

void reader1One(void) {
  reader1Count++;
  reader1 = reader1 << 1;
  reader1 |= 1;
}

void reader1Zero(void) {
  reader1Count++;
  reader1 = reader1 << 1;  
}


void setup()
{
  Serial.begin(57600);
  // Attach pin change interrupt service routines from the Wiegand RFID readers
  attachInterrupt(0, reader1Zero, FALLING);//DATA0 to pin 2
  attachInterrupt(1, reader1One, FALLING); //DATA1 to pin 3
  delay(10);
  // the interrupt in the Atmel processor mises out the first negitave pulse as the inputs are already high,
  // so this gives a pulse to each reader input line to get the interrupts working properly.
  // Then clear out the reader variables.
  // The readers are open collector sitting normally at a one so this is OK
  for(int i = 2; i<4; i++){
  pinMode(i, OUTPUT);
   digitalWrite(i, HIGH); // enable internal pull up causing a one
  digitalWrite(i, LOW); // disable internal pull up causing zero and thus an interrupt
  pinMode(i, INPUT);
  digitalWrite(i, HIGH); // enable internal pull up
  }
  delay(10);
  // put the reader input variables to zero
  reader1 = 0;
  reader1Count = 0;
  digitalWrite(13, HIGH);  // show Arduino has finished initilisation
}

void loop() {
  if(reader1Count >= 26){
//  Serial.print(" Reader 1 ");Serial.println(reader1,HEX);
  Serial.println("A");Serial.println(reader1 & 0xfffffff);
  reader1 = 0;
  reader1Count = 0;
     }    
}

That code seems to be working PERFECTLY, thank you sooooo much it is exactly what I need, now if i can just figure out how to have it output the data in the format that matches what is stored on the server.

update
I may have spoken too soon, it seems not to be reading the same numbers it was I was using the other code.
It now reads "35648190"
It used to read "31460673" what has changed?
the keyfob code is facility 240 serial 01696 is there anyway to have it serially output that code? can I convert what it is reading to what the server is expecting?

I would highly recommend you re-reading this code Mike posted earlier (look at the reply earlier in this topic for helpful color coding). I'd fool around with it now but it's getting late and I have work tomorrow. If you get this to work for you, post your code back so I can steal it :wink: . Otherwise, I'll work on it tomorrow and see if I can get it to work.

It is a very simple case of bit manipulation.
if you have the code: 1 01101101 0 101100000111111 0

then siteCode = (rawCode >> 17) & 0xff;
serialNumber = (rawCode >> 1) & 0x7fff;

All you are doing is shifting the bit pattern down until it's lease significant bit is at the start (bit zero) then ANDing it with a mask that as 1's in the bits you want to keep and zeros where you don't want the bits.

tgregor
For your code:-
001 1110 0000 0000 1101 0100 000

siteCode = (rawCode >> 16) & 0x3ff;
serialNumber = (rawCode >> 1) & 0x3fff

Alex