· 4 years ago · Dec 17, 2020, 04:20 PM
1/**\file*/
2#include <Wiegand.h>
3#include <string.h>
4#include <SPI.h>
5#include <SD.h>
6#include <Ethernet2.h>
7#include <sha1.h>
8#include <ArduinoJson.h>
9#include <NTPClient.h>
10#include <time.h>
11
12// enum {OPEN, CLOSED} lockState; TODO: maybe later...
13
14// These are the pins connected to the Wiegand D0 and D1 signals.
15// Ensure your board supports external Interruptions on these pins
16#define PIN_D0 3 /// D0 Wiegand pin
17#define PIN_D1 2 /// D1 Wiegand pin
18
19#define PIN_T 31 // Pin connected to relay. previously 7
20#define PIN_BUTT 30 // Pin connected to button
21#define PIN_LED 5 // Pin connected to led irrelevant ?
22#define PIN_SPK 6 // Pin connected to spk irrelevant ?
23
24#define OPEN 10 // Why this numbers ???
25#define CLOSED 11 // Why this numbers ???
26
27#define SPK_DELAY 200 // Duration of speaker sound
28#define ECHO_DELAY 600 // Count of loop cycles before sending echo
29
30#define KEYCOUNT 9 // Count of keys in key sequence
31#define MAXKEYCOUNT 9 // Max amount of keys based on numpad model
32
33#define VERBOSE
34
35//----------Network settings----------
36byte mac[] = {0x90, 0xA2, 0xDA, 0x12, 0x00, 0x02}; // NIC mac
37char* remoteServer = "lock-app.herokuapp.com"; // Server IP
38int port = 80; // Server port
39
40Wiegand wiegand; /// The object that handles the wiegand protocol
41EthernetClient client; /// The object that handles the ethernet
42
43EthernetServer server(8000);
44EthernetClient srvClient;
45EthernetUDP UDP;
46NTPClient timeClient(UDP);//, "192.168.1.12", 10800);
47
48//----------Global variables----------
49char* lockID = "1737f09e-d454-45e7-9b15-27c2f07bab36"; //7a7544c4-eba2-11ea-adc1-0242ac120002"; /// Lock ID
50char* masterKey = "5962813a-2b27-422f-8ad8-84b5fd34ca8f"; // Master Key
51char* versionSW = "1.20"; /// Version of software
52char* versionAPI = "1"; /// Version of lock API on backend
53bool lockState = 0; /// Variable to set HIGH or LOW to PIN
54bool access = 0; /// Access flag for shitcode in answer from server check
55bool connection_status = false; /// Connection status
56bool buttonActive = false; /// Variable to save button state
57bool longPressActive = false; /// Variable to save unlock mode state
58unsigned long buttonTimer = 0; /// Variable to save start of pushing button
59unsigned long last_switch = 0; /// Variable to save time of last switch
60unsigned long longPressTime = 2000; /// Variable to save duration of long press
61unsigned int systemState = CLOSED; /// Variable to save state of door
62unsigned long doorOpenDuration = 3000; /// Variable to save door open time
63unsigned int mainClock = 0; /// Counter of loop for sending echo
64uint32_t panelTimeout = 5000; /// The timeout after which the code will be wiped
65uint32_t panelTimeoutTimer = 0; /// Panel timeout timer
66uint8_t basicDelay = 100;
67char keySequence[KEYCOUNT+1]; /// Readed keys from numpad
68unsigned int keyCounter = 0; /// Count of readed keys from numpad
69char* logFilename = "log.log"; /// Log filename
70File logFile; /// Log file
71
72
73// Master cards, do not require network connection to open the lock
74char* mastercards[10];
75int cardSize = 0;
76void setMasterCards() {
77 mastercards[cardSize++] = "0E72B8"; // Денис Александрович Королев
78 mastercards[cardSize++] = "2079FA"; // Петр Владимирович Рыбаков
79 mastercards[cardSize++] = "974958"; // Петр Владимирович Рыбаков
80 //mastercards[cardSize++] = "98B319"; // Матвеев Данил
81 //mastercards[cardSize++] = "28ABF8"; // Security
82 //mastercards[cardSize++] = "70FE1E"; // Cleaner
83 //mastercards[cardSize++] = "7B563C"; // Cleaner
84 //ыmastercards[cardSize++] = "5BECE5"; // Диспетчер
85}
86char* masterKeySequence = "294294"; // Master key to open lock with numpad
87
88
89/**
90* @brief Get ISO formatted time from NTP client
91*
92* Get ISO formatted time from NTP client
93*
94* @param timeClient - NTP client pointer
95*
96* @return char with time
97*/
98char* getISOFormattedTime(NTPClient* timeClient) {
99// AVR implementation of localtime() uses midn. Jan 1 2000 as epoch
100// so UNIX_OFFSET has to be applied to time returned by getEpochTime()
101// More info here: https://www.nongnu.org/avr-libc/user-manual/group__avr__time.html
102// https://forum.arduino.cc/index.php?topic=567637.0
103 timeClient->update();
104 time_t rawtime = timeClient->getEpochTime() - UNIX_OFFSET;
105 struct tm *ti;
106 static char buf[] = "0000-00-00T00:00:00Z";
107 ti = localtime (&rawtime);
108 //Serial.println(timeClient->getEpochTime());
109 if (strftime(buf, sizeof buf, "%FT%TZ", ti)) {
110 // Serial.print("Succsessfully formatted: ");
111 //Serial.println(buf);
112 } else {
113 //Serial.println("strftime failed");
114 }
115 //buf[strlen(buf)-1] = '\0';
116 return buf;
117}
118
119/**
120* @brief Print line to file and serial
121*
122* If logFile then print data to file;
123* If serial then print data to serial without VERBOSE;
124*
125* @param message - string/char/int/char* with data
126* @param serial=0 - bool for print to serial without VERBOSE
127*
128* @return none
129*/
130void printData(char* message, bool serial = 0) {
131 if (logFile) {
132 logFile.print(message);
133 if (message[strlen(message)-1] == '\n') {
134 logFile.print(getISOFormattedTime(&timeClient));
135 logFile.print('\n');
136 }
137 }
138 if (serial)
139 Serial.print(message);
140 else {
141#ifdef VERBOSE
142 Serial.print(message);
143#endif
144 }
145}
146
147void printData(char message, bool serial = 0) {
148 if (logFile)
149 logFile.print(message);
150 if (serial)
151 Serial.print(message);
152 else {
153#ifdef VERBOSE
154 Serial.print(message);
155#endif
156 }
157}
158
159void printData(int message, bool serial = 0) {
160 if (logFile)
161 logFile.print(message);
162 if (serial)
163 Serial.print(message);
164 else {
165#ifdef VERBOSE
166 Serial.print(message);
167#endif
168 }
169}
170
171void printData(String message, bool serial = 0) {
172 if (logFile)
173 logFile.print(message);
174 if (serial)
175 Serial.print(message);
176 else {
177#ifdef VERBOSE
178 Serial.print(message);
179#endif
180 }
181}
182
183void printData(IPAddress message, bool serial = 0) {
184 if (logFile)
185 logFile.print(message);
186 if (serial)
187 Serial.print(message);
188 else {
189#ifdef VERBOSE
190 Serial.print(message);
191#endif
192 }
193}
194
195/**
196* @brief Close and open log file
197*
198* Close and open log file
199*
200* @return none
201*/
202void reopenLog() {
203 if (logFile)
204 logFile.close();
205 logFile = SD.open(logFilename, FILE_WRITE);
206}
207
208/**
209* @brief Try to connect to remote backend server
210*
211* If connection is success, then connection_status is true, else false
212*
213* @return none
214*/
215void connectToServer() {
216 // Test network connection
217 if (client.connect(remoteServer, port)) { // Connection successful
218 if (!connection_status)
219 printData("Get server connection\n", 1);
220 connection_status = true;
221 } else { // Connection failed
222 if (connection_status)
223 printData("No server connection\n", 1);
224 connection_status = false;
225 client.stop();
226 }
227 if (connection_status)
228 Serial.println("Connection +++");
229 else
230 Serial.println("Connection ---");
231}
232
233/**
234* @brief Sending lockID to server
235*
236* POST request to backend server with lockID, masterKey & versionSW
237*
238* @return none
239*/
240void sendLockID2Server() {
241 String data = "{\"uuid\":\"";
242 data.concat(lockID);
243 data.concat("\",\"master\":\"");
244 data.concat(masterKey);
245 data.concat("\",\"version\":\"");
246 data.concat(versionSW);
247 data.concat("\"}");
248 client.flush();
249 client.print("POST /lock-api/v");
250 client.print(versionAPI);
251 client.println("/register-lock/ HTTP/1.1");
252 sendHostHeader();
253 client.println("Content-Type: application/json");
254 client.println("Connection:close");
255 client.print("Content-Length:");
256 client.println(data.length());
257 client.println();
258 client.print(data);
259 client.flush();
260}
261
262/**
263* @brief Change lock state
264*
265* Change lock state with led and beep signals
266*
267* @return none
268*/
269void changeLockState(int newSystemState = 0) {
270 switch (newSystemState) {
271 case CLOSED :
272 systemState = CLOSED; // Set close mode
273 lockState = HIGH;
274 digitalWrite(PIN_T, lockState);
275 printData("Closed mode ON\n", 1);
276 digitalWrite(PIN_LED, HIGH);
277 digitalWrite(PIN_SPK, LOW);
278 delay(SPK_DELAY);
279 digitalWrite(PIN_SPK, HIGH);
280 break;
281 case OPEN :
282 systemState = OPEN; // Set open mode
283 lockState = LOW;
284 digitalWrite(PIN_T, lockState);
285 printData("Open mode ON\n", 1);
286 digitalWrite(PIN_LED, LOW);
287 digitalWrite(PIN_SPK, LOW);
288 delay(SPK_DELAY);
289 digitalWrite(PIN_SPK, HIGH);
290 break;
291 default :
292 if (systemState == CLOSED) {
293 systemState = OPEN; // Set open mode
294 lockState = LOW;
295 digitalWrite(PIN_T, lockState);
296 printData("Open mode ON\n", 1);
297 digitalWrite(PIN_LED, LOW);
298 digitalWrite(PIN_SPK, LOW);
299 delay(SPK_DELAY);
300 digitalWrite(PIN_SPK, HIGH);
301 } else {
302 systemState = CLOSED; // Set close mode
303 lockState = HIGH;
304 digitalWrite(PIN_T, lockState);
305 printData("Closed mode ON\n", 1);
306 digitalWrite(PIN_LED, HIGH);
307 digitalWrite(PIN_SPK, LOW);
308 delay(SPK_DELAY);
309 digitalWrite(PIN_SPK, HIGH);
310 }
311 break;
312 }
313}
314
315/**
316* @brief Processing button pressing
317*
318* If button is pressed shortly, open lock;
319* If button is pressed longly, call changeLockState;
320*
321* @return none
322*/
323void buttonProcessing() {
324 if (!digitalRead(PIN_BUTT)) {
325 if (buttonActive == false) {
326 buttonActive = true;
327 buttonTimer = millis(); // Save time when button is pressed
328 }
329 if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {
330 longPressActive = true; // If it's long press
331 printData("Long press detected, switching mode\n");
332 changeLockState();
333 }
334 } else { // If it's not long press
335 if (buttonActive == true) {
336 if (longPressActive == true) {
337 longPressActive = false; // It's not long press
338 } else if (systemState == CLOSED) {
339 lockState = LOW; // Unlock door by button
340 digitalWrite(PIN_T, lockState);
341 printData("Door unlocked by button\n", 1);
342 last_switch = millis();
343 digitalWrite(PIN_LED, LOW);
344 digitalWrite(PIN_SPK, LOW);
345 delay(SPK_DELAY);
346 digitalWrite(PIN_SPK, HIGH);
347 }
348 buttonActive = false;
349 }
350 }
351}
352
353/**
354* @brief Updating lock state
355*
356* If duration > 0 then open lock;
357* If duration = 0 or not set then check time after door may be opened;
358* If beep then beep;
359* If led then led;
360*
361* @param duration - duration for opening lock, default 0
362* @param beep - is beep signal is needed, default 1
363* @param led - is led signal is needed, default 1
364*
365* @return none
366*/
367void updateLock(int duration = 0, bool beep = 1, bool led = 1) {
368 if (duration) {
369 if (systemState == CLOSED) {
370 printData("Open lock for ");
371 printData(duration);
372 printData(" seconds\n");
373 lockState = LOW;
374 digitalWrite(PIN_T, lockState);
375 last_switch = millis();
376 if (led)
377 digitalWrite(PIN_LED, LOW);
378 if (beep) {
379 digitalWrite(PIN_SPK, LOW);
380 delay(SPK_DELAY);
381 digitalWrite(PIN_SPK, HIGH);
382 }
383 } else {
384 printData("Door already open\n");
385 }
386 } else {
387 if (systemState == CLOSED) { // If door neccessary to be closed
388 if (millis() - last_switch > doorOpenDuration && lockState == LOW) {
389 lockState = HIGH; // and time from last switch is more
390 digitalWrite(PIN_T, lockState); // than door open time
391 if (led)
392 digitalWrite(PIN_LED, HIGH);
393 if (beep)
394 digitalWrite(PIN_SPK, HIGH);
395 printData("Door locked\n"); // then close the door
396 }
397 }
398 }
399}
400
401/**
402* @brief Open lock
403*
404* Open lock for duration or doorOpenDuration, calling updateLock;
405*
406* @param duration - duration for opening lock, default doorOpenDuration
407* @param beep - is beep signal is needed, default 1
408* @param led - is led signal is needed, default 1
409*
410* @return none
411*/
412void openLock(int duration = doorOpenDuration, bool beep = 1, bool led = 1) {
413 updateLock(duration, beep, led);
414}
415
416/**
417* @brief String to integer
418*
419* Convert string to integer to compare in switch
420*
421* @param str - input string
422* @param h - from which symbol start
423*
424* @return output converted integer
425*/
426constexpr unsigned int str2int(const char* str, int h = 0)
427{
428 return !str[h] ? 5381 : (str2int(str, h+1) * 33) ^ str[h];
429}
430
431/**
432* @brief Check command from server
433*
434* Check command which is send from backend server and send web answer
435*
436* @param command - command from server
437* @param key - master key from server
438*
439* @return none
440*/
441void checkServerCommand(char* command, char* key, char* params) {
442 if (strcmp(key, masterKey) == 0) {
443 switch (str2int(command)) {
444 case str2int("openLock") :
445 openLock();
446 printData("---Response-200---\n");
447 srvClient.println("HTTP/1.1 200 OK");
448 srvClient.println("Content-Type: text/html");
449 srvClient.println("Connection: close");
450 srvClient.println();
451 break;
452 case str2int("changeState") :
453 if (strlen(params))
454 changeLockState((params[0]-'0')*10+params[1]-'0');
455 else
456 changeLockState();
457 printData("---Response-200---\n");
458 srvClient.println("HTTP/1.1 200 OK");
459 srvClient.println("Content-Type: text/html");
460 srvClient.println("Connection: close");
461 srvClient.println();
462 srvClient.print(systemState);
463 break;
464 default :
465 printData("---Response-404---\n");
466 srvClient.println("HTTP/1.1 404 Not found");
467 srvClient.println("Content-Type: text/html");
468 srvClient.println("Connection: close");
469 srvClient.println();
470 srvClient.print("Error: command not found");
471 }
472 } else {
473 printData("---Response-404---\n");
474 srvClient.println("HTTP/1.1 404 Not found");
475 srvClient.println("Content-Type: text/html");
476 srvClient.println("Connection: close");
477 srvClient.println();
478 srvClient.print("Error: key not found");
479 }
480}
481
482/**
483* @brief Parse JSON from server
484*
485* Parse JSON with command which is send from backend server & call checkServerCommand
486*
487* @param str - JSON string from server
488*
489* @return none
490*/
491void parseJSONCommand(char* str) {
492//…an Ethernet connection
493//deserializeJson(doc, ethernetClient)
494 DynamicJsonDocument doc(256);
495 deserializeJson(doc, str);
496 printData("---Json-begin---\n", 1);
497 printData(String(doc["command"].as<char*>()), 1);
498 printData("\n", 1);
499 printData(String(doc["key"].as<char*>()), 1);
500 printData("\n", 1);
501 printData("---Json-end--\n-", 1);
502 checkServerCommand(doc["command"].as<char*>(), doc["key"].as<char*>(), doc["params"].as<char*>());
503}
504
505/**
506* @brief Read message from server
507*
508* Check if there's a message from backend server and call parseJSONCommand
509*
510* @return none
511*/
512void readMessageFromServer() {
513 if (connection_status) { // If connected to a server
514 char str[256];
515 memset(str, '\0', 256*sizeof(char));
516 int i = 0;
517 int messageBegin = 0;
518 srvClient = server.available();
519 boolean currentLineIsBlank = true;
520 while (srvClient.connected()) {
521 if (srvClient.available()) {
522 char c = srvClient.read();
523 Serial.println("@"); printData(c, 1);
524 if (c == '\n') {
525 // you're starting a new line
526 if (currentLineIsBlank) {
527 printData("---Start-message---\n", 1);
528 messageBegin = 1;
529 } else
530 currentLineIsBlank = true;
531 } else if (c != '\r') {
532 // you've gotten a character on the current line
533 currentLineIsBlank = false;
534 if (messageBegin)
535 str[i++] = c;
536 }
537 } else {
538 if (messageBegin) {
539 str[i++] = '\0';
540 }
541 printData("\n", 1);
542 printData("---Message-begin---\n", 1);
543 printData(str, 1);
544 printData("\n", 1);
545 printData(int(strlen(str)), 1);
546 printData("\n", 1);
547 printData("---Message-end---\n", 1);
548 parseJSONCommand(str);
549 break;
550 }
551 }
552 srvClient.stop();
553 srvClient.flush();
554 }
555}
556
557/**
558* @brief Send echo to server
559*
560* Send echo signal by GET request to backend server
561*
562* @return none
563*/
564void sendEcho2Server() {
565 if (connection_status) { // If connected to a server
566 char str[30];
567 // Send GET request
568 printData("We connected\n");
569 client.print("POST /lock-api/v"); // sure POST?
570 client.print(versionAPI);
571 client.print("/lock");
572 client.print("?id=");
573
574 // Get hash of lock ID
575 Sha1.init();
576 Sha1.print(lockID);
577 uint8_t* hash = Sha1.result();
578 char hashStr[40];
579 int k = 0;
580 for (int i = 0; i < 20; i++) {
581 sprintf(str, "%X", hash[i] >> 4);
582 hashStr[k++] = str[0];
583 sprintf(str, "%X", hash[i] & 0xF);
584 hashStr[k++] = str[0];
585 }
586 hashStr[40] = '\0';
587 client.print(hashStr);
588 client.println(" HTTP/1.1");
589 sendHostHeader();
590 client.println();
591 client.flush();
592 printData("Data is sent\n");
593 }
594}
595
596/**
597* @brief Initialize Wiegand reader
598*
599* Set frequency for serial output;
600* Disable microSD cardreader SPI;
601* Set pins for LED and SPK;
602* Set master cards;
603* Install listeners on wiegand reader and initialize it;
604* Set wiegand data pins as INPUT and attaches interruptions;
605* Set pin for relay;
606* Set pin for button;
607* Check for test params;
608* Test connection to backend server;
609* Send lock ID to backend server;
610* Send the initial pin state to the wiegand library;
611*
612* @return none
613*/
614void setup() {
615 Serial.begin(9600);
616 Serial.println("Starting...");
617 delay(5000); // Delay for peripherals initialization
618
619 // Disabling the Ethernet shield SD cardreader SPI
620 if (!SD.begin(4))
621 Serial.println("Card failed, or not present");
622 else
623 Serial.println("SD Card initialized");
624 //reopenLog();
625 Ethernet.begin(mac); // Network init
626 server.begin();
627 printData("My IP address: ", 1);
628 printData(Ethernet.localIP(), 1);
629 printData("\n", 1);
630 if (logFile) {
631 logFile.println("Starting...");
632 } else {
633 Serial.print("error opening ");
634 Serial.println(logFilename);
635 }
636
637 // Set pins for LED and SPK
638 pinMode(PIN_LED, OUTPUT);
639 pinMode(PIN_SPK, OUTPUT);
640 digitalWrite(PIN_LED, HIGH);
641 digitalWrite(PIN_SPK, HIGH);
642
643 setMasterCards();
644
645 printData("FOSS Electronic lock controller v");
646 printData(versionSW);
647 printData("\n");
648 printData("Initialization...\n");
649 // Install listeners and initialize Wiegand reader
650 wiegand.onReceive(receivedData, "Card readed: ");
651 wiegand.onStateChange(stateChanged, "State changed: ");
652 wiegand.begin(Wiegand::LENGTH_ANY, true);
653
654 // Intialize pins as INPUT and attach interrupts
655 pinMode(PIN_D0, INPUT);
656 pinMode(PIN_D1, INPUT);
657 attachInterrupt(digitalPinToInterrupt(PIN_D0), pinStateChanged, CHANGE);
658 attachInterrupt(digitalPinToInterrupt(PIN_D1), pinStateChanged, CHANGE);
659
660 // Pin for relay
661 pinMode(PIN_T, OUTPUT);
662 // Pin for button
663 pinMode(PIN_BUTT, INPUT_PULLUP);
664 if (mac[3] == 0xFF && mac[4] == 0xFF && mac[5] == 0xFF)
665 printData("IT'S TEST LOCK MAC\n", 1);
666 if (lockID == "ffffffff-ffff-ffff-ffff-ffffffffffff")
667 printData("IT'S TEST LOCK ID\n", 1);
668 if (remoteServer == "172.18.198.34")
669 printData("IT'S TEST SERVER IP\n", 1);
670 connectToServer();
671 //sendLockID2Server();
672 // Sends the initial pin state to the Wiegand library
673 pinStateChanged();
674 printData("Initialization completed!\n", 1);
675}
676
677/**
678* @brief Main loop function
679*
680* Test connction to backend server;
681* Check for pending messages on wiegand reader;
682* Read message from backend server;
683* Check if button is pressed;
684* Update lock state;
685* Send echo message to backend server;
686*
687* @return none
688*/
689void loop() {
690 //reopenLog();
691 // Wiegand
692 noInterrupts();
693 wiegand.flush();
694 interrupts();
695
696 readMessageFromServer();
697
698 // Reading door button and changing modes
699 buttonProcessing();
700
701 // Timeout lock close after 3 seconds
702 updateLock();
703
704 // Timeout panel code
705 if (panelTimeoutTimer >= panelTimeout / basicDelay) {
706 flushKeySequence();
707 panelTimeoutTimer = 0;
708 }
709 panelTimeoutTimer++;
710
711 if (mainClock == ECHO_DELAY) {
712 mainClock = 0;
713 connectToServer();
714 //sendEcho2Server(); // ECHO
715 }
716 mainClock++;
717 delay(basicDelay); // Sleep a little - this doesn't have to run very often. // WHY?
718}
719
720/**
721* @brief Function which is called when wiegand data pins changed
722*
723* When any of the pins have changed, update the state of the wiegand library
724*
725* @return none
726*/
727void pinStateChanged() {
728 wiegand.setPin0State(digitalRead(PIN_D0));
729 wiegand.setPin1State(digitalRead(PIN_D1));
730}
731
732/**
733* @brief Function which is called when wiegand reader state is changed
734*
735* Notifies when a reader has been connected or disconnected.
736* Instead of a message, the seconds parameter can be anything you want -- Whatever you specify on `wiegand.onStateChange()`
737*
738* @param plugged - bool from wiegand lib
739* @param message - that you set in wiegand.onStateChange()
740*
741* @return none
742*/
743void stateChanged(bool plugged, const char* message) {
744 printData(message, 1);
745 printData(plugged ? "CONNECTED" : "DISCONNECTED", 1);
746 printData("\n", 1);
747}
748
749/**
750* @brief Check if given string is master card
751*
752* Check if given pass ID is in master card array
753
754* @param rez - string with pass ID
755*
756* @return True if is a master card, else False
757*/
758bool checkMasterCard(char*const rez) {
759 for(int i = 0; i < cardSize; ++i)
760 if (strcmp(rez, mastercards[i]) == 0)
761 return true;
762 return false;
763}
764
765/**
766* @brief Clear keySequence
767*
768* Fill keySequence with '\0' symbols and set keyCounter to 0.
769*/
770void flushKeySequence() {
771 for (int i = 0; i < KEYCOUNT+1; ++i)
772 keySequence[i] = '\0';
773 keyCounter = 0;
774}
775
776/**
777* @brief Send host header ("Host: [remoteServer]") to server
778*/
779void sendHostHeader() {
780 client.print("Host: ");
781 client.println(remoteServer);
782}
783
784/**
785* @brief Wait for server to send a response
786*/
787void waitForResponse() {
788 printData("Waiting for response");
789 bool gotResponse = 0;
790 int i = 0;
791 for (i = 0; i < 1000 && !gotResponse ; i++) {
792 delay(100);
793 gotResponse = client.available();
794 printData(".");
795 }
796 if (i == 1000)
797 printData("Connection timeout", 1);
798 printData("\n");
799 printData("Answer returned after ");
800 printData(i);
801 printData(" ms\n");
802 printData("Start listening, is available:");
803 printData(client.available());
804 printData("\n");
805 printData("Incoming message:");
806}
807
808/**
809* @brief Read response from server containing access decision
810*/
811bool readAccessDecision() {
812 char accessDecision = '#';
813 while (client.available()) {
814 char c = client.read();
815 printData(c, 1);
816 if (c == '*') {
817 accessDecision = c;
818 printData("Found *\n");
819 } else if (c == '#') {
820 accessDecision = c;
821 printData("Found #\n");
822 }
823 }
824 printData("\n");
825 printData("End of response\n");
826 client.stop();
827 client.flush();
828 printData("Connection closed\n");
829 if (accessDecision == '#')
830 access = 1;
831 else
832 access = 0;
833}
834
835void toLower(char s[]) {
836 int i = 0;
837 while (s[i]) {
838 if (isalpha(s[i]))
839 s[i] = tolower(s[i]);
840 i++;
841 }
842}
843
844/**
845* @brief Function which is called when data recieved from wiegand reader
846*
847* Notifies when a card or button was read.
848* Instead of a message, the seconds parameter can be anything you want -- Whatever you specify on `wiegand.onReceive()`
849* Convert bits to string;
850* If it's button data, then check for reset button, then check sequence length, then check for master sequence, then send request to backend server;
851* If it's card data, then check for master card, then send request to backend server;
852*
853* @param data - from wiegand lib
854* @param bits - from wiegand lib
855* @param message - that you set in wiegand.onStateChange()
856*
857* @return none
858*/
859void receivedData(uint8_t* data, uint8_t bits, const char* message) {
860 printData(message, 1);
861 panelTimeoutTimer = 0;
862 char str[30];
863 char rez[KEYCOUNT];
864 access = 0;
865 bool done = false;
866
867 // Convert from byte to String
868 int j = 0;
869 uint8_t bytes = (bits + 7) / 8;
870 for (int i = 0; i < bytes; i++) {
871 sprintf(str, "%X", data[i] >> 4);
872 rez[j++] = str[0];
873 sprintf(str, "%X", data[i] & 0xF);
874 rez[j++] = str[0];
875 }
876
877 if (bits == 4) {
878 // printData("---NUMPAD---");
879 if (rez[1] == 'A') { // '*' pushed
880 flushKeySequence();
881 } else if(rez[1] != 'B') // Anything other than '#' pushed
882 keySequence[keyCounter++] = rez[1];
883 if (rez[1] == 'B' || keyCounter == MAXKEYCOUNT) { // '#' pushed or key length reached max
884 // 9 keys is a max for this type of numpad (=> keyCounter == KEYCOUNT causes unexpected behaviour)
885 done = true;
886 keySequence[keyCounter] = '\0';
887 keyCounter = 0;
888 printData("[", 1); printData(keySequence, 1); printData("]\n", 1);
889 if (strcmp(keySequence, masterKeySequence) == 0) {
890 printData("This key sequence is a master-key sequence.\n", 1);
891 access = 1;
892 // Send info here(?)
893 } else {
894 printData("This key sequence is NOT a master-key sequence, trying to contact a server...\n", 1);
895 connectToServer();
896 if (connection_status) { // If connected to a server
897 // Send GET request
898 printData("We connected\n");
899 client.print("GET /lock-api/v");
900 client.print(versionAPI);
901 client.print("/check-access/key/");
902 client.print("?lock=");
903
904 // Get hash of lock ID
905 printData("Lock ID: [", 1); printData(lockID, 1); printData("]\n", 1);
906 Sha1.init();
907 Sha1.print(lockID);
908 uint8_t* hash = Sha1.result();
909 char hashStr[40];
910 int k = 0;
911 for (int i = 0; i < 20; i++) {
912 sprintf(str, "%X", hash[i] >> 4);
913 hashStr[k++] = str[0];
914 sprintf(str, "%X", hash[i] & 0xF);
915 hashStr[k++] = str[0];
916 }
917 hashStr[40] = '\0';
918 printData("Hash of lock: ["); printData(hashStr); printData("]\n");
919 client.print(hashStr);
920 client.print("&password=");
921
922 // Get hash of key sequence
923 Sha1.init();
924 Sha1.print(keySequence);
925 hash = Sha1.result();
926 k = 0;
927 for (int i = 0; i < 20; i++) {
928 sprintf(str, "%X", hash[i] >> 4);
929 hashStr[k++] = str[0];
930 sprintf(str, "%X", hash[i] & 0xF);
931 hashStr[k++] = str[0];
932 }
933 hashStr[40] = '\0';
934 toLower(hashStr);
935 printData("Hash of key sequence: ["); printData(hashStr); printData("]\n");
936 client.print(hashStr);
937 client.println(" HTTP/1.1");
938 sendHostHeader();
939 //client.println("Accept: */*");
940 client.println();
941 printData("Data is sent\n");
942
943 //Waiting for response
944 waitForResponse();
945 //readMessageFromServer();
946 // Reading answer
947 access = readAccessDecision();
948 }
949 }
950 flushKeySequence();
951 }
952 } else {
953 done = true;
954 rez[6] = '\0'; // String terminator
955 printData("[", 1); printData(rez, 1); printData("]\n", 1);
956
957 // Check given and converted pass ID
958 if (checkMasterCard(rez)) {
959 printData("This card is a master-card.\n", 1);
960 access = 1;
961 } else {
962 printData("This card is NOT a master-card, trying to contact a server...\n", 1);
963 connectToServer();
964 if (connection_status) { // If connected to a server
965 // Send GET request
966 printData("We connected\n", 1);
967 client.print("GET /lock-api/v");
968 client.print(versionAPI);
969 client.print("/check-access/card");
970 client.print("?lock=");
971
972 // Get hash of lock ID
973 printData("Lock ID: [", 1); printData(lockID, 1); printData("]\n", 1);
974 Sha1.init();
975 Sha1.print(lockID);
976 uint8_t* hash = Sha1.result();
977 char hashStr[40];
978 int k = 0;
979 for (int i = 0; i < 20; i++) {
980 sprintf(str, "%X", hash[i] >> 4);
981 hashStr[k++] = str[0];
982 sprintf(str, "%X", hash[i] & 0xF);
983 hashStr[k++] = str[0];
984 }
985 hashStr[40] = '\0';
986 printData("Hash of lock: [", 1); printData(hashStr, 1); printData("]\n", 1);
987 client.print(hashStr);
988 client.print("&password=");
989
990 // Get hash of pass ID
991 Sha1.init();
992 Sha1.print(rez);
993 hash = Sha1.result();
994 k = 0;
995 for (int i = 0; i < 20; i++) {
996 sprintf(str, "%X", hash[i] >> 4);
997 hashStr[k++] = str[0];
998 sprintf(str, "%X", hash[i] & 0xF);
999 hashStr[k++] = str[0];
1000 }
1001 hashStr[40] = '\0';
1002 printData("Hash of card: [", 1); printData(hashStr, 1); printData("]\n", 1);
1003 client.print(hashStr);
1004 client.println(" HTTP/1.1");
1005 sendHostHeader();
1006 //client.println("Accept: */*");
1007 client.println();
1008 printData("Data is sent\n", 1);
1009
1010 // Waiting for response
1011 waitForResponse();
1012
1013 // Reading answer
1014 access = readAccessDecision();
1015 }
1016 }
1017 }
1018 if (done) {
1019 if (access == 1) { // Opening lock if access granted
1020 printData("ACCESS GRANTED\n", 1);
1021 openLock();
1022 } else {
1023 printData("ACCESS DENIED\n", 1);
1024 }
1025 }
1026}
1027