Commit | Line | Data |
---|---|---|
0d5d6475 B |
1 | #include <TimeLib.h> |
2 | #include <Time.h> | |
f1139a83 B |
3 | #include <InputDebounce.h> |
4 | #include <Wiegand.h> | |
5 | #include <ESP8266WiFi.h> | |
0d5d6475 | 6 | #include <WiFiUdp.h> |
f1139a83 B |
7 | #include <DNSServer.h> |
8 | #include <ESP8266WebServer.h> | |
9 | #include <WiFiManager.h> | |
10 | #include <ESP8266mDNS.h> | |
11 | #include <FS.h> | |
12 | ||
13 | // MOSFET for door lock activation | |
14 | // ON/HIGH == ground the output screw terminal | |
15 | #define MOSFET 16 | |
16 | ||
17 | // Pin for LED / WS2812 | |
18 | #define STATUSLED 2 | |
19 | ||
20 | // Wiegand keyfob reader pins | |
21 | #define WD0 12 | |
22 | #define WD1 13 | |
23 | ||
24 | // Door lock sense pin | |
25 | #define SENSE 14 | |
26 | ||
27 | // emergency release switch | |
28 | #define ERELEASE 15 | |
29 | ||
30 | // Buzzer/LED on keyfob control | |
31 | #define BUZZER 4 // Gnd to beep | |
32 | #define DOORLED 5 // low = green, otherwise red | |
33 | ||
34 | // orientation of some signals | |
35 | #define DLED_GREEN LOW | |
36 | #define DLED_RED HIGH | |
37 | #define LOCK_OPEN HIGH | |
38 | #define LOCK_CLOSE LOW | |
39 | #define BUZZ_ON LOW | |
40 | #define BUZZ_OFF HIGH | |
41 | ||
42 | ||
43 | ||
44 | /*********************** | |
45 | * Configuration parameters | |
46 | */ | |
47 | // AP that it will apoear as for configuration | |
48 | #define MANAGER_AP "DoorLock" | |
49 | ||
50 | // Credentials required to reset or upload new info | |
51 | #define www_username "admin" | |
52 | #define www_password "wibble" | |
53 | ||
54 | // files to store card/fob data in | |
55 | #define CARD_TMPFILE "/cards.tmp" | |
56 | #define CARD_FILE "/cards.dat" | |
0d5d6475 | 57 | #define LOG_FILE "/log.dat" |
f1139a83 B |
58 | |
59 | // how long to hold the latch open in millis | |
60 | #define LATCH_HOLD 5000 | |
61 | ||
0d5d6475 B |
62 | // webserver for configuration portnumber |
63 | #define CONFIG_PORT 80 | |
64 | ||
65 | // ntp server to use | |
66 | #define NTP_SERVER "1.uk.pool.ntp.org" | |
67 | ||
f1139a83 B |
68 | /*************************** |
69 | * code below | |
70 | */ | |
71 | WIEGAND wg; | |
0d5d6475 B |
72 | ESP8266WebServer server(CONFIG_PORT); |
73 | ||
74 | const unsigned int localPort = 2390; | |
75 | IPAddress ntpServerIP; | |
76 | ||
77 | const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message | |
78 | ||
79 | byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets | |
80 | ||
81 | WiFiUDP udp; | |
82 | ||
83 | ||
84 | /* compose and send an NTP time request packet */ | |
85 | void ntp_send() | |
86 | { | |
87 | if (ntpServerIP == INADDR_NONE) { | |
88 | WiFi.hostByName(NTP_SERVER, ntpServerIP); | |
89 | Serial.print("Got NTP server " NTP_SERVER " address "); | |
90 | Serial.println(ntpServerIP); | |
91 | } | |
92 | ||
93 | ||
94 | memset(packetBuffer, 0, NTP_PACKET_SIZE); | |
95 | // Initialize values needed to form NTP request | |
96 | // (see URL above for details on the packets) | |
97 | packetBuffer[0] = 0b11100011; // LI, Version, Mode | |
98 | packetBuffer[1] = 0; // Stratum, or type of clock | |
99 | packetBuffer[2] = 6; // Polling Interval | |
100 | packetBuffer[3] = 0xEC; // Peer Clock Precision | |
101 | // 8 bytes of zero for Root Delay & Root Dispersion | |
102 | packetBuffer[12] = 49; | |
103 | packetBuffer[13] = 0x4E; | |
104 | packetBuffer[14] = 49; | |
105 | packetBuffer[15] = 52; | |
106 | ||
107 | // all NTP fields have been given values, now | |
108 | // you can send a packet requesting a timestamp: | |
109 | udp.beginPacket(ntpServerIP, 123); //NTP requests are to port 123 | |
110 | udp.write(packetBuffer, NTP_PACKET_SIZE); | |
111 | udp.endPacket(); | |
112 | ||
113 | Serial.println("Sending NTP request"); | |
114 | } | |
115 | ||
116 | /* request a time update from NTP and parse the result */ | |
117 | time_t ntp_fetch() | |
118 | { | |
119 | while (udp.parsePacket() > 0); // discard old udp packets | |
120 | ntp_send(); | |
f1139a83 | 121 | |
0d5d6475 | 122 | uint32_t beginWait = millis(); |
f1139a83 | 123 | |
0d5d6475 B |
124 | while (millis() - beginWait < 2500) { |
125 | int size = udp.parsePacket(); | |
126 | if (size >= NTP_PACKET_SIZE) { | |
127 | udp.read(packetBuffer, NTP_PACKET_SIZE); | |
128 | ||
129 | // this is NTP time (seconds since Jan 1 1900): | |
130 | unsigned long secsSince1900 = packetBuffer[40] << 24 | packetBuffer[41] << 16 | packetBuffer[42] << 8 | packetBuffer[43]; | |
131 | const unsigned long seventyYears = 2208988800UL; | |
132 | time_t unixtime = secsSince1900 - seventyYears; | |
133 | ||
134 | Serial.print("NTP update unixtime="); | |
135 | Serial.println(unixtime); | |
136 | return unixtime; | |
137 | } | |
138 | } | |
139 | Serial.println("No NTP response"); | |
140 | return 0; | |
141 | } | |
142 | ||
143 | /* how big is a file */ | |
f1139a83 B |
144 | int fileSize(const char *filename) |
145 | { | |
146 | int ret = -1; | |
147 | File file = SPIFFS.open(filename, "r"); | |
148 | if (file) { | |
149 | ret = file.size(); | |
150 | file.close(); | |
151 | } | |
152 | return ret; | |
153 | } | |
154 | ||
0d5d6475 B |
155 | |
156 | /* HTTP page request for / */ | |
f1139a83 B |
157 | void handleRoot() |
158 | { | |
159 | char mtime[16]; | |
160 | int sec = millis() / 1000; | |
161 | int mi = sec / 60; | |
162 | int hr = mi / 60; | |
163 | int day = hr / 24; | |
164 | ||
165 | snprintf(mtime, 16, "%dd %02d:%02d:%02d", day, hr % 24, mi % 60, sec % 60); | |
166 | ||
167 | String out = "<html>\ | |
168 | <head>\ | |
169 | <title>Door Lock</title>\ | |
170 | <style>\ | |
171 | body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; }\ | |
172 | </style>\ | |
173 | </head>\ | |
174 | <body>\ | |
175 | <h1>Door Lock!</h1>\ | |
0d5d6475 B |
176 | <p>Uptime: " + (String)mtime + "</p>\n"; |
177 | ||
178 | if (timeStatus() == timeSet) { | |
179 | time_t when = now(); | |
180 | out += "<p>Time now: " + getDate(when) + " " + getTime(when) + "</p>\n"; | |
181 | } | |
182 | ||
183 | ||
184 | FSInfo fs_info; | |
185 | if (SPIFFS.info(fs_info)) { | |
186 | out += "<p>Onboard Flash disk: - Size:"+String(fs_info.totalBytes)+" Used:"+String(fs_info.usedBytes)+"</p>\n"; | |
187 | } | |
188 | ||
189 | out += "<p>Lock is currently "; | |
190 | if (digitalRead(SENSE) == HIGH) out += "LOCKED"; else out += "OPEN"; | |
191 | out += "</p>\n"; | |
f1139a83 B |
192 | |
193 | if (SPIFFS.exists(CARD_FILE)) { | |
194 | ||
195 | out += "<p>Cardfile: " + String(CARD_FILE) + " is " + fileSize(CARD_FILE) + " bytes"; | |
196 | int count = sanityCheck(CARD_FILE); | |
197 | if (count <= 0) { | |
198 | out += ", in an invalid file"; | |
199 | } else { | |
200 | out += ", contains " + String(count) + " keyfob IDs"; | |
201 | out += " - <a href=\"/download\">Download</a>"; | |
202 | } | |
203 | ||
204 | out += ".</p>"; | |
205 | } | |
206 | ||
207 | out += "<ul>\ | |
208 | <li><a href=\"/reset\">Reset Configuration</a>\ | |
209 | <li><a href=\"/upload\">Upload Cardlist</a>"; | |
210 | ||
0d5d6475 B |
211 | |
212 | if (SPIFFS.exists(LOG_FILE)) { | |
213 | out += "<li><a href=\"/wipelog\">Wipe log file</a>"; | |
214 | out += "<li><a href=\"/download_logfile\">Download full logfile</a>"; | |
f1139a83 | 215 | } |
0d5d6475 | 216 | |
f1139a83 | 217 | |
0d5d6475 B |
218 | out += "</ul>"; |
219 | ||
220 | if (SPIFFS.exists(LOG_FILE)) out += printLog(true, 10); | |
221 | ||
222 | out += "</body>\ | |
f1139a83 B |
223 | </html>"; |
224 | ||
225 | server.send( 200, "text/html", out); | |
226 | } | |
227 | ||
228 | void handleDownload() | |
229 | { | |
230 | if (!server.authenticate(www_username, www_password)) | |
231 | return server.requestAuthentication(); | |
232 | ||
233 | if (!SPIFFS.exists(CARD_FILE)) { | |
234 | server.send(404, "text/plain", "Card file not found"); | |
235 | return; | |
236 | } | |
237 | ||
238 | File f = SPIFFS.open(CARD_FILE, "r"); | |
239 | server.streamFile(f, "text/csv"); | |
240 | f.close(); | |
241 | } | |
242 | ||
0d5d6475 B |
243 | void handleWipelog() |
244 | { | |
245 | if (!server.authenticate(www_username, www_password)) | |
246 | return server.requestAuthentication(); | |
247 | ||
248 | SPIFFS.remove(LOG_FILE); | |
249 | server.send(200, "text/plain", "logfile deleted"); | |
250 | } | |
251 | ||
252 | void handleDownloadLogfile() | |
253 | { | |
254 | if (!server.authenticate(www_username, www_password)) | |
255 | return server.requestAuthentication(); | |
256 | ||
257 | String result = printLog(false, 0); | |
258 | server.send(200, "text/csv", result); | |
259 | } | |
260 | ||
f1139a83 B |
261 | void handleNotFound() { |
262 | String out = "File Not found\n\n"; | |
263 | server.send(404, "text/plain", out); | |
264 | } | |
265 | ||
266 | // User wants to reset config | |
267 | void handleReset() { | |
268 | if (!server.authenticate(www_username, www_password)) | |
269 | return server.requestAuthentication(); | |
270 | ||
271 | server.send(200, "text/plain", "Rebooting to config manager...\n\n"); | |
272 | ||
273 | WiFiManager wfm; | |
274 | wfm.resetSettings(); | |
275 | WiFi.disconnect(); | |
276 | ESP.reset(); | |
277 | delay(5000); | |
278 | } | |
279 | ||
280 | void handleUploadRequest() { | |
281 | String out = "<html><head><title>Upload Keyfob list</title></head><body>\ | |
282 | <form enctype=\"multipart/form-data\" action=\"/upload\" method=\"POST\">\ | |
283 | <input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"32000\" />\ | |
284 | Select file to upload: <input name=\"file\" type=\"file\" />\ | |
285 | <input type=\"submit\" value=\"Upload file\" />\ | |
286 | </form></body></html>"; | |
287 | server.send(200, "text/html", out); | |
288 | } | |
289 | ||
290 | File uploadFile; | |
291 | ||
292 | String upload_error; | |
293 | int upload_code = 200; | |
294 | ||
295 | void handleFileUpload() | |
296 | { | |
297 | if (server.uri() != "/upload") return; | |
298 | ||
299 | if (!server.authenticate(www_username, www_password)) | |
300 | return server.requestAuthentication(); | |
301 | ||
302 | HTTPUpload& upload = server.upload(); | |
303 | if (upload.status == UPLOAD_FILE_START) { | |
304 | upload_error = ""; | |
305 | upload_code = 200; | |
306 | uploadFile = SPIFFS.open(CARD_TMPFILE, "w"); | |
307 | if (!uploadFile) { | |
308 | upload_error = "error opening file"; | |
309 | Serial.println("Opening tmpfile failed!"); | |
310 | upload_code = 403; | |
311 | } | |
312 | }else | |
313 | if (upload.status == UPLOAD_FILE_WRITE) { | |
314 | if (uploadFile) { | |
315 | if (uploadFile.write(upload.buf, upload.currentSize) != upload.currentSize) { | |
316 | upload_error = "write error"; | |
317 | upload_code = 409; | |
318 | } | |
319 | } | |
320 | }else | |
321 | if (upload.status == UPLOAD_FILE_END) { | |
322 | if (uploadFile) { | |
323 | uploadFile.close(); | |
324 | } | |
325 | } | |
326 | } | |
327 | ||
328 | void handleUploadComplete() | |
329 | { | |
330 | String out = "Upload finished."; | |
331 | if (upload_code != 200) { | |
332 | out += "Error: "+upload_error; | |
333 | } else { | |
334 | out += " Success"; | |
335 | // upload with no errors, replace old one | |
336 | SPIFFS.remove(CARD_FILE); | |
337 | SPIFFS.rename(CARD_TMPFILE, CARD_FILE); | |
338 | } | |
0d5d6475 | 339 | out += "</p><a href=\"/\">Back</a>"; |
f1139a83 B |
340 | server.send(upload_code, "text/plain", out); |
341 | } | |
342 | ||
343 | ||
344 | void returnOK() { | |
345 | server.send(200, "text/plain", ""); | |
346 | } | |
347 | ||
0d5d6475 B |
348 | String getTime(time_t when) |
349 | { | |
350 | String ans; | |
351 | int h = hour(when); | |
352 | int m = minute(when); | |
353 | int s = second(when); | |
354 | ||
355 | if (h<10) ans += "0"; | |
356 | ans += String(h) + ":"; | |
357 | if (m<10) ans += "0"; | |
358 | ans += String(m) + ":"; | |
359 | if (s<10) ans += "0"; | |
360 | ans += String(s); | |
361 | ||
362 | return ans; | |
f1139a83 B |
363 | } |
364 | ||
0d5d6475 | 365 | String getDate(time_t when) |
f1139a83 | 366 | { |
0d5d6475 B |
367 | String ans; |
368 | ||
369 | ans += String(year(when)) + "-" + String(month(when)) + "-" + String(day(when)); | |
370 | return ans; | |
f1139a83 B |
371 | } |
372 | ||
0d5d6475 | 373 | |
f1139a83 B |
374 | int sanityCheck(const char * filename) |
375 | { | |
376 | int count = 0; | |
377 | ||
378 | File f = SPIFFS.open(filename, "r"); | |
379 | if (!f) { | |
380 | Serial.print("Sanity Check: Could not open "); | |
381 | Serial.println(filename); | |
382 | return -1; | |
383 | } | |
384 | while (f.available()) { | |
385 | char c = f.peek(); | |
386 | // skip comment lines | |
387 | if (c == '#') { | |
388 | f.find("\n"); | |
389 | continue; | |
390 | } | |
391 | ||
392 | String wcode = f.readStringUntil(','); | |
393 | String wname = f.readStringUntil('\n'); | |
394 | unsigned int newcode = wcode.toInt(); | |
395 | ||
396 | if (newcode != 0) count++; | |
397 | } | |
398 | f.close(); | |
399 | ||
400 | return count; | |
401 | } | |
402 | ||
403 | String findKeyfob(unsigned int code) | |
404 | { | |
405 | File f = SPIFFS.open(CARD_FILE, "r"); | |
406 | if (!f) { | |
407 | Serial.println("Error opening card file " CARD_FILE); | |
408 | return ""; | |
409 | } | |
410 | ||
411 | String answer = ""; | |
412 | while (f.available()) { | |
413 | char c = f.peek(); | |
414 | // skip comment lines | |
415 | if (c == '#') { | |
416 | f.find("\n"); | |
417 | continue; | |
418 | } | |
419 | ||
420 | String wcode = f.readStringUntil(','); | |
421 | String wname = f.readStringUntil('\n'); | |
422 | ||
423 | unsigned int newcode = wcode.toInt(); | |
424 | ||
425 | /* debug | |
426 | Serial.print("Line: code='"); | |
427 | Serial.print(wcode); | |
428 | Serial.print("' ("); | |
429 | Serial.print(newcode); | |
430 | Serial.print(") name='"); | |
431 | Serial.print(wname); | |
432 | Serial.print("'"); | |
433 | */ | |
434 | if (code == newcode) { | |
435 | // Serial.println(" - FOUND IT"); | |
436 | answer = wname; | |
437 | break; | |
438 | } | |
439 | //Serial.println(); | |
440 | } | |
441 | f.close(); | |
442 | return answer; | |
443 | } | |
444 | ||
0d5d6475 B |
445 | void logEntry(time_t when, uint32_t card) |
446 | { | |
447 | unsigned char entry[8]; | |
448 | ||
449 | File f = SPIFFS.open(LOG_FILE, "a"); | |
450 | if (!f) { | |
451 | Serial.println("Error opening log file"); | |
452 | return; | |
453 | } | |
454 | ||
455 | // compose the record to write | |
456 | ((uint32_t *)entry)[0] = when; | |
457 | ((uint32_t *)entry)[1] = card; | |
458 | f.write(entry, 8); | |
459 | f.close(); | |
460 | } | |
461 | ||
462 | String printLog(int html, int last) | |
463 | { | |
464 | String out; | |
465 | File f = SPIFFS.open(LOG_FILE, "r"); | |
466 | if (!f) return String("Could not open log file"); | |
467 | ||
468 | unsigned char entry[8]; | |
469 | uint32_t * data = (uint32_t *)entry; | |
470 | ||
471 | if (last != 0) { | |
472 | // print only the last N items | |
473 | int pos = f.size() / 8; | |
474 | if (pos > last) pos -= last; else pos = 0; | |
475 | f.seek( pos * 8, SeekSet); | |
476 | if (html) out += "Last " + String(last) + " log entries :-"; | |
477 | } | |
478 | if (html) out += "<ul>"; | |
479 | ||
480 | while (f.available()) { | |
481 | f.read(entry, 8); | |
482 | if (html) out += "<li> "; | |
483 | out += getDate( data[0] ); | |
484 | out += " "; | |
485 | out += getTime( data[0] ); | |
486 | if (html) out += " - "; else out += "," + String(data[1]) + ","; | |
487 | ||
488 | if (data[1] == 0) { | |
489 | if (html) out += "<i>"; | |
490 | out += "Emergency Release"; | |
491 | if (html) out += "</i>"; | |
492 | } else { | |
493 | String whom = findKeyfob(data[1]); | |
494 | if (whom == "") { | |
495 | if (html) out += "<i>by "; | |
496 | out += "Unknown keyfob"; | |
497 | if (html) out += "</i>"; | |
498 | } else { | |
499 | out += whom; | |
500 | } | |
501 | if (html) out += " (" + String(data[1]) + ")"; | |
502 | } | |
503 | out += "\n"; | |
504 | } | |
505 | f.close(); | |
506 | if (html) out += "</ul>"; | |
507 | return out; | |
508 | } | |
509 | ||
510 | ||
511 | static InputDebounce release_button; | |
512 | ||
513 | /******************************************** | |
514 | * Main setup routine | |
515 | */ | |
516 | void setup() { | |
517 | // some serial, for debug | |
518 | Serial.begin(115200); | |
519 | ||
520 | // The lock mechanism, set HIGH to turn on and connect ground to output pin | |
521 | pinMode(MOSFET, OUTPUT); | |
522 | digitalWrite(MOSFET, LOCK_CLOSE); | |
523 | ||
524 | // lock sense microswitch | |
525 | pinMode(SENSE, INPUT_PULLUP); | |
526 | ||
527 | // emergency door release switch | |
528 | pinMode(ERELEASE, INPUT); | |
529 | ||
530 | // indicators on the keyfob reader | |
531 | pinMode(BUZZER, OUTPUT); | |
532 | pinMode(DOORLED, OUTPUT); | |
533 | digitalWrite(BUZZER, BUZZ_OFF); | |
534 | digitalWrite(DOORLED, DLED_RED); | |
f1139a83 | 535 | |
0d5d6475 B |
536 | Serial.println("DoorLock. Testing WiFi config..."); |
537 | ||
538 | // if we have no config, enter config mode | |
539 | WiFiManager wfm; | |
540 | wfm.autoConnect(MANAGER_AP); | |
541 | ||
542 | // we have config, enable web server | |
543 | server.on( "/", handleRoot ); | |
544 | server.on( "/reset", handleReset ); | |
545 | server.on( "/download", handleDownload ); | |
546 | server.on( "/wipelog", handleWipelog ); | |
547 | server.on( "/download_logfile", handleDownloadLogfile ); | |
548 | server.onFileUpload( handleFileUpload); | |
549 | server.on( "/upload", HTTP_GET, handleUploadRequest); | |
550 | server.on( "/upload", HTTP_POST, handleUploadComplete); | |
551 | server.onNotFound( handleNotFound ); | |
552 | server.begin(); | |
553 | ||
554 | // advertise we exist via MDNS | |
555 | if (!MDNS.begin("doorlock")) { | |
556 | Serial.println("Error setting up MDNS responder."); | |
557 | } else { | |
558 | MDNS.addService("http", "tcp", 80); | |
559 | } | |
560 | ||
561 | // enable internal flash filesystem | |
562 | SPIFFS.begin(); | |
563 | ||
564 | // init wiegand keyfob reader | |
565 | Serial.println("Starting Wiegand test reader"); | |
566 | wg.begin(WD0, WD0, WD1, WD1); | |
567 | ||
568 | // setup button debounce for the release switch | |
569 | release_button.setup(ERELEASE, 20, InputDebounce::PIM_EXT_PULL_DOWN_RES); | |
570 | ||
571 | // listener port for replies from NTP | |
572 | udp.begin(localPort); | |
573 | setSyncProvider(ntp_fetch); | |
574 | } | |
575 | ||
576 | unsigned long locktime = 0; | |
577 | ||
578 | ||
579 | void unlock_door() | |
580 | { | |
581 | digitalWrite(DOORLED, DLED_GREEN); | |
582 | digitalWrite(MOSFET, LOCK_OPEN); | |
583 | if (locktime == 0) { | |
584 | digitalWrite(BUZZER, BUZZ_ON); | |
585 | delay(100); | |
586 | digitalWrite(BUZZER, BUZZ_OFF); | |
587 | delay(50); | |
588 | digitalWrite(BUZZER, BUZZ_ON); | |
589 | delay(100); | |
590 | digitalWrite(BUZZER, BUZZ_OFF); | |
591 | } | |
592 | locktime = millis(); | |
593 | } | |
594 | ||
595 | ||
596 | void loop() { | |
f1139a83 B |
597 | // is the latch held open ? |
598 | if (locktime != 0) { | |
0d5d6475 | 599 | if (locktime + LATCH_HOLD < millis()) { |
f1139a83 B |
600 | locktime = 0; |
601 | digitalWrite(MOSFET, LOCK_CLOSE); | |
602 | digitalWrite(DOORLED, DLED_RED); | |
603 | } | |
604 | } | |
605 | // handle web requests | |
606 | server.handleClient(); | |
607 | ||
0d5d6475 | 608 | unsigned int ertime = release_button.process(millis()); |
f1139a83 B |
609 | unsigned int count = release_button.getStateOnCount(); |
610 | static unsigned last_count = 0; | |
611 | if (ertime > 0) { | |
612 | if (count != last_count) { | |
613 | last_count = count; | |
614 | Serial.println("Door Release button triggered."); | |
615 | unlock_door(); | |
0d5d6475 | 616 | logEntry(now(), 0); |
f1139a83 B |
617 | } else { |
618 | // buttons is still pressed, do nothing | |
619 | } | |
620 | } | |
621 | ||
622 | // handle card swipes | |
623 | if (wg.available()) { | |
624 | unsigned long code = wg.getCode(); | |
625 | ||
626 | Serial.print("wiegand HEX = "); | |
627 | Serial.print(code,HEX); | |
628 | Serial.print(", DECIMAL= "); | |
629 | Serial.print(code); | |
630 | Serial.print(", TYPE W"); | |
631 | Serial.println(wg.getWiegandType()); | |
632 | ||
633 | String who = findKeyfob(code); | |
634 | if (who != NULL) { | |
635 | Serial.print("Unlocking door for "); | |
636 | Serial.println(who); | |
637 | unlock_door(); | |
0d5d6475 | 638 | logEntry(now(), code); |
f1139a83 B |
639 | } |
640 | } | |
641 | ||
642 | } |