#include "main.h" volatile uint64_t cardData = 0; volatile bool newDataAvailable = false; unsigned long lastDataTime = 0; const unsigned long displayDelay = 1000; // Delay in milliseconds after which the data is displayed const unsigned long wifiRebootTimeout = 20000; // Delay before reboot after disconnect unsigned int bitCount = 0; // Variable to keep track of the bit count unsigned int maxReaderWaitTime = 9000; // Variable to timeout reader after too long of no data. #ifdef WIFI void connectToWiFi() { WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); #ifdef SERIAL_DEBUG Serial.print("Connecting to WiFi .."); #endif unsigned long startTime = millis(); // Record the start time of connection while (WiFi.status() != WL_CONNECTED) { delay(500); #ifdef SERIAL_DEBUG Serial.print("."); #endif // Check if it's time to reboot if (millis() - startTime >= wifiRebootTimeout) { // Reboot after 20 seconds #ifdef SERIAL_DEBUG Serial.println("\nFailed to connect. Rebooting..."); #endif ESP.restart(); // Reboot the Arduino board } } #ifdef SERIAL_DEBUG Serial.println("WiFi connected"); #endif } void GetWifiInfo(){ if(WiFi.status() == WL_CONNECTED) { #ifdef SERIAL_DEBUG Serial.print("[*] Network information for "); Serial.println(ssid); Serial.println("[+] BSSID : " + WiFi.BSSIDstr()); Serial.print("[+] Gateway IP : "); Serial.println(WiFi.gatewayIP()); Serial.print("[+] Subnet Mask : "); Serial.println(WiFi.subnetMask()); Serial.println((String)"[+] RSSI : " + WiFi.RSSI() + " dB"); Serial.print("[+] ESP32 IP : "); Serial.println(WiFi.localIP()); #endif } } void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info){ #ifdef NET_FAIL_SAFE unlockDoor(true); #endif #ifdef SERIAL_DEBUG Serial.println("Disconnected from WiFi access point"); Serial.print("WiFi lost connection. Reason: "); Serial.println(info.wifi_sta_disconnected.reason); Serial.println("Trying to Reconnect"); #endif connectToWiFi(); } void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info){ #ifdef SERIAL_DEBUG Serial.println("Connected to AP successfully!"); #endif #ifdef NET_FAIL_SAFE lockDoor(); #endif } void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info){ #ifdef SERIAL_DEBUG #ifdef WIFI GetWifiInfo(); #endif #endif } #endif #ifdef WEB_SERVER String outputState(int output){ if(digitalRead(output)){ return "checked"; } else { return ""; } } // Replaces placeholder with button section in your web page String processor(const String& var){ //Serial.println(var); if(var == "BUTTONPLACEHOLDER"){ String buttons = ""; #ifdef RELAY1 buttons += "

Output - Relay 1

