New code version which impliments a Wiegand card reader instead
[doorlock_v1.git] / door_wiegand.ino
CommitLineData
f1139a83
B
1#include <InputDebounce.h>
2#include <Wiegand.h>
3#include <ESP8266WiFi.h>
4#include <DNSServer.h>
5#include <ESP8266WebServer.h>
6#include <WiFiManager.h>
7#include <ESP8266mDNS.h>
8#include <FS.h>
9
10// MOSFET for door lock activation
11// ON/HIGH == ground the output screw terminal
12#define MOSFET 16
13
14// Pin for LED / WS2812
15#define STATUSLED 2
16
17// Wiegand keyfob reader pins
18#define WD0 12
19#define WD1 13
20
21// Door lock sense pin
22#define SENSE 14
23
24// emergency release switch
25#define ERELEASE 15
26
27// Buzzer/LED on keyfob control
28#define BUZZER 4 // Gnd to beep
29#define DOORLED 5 // low = green, otherwise red
30
31// orientation of some signals
32#define DLED_GREEN LOW
33#define DLED_RED HIGH
34#define LOCK_OPEN HIGH
35#define LOCK_CLOSE LOW
36#define BUZZ_ON LOW
37#define BUZZ_OFF HIGH
38
39
40
41/***********************
42 * Configuration parameters
43 */
44// AP that it will apoear as for configuration
45#define MANAGER_AP "DoorLock"
46
47// Credentials required to reset or upload new info
48#define www_username "admin"
49#define www_password "wibble"
50
51// files to store card/fob data in
52#define CARD_TMPFILE "/cards.tmp"
53#define CARD_FILE "/cards.dat"
54
55// how long to hold the latch open in millis
56#define LATCH_HOLD 5000
57
58/***************************
59 * code below
60 */
61WIEGAND wg;
62ESP8266WebServer server(80);
63
64
65int fileSize(const char *filename)
66{
67 int ret = -1;
68 File file = SPIFFS.open(filename, "r");
69 if (file) {
70 ret = file.size();
71 file.close();
72 }
73 return ret;
74}
75
76void handleRoot()
77{
78 char mtime[16];
79 int sec = millis() / 1000;
80 int mi = sec / 60;
81 int hr = mi / 60;
82 int day = hr / 24;
83
84 snprintf(mtime, 16, "%dd %02d:%02d:%02d", day, hr % 24, mi % 60, sec % 60);
85
86 String out = "<html>\
87 <head>\
88 <title>Door Lock</title>\
89 <style>\
90 body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; }\
91 </style>\
92 </head>\
93 <body>\
94 <h1>Door Lock!</h1>\
95 <p>Uptime: " + (String)mtime + "</p>";
96
97 if (SPIFFS.exists(CARD_FILE)) {
98
99 out += "<p>Cardfile: " + String(CARD_FILE) + " is " + fileSize(CARD_FILE) + " bytes";
100 int count = sanityCheck(CARD_FILE);
101 if (count <= 0) {
102 out += ", in an invalid file";
103 } else {
104 out += ", contains " + String(count) + " keyfob IDs";
105 out += " - <a href=\"/download\">Download</a>";
106 }
107
108 out += ".</p>";
109 }
110
111 out += "<ul>\
112 <li><a href=\"/reset\">Reset Configuration</a>\
113 <li><a href=\"/upload\">Upload Cardlist</a>";
114
115 FSInfo fs_info;
116 if (SPIFFS.info(fs_info)) {
117 out += "<li><a href=\"/format\">Format SPIFFS</a> - Size:"+String(fs_info.totalBytes)+" Used:"+String(fs_info.usedBytes);
118 }
119
120 out += "<li>Lock is currently ";
121 if (digitalRead(SENSE) == HIGH) out += "LOCKED"; else out += "OPEN";
122
123 out += "</ul>\
124 </body>\
125</html>";
126
127 server.send( 200, "text/html", out);
128}
129
130void handleDownload()
131{
132 if (!server.authenticate(www_username, www_password))
133 return server.requestAuthentication();
134
135 if (!SPIFFS.exists(CARD_FILE)) {
136 server.send(404, "text/plain", "Card file not found");
137 return;
138 }
139
140 File f = SPIFFS.open(CARD_FILE, "r");
141 server.streamFile(f, "text/csv");
142 f.close();
143}
144
145void handleNotFound() {
146 String out = "File Not found\n\n";
147 server.send(404, "text/plain", out);
148}
149
150// User wants to reset config
151void handleReset() {
152 if (!server.authenticate(www_username, www_password))
153 return server.requestAuthentication();
154
155 server.send(200, "text/plain", "Rebooting to config manager...\n\n");
156
157 WiFiManager wfm;
158 wfm.resetSettings();
159 WiFi.disconnect();
160 ESP.reset();
161 delay(5000);
162}
163
164void handleUploadRequest() {
165 String out = "<html><head><title>Upload Keyfob list</title></head><body>\
166<form enctype=\"multipart/form-data\" action=\"/upload\" method=\"POST\">\
167<input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"32000\" />\
168Select file to upload: <input name=\"file\" type=\"file\" />\
169<input type=\"submit\" value=\"Upload file\" />\
170</form></body></html>";
171 server.send(200, "text/html", out);
172}
173
174File uploadFile;
175
176String upload_error;
177int upload_code = 200;
178
179void handleFileUpload()
180{
181 if (server.uri() != "/upload") return;
182
183 if (!server.authenticate(www_username, www_password))
184 return server.requestAuthentication();
185
186 HTTPUpload& upload = server.upload();
187 if (upload.status == UPLOAD_FILE_START) {
188 upload_error = "";
189 upload_code = 200;
190 uploadFile = SPIFFS.open(CARD_TMPFILE, "w");
191 if (!uploadFile) {
192 upload_error = "error opening file";
193 Serial.println("Opening tmpfile failed!");
194 upload_code = 403;
195 }
196 }else
197 if (upload.status == UPLOAD_FILE_WRITE) {
198 if (uploadFile) {
199 if (uploadFile.write(upload.buf, upload.currentSize) != upload.currentSize) {
200 upload_error = "write error";
201 upload_code = 409;
202 }
203 }
204 }else
205 if (upload.status == UPLOAD_FILE_END) {
206 if (uploadFile) {
207 uploadFile.close();
208 }
209 }
210}
211
212void handleUploadComplete()
213{
214 String out = "Upload finished.";
215 if (upload_code != 200) {
216 out += "Error: "+upload_error;
217 } else {
218 out += " Success";
219 // upload with no errors, replace old one
220 SPIFFS.remove(CARD_FILE);
221 SPIFFS.rename(CARD_TMPFILE, CARD_FILE);
222 }
223 server.send(upload_code, "text/plain", out);
224}
225
226
227void returnOK() {
228 server.send(200, "text/plain", "");
229}
230
231
232static InputDebounce release_button;
233
234void setup() {
235 // some serial, for debug
236 Serial.begin(115200);
237
238 // The lock mechanism, set HIGH to turn on and connect ground to output pin
239 pinMode(MOSFET, OUTPUT);
240 digitalWrite(MOSFET, LOCK_CLOSE);
241
242 // lock sense microswitch
243 pinMode(SENSE, INPUT_PULLUP);
244
245 // emergency door release switch
246 pinMode(ERELEASE, INPUT);
247
248 // indicators on the keyfob reader
249 pinMode(BUZZER, OUTPUT);
250 pinMode(DOORLED, OUTPUT);
251 digitalWrite(BUZZER, BUZZ_OFF);
252 digitalWrite(DOORLED, DLED_RED);
253
254 Serial.println("DoorLock. Testing WiFi config...");
255
256 // if we have no config, enter config mode
257 WiFiManager wfm;
258 wfm.autoConnect(MANAGER_AP);
259
260 // we have config, enable web server
261 server.on( "/", handleRoot );
262 server.on( "/reset", handleReset );
263 server.onFileUpload( handleFileUpload);
264 server.on( "/upload", HTTP_GET, handleUploadRequest);
265 server.on( "/upload", HTTP_POST, handleUploadComplete);
266 server.on( "/download", handleDownload );
267 server.onNotFound( handleNotFound );
268 server.begin();
269
270 // advertise we exist via MDNS
271 if (!MDNS.begin("doorlock")) {
272 Serial.println("Error setting up MDNS responder.");
273 } else {
274 MDNS.addService("http", "tcp", 80);
275 }
276
277 // enable internal flash filesystem
278 SPIFFS.begin();
279
280 // init wiegand keyfob reader
281 Serial.println("Starting Wiegand test reader");
282 wg.begin(WD0, WD0, WD1, WD1);
283
284 // setup button debounce for the release switch
285 release_button.setup(ERELEASE, 20, InputDebounce::PIM_EXT_PULL_DOWN_RES);
286}
287
288unsigned long locktime = 0;
289
290
291void unlock_door()
292{
293 digitalWrite(DOORLED, DLED_GREEN);
294 digitalWrite(MOSFET, LOCK_OPEN);
295 if (locktime == 0) {
296 digitalWrite(BUZZER, BUZZ_ON);
297 delay(100);
298 digitalWrite(BUZZER, BUZZ_OFF);
299 delay(50);
300 digitalWrite(BUZZER, BUZZ_ON);
301 delay(100);
302 digitalWrite(BUZZER, BUZZ_OFF);
303 }
304 locktime = millis();
305}
306
307int sanityCheck(const char * filename)
308{
309 int count = 0;
310
311 File f = SPIFFS.open(filename, "r");
312 if (!f) {
313 Serial.print("Sanity Check: Could not open ");
314 Serial.println(filename);
315 return -1;
316 }
317 while (f.available()) {
318 char c = f.peek();
319 // skip comment lines
320 if (c == '#') {
321 f.find("\n");
322 continue;
323 }
324
325 String wcode = f.readStringUntil(',');
326 String wname = f.readStringUntil('\n');
327 unsigned int newcode = wcode.toInt();
328
329 if (newcode != 0) count++;
330 }
331 f.close();
332
333 return count;
334}
335
336String findKeyfob(unsigned int code)
337{
338 File f = SPIFFS.open(CARD_FILE, "r");
339 if (!f) {
340 Serial.println("Error opening card file " CARD_FILE);
341 return "";
342 }
343
344 String answer = "";
345 while (f.available()) {
346 char c = f.peek();
347 // skip comment lines
348 if (c == '#') {
349 f.find("\n");
350 continue;
351 }
352
353 String wcode = f.readStringUntil(',');
354 String wname = f.readStringUntil('\n');
355
356 unsigned int newcode = wcode.toInt();
357
358/* debug
359 Serial.print("Line: code='");
360 Serial.print(wcode);
361 Serial.print("' (");
362 Serial.print(newcode);
363 Serial.print(") name='");
364 Serial.print(wname);
365 Serial.print("'");
366*/
367 if (code == newcode) {
368 // Serial.println(" - FOUND IT");
369 answer = wname;
370 break;
371 }
372 //Serial.println();
373 }
374 f.close();
375 return answer;
376}
377
378void loop() {
379 unsigned long now = millis();
380
381 // is the latch held open ?
382 if (locktime != 0) {
383 if (locktime + LATCH_HOLD < now) {
384 locktime = 0;
385 digitalWrite(MOSFET, LOCK_CLOSE);
386 digitalWrite(DOORLED, DLED_RED);
387 }
388 }
389 // handle web requests
390 server.handleClient();
391
392 unsigned int ertime = release_button.process(now);
393 unsigned int count = release_button.getStateOnCount();
394 static unsigned last_count = 0;
395 if (ertime > 0) {
396 if (count != last_count) {
397 last_count = count;
398 Serial.println("Door Release button triggered.");
399 unlock_door();
400 } else {
401 // buttons is still pressed, do nothing
402 }
403 }
404
405 // handle card swipes
406 if (wg.available()) {
407 unsigned long code = wg.getCode();
408
409 Serial.print("wiegand HEX = ");
410 Serial.print(code,HEX);
411 Serial.print(", DECIMAL= ");
412 Serial.print(code);
413 Serial.print(", TYPE W");
414 Serial.println(wg.getWiegandType());
415
416 String who = findKeyfob(code);
417 if (who != NULL) {
418 Serial.print("Unlocking door for ");
419 Serial.println(who);
420 unlock_door();
421 }
422 }
423
424}