+#include <TimeLib.h>
+#include <Time.h>
#include <InputDebounce.h>
#include <Wiegand.h>
#include <ESP8266WiFi.h>
+#include <WiFiUdp.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
// files to store card/fob data in
#define CARD_TMPFILE "/cards.tmp"
#define CARD_FILE "/cards.dat"
+#define LOG_FILE "/log.dat"
// how long to hold the latch open in millis
#define LATCH_HOLD 5000
+// webserver for configuration portnumber
+#define CONFIG_PORT 80
+
+// ntp server to use
+#define NTP_SERVER "1.uk.pool.ntp.org"
+
/***************************
* code below
*/
WIEGAND wg;
-ESP8266WebServer server(80);
+ESP8266WebServer server(CONFIG_PORT);
+
+const unsigned int localPort = 2390;
+IPAddress ntpServerIP;
+
+const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
+
+byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
+
+WiFiUDP udp;
+
+
+/* compose and send an NTP time request packet */
+void ntp_send()
+{
+ if (ntpServerIP == INADDR_NONE) {
+ WiFi.hostByName(NTP_SERVER, ntpServerIP);
+ Serial.print("Got NTP server " NTP_SERVER " address ");
+ Serial.println(ntpServerIP);
+ }
+
+
+ memset(packetBuffer, 0, NTP_PACKET_SIZE);
+ // Initialize values needed to form NTP request
+ // (see URL above for details on the packets)
+ packetBuffer[0] = 0b11100011; // LI, Version, Mode
+ packetBuffer[1] = 0; // Stratum, or type of clock
+ packetBuffer[2] = 6; // Polling Interval
+ packetBuffer[3] = 0xEC; // Peer Clock Precision
+ // 8 bytes of zero for Root Delay & Root Dispersion
+ packetBuffer[12] = 49;
+ packetBuffer[13] = 0x4E;
+ packetBuffer[14] = 49;
+ packetBuffer[15] = 52;
+
+ // all NTP fields have been given values, now
+ // you can send a packet requesting a timestamp:
+ udp.beginPacket(ntpServerIP, 123); //NTP requests are to port 123
+ udp.write(packetBuffer, NTP_PACKET_SIZE);
+ udp.endPacket();
+
+ Serial.println("Sending NTP request");
+}
+
+/* request a time update from NTP and parse the result */
+time_t ntp_fetch()
+{
+ while (udp.parsePacket() > 0); // discard old udp packets
+ ntp_send();
+ uint32_t beginWait = millis();
+ while (millis() - beginWait < 2500) {
+ int size = udp.parsePacket();
+ if (size >= NTP_PACKET_SIZE) {
+ udp.read(packetBuffer, NTP_PACKET_SIZE);
+
+ // this is NTP time (seconds since Jan 1 1900):
+ unsigned long secsSince1900 = packetBuffer[40] << 24 | packetBuffer[41] << 16 | packetBuffer[42] << 8 | packetBuffer[43];
+ const unsigned long seventyYears = 2208988800UL;
+ time_t unixtime = secsSince1900 - seventyYears;
+
+ Serial.print("NTP update unixtime=");
+ Serial.println(unixtime);
+ return unixtime;
+ }
+ }
+ Serial.println("No NTP response");
+ return 0;
+}
+
+/* how big is a file */
int fileSize(const char *filename)
{
int ret = -1;
return ret;
}
+
+/* HTTP page request for / */
void handleRoot()
{
char mtime[16];
</head>\
<body>\
<h1>Door Lock!</h1>\
- <p>Uptime: " + (String)mtime + "</p>";
+ <p>Uptime: " + (String)mtime + "</p>\n";
+
+ if (timeStatus() == timeSet) {
+ time_t when = now();
+ out += "<p>Time now: " + getDate(when) + " " + getTime(when) + "</p>\n";
+ }
+
+
+ FSInfo fs_info;
+ if (SPIFFS.info(fs_info)) {
+ out += "<p>Onboard Flash disk: - Size:"+String(fs_info.totalBytes)+" Used:"+String(fs_info.usedBytes)+"</p>\n";
+ }
+
+ out += "<p>Lock is currently ";
+ if (digitalRead(SENSE) == HIGH) out += "LOCKED"; else out += "OPEN";
+ out += "</p>\n";
if (SPIFFS.exists(CARD_FILE)) {
<li><a href=\"/reset\">Reset Configuration</a>\
<li><a href=\"/upload\">Upload Cardlist</a>";
- FSInfo fs_info;
- if (SPIFFS.info(fs_info)) {
- out += "<li><a href=\"/format\">Format SPIFFS</a> - Size:"+String(fs_info.totalBytes)+" Used:"+String(fs_info.usedBytes);
+
+ if (SPIFFS.exists(LOG_FILE)) {
+ out += "<li><a href=\"/wipelog\">Wipe log file</a>";
+ out += "<li><a href=\"/download_logfile\">Download full logfile</a>";
}
-
- out += "<li>Lock is currently ";
- if (digitalRead(SENSE) == HIGH) out += "LOCKED"; else out += "OPEN";
+
- out += "</ul>\
- </body>\
+ out += "</ul>";
+
+ if (SPIFFS.exists(LOG_FILE)) out += printLog(true, 10);
+
+ out += "</body>\
</html>";
server.send( 200, "text/html", out);
f.close();
}
+void handleWipelog()
+{
+ if (!server.authenticate(www_username, www_password))
+ return server.requestAuthentication();
+
+ SPIFFS.remove(LOG_FILE);
+ server.send(200, "text/plain", "logfile deleted");
+}
+
+void handleDownloadLogfile()
+{
+ if (!server.authenticate(www_username, www_password))
+ return server.requestAuthentication();
+
+ String result = printLog(false, 0);
+ server.send(200, "text/csv", result);
+}
+
void handleNotFound() {
String out = "File Not found\n\n";
server.send(404, "text/plain", out);
SPIFFS.remove(CARD_FILE);
SPIFFS.rename(CARD_TMPFILE, CARD_FILE);
}
+ out += "</p><a href=\"/\">Back</a>";
server.send(upload_code, "text/plain", out);
}
server.send(200, "text/plain", "");
}
-
-static InputDebounce release_button;
-
-void setup() {
- // some serial, for debug
- Serial.begin(115200);
-
- // The lock mechanism, set HIGH to turn on and connect ground to output pin
- pinMode(MOSFET, OUTPUT);
- digitalWrite(MOSFET, LOCK_CLOSE);
-
- // lock sense microswitch
- pinMode(SENSE, INPUT_PULLUP);
-
- // emergency door release switch
- pinMode(ERELEASE, INPUT);
-
- // indicators on the keyfob reader
- pinMode(BUZZER, OUTPUT);
- pinMode(DOORLED, OUTPUT);
- digitalWrite(BUZZER, BUZZ_OFF);
- digitalWrite(DOORLED, DLED_RED);
-
- Serial.println("DoorLock. Testing WiFi config...");
-
- // if we have no config, enter config mode
- WiFiManager wfm;
- wfm.autoConnect(MANAGER_AP);
-
- // we have config, enable web server
- server.on( "/", handleRoot );
- server.on( "/reset", handleReset );
- server.onFileUpload( handleFileUpload);
- server.on( "/upload", HTTP_GET, handleUploadRequest);
- server.on( "/upload", HTTP_POST, handleUploadComplete);
- server.on( "/download", handleDownload );
- server.onNotFound( handleNotFound );
- server.begin();
-
- // advertise we exist via MDNS
- if (!MDNS.begin("doorlock")) {
- Serial.println("Error setting up MDNS responder.");
- } else {
- MDNS.addService("http", "tcp", 80);
- }
-
- // enable internal flash filesystem
- SPIFFS.begin();
-
- // init wiegand keyfob reader
- Serial.println("Starting Wiegand test reader");
- wg.begin(WD0, WD0, WD1, WD1);
-
- // setup button debounce for the release switch
- release_button.setup(ERELEASE, 20, InputDebounce::PIM_EXT_PULL_DOWN_RES);
+String getTime(time_t when)
+{
+ String ans;
+ int h = hour(when);
+ int m = minute(when);
+ int s = second(when);
+
+ if (h<10) ans += "0";
+ ans += String(h) + ":";
+ if (m<10) ans += "0";
+ ans += String(m) + ":";
+ if (s<10) ans += "0";
+ ans += String(s);
+
+ return ans;
}
-unsigned long locktime = 0;
-
-
-void unlock_door()
+String getDate(time_t when)
{
- digitalWrite(DOORLED, DLED_GREEN);
- digitalWrite(MOSFET, LOCK_OPEN);
- if (locktime == 0) {
- digitalWrite(BUZZER, BUZZ_ON);
- delay(100);
- digitalWrite(BUZZER, BUZZ_OFF);
- delay(50);
- digitalWrite(BUZZER, BUZZ_ON);
- delay(100);
- digitalWrite(BUZZER, BUZZ_OFF);
- }
- locktime = millis();
+ String ans;
+
+ ans += String(year(when)) + "-" + String(month(when)) + "-" + String(day(when));
+ return ans;
}
+
int sanityCheck(const char * filename)
{
int count = 0;
return answer;
}
-void loop() {
- unsigned long now = millis();
+void logEntry(time_t when, uint32_t card)
+{
+ unsigned char entry[8];
+
+ File f = SPIFFS.open(LOG_FILE, "a");
+ if (!f) {
+ Serial.println("Error opening log file");
+ return;
+ }
+
+ // compose the record to write
+ ((uint32_t *)entry)[0] = when;
+ ((uint32_t *)entry)[1] = card;
+ f.write(entry, 8);
+ f.close();
+}
+
+String printLog(int html, int last)
+{
+ String out;
+ File f = SPIFFS.open(LOG_FILE, "r");
+ if (!f) return String("Could not open log file");
+
+ unsigned char entry[8];
+ uint32_t * data = (uint32_t *)entry;
+
+ if (last != 0) {
+ // print only the last N items
+ int pos = f.size() / 8;
+ if (pos > last) pos -= last; else pos = 0;
+ f.seek( pos * 8, SeekSet);
+ if (html) out += "Last " + String(last) + " log entries :-";
+ }
+ if (html) out += "<ul>";
+
+ while (f.available()) {
+ f.read(entry, 8);
+ if (html) out += "<li> ";
+ out += getDate( data[0] );
+ out += " ";
+ out += getTime( data[0] );
+ if (html) out += " - "; else out += "," + String(data[1]) + ",";
+
+ if (data[1] == 0) {
+ if (html) out += "<i>";
+ out += "Emergency Release";
+ if (html) out += "</i>";
+ } else {
+ String whom = findKeyfob(data[1]);
+ if (whom == "") {
+ if (html) out += "<i>by ";
+ out += "Unknown keyfob";
+ if (html) out += "</i>";
+ } else {
+ out += whom;
+ }
+ if (html) out += " (" + String(data[1]) + ")";
+ }
+ out += "\n";
+ }
+ f.close();
+ if (html) out += "</ul>";
+ return out;
+}
+
+
+static InputDebounce release_button;
+
+/********************************************
+ * Main setup routine
+ */
+void setup() {
+ // some serial, for debug
+ Serial.begin(115200);
+
+ // The lock mechanism, set HIGH to turn on and connect ground to output pin
+ pinMode(MOSFET, OUTPUT);
+ digitalWrite(MOSFET, LOCK_CLOSE);
+
+ // lock sense microswitch
+ pinMode(SENSE, INPUT_PULLUP);
+
+ // emergency door release switch
+ pinMode(ERELEASE, INPUT);
+
+ // indicators on the keyfob reader
+ pinMode(BUZZER, OUTPUT);
+ pinMode(DOORLED, OUTPUT);
+ digitalWrite(BUZZER, BUZZ_OFF);
+ digitalWrite(DOORLED, DLED_RED);
+ Serial.println("DoorLock. Testing WiFi config...");
+
+ // if we have no config, enter config mode
+ WiFiManager wfm;
+ wfm.autoConnect(MANAGER_AP);
+
+ // we have config, enable web server
+ server.on( "/", handleRoot );
+ server.on( "/reset", handleReset );
+ server.on( "/download", handleDownload );
+ server.on( "/wipelog", handleWipelog );
+ server.on( "/download_logfile", handleDownloadLogfile );
+ server.onFileUpload( handleFileUpload);
+ server.on( "/upload", HTTP_GET, handleUploadRequest);
+ server.on( "/upload", HTTP_POST, handleUploadComplete);
+ server.onNotFound( handleNotFound );
+ server.begin();
+
+ // advertise we exist via MDNS
+ if (!MDNS.begin("doorlock")) {
+ Serial.println("Error setting up MDNS responder.");
+ } else {
+ MDNS.addService("http", "tcp", 80);
+ }
+
+ // enable internal flash filesystem
+ SPIFFS.begin();
+
+ // init wiegand keyfob reader
+ Serial.println("Starting Wiegand test reader");
+ wg.begin(WD0, WD0, WD1, WD1);
+
+ // setup button debounce for the release switch
+ release_button.setup(ERELEASE, 20, InputDebounce::PIM_EXT_PULL_DOWN_RES);
+
+ // listener port for replies from NTP
+ udp.begin(localPort);
+ setSyncProvider(ntp_fetch);
+}
+
+unsigned long locktime = 0;
+
+
+void unlock_door()
+{
+ digitalWrite(DOORLED, DLED_GREEN);
+ digitalWrite(MOSFET, LOCK_OPEN);
+ if (locktime == 0) {
+ digitalWrite(BUZZER, BUZZ_ON);
+ delay(100);
+ digitalWrite(BUZZER, BUZZ_OFF);
+ delay(50);
+ digitalWrite(BUZZER, BUZZ_ON);
+ delay(100);
+ digitalWrite(BUZZER, BUZZ_OFF);
+ }
+ locktime = millis();
+}
+
+
+void loop() {
// is the latch held open ?
if (locktime != 0) {
- if (locktime + LATCH_HOLD < now) {
+ if (locktime + LATCH_HOLD < millis()) {
locktime = 0;
digitalWrite(MOSFET, LOCK_CLOSE);
digitalWrite(DOORLED, DLED_RED);
// handle web requests
server.handleClient();
- unsigned int ertime = release_button.process(now);
+ unsigned int ertime = release_button.process(millis());
unsigned int count = release_button.getStateOnCount();
static unsigned last_count = 0;
if (ertime > 0) {
last_count = count;
Serial.println("Door Release button triggered.");
unlock_door();
+ logEntry(now(), 0);
} else {
// buttons is still pressed, do nothing
}
Serial.print("Unlocking door for ");
Serial.println(who);
unlock_door();
+ logEntry(now(), code);
}
}