-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
// Module for reading keycards via Wiegand protocol of various formats | ||
|
||
// Mostly taken from https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Arduino, adapted to send card as NodeMCU event | ||
// ## Contributors | ||
// [Cody Cutrer](https://github.com/ccutrer) adapted to being a NodeMCU module | ||
// [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) | ||
// [Apollon77](https://github.com/Apollon77) improved interrupt safety and removed sysTick from global | ||
// [paulfurley](https://github.com/paulfurley) added 4 bit code | ||
// [PaulStoffregen](https://github.com/PaulStoffregen) added Use digitalPinToInterrupt on newer Arduino software, if present | ||
// [tholum](https://github.com/tholum) Simpler Instructions | ||
// [zanhecht](https://github.com/zanhecht) Recognize 24- and 32-bit | ||
// [luckymallari](https://github.com/luckymallari) Simplified begin(D0, D1) for ESP(8266/32) devices. | ||
// Written by [JP Liew](http://jpliew.com) | ||
// Project home: http://www.monkeyboard.org/tutorials/82-protocol/24-wiegand-converter | ||
// *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.* | ||
// *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.* | ||
|
||
#include "module.h" | ||
#include "lauxlib.h" | ||
#include "platform.h" | ||
#include "task/task.h" | ||
#include "user_interface.h" | ||
#include "pm/swtimer.h" | ||
|
||
#ifdef LUA_USE_MODULES_WIEGAND | ||
#if !defined(GPIO_INTERRUPT_ENABLE) || !defined(GPIO_INTERRUPT_HOOK_ENABLE) | ||
#error Must have GPIO_INTERRUPT and GPIO_INTERRUPT_HOOK if using WIEGAND module | ||
#endif | ||
#endif | ||
|
||
static volatile unsigned long _cardTempHigh; | ||
static volatile unsigned long _cardTemp; | ||
static volatile int _bitCount; | ||
static int _wiegandType; | ||
static unsigned long _code; | ||
static int wiegand_cb_ref; | ||
static ETSTimer wiegand_timer; | ||
static int wiegand_timer_running; | ||
static int wiegand_tasknumber; | ||
static unsigned wiegand_pinD0, wiegand_pinD1; | ||
|
||
static uint32_t ICACHE_RAM_ATTR wiegand_readD0(uint32_t ret_gpio_status) | ||
{ | ||
_bitCount++; // Increament bit count for Interrupt connected to D0 | ||
if (_bitCount>31) // If bit count more than 31, process high bits | ||
{ | ||
_cardTempHigh |= ((0x80000000 & _cardTemp)>>31); // shift value to high bits | ||
_cardTempHigh <<= 1; | ||
_cardTemp <<=1; | ||
} | ||
else | ||
{ | ||
_cardTemp <<= 1; // D0 represent binary 0, so just left shift card data | ||
} | ||
|
||
task_post_medium(wiegand_tasknumber, 0); | ||
|
||
ret_gpio_status &= ~(1 << pin_num[wiegand_pinD0]); | ||
return ret_gpio_status; | ||
} | ||
|
||
static uint32_t ICACHE_RAM_ATTR wiegand_readD1(uint32_t ret_gpio_status) | ||
{ | ||
_bitCount ++; // Increment bit count for Interrupt connected to D1 | ||
if (_bitCount>31) // If bit count more than 31, process high bits | ||
{ | ||
_cardTempHigh |= ((0x80000000 & _cardTemp)>>31); // shift value to high bits | ||
_cardTempHigh <<= 1; | ||
_cardTemp |= 1; | ||
_cardTemp <<=1; | ||
} | ||
else | ||
{ | ||
_cardTemp |= 1; // D1 represent binary 1, so OR card data with 1 then | ||
_cardTemp <<= 1; // left shift card data | ||
} | ||
|
||
task_post_medium(wiegand_tasknumber, 0); | ||
|
||
ret_gpio_status &= ~(1 << pin_num[wiegand_pinD1]); | ||
return ret_gpio_status; | ||
} | ||
|
||
static unsigned long wiegand_get_card_id (volatile unsigned long *codehigh, volatile unsigned long *codelow, char bitlength) | ||
{ | ||
|
||
if (bitlength==26) // EM tag | ||
return (*codelow & 0x1FFFFFE) >>1; | ||
|
||
if (bitlength==34) // Mifare | ||
{ | ||
*codehigh = *codehigh & 0x03; // only need the 2 LSB of the codehigh | ||
*codehigh <<= 30; // shift 2 LSB to MSB | ||
*codelow >>=1; | ||
return *codehigh | *codelow; | ||
} | ||
return *codelow; // EM tag or Mifare without parity bits | ||
} | ||
|
||
static inline char translateEnterEscapeKeyPress(char originalKeyPress) { | ||
switch(originalKeyPress) { | ||
case 0x0b: // 11 or * key | ||
return 0x0d; // 13 or ASCII ENTER | ||
|
||
case 0x0a: // 10 or # key | ||
return 0x1b; // 27 or ASCII ESCAPE | ||
|
||
default: | ||
return originalKeyPress; | ||
} | ||
} | ||
|
||
static bool wiegand_store_value() | ||
{ | ||
unsigned long cardID; | ||
|
||
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 | ||
{ | ||
_cardTemp >>= 1; // shift right 1 bit to get back the real value - interrupt done 1 left shift in advance | ||
if (_bitCount>32) // bit count more than 32 bits, shift high bits right to make adjustment | ||
_cardTempHigh >>= 1; | ||
|
||
if (_bitCount==8) // keypress wiegand with integrity | ||
{ | ||
// 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble | ||
// eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001 | ||
char highNibble = (_cardTemp & 0xf0) >>4; | ||
char lowNibble = (_cardTemp & 0x0f); | ||
_wiegandType=_bitCount; | ||
_bitCount=0; | ||
_cardTemp=0; | ||
_cardTempHigh=0; | ||
|
||
if (lowNibble == (~highNibble & 0x0f)) // check if low nibble matches the "NOT" of high nibble. | ||
{ | ||
_code = (int)translateEnterEscapeKeyPress(lowNibble); | ||
return true; | ||
} | ||
else { | ||
_bitCount=0; | ||
_cardTemp=0; | ||
_cardTempHigh=0; | ||
return false; | ||
} | ||
|
||
// TODO: Handle validation failure case! | ||
} | ||
else if (4 == _bitCount) { | ||
// 4-bit Wiegand codes have no data integrity check so we just | ||
// read the LOW nibble. | ||
_code = (int)translateEnterEscapeKeyPress(_cardTemp & 0x0000000F); | ||
|
||
_wiegandType = _bitCount; | ||
_bitCount = 0; | ||
_cardTemp = 0; | ||
_cardTempHigh = 0; | ||
|
||
return true; | ||
} | ||
else // wiegand 26 or wiegand 34 | ||
{ | ||
cardID = wiegand_get_card_id (&_cardTempHigh, &_cardTemp, _bitCount); | ||
_wiegandType=_bitCount; | ||
_bitCount=0; | ||
_cardTemp=0; | ||
_cardTempHigh=0; | ||
_code=cardID; | ||
return true; | ||
} | ||
} | ||
else | ||
{ | ||
// well time over 25 ms and bitCount !=8 , !=26, !=34 , must be noise or nothing then. | ||
_bitCount=0; | ||
_cardTemp=0; | ||
_cardTempHigh=0; | ||
return false; | ||
} | ||
} | ||
|
||
static void lwiegand_cb(os_param_t param, uint8_t prio) | ||
{ | ||
(void) param; | ||
(void) prio; | ||
|
||
if (wiegand_timer_running) | ||
os_timer_disarm(&wiegand_timer); | ||
|
||
os_timer_arm(&wiegand_timer, 25, 0); | ||
} | ||
|
||
static void lwiegand_timer_done(void *param) | ||
{ | ||
lua_State *L = lua_getstate(); | ||
|
||
(void) param; | ||
|
||
os_timer_disarm(&wiegand_timer); | ||
|
||
if (wiegand_store_value()) { | ||
if (wiegand_cb_ref != LUA_NOREF) { | ||
|
||
lua_rawgeti(L, LUA_REGISTRYINDEX, wiegand_cb_ref); | ||
|
||
lua_pushinteger(L, _code); | ||
lua_pushinteger(L, _wiegandType); | ||
|
||
lua_call(L, 2, 0); | ||
} | ||
} | ||
} | ||
|
||
static int lwiegand_close( lua_State* L) | ||
{ | ||
if(wiegand_cb_ref != LUA_NOREF) { | ||
luaL_unref(L, LUA_REGISTRYINDEX, wiegand_cb_ref); | ||
wiegand_cb_ref = LUA_NOREF; | ||
} | ||
return 0; | ||
} | ||
|
||
// Lua: setup( d0pin, d1pin, function ) | ||
static int lwiegand_setup( lua_State* L) | ||
{ | ||
unsigned pinD0 = luaL_checkinteger(L, 1); | ||
unsigned pinD1 = luaL_checkinteger(L, 2); | ||
luaL_argcheck(L, platform_gpio_exists(pinD0) && pinD0>0, 1, "Invalid pin for D0"); | ||
luaL_argcheck(L, platform_gpio_exists(pinD1) && pinD1>0, 2, "Invalid pin for D1"); | ||
luaL_checkfunction(L, 3); | ||
|
||
lwiegand_close(L); | ||
_cardTempHigh = 0; | ||
_cardTemp = 0; | ||
_code = 0; | ||
_wiegandType = 0; | ||
_bitCount = 0; | ||
wiegand_pinD0 = pinD0; | ||
wiegand_pinD0 = pinD1; | ||
platform_gpio_mode( pinD0, PLATFORM_GPIO_INT, PLATFORM_GPIO_FLOAT); | ||
platform_gpio_mode( pinD1, PLATFORM_GPIO_INT, PLATFORM_GPIO_FLOAT); | ||
|
||
wiegand_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); | ||
|
||
os_timer_setfn(&wiegand_timer, lwiegand_timer_done, NULL); | ||
SWTIMER_REG_CB(lrotarwiegand_timer_done, SWTIMER_RESUME); | ||
|
||
uint32_t mask = 1 << pin_num[pinD0]; | ||
platform_gpio_register_intr_hook(mask, wiegand_readD0); | ||
mask = 1 << pin_num[pinD1]; | ||
platform_gpio_register_intr_hook(mask, wiegand_readD1); | ||
platform_gpio_intr_init(pinD0, GPIO_PIN_INTR_NEGEDGE); | ||
platform_gpio_intr_init(pinD1, GPIO_PIN_INTR_NEGEDGE); | ||
|
||
return 0; | ||
} | ||
|
||
// Module function map | ||
LROT_BEGIN(wiegand, NULL, 0) | ||
LROT_FUNCENTRY( setup, lwiegand_setup ) | ||
LROT_FUNCENTRY( close, lwiegand_close ) | ||
LROT_END (wiegand, NULL, 0) | ||
|
||
int luaopen_wiegand( lua_State *L ) { | ||
wiegand_cb_ref = LUA_NOREF; | ||
wiegand_tasknumber = task_get_id(lwiegand_cb); | ||
|
||
return 0; | ||
} | ||
|
||
NODEMCU_MODULE(WIEGAND, "wiegand", wiegand, luaopen_wiegand); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# wiegand Module | ||
| Since | Origin / Contributor | Maintainer | Source | | ||
| :----- | :-------------------- | :---------- | :------ | | ||
| 2020-07-08 | [Cody Cutrer](https://github.com/ccutrer) | [Cody Cutrer](https://github.com/ccutrer) | [wiegand.c](../../app/modules/wiegand.c)| | ||
|
||
This module can read the input from RFID/keypad readers that support Wiegand outputs. 4 (keypress), 8 (keypress), 24, 26, 32, and 34 bit formats are supported. Wiegand requires three connections - two GPIOs connected to D0 and D1 datalines, and a ground connection. | ||
|
||
## wiegand.setup() | ||
Initialize the nodemcu to talk to a Wiegand keypad | ||
|
||
#### Syntax | ||
`wiegand.setup(pinD0, pinD1, callback)` | ||
|
||
#### Parameters | ||
- `pinD0` This is a GPIO number (excluding 0) and connects to the D0 data line | ||
- `pinD1` This is a GPIO number (excluding 0) and connects to the D1 data line | ||
- `callback` This is a function that will invoked when a full card or keypress is read. | ||
|
||
The callback will be invoked with two arguments when a card is received. The first argument is the ID number, | ||
the second is the number of bits in the format (4, 8, 24, 26, 32, 34). | ||
|
||
#### Returns | ||
Nothing. If the arguments are in error, or the operation cannot be completed, then an error is thrown. | ||
|
||
#### Example | ||
|
||
wiegand.setup(1, 2, function (card, type) | ||
print("Card=" .. card .. " type=" .. type) | ||
end) | ||
|
||
## wiegand.close() | ||
Releases the resources associated with the card reader. | ||
|
||
#### Syntax | ||
`wiegand.close()` | ||
|
||
#### Example | ||
|
||
wiegand.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters