tinance2-doorbot/src/main.cpp

668 lines
17 KiB
C++
Raw Normal View History

2023-06-12 17:17:28 +00:00
#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.
2023-06-21 19:03:58 +00:00
#ifdef WEB_SERVER
AsyncWebServer server(80);
#endif
2023-06-12 17:17:28 +00:00
#ifdef WIFI
2023-11-21 20:36:49 +00:00
String getESP32ChipID() {
uint64_t chipId = ESP.getEfuseMac(); // Get the ESP32 chip ID
return String(chipId, HEX);
}
2023-11-20 16:41:10 +00:00
2023-11-21 20:36:49 +00:00
void connectToWiFi(String ssid, String password) {
String hostname = "MX-" + getESP32ChipID() + "-RDR";
WiFi.hostname(hostname.c_str());
2023-11-20 16:41:10 +00:00
2023-11-21 20:36:49 +00:00
#ifdef SERIAL_DEBUG
Serial.println("Connecting to WiFi...");
#endif
2023-11-20 17:24:15 +00:00
2023-11-21 20:36:49 +00:00
WiFi.begin(ssid.c_str(), password.c_str());
2023-11-20 17:24:15 +00:00
2023-11-21 20:36:49 +00:00
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
delay(1000);
#ifdef SERIAL_DEBUG
Serial.print(".");
#endif
attempts++;
2023-06-12 17:17:28 +00:00
}
2023-11-20 17:24:15 +00:00
2023-11-21 20:36:49 +00:00
if (WiFi.status() == WL_CONNECTED) {
#ifdef SERIAL_DEBUG
Serial.println("\nConnected to WiFi");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
#endif
} else {
#ifdef SERIAL_DEBUG
Serial.println("\nFailed to connect to WiFi. Rebooting...");
#endif
delay(1000);
ESP.restart();
}
2023-06-12 17:17:28 +00:00
}
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
2023-11-21 20:36:49 +00:00
connectToWiFi(ssid, password);
2023-06-12 17:17:28 +00:00
}
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
#endif
void handleInterrupt(int bitValue) {
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = micros();
2023-12-26 16:00:00 +00:00
if ((interruptTime - lastInterruptTime) > 200) {
if (bitCount < 34) {
cardData <<= 1;
cardData |= bitValue;
newDataAvailable = true;
bitCount++; // Increment the bit count
}
2023-06-12 17:17:28 +00:00
}
2023-12-26 16:00:00 +00:00
if (bitCount <= 34) {
lastInterruptTime = interruptTime;
lastDataTime = millis(); // Update the time of last received data
}
2023-06-12 17:17:28 +00:00
}
void handleData0Interrupt() {
handleInterrupt(0);
}
void handleData1Interrupt() {
handleInterrupt(1);
}
2023-07-29 16:54:47 +00:00
2023-06-12 17:17:28 +00:00
void setup() {
2023-07-29 16:54:47 +00:00
#if defined SERIAL_DEBUG || defined SERIAL_ACL
2023-06-12 17:17:28 +00:00
Serial.begin(9600);
#endif
#ifdef WEB_SERVER
WiFi.disconnect(true);
#endif
#ifdef LOCAL_ACL
acl.loadFromEEPROM();
#endif
2023-06-13 18:01:13 +00:00
settings.loadFromEEPROM();
2023-06-12 17:17:28 +00:00
// 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);
2023-12-26 15:41:27 +00:00
#ifdef BIT_MODE_WG34
pinMode(BIT_MODE_WG34_PIN, OUTPUT);
digitalWrite(BIT_MODE_WG34_PIN, HIGH);
#endif
2023-06-12 17:57:43 +00:00
#ifdef LEDCTL_PIN
pinMode(LEDCTL_PIN, OUTPUT);
#endif
2023-06-12 17:17:28 +00:00
#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);
2023-11-21 20:36:49 +00:00
connectToWiFi(ssid, password);
2023-06-12 17:17:28 +00:00
#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);
digitalWrite(ALARM_PIN, HIGH); // Do not set to low or it will constantly beep.
2023-06-12 17:17:28 +00:00
#endif
#ifdef WEB_SERVER
2023-06-13 19:11:23 +00:00
MainWebServer.begin(&server, http_username, http_password);
2023-06-12 17:17:28 +00:00
#ifdef LOCAL_ACL
#ifdef LOCAL_ACL_API
ACLWebServer.begin(&server, http_username, http_password);
2023-06-12 17:17:28 +00:00
#endif
#endif
server.begin();
#endif
}
2023-07-29 16:54:47 +00:00
#ifdef LOCAL_ACL
#ifdef SERIAL_ACL
struct WordList {
char** words;
int length;
};
WordList splitString(const char* inputString, const char* delimiter = " ") {
WordList result;
result.words = NULL;
result.length = 0;
// Create a temporary copy of the input string since strtok modifies the string
char* inputCopy = strdup(inputString);
// Get the first word
char* word = strtok(inputCopy, delimiter);
// Split the string and store each word in the dynamic list
while (word != NULL) {
result.length++;
// Reallocate memory for the dynamic list
result.words = (char**)realloc(result.words, (result.length + 1) * sizeof(char*));
// Allocate memory for the current word and copy it into the list
result.words[result.length - 1] = (char*)malloc(strlen(word) + 1);
strcpy(result.words[result.length - 1], word);
// Set the next element in the list to NULL for termination
result.words[result.length] = NULL;
// Get the next word
word = strtok(NULL, delimiter);
}
// Free the temporary copy of the input string
free(inputCopy);
return result;
}
void freeWordList(char** wordList) {
for (int i = 0; wordList[i] != NULL; i++) {
free(wordList[i]);
}
free(wordList);
}
void checkSerialCommand() {
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n'); // Read the incoming command until a newline character is received
Serial.println("Received command: " + command); // Echo back the received command
command.trim(); // Remove any leading or trailing whitespaces
if (command.length() == 0) {
Serial.println("Invalid command"); // If the command is not recognized
}
else {
WordList wordData = splitString(command.c_str());
char** wordList = wordData.words;
int wordCount = wordData.length;
String prefix = wordList[0];
// Command Validate Access
if (prefix.equals("validateAccess")) {
if (wordCount == 2) {
bool result = acl.validateAccess(String(wordList[1]));
if (result) {
Serial.println("Access granted!");
}
else {
Serial.println("Access denied!");
}
}
else {
Serial.println("validateAccess requires exactly 1 arguments (cardId)");
return;
}
}
// Command Add User
if (prefix.equals("addUser")) {
if (wordCount == 3) {
acl.addUser(wordList[1], wordList[2]);
acl.saveToEEPROM();
Serial.println("User Added!");
}
else {
Serial.println("addUser requires exactly 2 arguments (cardId, Description)");
return;
}
}
// Command Update User
if (prefix.equals("updateUser")) {
if (wordCount == 4) {
acl.updateUser(wordList[1], wordList[2], wordList[3]);
acl.saveToEEPROM();
Serial.println("User Updated!");
}
else {
Serial.println("userUser requires exactly 2 arguments (cardId, newCardId, newDescription)");
return;
}
}
// Command Remove User
if (prefix.equals("removeUser")) {
if (wordCount == 2) {
acl.removeUser(wordList[1]);
acl.saveToEEPROM();
Serial.println("User Removed!!");
}
else {
Serial.println("removeUser requires exactly 1 argument (cardId)");
return;
}
}
// Command List Uses
if (prefix.equals("listUsers")) {
if (wordCount == 1) {
// 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++) {
Serial.print(aclData[i].cardId);
Serial.print(" : ");
Serial.println(aclData[i].desc);
}
}
else {
Serial.println("removeUser requires exactly 0 arguments");
return;
}
}
freeWordList(wordList);
}
}
}
#endif
#endif
2023-11-21 10:29:27 +00:00
#ifdef LOCAL_ACL
void localAcl(String cardID) {
if (acl.validateAccess(String(cardID))) {
#ifdef SERIAL_DEBUG
Serial.println("LOCAL_AUTH: Access granted!");
#endif
// Perform actions for authorized access
#ifdef LATCH_DOOR
if (!settings.DoorDisabled()) {
unlockDoor(false);
delay(RELAY_DELAY);
lockDoor();
}
#endif
#ifdef TOGGLE_DOOR
if (!settings.DoorDisabled()) {
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
#ifdef TINANCE2_BACKEND
#include <HTTPClient.h>
// Function to send the authentication request to the endpoint
void tinance2authrequest(String fullCardID, String cardID) {
#ifdef SERIAL_DEBUG
Serial.println("WIFI Status: " + String(WiFi.status()));
#endif
if (WiFi.status() == WL_CONNECTED) {
2023-11-20 20:25:11 +00:00
#ifdef SERIAL_DEBUG
Serial.println("Sending Request to Tinance2 for card: " + fullCardID);
#endif
HTTPClient http;
// Set the endpoint URL
String url = tinance2_url;
2023-11-21 10:29:27 +00:00
if (!http.begin(url)) {
#ifdef SERIAL_DEBUG
Serial.println("Tinance2: Failed to begin HTTP request");
#endif
http.end();
#ifdef LOCAL_ACL
localAcl(String(cardID));
#endif
return;
}
2023-11-20 20:25:11 +00:00
// Set the headers
http.addHeader("Content-Type", "application/json");
http.addHeader("X-Identifier", tinance2_reader_identifer);
http.addHeader("X-Access-Key", tinance2_reader_key);
2023-11-21 10:29:27 +00:00
// Set the HTTP timeout to 5 seconds
http.setTimeout(5000);
2023-11-20 20:25:11 +00:00
// Create the JSON payload
String payload = "{\"full_card_id\":\"" + String(fullCardID) + "\"}";
2023-11-21 10:29:27 +00:00
2023-11-20 20:25:11 +00:00
#ifdef SERIAL_DEBUG
2023-11-21 10:29:27 +00:00
Serial.println("Payload: " + payload);
2023-11-20 20:25:11 +00:00
#endif
// Send the POST request
int httpResponseCode = http.POST(payload);
2023-11-21 10:29:27 +00:00
2023-11-20 20:25:11 +00:00
#ifdef SERIAL_DEBUG
2023-11-21 10:29:27 +00:00
// Print the response code
Serial.print("HTTP Response Code: ");
Serial.println(httpResponseCode);
// Print the response body
String responseBody = http.getString();
Serial.print("HTTP Response Body: ");
Serial.println(responseBody);
2023-11-20 20:25:11 +00:00
#endif
2023-11-21 10:29:27 +00:00
if (httpResponseCode <= 0) {
#ifdef SERIAL_DEBUG
Serial.println("Tinance2 Request Failed (response less then 0)");
#endif
http.end();
#ifdef LOCAL_ACL
localAcl(String(cardID));
#endif
return;
2023-11-20 20:25:11 +00:00
}
2023-11-21 10:29:27 +00:00
if (httpResponseCode == 500 || httpResponseCode == 502) {
// Unlock the door
http.end();
#ifdef LOCAL_ACL
localAcl(String(cardID));
#endif
2023-11-20 20:25:11 +00:00
#ifdef SERIAL_DEBUG
2023-11-21 10:29:27 +00:00
Serial.println("Emergency Tinance Process");
2023-11-20 20:25:11 +00:00
#endif
2023-11-21 10:29:27 +00:00
return;
}
// Check the response code
if (httpResponseCode == 200) {
// Unlock the door
2023-11-20 20:25:11 +00:00
http.end();
2023-11-21 10:29:27 +00:00
Serial.println("Tinance2 Door Access Granted");
#ifdef LATCH_DOOR
if (!settings.DoorDisabled()) {
unlockDoor(false);
delay(RELAY_DELAY);
lockDoor();
}
#endif
#ifdef TOGGLE_DOOR
if (!settings.DoorDisabled()) {
toggleDoor();
delay(RELAY_DELAY);
}
#endif
return;
} else {
#ifdef SERIAL_DEBUG
Serial.println("Tinance2 Door Access Denied");
#endif
http.end();
#ifdef BUZZER
denied_beep();
#endif
return;
2023-11-20 20:25:11 +00:00
}
2023-11-21 10:29:27 +00:00
// Cleanup
http.end();
return;
}
else {
#ifdef SERIAL_DEBUG
Serial.println("Tinance2 Wifi Disconnected using offline processess.");
#ifdef LOCAL_ACL
localAcl(String(cardID));
#endif
2023-11-20 20:25:11 +00:00
#endif
2023-11-21 10:29:27 +00:00
}
2023-11-20 20:25:11 +00:00
2023-11-21 10:29:27 +00:00
}
#endif
2023-11-20 20:25:11 +00:00
2023-06-12 17:17:28 +00:00
void loop() {
2023-07-29 16:54:47 +00:00
#ifdef LOCAL_ACL
#ifdef SERIAL_ACL
checkSerialCommand();
#endif
#endif
2023-06-12 17:17:28 +00:00
if (newDataAvailable) {
newDataAvailable = false;
lastDataTime = millis(); // Reset the time of last received data
}
if (millis() - lastDataTime >= displayDelay && cardData != 0 && (bitCount == 26 || bitCount == 34 || bitCount == 37)) {
2023-06-12 17:17:28 +00:00
uint64_t facilityID = (cardData >> 17) & 0xFFFF;
uint64_t cardID = (cardData >> 1) & 0xFFFF;
#ifdef SERIAL_DEBUG
Serial.println("--- Reader Input Processed ---");
2023-06-12 17:17:28 +00:00
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
2023-11-20 20:25:11 +00:00
#ifdef TINANCE2_BACKEND
2023-11-21 10:29:27 +00:00
tinance2authrequest(String(fullCardID), String(cardID));
2023-11-20 20:25:11 +00:00
#endif
2023-06-12 17:17:28 +00:00
#ifdef LOCAL_ACL
2023-11-21 10:29:27 +00:00
#ifndef TINANCE2_BACKEND
localAcl(String(cardID));
#endif
2023-06-12 17:17:28 +00:00
#endif
cardData = 0; // Reset the card data
lastDataTime = millis(); // Reset the time of last received data
bitCount = 0; // Reset the bit count
2023-06-12 17:17:28 +00:00
}
else if (millis() - lastDataTime >= maxReaderWaitTime && cardData != 0) {
uint64_t facilityID = (cardData >> 17) & 0xFFFF;
uint64_t cardID = (cardData >> 1) & 0xFFFF;
#ifdef SERIAL_DEBUG
Serial.println("--- Reader Input Error ---");
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
cardData = 0; // Reset the card data
lastDataTime = millis(); // Reset the time of last received data
bitCount = 0; // Reset the bit count
}
2023-06-12 17:17:28 +00:00
}