Goodbye Dryspell – Hello Raspberry Pi + Wiegand + RFID

Yeah.. I can’t believe its been 2 years since my last blog post… I knew it has been a while… But 2 years?  How life has changed.  But nevermind that.  I’m here to write about something a wee-bit more relevant.

Over my hiatus, I’ve dabbled in a number of projects, but probably the most recent, and largest of those has been the purchase of a new house with a new garage that is slowly being converted to a small motion picture production studio.  I’ll get into those details later… maybe.  In this effort, I have been working on a security system for said studio, that would allow those who us it (more than myself) to have unrestricted, but secure access to the building.  So, I’ve been working on a Raspberry Pi-based, RFID access system.  You know, the basics:  RFID reader/keypad, electric door strike, and a Raspberry Pi for brains, PiCamera for… Nevermind, that’s classified.

I’ve been building this over the last few weeks, working on various aspects of the software (SDL, OpenCV, etc), but recently I started working on the interface between the Wiegand RFID/Keypad and the Pi.  There are a few articles and posts about this out there, but so far all of the code to read the Wiegand protocol that I could find, was kind of hit-and-miss.  Probably, most likely to do with the timing variances on the Pi, when not running a real-time kernel.  So, of course, I whipped up a new library.  I’ve debated putting code somewhere, but its literally, less than 150 lines of code, including the example app.  Maybe I’ll change my mind, but until then, I wanted a place to post the code.

/*
 * Wiegand API Raspberry Pi
 * By Kyle Mallory
 * 12/01/2013
 * Based on previous code by Daniel Smith (www.pagemac.com) and Ben Kent (www.pidoorman.com)
 * Depends on the wiringPi library by Gordon Henterson: https://projects.drogon.net/raspberry-pi/wiringpi/
 *
 * The Wiegand interface has two data lines, DATA0 and DATA1.  These lines are normall held
 * high at 5V.  When a 0 is sent, DATA0 drops to 0V for a few us.  When a 1 is sent, DATA1 drops
 * to 0V for a few us. There are a few ms between the pulses.
 *   *************
 *   * IMPORTANT *
 *   *************
 *   The Raspberry Pi GPIO pins are 3.3V, NOT 5V. Please take appropriate precautions to bring the
 *   5V Data 0 and Data 1 voltges down. I used a 330 ohm resistor and 3V3 Zenner diode for each
 *   connection. FAILURE TO DO THIS WILL PROBABLY BLOW UP THE RASPBERRY PI!
 */
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>
#include <time.h>
#include <unistd.h>
#include <memory.h>

#define D0_PIN 0
#define D1_PIN 1

#define WIEGANDMAXDATA 32
#define WIEGANDTIMEOUT 3000000

static unsigned char __wiegandData[WIEGANDMAXDATA];    // can capture upto 32 bytes of data -- FIXME: Make this dynamically allocated in init?
static unsigned long __wiegandBitCount;            // number of bits currently captured
static struct timespec __wiegandBitTime;        // timestamp of the last bit received (used for timeouts)

void data0Pulse(void) {
    if (__wiegandBitCount / 8 < WIEGANDMAXDATA) {
        __wiegandData[__wiegandBitCount / 8] <<= 1;
        __wiegandBitCount++;
    }
    clock_gettime(CLOCK_MONOTONIC, &__wiegandBitTime);
}

void data1Pulse(void) {
    if (__wiegandBitCount / 8 < WIEGANDMAXDATA) {
        __wiegandData[__wiegandBitCount / 8] <<= 1;
        __wiegandData[__wiegandBitCount / 8] |= 1;
        __wiegandBitCount++;
    }
    clock_gettime(CLOCK_MONOTONIC, &__wiegandBitTime);
}

int wiegandInit(int d0pin, int d1pin) {
    // Setup wiringPi
    wiringPiSetup() ;
    pinMode(d0pin, INPUT);
    pinMode(d1pin, INPUT);

    wiringPiISR(d0pin, INT_EDGE_FALLING, data0Pulse);
    wiringPiISR(d1pin, INT_EDGE_FALLING, data1Pulse);
}

void wiegandReset() {
    memset((void *)__wiegandData, 0, WIEGANDMAXDATA);
    __wiegandBitCount = 0;
}

