-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwiegand_rpi.c
282 lines (238 loc) · 6.81 KB
/
wiegand_rpi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*
* Wiegand data reader with Raspberry Pi
* Tested with RPi Model B rev2, and an 26bit Wiegand Cards
* Spiros Ioannou 2017
*
* This is interrupt drivern, no polling occurs.
* After each bit is read, a timeout is set.
* If timeout is reached read code is evaluated for correctness.
*
* Wiegand Bits:
* pFFFFFFFFNNNNNNNNNNNNNNNNP
* p: even parity of F-bits and leftmost 4 N-bits
* F: Facility code
* N: Card Number
* P: odd parity of rightmost 12 N-bits
*
* Compile with: gcc wiegand_rpi.c -lwiringPi -lpthread -lrt -Wall -o wiegand_rpi -O
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <memory.h>
#include <stdint.h>
#include <sys/time.h>
#include <signal.h>
#include <wiringPi.h>
/* Defaults, change with command-line options */
#define D0_PIN 0
#define D1_PIN 1
#define WIEGANDMAXBITS 40
/* Set some timeouts */
/* Wiegand defines:
* a Pulse Width time between 20 μs and 100 μs (microseconds)
* a Pulse Interval time between 200 μs and 20000 μsec
*/
/* Each bit takes 4-6 usec, so all 26 bit sequence would take < 200usec */
#define WIEGAND_BIT_INTERVAL_TIMEOUT_USEC 20000 /* interval between bits, typically 1000us */
struct itimerval it_val;
struct sigaction sa;
struct wiegand_data {
unsigned char p0, p1; //parity 0 , parity 1
uint8_t facility_code;
uint16_t card_code;
uint32_t full_code;;
int code_valid;
unsigned long bitcount; // bits read
} wds;
struct option_s {
int d0pin;
int d1pin;
int debug;
} options;
void show_code();
void wiegand_sequence_reset();
void reset_timeout_timer(long usec);
void gpio_init(int d0pin, int d1pin);
static struct timespec wbit_tm; //for debug
/* Timeout from last bit read, sequence may be completed or stopped */
void wiegand_timeout() {
if (options.debug)
fprintf(stderr, "wiegand_timeout()\n");
wiegand_sequence_reset();
}
void show_code() {
printf("%d\n", wds.full_code);
fflush(stdout);
}
int setup_wiegand_timeout_handler() {
sigemptyset(&sa.sa_mask);
sa.sa_handler = wiegand_timeout;;
//sa.sa_flags = SA_SIGINFO;
if (options.debug)
fprintf(stderr, "\n");
if (sigaction(SIGALRM, &sa, NULL) < 0) {
perror("sigaction");
return 1;
};
return 0;
}
/* Parse Wiegand 26bit format
* Called wherever a new bit is read
* bit: 0 or 1
*/
void add_bit_w26(int bit) {
static int parity0 = 0;
static int parity1 = 0;
//Parity calculation
if (wds.bitcount == 0) {
parity0 = 0;
parity1 = 0;
}
if (wds.bitcount > 0 && wds.bitcount <= 12) {
parity0 += bit;
}
else if (wds.bitcount >= 13 && wds.bitcount <= 24) {
parity1 += bit;
}
//Code calculation
if (wds.bitcount == 0) {
/* Reset */
wds.code_valid = 0;
wds.facility_code = 0;
wds.card_code = 0;
wds.p0 = bit; //parity
}
else if (wds.bitcount <= 8) {
wds.facility_code <<= 1;
if (bit)
wds.facility_code |= 1;
}
else if (wds.bitcount < 25) {
wds.card_code <<= 1;
if (bit)
wds.card_code |= 1;
}
else if (wds.bitcount == 25) {
wds.p1 = bit;
wds.full_code = wds.facility_code;
wds.full_code = wds.full_code << 16;
wds.full_code += wds.card_code;
wds.code_valid = 1;
//check parity
if ((parity0 % 2) != wds.p0) {
wds.code_valid = 0;
if (options.debug) {
fprintf(stderr, "Incorrect even parity bit (leftmost)\n");
}
}
else if ((!(parity1 % 2)) != wds.p1) {
wds.code_valid = 0;
if (options.debug) {
fprintf(stderr, "Incorrect odd parity bit (rightmost)\n");
}
}
if (wds.code_valid) {
show_code();
}
}
else if (wds.bitcount > 25) {
wds.code_valid = 0;
wiegand_sequence_reset();
}
if (wds.bitcount < WIEGANDMAXBITS) {
wds.bitcount++;
}
}
unsigned long get_bit_timediff_ns() {
struct timespec now, delta;
unsigned long tdiff;
clock_gettime(CLOCK_MONOTONIC, &now);
delta.tv_sec = now.tv_sec - wbit_tm.tv_sec;
delta.tv_nsec = now.tv_nsec - wbit_tm.tv_nsec;
tdiff = delta.tv_sec * 1000000000 + delta.tv_nsec;
return tdiff;
}
void d0_pulse(void) {
reset_timeout_timer(WIEGAND_BIT_INTERVAL_TIMEOUT_USEC); //timeout waiting for next bit
if (options.debug) {
fprintf(stderr, "Bit:%02ld, Pulse 0, %ld us since last bit\n",
wds.bitcount, get_bit_timediff_ns() / 1000);
clock_gettime(CLOCK_MONOTONIC, &wbit_tm);
}
add_bit_w26(0);
}
void d1_pulse(void) {
reset_timeout_timer(WIEGAND_BIT_INTERVAL_TIMEOUT_USEC); //timeout waiting for next bit
if (options.debug) {
fprintf(stderr, "Bit:%02ld, Pulse 1, %ld us since last bit\n",
wds.bitcount, get_bit_timediff_ns() / 1000);
clock_gettime(CLOCK_MONOTONIC, &wbit_tm);
}
add_bit_w26(1);
}
void wiegand_sequence_reset() {
wds.bitcount = 0;
}
/* timeout handler, should fire after bit sequence has been read */
void reset_timeout_timer(long usec) {
it_val.it_value.tv_sec = 0;
it_val.it_value.tv_usec = usec;
it_val.it_interval.tv_sec = 0;
it_val.it_interval.tv_usec = 0;
if (setitimer(ITIMER_REAL, &it_val, NULL) == -1) {
perror("setitimer");
exit(1);
}
}
void gpio_init(int d0pin, int d1pin) {
wiringPiSetup();
pinMode(d0pin, INPUT);
pinMode(d1pin, INPUT);
wiringPiISR(d0pin, INT_EDGE_FALLING, d0_pulse);
wiringPiISR(d1pin, INT_EDGE_FALLING, d1_pulse);
}
void show_usage() {
printf("Wiegand Reader (https://bitbucket.org/sivann/wiegand_read)\n");
printf("Usage: wiegand_read [-d] [-0 D0-pin] [-1 D1-pin]\n");
printf("\t-d\t\tdebug\n");
printf("\t-0\t\tGPIO pin for data0 pulse\n");
printf("\t-1\t\tGPIO pin for data1 pulse\n");
printf("\tCheck http://wiringpi.com/pins for WiringPi pin numbers\n");
printf("\n");
}
int main(int argc, char *argv[]) {
int opt;
/* defaults */
options.d0pin = D0_PIN;
options.d1pin = D1_PIN;
options.debug = 0;
/* Parse Options */
while ((opt = getopt(argc, argv, "d0:1:")) != -1) {
switch (opt) {
case 'd':
options.debug++;
break;
case '0':
options.d0pin = atoi(optarg);
break;
case '1':
options.d1pin = atoi(optarg);
break;
case 'h':
show_usage();
exit(0);
break;
default: /* '?' */
show_usage();
exit(EXIT_FAILURE);
}
}
setup_wiegand_timeout_handler();
gpio_init(options.d0pin, options.d1pin);
wiegand_sequence_reset();
while (1) {
pause();
}
}