wiegand.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // Module for reading keycards via Wiegand protocol
  2. // ## Contributors
  3. // [Cody Cutrer](https://github.com/ccutrer) adapted to being a NodeMCU module
  4. #include "module.h"
  5. #include "lauxlib.h"
  6. #include "platform.h"
  7. #include "task/task.h"
  8. #include "user_interface.h"
  9. #include "pm/swtimer.h"
  10. #ifdef LUA_USE_MODULES_WIEGAND
  11. #if !defined(GPIO_INTERRUPT_ENABLE) || !defined(GPIO_INTERRUPT_HOOK_ENABLE)
  12. #error Must have GPIO_INTERRUPT and GPIO_INTERRUPT_HOOK if using WIEGAND module
  13. #endif
  14. #endif
  15. typedef struct {
  16. uint32_t current_card;
  17. int bit_count;
  18. uint32_t last_card;
  19. uint32_t last_bit_count;
  20. int cb_ref;
  21. int self_ref;
  22. ETSTimer timer;
  23. int timer_running;
  24. int task_posted;
  25. int pinD0;
  26. int pinD1;
  27. uint32_t last_bit_time;
  28. } wiegand_struct_t;
  29. typedef wiegand_struct_t* wiegand_t;
  30. static int tasknumber;
  31. static volatile wiegand_t pins_to_wiegand_state[NUM_GPIO];
  32. static wiegand_t wiegand_get( lua_State *L, int stack)
  33. {
  34. wiegand_t w = (wiegand_t)luaL_checkudata(L, stack, "wiegand.wiegand");
  35. if (w == NULL)
  36. return (wiegand_t)luaL_error(L, "wiegand object expected");
  37. return w;
  38. }
  39. static uint32_t ICACHE_RAM_ATTR wiegand_intr(uint32_t ret_gpio_status)
  40. {
  41. uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
  42. uint32_t gpio_bits = gpio_status;
  43. for(int i = 0; gpio_bits > 0; ++i, gpio_bits >>= 1) {
  44. if (i == NUM_GPIO)
  45. break;
  46. if ((gpio_bits & 1) == 0)
  47. continue;
  48. // find the struct registered for this pin
  49. volatile wiegand_t w = pins_to_wiegand_state[i];
  50. if (!w) {
  51. continue;
  52. }
  53. ++w->bit_count;
  54. w->current_card <<= 1;
  55. if (i == pin_num[w->pinD1])
  56. w->current_card |= 1;
  57. w->last_bit_time = system_get_time();
  58. if (!w->task_posted) {
  59. task_post_medium(tasknumber, (os_param_t)w);
  60. w->task_posted = 1;
  61. }
  62. GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & (1 << i));
  63. ret_gpio_status &= ~(1 << i);
  64. }
  65. return ret_gpio_status;
  66. }
  67. static int parity(int val)
  68. {
  69. int parity = 0;
  70. while (val > 0) {
  71. parity ^= val & 1;
  72. val >>= 1;
  73. }
  74. return parity;
  75. }
  76. static bool wiegand_store_card(volatile wiegand_t w)
  77. {
  78. uint32_t card = w->current_card;
  79. int bit_count = w->bit_count;
  80. w->current_card = 0;
  81. w->bit_count = 0;
  82. switch(bit_count) {
  83. case 4:
  84. w->last_card = card;
  85. w->last_bit_count = bit_count;
  86. return true;
  87. case 26:
  88. // even parity over the first 13 bits, odd parity over the last 13 bits
  89. if (parity((card & 0x3ffe000) >> 13) != 0 || parity(card & 0x1fff) != 1)
  90. return false;
  91. w->last_card = (card >> 1) & 0xffffff;
  92. w->last_bit_count = bit_count;
  93. return true;
  94. }
  95. return false;
  96. }
  97. static void lwiegand_timer_done(void *param)
  98. {
  99. lua_State *L = lua_getstate();
  100. wiegand_t w = (wiegand_t) param;
  101. os_timer_disarm(&w->timer);
  102. if (wiegand_store_card(w)) {
  103. lua_rawgeti(L, LUA_REGISTRYINDEX, w->cb_ref);
  104. lua_pushinteger(L, w->last_card);
  105. lua_pushinteger(L, w->last_bit_count);
  106. lua_call(L, 2, 0);
  107. }
  108. }
  109. static void lwiegand_cb(os_param_t param, uint8_t prio)
  110. {
  111. wiegand_t w = (wiegand_t) param;
  112. (void) prio;
  113. *(volatile int *)&w->task_posted = 0;
  114. if (w->timer_running)
  115. os_timer_disarm(&w->timer);
  116. int timeout = 25 - (system_get_time() - w->last_bit_time) / 1000;
  117. if (timeout < 0) {
  118. lwiegand_timer_done(w);
  119. } else {
  120. os_timer_arm(&w->timer, timeout, 0);
  121. }
  122. }
  123. static void reregister_gpio_hooks()
  124. {
  125. uint32_t mask = 0;
  126. for (int i = 0; i < NUM_GPIO; ++i) {
  127. if (pins_to_wiegand_state[i])
  128. mask |= (1 << i);
  129. }
  130. platform_gpio_register_intr_hook(mask, wiegand_intr);
  131. }
  132. static int lwiegand_close( lua_State* L)
  133. {
  134. wiegand_t w = wiegand_get(L, 1);
  135. luaL_unref(L, LUA_REGISTRYINDEX, w->cb_ref);
  136. w->cb_ref = LUA_NOREF;
  137. if (w->timer_running) {
  138. os_timer_disarm(&w->timer);
  139. }
  140. luaL_unref(L, LUA_REGISTRYINDEX, w->self_ref);
  141. w->self_ref = LUA_NOREF;
  142. pins_to_wiegand_state[pin_num[w->pinD0]] = NULL;
  143. pins_to_wiegand_state[pin_num[w->pinD1]] = NULL;
  144. reregister_gpio_hooks();
  145. platform_gpio_intr_init(w->pinD0, GPIO_PIN_INTR_DISABLE);
  146. platform_gpio_intr_init(w->pinD1, GPIO_PIN_INTR_DISABLE);
  147. return 0;
  148. }
  149. // Lua: wiegand.created0pin, d1pin)
  150. static int lwiegand_create(lua_State* L)
  151. {
  152. unsigned pinD0 = luaL_checkinteger(L, 1);
  153. unsigned pinD1 = luaL_checkinteger(L, 2);
  154. luaL_argcheck(L, platform_gpio_exists(pinD0) && pinD0>0, 1, "Invalid pin for D0");
  155. luaL_argcheck(L, platform_gpio_exists(pinD1) && pinD1>0 && pinD0 != pinD1, 2, "Invalid pin for D1");
  156. luaL_checktype(L, 3, LUA_TFUNCTION);
  157. if (pins_to_wiegand_state[pin_num[pinD0]] || pins_to_wiegand_state[pin_num[pinD1]])
  158. return luaL_error(L, "pin already in use");
  159. wiegand_t ud = (wiegand_t)lua_newuserdata(L, sizeof(wiegand_struct_t));
  160. if (!ud) return luaL_error(L, "not enough memory");
  161. luaL_getmetatable(L, "wiegand.wiegand");
  162. lua_setmetatable(L, -2);
  163. ud->current_card = 0;
  164. ud->bit_count = 0;
  165. ud->timer_running = 0;
  166. ud->task_posted = 0;
  167. ud->pinD0 = pinD0;
  168. ud->pinD1 = pinD1;
  169. platform_gpio_mode( pinD0, PLATFORM_GPIO_INT, PLATFORM_GPIO_FLOAT);
  170. platform_gpio_mode( pinD1, PLATFORM_GPIO_INT, PLATFORM_GPIO_FLOAT);
  171. lua_pushvalue(L, 3);
  172. ud->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  173. lua_pushvalue(L, -1);
  174. ud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  175. os_timer_setfn(&ud->timer, lwiegand_timer_done, ud);
  176. SWTIMER_REG_CB(lwiegand_timer_done, SWTIMER_RESUME);
  177. pins_to_wiegand_state[pin_num[pinD0]] = ud;
  178. pins_to_wiegand_state[pin_num[pinD1]] = ud;
  179. reregister_gpio_hooks();
  180. platform_gpio_intr_init(pinD0, GPIO_PIN_INTR_NEGEDGE);
  181. platform_gpio_intr_init(pinD1, GPIO_PIN_INTR_NEGEDGE);
  182. return 1;
  183. }
  184. // Module function map
  185. LROT_BEGIN(wiegand_dyn, NULL, LROT_MASK_GC_INDEX)
  186. LROT_FUNCENTRY( __gc, lwiegand_close )
  187. LROT_TABENTRY( __index, wiegand_dyn )
  188. LROT_FUNCENTRY( close, lwiegand_close )
  189. LROT_END(wiegand_dyn, NULL, LROT_MASK_GC_INDEX)
  190. LROT_BEGIN(wiegand, NULL, 0)
  191. LROT_FUNCENTRY( create, lwiegand_create )
  192. LROT_END (wiegand, NULL, 0)
  193. int luaopen_wiegand( lua_State *L ) {
  194. luaL_rometatable(L, "wiegand.wiegand", LROT_TABLEREF(wiegand_dyn));
  195. tasknumber = task_get_id(lwiegand_cb);
  196. memset((void *)pins_to_wiegand_state, 0, sizeof(pins_to_wiegand_state));
  197. return 0;
  198. }
  199. NODEMCU_MODULE(WIEGAND, "wiegand", wiegand, luaopen_wiegand);