int wiegandGetPendingBitCount() {
    struct timespec now, delta;
    clock_gettime(CLOCK_MONOTONIC, &now);
    delta.tv_sec = now.tv_sec - __wiegandBitTime.tv_sec;
    delta.tv_nsec = now.tv_nsec - __wiegandBitTime.tv_nsec;

    if ((delta.tv_sec > 1) || (delta.tv_nsec > WIEGANDTIMEOUT))
        return __wiegandBitCount;

    return 0;
}

/*
 * wiegandReadData is a simple, non-blocking method to retrieve the last code
 * processed by the API.
 * data : is a pointer to a block of memory where the decoded data will be stored.
 * dataMaxLen : is the maximum number of -bytes- that can be read and stored in data.
 * Result : returns the number of -bits- in the current message, 0 if there is no
 * data available to be read, or -1 if there was an error.
 * Notes : this function clears the read data when called. On subsequent calls,
 * without subsequent data, this will return 0.
 */
int wiegandReadData(void* data, int dataMaxLen) {
    if (wiegandGetPendingBitCount() > 0) {
        int bitCount = __wiegandBitCount;
        int byteCount = (__wiegandBitCount / 8) + 1;
        memcpy(data, (void *)__wiegandData, ((byteCount > dataMaxLen) ? dataMaxLen : byteCount));

        wiegandReset();
        return bitCount;
    }
    return 0;
}

void printCharAsBinary(unsigned char ch) {
    int i;
    for (i = 0; i < 8; i++) {
        printf("%d", (ch & 0x80) ? 1 : 0);
        ch <<= 1;
    }
}

void main(void) {
    int i;

    wiegandInit(D0_PIN, D1_PIN);

    while(1) {
        int bitLen = wiegandGetPendingBitCount();
        if (bitLen == 0) {
            usleep(5000);
        } else {
            char data[100];
            bitLen = wiegandReadData((void *)data, 100);
            int bytes = bitLen / 8 + 1;
            printf("Read %d bits (%d bytes): ", bitLen, bytes);
            for (i = 0; i < bytes; i++)
                printf("%02X", (int)data[i]);
            printf(" : ");
            for (i = 0; i < bytes; i++)
                printCharAsBinary(data[i]);
            printf("\n");
        }
    }
}

This is linked with -lpthread -lwiringPi -lrt

One of they key improvements in my code is the use of the high-resolution clock kernel module to handle the timeout after data is read.  In the pidoorman code, this was based on a countdown loop, which would vary depending on system load, and whether your Pi was overclocked.  Wiegand is a very tolerant protocol, and so there should be few if any issues with timings on the pulses themselves.  My code also utilizes the new ISR API from wiringPi.

Enjoy.

Advertisements

~ by kylemallory on December 2, 2013.

