tinance2-doorbot/src/main.cpp

477 lines
14 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 maxBitCount = 34; // Variable to make sure reader does not exceed maxiumum bits
#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 += "<h4>Output - Relay 1 </h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\""+String(RELAY1_PIN)+"\" " + outputState(RELAY1_PIN) + "><span class=\"slider\"></span></label>";
#endif
#ifdef BUZZER
buttons += "<h4>Output - Buzzer </h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\""+String(ALARM_PIN)+"\" " + outputState(ALARM_PIN) + "><span class=\"slider\"></span></label>";
#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<JsonArray>();
// 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 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(404, "text/plain", "The content you are looking for was not found.");
});
// Send a GET request to <ESP_IP>/gpio?output=<inputMessage1>&state=<inputMessage2>
server.on("/gpio", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage1;
String inputMessage2;
if(!request->authenticate(http_username, http_password))
return request->requestAuthentication();
// GET input1 value on <ESP_IP>/gpio?output=<inputMessage1>&state=<inputMessage2>
if (request->hasParam("output") && request->hasParam("state")) {
inputMessage1 = request->getParam("output")->value();
inputMessage2 = request->getParam("state")->value();
digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());
}
else {
inputMessage1 = "No message sent";
inputMessage2 = "No message sent";
}
#ifdef SERIAL_DEBUG
Serial.print("GPIO: ");
Serial.print(inputMessage1);
Serial.print(" - Set to: ");
Serial.println(inputMessage2);
request->send(200, "text/plain", "OK");
#endif
#ifdef WEB_SERIAL_DEBUG
WebSerial.print("GPIO: ");
WebSerial.print(inputMessage1);
WebSerial.print(" - Set to: ");
WebSerial.println(inputMessage2);
#endif
});
#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 && bitCount == maxBitCount) {
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
}
}