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