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