├── .gitignore ├── README.md ├── examples └── WiegandTest │ └── WiegandTest.ino ├── keywords.txt ├── library.properties └── src ├── WiegandMulti.cpp └── WiegandMulti.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi Reader Wiegand 4 bit, 8 bit, 24 bit, 26 bit, 32 bit and 34 bit library for Arduino 2 | 3 | The Wiegand interface is a de facto standard commonly used to connect a card reader or keypad to an electronic entry system. Wiegand interface has the ability to transmit signal over long distance with a simple 3 wires connection. This library uses interrupt pins from Arduino to read the pulses from Wiegand interface and return the code and type of the Wiegand. 4 | 5 | # Why another Wiegand library? 6 | 7 | The original [Wiegand library](https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Arduino) was written to support a single wiegand reader for an Arduino UNO. As time goes by, gathered by the requests from users, I wrote another [Wiegand NG library](https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino) to solve readers that send out non-standard bit length wiegand data. Due to different methods used in storing the raw data, the two library were maintained in different repos. 8 | 9 | This Multi Wiegand library was a modified version of the original Wiegand library written to support C++ objects so that the same object can be reused by multiple wiegand readers and yet with the variables stored in the individual object's memory. 10 | 11 | The reason why when I first wrote the original Wiegand library I did not write the library in pure C++ object method was because [Arduino attachinterrupt cannot be attached to class method directly](https://www.google.com/search?q=arduino+attachinterrupt+in+class&oq=arduino+attachinterrupt+in+class) and I also wanted the first library to be clean and simple to use instead of adding hacks and workaround like this library does. 12 | 13 | 14 | # Different Wiegand libraries comparison 15 | 16 | | Library | Description | 17 | | ---| --- | 18 | | [Wiegand library](https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Arduino) | This is the easiest and cleanest library to use when only a single wiegand reader is needed. I strongly suggest to use this version if you only need one reader support | 19 | | [Wiegand NG library](https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino) | This is another single reader library, however it uses dynamic memory to store the raw wiegand thus allowing unlimited bit length to be stored as long as your Arduino board has the memory to store it. I won't recommend to use this library unless you are facing a non-standard wiegand reader that sends out data that will not be decoded by the original Wiegand library. | 20 | | Multi Wiegand library | This library uses pin change interrupt, thus allowing all the pin change interrupt supported pins to be used. With this, multiple readers can also be supported. Due to a workaround (hack) used to overcome the limitation with `attachInterrupt` not being able to be attached to the class method directly, the initialisation of the sketch is more ugly and complicated. | 21 | 22 | ## NON Standard Bit Length 23 | 24 | This library was designed to decode standard Wiegand protocol with 1 START bit, 1 STOP bit and bit length stated in the title. For multi-bit length protocol, please try [Wiegand NG Multi Bit Wiegand Library for Arduino](https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino). 25 | 26 | ## Requirements for the Example to Work 27 | 28 | The following are needed 29 | 30 | * [Arduino](http://www.arduino.cc) - Any ATMEGA328 compatible board should work. 31 | * [Pin Change Interrupt Library](https://github.com/NicoHood/PinChangeInterrupt) 32 | * [Wiegand RFID Reader](http://www.monkeyboard.org/products/85-developmentboard/84-rfid-wiegand-protocol-development-kit) - The code was written for this reader however customers reported working with [HID](http://www.hidglobal.com/products/cards-and-credentials) compatible readers. 33 | * DATA0 of the first Wiegand reader connects to Arduino PIN 2 and DATA1 of the first Wiegand reader connects to Arduino PIN 3 34 | * DATA0 of the second Wiegand reader connects to Arduino PIN 4 and DATA1 of the second Wiegand reader connects to Arduino PIN 5 35 | 36 | 37 | ## Installation 38 | 39 | Create a folder named WiegandMulti in Arduino's libraries folder. You will have the following folder structure: 40 | 41 | cd arduino/libraries 42 | git clone https://github.com/jpliew/Multi-Reader-Wiegand-Protocol-Library-for-Arduino.git WiegandMulti 43 | 44 | ## Arduino Sketch 45 | 46 | ![multi-wiegand](https://user-images.githubusercontent.com/1773147/107901053-cec92b80-6f96-11eb-8b93-ffd8e923b624.png "Multi RFID Reader to Arduino connection diagram") 47 | 48 | Execute Arduino IDE, select Example-->WiegandMulti-->WiegandTest 49 | 50 | ### Example 51 | 52 | Please see example in the example folder. 53 | 54 | ## Contributors 55 | 56 | [softwarefoundry](https://github.com/softwarefoundry) added library.properties 57 | 58 | [Francesco Uggetti (ugge75)](https://github.com/ugge75) improved this version of library to support multiple readers for ATMEGA2560. Please check out [his version of multiple wiegand readers library here](https://github.com/ugge75/Wiegand-Protocol-Library-for-Arduino-MEGA-2560) 59 | 60 | [Apollon77](https://github.com/Apollon77) improved interrupt safety and removed sysTick from global 61 | 62 | [paulfurley](https://github.com/paulfurley) added 4 bit code 63 | 64 | [PaulStoffregen](https://github.com/PaulStoffregen) added Use digitalPinToInterrupt on newer Arduino software, if present 65 | 66 | [tholum](https://github.com/tholum) Simpler Instructions 67 | 68 | [zanhecht](https://github.com/zanhecht) Recognize 24- and 32-bit 69 | 70 | [luckymallari](https://github.com/luckymallari) Simplified begin(D0, D1) for ESP(8266/32) devices. 71 | 72 | Written by [JP Liew](http://jpliew.com) 73 | 74 | Project home: http://www.monkeyboard.org/tutorials/82-protocol/24-wiegand-converter 75 | 76 | *This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.* 77 | 78 | *This library 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 Lesser General Public License for more details.* 79 | -------------------------------------------------------------------------------- /examples/WiegandTest/WiegandTest.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | WIEGANDMULTI wg; 4 | WIEGANDMULTI wg2; 5 | 6 | void Reader1D0Interrupt(void) 7 | { 8 | wg.ReadD0(); 9 | } 10 | 11 | void Reader1D1Interrupt(void) 12 | { 13 | wg.ReadD1(); 14 | } 15 | 16 | void Reader2D0Interrupt(void) 17 | { 18 | wg2.ReadD0(); 19 | } 20 | 21 | void Reader2D1Interrupt(void) 22 | { 23 | wg2.ReadD1(); 24 | } 25 | 26 | 27 | void setup() { 28 | Serial.begin(9600); 29 | wg.begin(2,3,Reader1D0Interrupt,Reader1D1Interrupt); 30 | wg2.begin(4,5,Reader2D0Interrupt,Reader2D1Interrupt); 31 | } 32 | 33 | void loop() { 34 | if(wg.available()) 35 | { 36 | Serial.print("Wiegand1 HEX = "); 37 | Serial.print(wg.getCode(),HEX); 38 | Serial.print(", DECIMAL = "); 39 | Serial.print(wg.getCode()); 40 | Serial.print(", Type W"); 41 | Serial.println(wg.getWiegandType()); 42 | } 43 | 44 | if(wg2.available()) 45 | { 46 | Serial.print("Wiegand2 HEX = "); 47 | Serial.print(wg2.getCode(),HEX); 48 | Serial.print(", DECIMAL = "); 49 | Serial.print(wg2.getCode()); 50 | Serial.print(", Type W"); 51 | Serial.println(wg2.getWiegandType()); 52 | } 53 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ###################################################### 2 | # Syntax Coloring Map For Multi Reader Wiegand Library 3 | ###################################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | WIEGANDMULTI KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | getCode KEYWORD2 16 | getWiegandType KEYWORD2 17 | available KEYWORD2 18 | 19 | ####################################### 20 | # Constants (LITERAL1) 21 | ####################################### 22 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Wiegand Protocol Multi Reader Library for Arduino 2 | version=1.0.1 3 | author=JP Liew 4 | maintainer=https://github.com/monkeyboard 5 | sentence=Multi Reader Wiegand 26 and Wiegand 34 Protocol Library for Arduino 6 | paragraph=The Wiegand interface is a de facto standard commonly used to connect a card reader or keypad to an electronic entry system. Wiegand interface has the ability to transmit signal over long distance with a simple 3 wires connection. This library uses interrupt pins from Arduino to read the pulses from Wiegand interface and return the code and type of the Wiegand. 7 | category=Communication 8 | url=https://github.com/jpliew/Multi-Reader-Wiegand-Protocol-Library-for-Arduino.git 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/WiegandMulti.cpp: -------------------------------------------------------------------------------- 1 | #include "WiegandMulti.h" 2 | #include "PinChangeInterrupt.h" 3 | 4 | #if defined(ESP8266) 5 | #define INTERRUPT_ATTR ICACHE_RAM_ATTR 6 | #elif defined(ESP32) 7 | #define INTERRUPT_ATTR IRAM_ATTR 8 | #else 9 | #define INTERRUPT_ATTR 10 | #endif 11 | 12 | WIEGANDMULTI::WIEGANDMULTI() 13 | { 14 | } 15 | 16 | unsigned long WIEGANDMULTI::getCode() 17 | { 18 | return _code; 19 | } 20 | 21 | int WIEGANDMULTI::getWiegandType() 22 | { 23 | return _wiegandType; 24 | } 25 | 26 | bool WIEGANDMULTI::available() 27 | { 28 | bool ret; 29 | //noInterrupts(); 30 | ret=DoWiegandConversion(); 31 | //interrupts(); 32 | return ret; 33 | } 34 | 35 | void WIEGANDMULTI::begin(void (*ISR_D0)(void), void (*ISR_D1)(void)) 36 | { 37 | begin(2,3, ISR_D0, ISR_D1); 38 | } 39 | 40 | void WIEGANDMULTI::begin(int pinD0, int pinD1, void (*ISR_D0)(void), void (*ISR_D1)(void)) 41 | { 42 | _lastWiegand = 0; 43 | _cardTempHigh = 0; 44 | _cardTemp = 0; 45 | _code = 0; 46 | _wiegandType = 0; 47 | _bitCount = 0; 48 | pinMode(pinD0, INPUT); // Set D0 pin as input 49 | pinMode(pinD1, INPUT); // Set D1 pin as input 50 | 51 | attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(pinD0), ISR_D0, FALLING); // Hardware interrupt - high to low pulse 52 | attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(pinD1), ISR_D1, FALLING); // Hardware interrupt - high to low pulse 53 | } 54 | 55 | INTERRUPT_ATTR void WIEGANDMULTI::ReadD0 () 56 | { 57 | _bitCount++; // Increament bit count for Interrupt connected to D0 58 | if (_bitCount>31) // If bit count more than 31, process high bits 59 | { 60 | _cardTempHigh |= ((0x80000000 & _cardTemp)>>31); // shift value to high bits 61 | _cardTempHigh <<= 1; 62 | _cardTemp <<=1; 63 | } 64 | else 65 | { 66 | _cardTemp <<= 1; // D0 represent binary 0, so just left shift card data 67 | } 68 | _lastWiegand = millis(); // Keep track of last wiegand bit received 69 | } 70 | 71 | INTERRUPT_ATTR void WIEGANDMULTI::ReadD1() 72 | { 73 | _bitCount ++; // Increment bit count for Interrupt connected to D1 74 | if (_bitCount>31) // If bit count more than 31, process high bits 75 | { 76 | _cardTempHigh |= ((0x80000000 & _cardTemp)>>31); // shift value to high bits 77 | _cardTempHigh <<= 1; 78 | _cardTemp |= 1; 79 | _cardTemp <<=1; 80 | } 81 | else 82 | { 83 | _cardTemp |= 1; // D1 represent binary 1, so OR card data with 1 then 84 | _cardTemp <<= 1; // left shift card data 85 | } 86 | _lastWiegand = millis(); // Keep track of last wiegand bit received 87 | } 88 | 89 | unsigned long WIEGANDMULTI::GetCardId (volatile unsigned long *codehigh, volatile unsigned long *codelow, char bitlength) 90 | { 91 | if (bitlength==26) // EM tag 92 | return (*codelow & 0x1FFFFFE) >>1; 93 | 94 | if (bitlength==24) 95 | return (*codelow & 0x7FFFFE) >>1; 96 | 97 | if (bitlength==34) // Mifare 98 | { 99 | *codehigh = *codehigh & 0x03; // only need the 2 LSB of the codehigh 100 | *codehigh <<= 30; // shift 2 LSB to MSB 101 | *codelow >>=1; 102 | return *codehigh | *codelow; 103 | } 104 | 105 | if (bitlength==32) { 106 | return (*codelow & 0x7FFFFFFE ) >>1; 107 | } 108 | 109 | return *codelow; // EM tag or Mifare without parity bits 110 | } 111 | 112 | char translateEnterEscapeKeyPress(char originalKeyPress) { 113 | switch(originalKeyPress) { 114 | case 0x0b: // 11 or * key 115 | return 0x0d; // 13 or ASCII ENTER 116 | 117 | case 0x0a: // 10 or # key 118 | return 0x1b; // 27 or ASCII ESCAPE 119 | 120 | default: 121 | return originalKeyPress; 122 | } 123 | } 124 | 125 | bool WIEGANDMULTI::DoWiegandConversion () 126 | { 127 | unsigned long cardID; 128 | unsigned long sysTick = millis(); 129 | 130 | if ((sysTick - _lastWiegand) > 25) // if no more signal coming through after 25ms 131 | { 132 | if ((_bitCount==24) || (_bitCount==26) || (_bitCount==32) || (_bitCount==34) || (_bitCount==8) || (_bitCount==4)) // bitCount for keypress=4 or 8, Wiegand 26=24 or 26, Wiegand 34=32 or 34 133 | { 134 | _cardTemp >>= 1; // shift right 1 bit to get back the real value - interrupt done 1 left shift in advance 135 | if (_bitCount>32) // bit count more than 32 bits, shift high bits right to make adjustment 136 | _cardTempHigh >>= 1; 137 | 138 | if (_bitCount==8) // keypress wiegand with integrity 139 | { 140 | // 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble 141 | // eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001 142 | char highNibble = (_cardTemp & 0xf0) >>4; 143 | char lowNibble = (_cardTemp & 0x0f); 144 | _wiegandType=_bitCount; 145 | _bitCount=0; 146 | _cardTemp=0; 147 | _cardTempHigh=0; 148 | 149 | if (lowNibble == (~highNibble & 0x0f)) // check if low nibble matches the "NOT" of high nibble. 150 | { 151 | _code = (int)translateEnterEscapeKeyPress(lowNibble); 152 | return true; 153 | } 154 | else { 155 | _lastWiegand=sysTick; 156 | _bitCount=0; 157 | _cardTemp=0; 158 | _cardTempHigh=0; 159 | return false; 160 | } 161 | 162 | // TODO: Handle validation failure case! 163 | } 164 | else if (4 == _bitCount) { 165 | // 4-bit Wiegand codes have no data integrity check so we just 166 | // read the LOW nibble. 167 | _code = (int)translateEnterEscapeKeyPress(_cardTemp & 0x0000000F); 168 | 169 | _wiegandType = _bitCount; 170 | _bitCount = 0; 171 | _cardTemp = 0; 172 | _cardTempHigh = 0; 173 | 174 | return true; 175 | } 176 | else // wiegand 26 or wiegand 34 177 | { 178 | cardID = GetCardId (&_cardTempHigh, &_cardTemp, _bitCount); 179 | _wiegandType=_bitCount; 180 | _bitCount=0; 181 | _cardTemp=0; 182 | _cardTempHigh=0; 183 | _code=cardID; 184 | return true; 185 | } 186 | } 187 | else 188 | { 189 | // well time over 25 ms and bitCount !=8 , !=26, !=34 , must be noise or nothing then. 190 | _lastWiegand=sysTick; 191 | _bitCount=0; 192 | _cardTemp=0; 193 | _cardTempHigh=0; 194 | return false; 195 | } 196 | } 197 | else 198 | return false; 199 | } 200 | -------------------------------------------------------------------------------- /src/WiegandMulti.h: -------------------------------------------------------------------------------- 1 | #ifndef _WIEGANDMULTI_H 2 | #define _WIEGANDMULTI_H 3 | 4 | #if defined(ARDUINO) && ARDUINO >= 100 5 | #include "Arduino.h" 6 | #else 7 | #include "WProgram.h" 8 | #endif 9 | 10 | class WIEGANDMULTI { 11 | 12 | private: 13 | bool DoWiegandConversion (); 14 | unsigned long GetCardId (volatile unsigned long *codehigh, volatile unsigned long *codelow, char bitlength); 15 | volatile unsigned long _cardTempHigh; 16 | volatile unsigned long _cardTemp; 17 | volatile unsigned long _lastWiegand; 18 | volatile int _bitCount; 19 | int _wiegandType; 20 | unsigned long _code; 21 | 22 | public: 23 | WIEGANDMULTI(); 24 | void begin(void (*ISR_D0)(void),void (*ISR_D1)(void)); 25 | void begin(int pinD0, int pinD1, void (*ISR_D0)(void), void (*ISR_D1)(void)); 26 | bool available(); 27 | void ReadD0(); 28 | void ReadD1(); 29 | unsigned long getCode(); 30 | int getWiegandType(); 31 | }; 32 | 33 | #endif 34 | --------------------------------------------------------------------------------