"; #endif return buttons; } return String(); } #ifdef LOCAL_ACL #ifdef LOCAL_ACL_API // Handler for the '/users' endpoint to list all users void handleListUsers(AsyncWebServerRequest* request) { if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); // Create a JSON array to store the users StaticJsonDocument<512> jsonDoc; JsonArray usersArray = jsonDoc.to(); // Retrieve the ACL data using the getter function const User* aclData = acl.getACL(); // Iterate over each user in the ACL and add it to the JSON array for (int i = 0; i < acl.getACLSize(); i++) { JsonObject user = usersArray.createNestedObject(); user["cardId"] = aclData[i].cardId; user["desc"] = aclData[i].desc; } // Convert the JSON array to a string String response; serializeJson(usersArray, response); // Set the response content type to JSON request->send(200, "application/json", response); } void handleCreateUser(AsyncWebServerRequest* request) { if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); if(request->hasParam("cardId", true)) {} //This is important, otherwise the sketch will crash if there is no body else { AsyncWebServerResponse *response = request->beginResponse(400, "application/json", "{\"msg\":\"No cardId\"}"); request->send(response); } if(request->hasParam("desc", true)) {} //This is important, otherwise the sketch will crash if there is no body else { AsyncWebServerResponse *response = request->beginResponse(400, "application/json", "{\"msg\":\"No desc\"}"); request->send(response); } String cardId = String(request->arg("cardId")); String desc = String(request->arg("desc")); if (acl.validateAccess(String(cardId))) { AsyncWebServerResponse *response = request->beginResponse(400, "application/json", "{\"msg\":\"Duplicate ACL\"}"); request->send(response); } else { acl.addUser(cardId, desc); acl.saveToEEPROM(); request->send(201); // Create } } // Handler for the '/users/update' endpoint to remove a user void handleUpdateUser(AsyncWebServerRequest* request) { if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); if(request->hasParam("cardId", true)) {} //This is important, otherwise the sketch will crash if there is no body else { AsyncWebServerResponse *response = request->beginResponse(400, "application/json", "{\"msg\":\"No cardId\"}"); request->send(response); } if(request->hasParam("newCardId", true)) {} //This is important, otherwise the sketch will crash if there is no body else { AsyncWebServerResponse *response = request->beginResponse(400, "application/json", "\"msg\":\"No newCardId\"}"); request->send(response); } if(request->hasParam("desc", true)) {} //This is important, otherwise the sketch will crash if there is no body else { AsyncWebServerResponse *response = request->beginResponse(400, "application/json", "{\"msg\":\"No desc\"}"); request->send(response); } String cardId = String(request->arg("cardId")); String newCardId = String(request->arg("newCardId")); String desc = String(request->arg("desc")); acl.updateUser(cardId, newCardId, desc); acl.saveToEEPROM(); request->send(201); // Created } // Handler for the '/users/remove' endpoint to remove a user void handleRemoveUser(AsyncWebServerRequest* request) { if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); if(request->hasParam("cardId", true)) {} //This is important, otherwise the sketch will crash if there is no body else { AsyncWebServerResponse *response = request->beginResponse(400, "application/json", "{\"msg\":\"No cardId\"}"); request->send(response); } String cardId = String(request->arg("cardId")); acl.removeUser(cardId); acl.saveToEEPROM(); request->send(201); // Created } #endif #endif #endif void handleInterrupt(int bitValue) { static unsigned long lastInterruptTime = 0; unsigned long interruptTime = micros(); if (interruptTime - lastInterruptTime > 200) { cardData <<= 1; cardData |= bitValue; newDataAvailable = true; bitCount++; // Increment the bit count } lastInterruptTime = interruptTime; lastDataTime = millis(); // Update the time of last received data } void handleData0Interrupt() { handleInterrupt(0); } void handleData1Interrupt() { handleInterrupt(1); } void setup() { #ifdef SERIAL_DEBUG Serial.begin(9600); #endif #ifdef WEB_SERVER WiFi.disconnect(true); #endif #ifdef LOCAL_ACL acl.loadFromEEPROM(); #endif // Initialize SPIFFS #ifdef WEB_SERVER if(!SPIFFS.begin(true)){ #ifdef SERIAL_DEBUG Serial.println("An Error has occurred while mounting SPIFFS"); #endif return; } #ifdef WEB_OTA_UPDATE AsyncElegantOTA.begin(&server, http_username, http_password); #endif #ifdef WEB_SERIAL_DEBUG WebSerial.begin(&server); #endif #endif pinMode(DATA0_PIN, INPUT_PULLUP); pinMode(DATA1_PIN, INPUT_PULLUP); #ifdef LEDCTL_PIN pinMode(LEDCTL_PIN, OUTPUT); #endif #ifdef RELAY1 pinMode(RELAY1_PIN, OUTPUT); #endif attachInterrupt(digitalPinToInterrupt(DATA0_PIN), handleData0Interrupt, FALLING); attachInterrupt(digitalPinToInterrupt(DATA1_PIN), handleData1Interrupt, FALLING); #ifdef WIFI WiFi.onEvent(WiFiStationConnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_CONNECTED); WiFi.onEvent(WiFiGotIP, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); WiFi.onEvent(WiFiStationDisconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); connectToWiFi(); #endif #ifdef RELAY1 #ifdef SERIAL_DEBUG Serial.println("Enabling Relay: Locking Door"); #endif lockDoor(); #endif #ifdef RELAY2 pinMode(RELAY2_PIN, OUTPUT); #endif #ifdef BUZZER pinMode(ALARM_PIN, OUTPUT); // Do not set to low or it will constantly beep. digitalWrite(ALARM_PIN, HIGH); #endif #ifdef WEB_SERVER // Route for root / web page server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); request->send(SPIFFS, "/index.html", String(), false, processor); }); server.onNotFound([](AsyncWebServerRequest *request){ request->send(200, "application/json", "{\"msg\":\"The content you are looking for was not found\"}"); }); #ifdef RELAY1 server.on("/state/relay1", HTTP_GET, [] (AsyncWebServerRequest *request) { if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); AsyncWebServerResponse *response = request->beginResponse(200, "application/json", "{\"state\":\""+stateDoor()+"\"}"); request->send(response); }); #endif // Send a GET request to /gpio?output=&state= server.on("/gpio", HTTP_GET, [] (AsyncWebServerRequest *request) { String paramOutput; String paramState; if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); // GET input1 value on /gpio?output=&state= if (request->hasParam("output") && request->hasParam("state")) { paramOutput = request->getParam("output")->value(); paramState = request->getParam("state")->value(); #ifdef RELAY1 if (paramOutput == "relay1") { if (paramState == String(0)){ unlockDoor(false); AsyncWebServerResponse *response = request->beginResponse(200, "application/json", "{\"msg\":\"Door Unlocked.\"}"); request->send(response); } else if (paramState == String(1)) { lockDoor(); AsyncWebServerResponse *response = request->beginResponse(200, "application/json", "{\"msg\":\"Door Locked.\"}"); request->send(response); } else { AsyncWebServerResponse *response = request->beginResponse(200, "application/json", "{\"msg\":\"Incorect state provide 0 to unlock and 1 to lock.\"}"); request->send(response); } } #endif AsyncWebServerResponse *response = request->beginResponse(400, "application/json", "{\"msg\":\"Error with request, incorrect GPIO pin number.\"}"); request->send(response); } }); #ifdef LOCAL_ACL #ifdef LOCAL_ACL_API server.on("/users", HTTP_GET, handleListUsers); server.on("/users/create", HTTP_POST, handleCreateUser); server.on("/users/update", HTTP_POST, handleUpdateUser); server.on("/users/remove", HTTP_POST, handleRemoveUser); #endif #endif server.begin(); #endif } void loop() { if (newDataAvailable) { newDataAvailable = false; lastDataTime = millis(); // Reset the time of last received data } if (millis() - lastDataTime >= displayDelay && cardData != 0) { uint64_t facilityID = (cardData >> 17) & 0xFFFF; uint64_t cardID = (cardData >> 1) & 0xFFFF; #ifdef SERIAL_DEBUG Serial.print("Facility ID (Decimal): "); Serial.println(facilityID); Serial.print("Facility ID (Binary): "); Serial.println(facilityID, BIN); Serial.print("Card ID (Decimal): "); Serial.println(cardID); Serial.print("Card ID (Binary): "); Serial.println(cardID, BIN); Serial.print("Card Data (Binary): "); Serial.println(cardData, BIN); Serial.print("Total Bits Received: "); Serial.println(bitCount); #endif #ifdef WEB_SERIAL_DEBUG WebSerial.print("Facility ID (Decimal): "); WebSerial.println(facilityID); WebSerial.print("Facility ID (Binary): "); WebSerial.println(facilityID, BIN); WebSerial.print("Card ID (Decimal): "); WebSerial.println(cardID); WebSerial.print("Card ID (Binary): "); WebSerial.println(cardID, BIN); WebSerial.print("Card Data (Binary): "); WebSerial.println(cardData, BIN); WebSerial.print("Total Bits Received: "); WebSerial.println(bitCount); #endif String fullCardID = String(facilityID)+":"+String(cardID); #ifdef SERIAL_DEBUG Serial.print("Full Card (Tinance2): "); Serial.println(fullCardID); #endif #ifdef WEB_SERIAL_DEBUG WebSerial.print("Full Card (Tinance2): "); WebSerial.println(fullCardID); #endif cardData = 0; // Reset the card data lastDataTime = millis(); // Reset the time of last received data bitCount = 0; // Reset the bit count #ifdef LOCAL_ACL if (acl.validateAccess(String(cardID))) { #ifdef SERIAL_DEBUG Serial.println("LOCAL_AUTH: Access granted!"); #endif // Perform actions for authorized access #ifdef LATCH_DOOR unlockDoor(false); delay(RELAY_DELAY); lockDoor(); #endif #ifdef TOGGLE_DOOR toggleDoor(); delay(RELAY_DELAY); #endif } else { #ifdef SERIAL_DEBUG Serial.println("LOCAL_AUTH: Access denied!"); #endif // Perform actions for denied access #ifdef BUZZER denied_beep(); #endif } #endif } else if (millis() - lastDataTime >= maxReaderWaitTime) { cardData = 0; // Reset the card data lastDataTime = millis(); // Reset the time of last received data bitCount = 0; // Reset the bit count } }