16 Responses to “Goodbye Dryspell – Hello Raspberry Pi + Wiegand + RFID”

  1. awesome! thanks for the code! i was working on the exact same problems… having 3 wiegand readers in parallel and 4 doors via relays.
    how far are you on the “business logic”? webinterface? the 3 sebury fingerprint/rfid readers are also remote manageable via rs-485 and i may one day even implement their administration into the pi solution.

    • On the business-logic, I’ve got it pretty complete. I’m doing facial detection in my code with the PiCam and OpenCV… I don’t to “recognition”, but the intent is to have the reader detect the presence of a face, and record the image to the access log, before unlocking the door. Not a deterrant, but as an audit trail. Anyway, its giving me some headaches in the JPEG/Detection code, and performance has been an issue with it. As for reading the RFID code and/or keypad code, and authorizing access, its working rather well. I have intentionally pushed all the access management off the reader and onto the Pi, because I want to tightly control (to the hour), when a particular code/fob will grant access.

  2. Nice job on the reader in code. Did you make any progress on parsing the Wiegand 26 into the card ID and Facility code?

    • Unfortunately not. My particular reader only returns the code from the fob, and no additional [facility] code (that I’m aware of). For my purposes, either a fob/code matches in the database, or it doesn’t. The Wiegand protocol is pretty straight forward though. Not knowing more about how the facility is embedded into the overall response, I would think it a matter of bit-shifting/masking: facId = (code >> 20), fobId = (code & 0x08FFFF).

  3. […] – 3 identische (bis auf die gpio pin nummern und "sensor-id") c programme, auf basis von Goodbye Dryspell – Hello Raspberry Pi + Wiegand + RFID | If Jesus Were Irish… welche als daemon beim boot starten. die c programme machen nichts anderes als die bitfolge bzw […]

  4. Very nice!

    However, I am having an issue when I try to read a 26 bit Wiegand code. It looks like the last byte is not interpreted properly. I’ll have to trace that.

    By the way, thanks for the head start. I was looking into making my own access reader. This certainly pointed me in the right direction.

    Cheers,

    • Thanks Jeff. Unfortunately, I have no way of verifying what my codes are to ensure that the decode matches expectation. If something is missing in the last byte only, my guess is its dropping the last bit. I’ll poke around. If I had a few fobs with known codes, I’d be happy to help find and correct the code.

  5. Hello, thanks for the code.

    I’m too am trying to read Wiegand Data with my PI, and as recommended I used a 330ohm resistor and a 3.3V Zener Diode for my Data0 and Data1.

    I’m using this reader… http://www.hidglobal.com/sites/hidglobal.com/files/resource_files/prox-proxpoint-plus-reader-ds-en.pdf

    I have a 26-bit card/fob that I know the facility and card code for, but for some reason the program is spitting out gibberish.

    Here is excerpt…

    Read 18 bits (3 bytes): D78B02 : 110101111000101100000010
    Read 8 bits (2 bytes): D800 : 1101100000000000
    Read 14 bits (2 bytes): D722 : 1101011100100010
    Read 12 bits (2 bytes): ED08 : 1110110100001000
    Read 10 bits (2 bytes): D702 : 1101011100000010
    Read 15 bits (2 bytes): 2ED8 : 0010111011011000
    Read 2 bits (1 bytes): 03 : 00000011
    Read 9 bits (2 bytes): 5E00 : 0101111000000000
    Read 15 bits (2 bytes): 5D58 : 0101110101011000
    Read 2 bits (1 bytes): 01 : 00000001

    Any ideas?

    • I actually just figured it out…

      I had to increase the WIEGANDTIMEOUT to 350000 and it started to read my cards properly… However I am finding that its sending too many bits.

      Read 26 bits (4 bytes): D78BB600 : 11010111100010111011011000000000

      Comes back with 32 bits… any ideas?

      • Hey Eugene,

        Its reporting 26 bits read, but returning 32. It returns 32 because it has to pack the bits into 8-bit bytes. Just ignore the last 6 bits (or right-shift the long int by 6). Glad you found the timeout parameter.

      • Hi Eugene,

        Kyle is right. Just right shifting by 6 will do the trick. On my side I used Kyle code to write several c++ object (just for fun). You might want to consider using something like this http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html for managing the bit buffer. It is lot easier to manage this way.

        You might also want to check the parity of your read to automatically validate your data. I do it this way:

        /**
        * Calculate the Wiegand parity. the leading parity bit (even) is linked to the next 12 data bits. If the 12 data bits result is an odd
        * number, the leading parity bit is set to one to make the 13-bit total come out even. The final 13 bits are similarly set to an odd total with the reverse logic.
        *
        * @param theStartIndex – First bit to start the calculation (ex.: start at 0 to evaluate and at 1 to create a parity)
        * @param theNbrOfBit – How many bit to do a chain XOR
        * @return Binary 1 or 0
        */
        bool Card::CalculateParity( unsigned int theStartIndex, unsigned int theNbrOfBit){

        //bool result = TestBit( theStartIndex);
        bool result = 0;

        for (unsigned int i = theStartIndex; i < (theStartIndex + theNbrOfBit); i++) {
        result ^= TestBit( i);
        }

        return(result);
        }

        For a 26 bit Wiegand card (I used a similar model than you), the validation would go like this:

        evenParity = CalculateParity(0, 13);
        oddParity = CalculateParity(13, 13);

        Cheers,

      • Hi Guys,

        Thanks for your amazing responses… However unfortunately I have no idea how to right shift 6-bits – I only started to dabble in C around 1 week ago. Is there any chance you can share the code on how you did this?

        Should I be concerned about parity? Right now I have some code that takes the HEX value and looks it up in my MSSQL database – through HTTP Shield (have no idea yet on how C can directly interact with MSSQL), and returns a access value, which will match the predetermined value set in the Pi to switch a relay…

        I’m happy with the HID Brand Reader because it appears that it can power the entire reader with the 5V from the Pi, no need for a external power adapter for the reader…. Speaking of which I can drive the Green LED with the GPIO Pin 8 without any issues right? Running the command gpio mode 8 out will turn the LED green. Any adverse side affects I should be aware of?

      • Hi Eugene,

        The hyperlink I posted is basically showing you a way to implement a bit buffer that takes care of the issue (you don’t have to deal with shifting the final 6 bit). I’ll see if I can come up with a C version of my code.

        Parity is only for error checking. It is only to validate what you received. You can ignore it if you want.

        As far as MSSQL, I would say that you best bet would be to write a small server service and connect your PI to it by TCP/IP socket. Of course, this is one way of doing it.

        You could also run a local db in the PI itself like SQLite or even MYSQL.

        I don’t see any problem with flashing the LED as long as you put a resistance in it.

      • Hi Jeff,

        I have absolutely no idea or how to implement the bit buffer even with that website! If you could some how come up with some code for you’d be awesome. Haha call me a newbie…

        Anyways I’m not too sure a simple right shift will work… These are some of the two cards I know the binary for…

        11101000101001101111111000000011
        11101000101001101111111011

        11010111100010111011011000000000
        11010111100010111011011000

        The shorter one is the actual as you can tell… I see that the code is adding 6 zeros after the 24th bit?

        Not too sure how a buffer will help me with this.

  6. Eugene,

    You are right. Looking at your data, and thinking about the way I wrote this, a shift won’t work, directly. Data is left shifted into the next available byte of the buffer. So, in the case of the first card, the last 2 bits of data are at the beginning (LSb) of the 8-bit byte. This is a bug in the code, and I’ll see if I can correct it.

    As for a byte buffer, the code doesn’t really need one (it handles it on its own). What you should do (what I do), is rather than defining “data[100]” as a character buffer, I define it as an ‘unsigned long’, which is assigned 4 bytes. You just have to limit the maxbytes in wiegandReadData() to 4 bytes, instead of 100. So, the call would look more like this:

            if (bitLen == 0) {
                usleep(5000);
            } else {
                unsigned long data;
                bitLen = wiegandReadData((void *)&data, 4);
                int shiftRight = 32 - bitLen;  // how many bits to shift
                data >>= shiftRight; // shift the data and store the result back to itself
                printf("Read code: %lX (%d bits): ", data, bitLen);
            }
    
    • Hey Kyle,

      Actually, I was not refering to a 100 char buffer. The buffer could still be
      4 byte. The function to set a bit would go like this:

      int internalBuffer[4]; //This would be a 128 bit buffer (32 x 4)
      
      void SetBit( int theBitIndex)
      {
         //INT_SIZE is 32
          int i = theBitIndex/INT_SIZE;
          int pos = theBitIndex%INT_SIZE;
      
          unsigned int flag = 1;   // flag = 0000.....00001
      
          flag = flag << pos;      // flag = 0000...010...000   (shifted k positions)
      
          internalBuffer[i] = internalBuffer[i] | flag;      // Set the bit at the k-th                  position in A[i]
      }
      

      This way, every time data1Pulse is called, you just make a call to SetBit with
      __wiegandBitCount as argument.

      The beauty of this is that you don't have to worry about shiffting bits around.

      Retrieving the bit goes like this:

      bool TestBit(int theBitIndex){
          int i = theBitIndex/INT_SIZE;
          int pos = theBitIndex%INT_SIZE;
      
          unsigned int flag = 1;  // flag = 0000.....00001
      
          flag = flag << pos;     // flag = 0000...010...000   (shifted k positions)
      
          if ( internalBuffer[i] & flag )      // Test the bit at the k-th position in A[i]
             return 1;
          else
             return 0;
      }
      

      That's the beaty of c/c++, there are so many ways to achieve the same goal 🙂

      Cheers,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: