Initial commit
This commit is contained in:
commit
94c087ece4
21 changed files with 1309 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
include/secrets.h
|
||||
.DS_Store
|
||||
upload_params.ini
|
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
13
.vscode/settings.json
vendored
Normal file
13
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"string_view": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"regex": "cpp"
|
||||
}
|
||||
}
|
188
Insomnia-local-acl-api-spec.yaml
Normal file
188
Insomnia-local-acl-api-spec.yaml
Normal file
|
@ -0,0 +1,188 @@
|
|||
_type: export
|
||||
__export_format: 4
|
||||
__export_date: 2023-06-12T09:12:10.701Z
|
||||
__export_source: insomnia.desktop.app:v2023.2.2
|
||||
resources:
|
||||
- _id: req_fab13abf4f6146a3b0db6955b4968837
|
||||
parentId: wrk_3465ebd3f0ef47dcac39369bc6f6475e
|
||||
modified: 1686560719521
|
||||
created: 1680522514230
|
||||
url: http://reader-hostname/users/
|
||||
name: Get Card ACL's
|
||||
description: ""
|
||||
method: GET
|
||||
body:
|
||||
mimeType: application/json
|
||||
text: ""
|
||||
parameters: []
|
||||
headers:
|
||||
- id: pair_cf9752f319fc4ce2bbc5c1d802d7a82b
|
||||
name: Content-Type
|
||||
value: application/json
|
||||
description: ""
|
||||
authentication:
|
||||
type: basic
|
||||
useISO88591: false
|
||||
disabled: false
|
||||
username: admin
|
||||
password: admin
|
||||
metaSortKey: -1680522514230
|
||||
isPrivate: false
|
||||
settingStoreCookies: true
|
||||
settingSendCookies: true
|
||||
settingDisableRenderRequestBody: false
|
||||
settingEncodeUrl: true
|
||||
settingRebuildPath: true
|
||||
settingFollowRedirects: global
|
||||
_type: request
|
||||
- _id: wrk_3465ebd3f0ef47dcac39369bc6f6475e
|
||||
parentId: null
|
||||
modified: 1680522505347
|
||||
created: 1680522505347
|
||||
name: my-spec.yaml
|
||||
description: ""
|
||||
scope: design
|
||||
_type: workspace
|
||||
- _id: req_fbe2da3ea1b942149289048a145c2e1f
|
||||
parentId: wrk_3465ebd3f0ef47dcac39369bc6f6475e
|
||||
modified: 1686561076133
|
||||
created: 1686523684626
|
||||
url: http://reader-hostname/users/create
|
||||
name: Create Card ACL
|
||||
description: ""
|
||||
method: POST
|
||||
body:
|
||||
mimeType: multipart/form-data
|
||||
params:
|
||||
- name: cardId
|
||||
value: "1234"
|
||||
id: pair_601e929fe2cf4f35a8fef542dbdff232
|
||||
- id: pair_8156862f80b84f988f5677eec2332f70
|
||||
name: desc
|
||||
value: Person A
|
||||
description: ""
|
||||
parameters: []
|
||||
headers:
|
||||
- id: pair_cf9752f319fc4ce2bbc5c1d802d7a82b
|
||||
name: Content-Type
|
||||
value: multipart/form-data
|
||||
description: ""
|
||||
authentication:
|
||||
type: basic
|
||||
useISO88591: false
|
||||
disabled: false
|
||||
username: admin
|
||||
password: admin
|
||||
metaSortKey: -1678709813012.5
|
||||
isPrivate: false
|
||||
settingStoreCookies: true
|
||||
settingSendCookies: true
|
||||
settingDisableRenderRequestBody: false
|
||||
settingEncodeUrl: true
|
||||
settingRebuildPath: true
|
||||
settingFollowRedirects: global
|
||||
_type: request
|
||||
- _id: req_60a5d5a1e2d444b18d05c1892f23ba0c
|
||||
parentId: wrk_3465ebd3f0ef47dcac39369bc6f6475e
|
||||
modified: 1686560730675
|
||||
created: 1686527136306
|
||||
url: http://reader-hostname/users/update
|
||||
name: Update Card ACL
|
||||
description: ""
|
||||
method: POST
|
||||
body:
|
||||
mimeType: multipart/form-data
|
||||
params:
|
||||
- name: cardId
|
||||
value: "1234"
|
||||
id: pair_601e929fe2cf4f35a8fef542dbdff232
|
||||
- id: pair_fb6e32685ee1484da24a96d5a445dab5
|
||||
name: newCardId
|
||||
value: "1234"
|
||||
description: ""
|
||||
- id: pair_688c548f6db94924be254a3b4593862c
|
||||
name: desc
|
||||
value: Test
|
||||
description: ""
|
||||
parameters: []
|
||||
headers:
|
||||
- id: pair_cf9752f319fc4ce2bbc5c1d802d7a82b
|
||||
name: Content-Type
|
||||
value: multipart/form-data
|
||||
description: ""
|
||||
authentication:
|
||||
type: basic
|
||||
useISO88591: false
|
||||
disabled: false
|
||||
username: admin
|
||||
password: admin
|
||||
metaSortKey: -1678256637708.125
|
||||
isPrivate: false
|
||||
settingStoreCookies: true
|
||||
settingSendCookies: true
|
||||
settingDisableRenderRequestBody: false
|
||||
settingEncodeUrl: true
|
||||
settingRebuildPath: true
|
||||
settingFollowRedirects: global
|
||||
_type: request
|
||||
- _id: req_df860cce04cf49c98c9abc4e92ec5a72
|
||||
parentId: wrk_3465ebd3f0ef47dcac39369bc6f6475e
|
||||
modified: 1686560728749
|
||||
created: 1686526808857
|
||||
url: http://reader-hostname/users/remove
|
||||
name: Remove Card ACL
|
||||
description: ""
|
||||
method: POST
|
||||
body:
|
||||
mimeType: multipart/form-data
|
||||
params:
|
||||
- name: cardId
|
||||
value: "1234"
|
||||
id: pair_601e929fe2cf4f35a8fef542dbdff232
|
||||
parameters: []
|
||||
headers:
|
||||
- id: pair_cf9752f319fc4ce2bbc5c1d802d7a82b
|
||||
name: Content-Type
|
||||
value: multipart/form-data
|
||||
description: ""
|
||||
authentication:
|
||||
type: basic
|
||||
useISO88591: false
|
||||
disabled: false
|
||||
username: admin
|
||||
password: admin
|
||||
metaSortKey: -1677803462403.75
|
||||
isPrivate: false
|
||||
settingStoreCookies: true
|
||||
settingSendCookies: true
|
||||
settingDisableRenderRequestBody: false
|
||||
settingEncodeUrl: true
|
||||
settingRebuildPath: true
|
||||
settingFollowRedirects: global
|
||||
_type: request
|
||||
- _id: env_72c9ec50419adc3e8768056161ce4d086c486204
|
||||
parentId: wrk_3465ebd3f0ef47dcac39369bc6f6475e
|
||||
modified: 1680522505349
|
||||
created: 1680522505349
|
||||
name: Base Environment
|
||||
data: {}
|
||||
dataPropertyOrder: null
|
||||
color: null
|
||||
isPrivate: false
|
||||
metaSortKey: 1680522505349
|
||||
_type: environment
|
||||
- _id: jar_72c9ec50419adc3e8768056161ce4d086c486204
|
||||
parentId: wrk_3465ebd3f0ef47dcac39369bc6f6475e
|
||||
modified: 1680522505350
|
||||
created: 1680522505350
|
||||
name: Default Jar
|
||||
cookies: []
|
||||
_type: cookie_jar
|
||||
- _id: spc_deaf3f00ef8847429199de99aca75d06
|
||||
parentId: wrk_3465ebd3f0ef47dcac39369bc6f6475e
|
||||
modified: 1680522505347
|
||||
created: 1680522505347
|
||||
fileName: my-spec.yaml
|
||||
contents: ""
|
||||
contentType: yaml
|
||||
_type: api_spec
|
23
README
Normal file
23
README
Normal file
|
@ -0,0 +1,23 @@
|
|||
This project is a card reader system for Techinc that uses a WIEGAND based reader and allows platform.io based user flags to allow change of certain features.
|
||||
|
||||
|
||||
# Currently supported flags:
|
||||
|
||||
-DBOARD1 : Used to set Pins for BOARD1 config
|
||||
-DBOARD2 : Used to set Pins for BOARD2 config
|
||||
-DRELAY1 : Enable the First Relay
|
||||
-DRELAY2 : Not implemented yet
|
||||
-DWIFI : Enable WIFI and use secrets.h to manage.
|
||||
-DWEB_SERVER : Enable Web Server
|
||||
-DSERIAL_DEBUG : Enable Serial Console output and debug.
|
||||
-DWEB_SERIAL_DEBUG : Enable Web Serial console to monitor for issues (does not support auth, needs -DWEB_SERVER)
|
||||
-DNET_FAIL_SAFE : If WIFI disconnects release door / relay.
|
||||
-DWEB_OTA_UPDATE : Present Web Interface on /update for managing firmware over the air (needs -DWEB_SERVER)
|
||||
-DLOCAL_ACL : Enable local ACL list
|
||||
-DLOCAL_ACL_API : Enable API for managing ACL's (needs -DWEB_SERVER and -DLOCAL_ACL)
|
||||
-DLATCH_DOOR : Release RELAY1 for a a certain amount of time in this case its 1 second
|
||||
-DTOGGLE_DOOR : Instead of Relasing relay for 1 second keep it in the opposite state to what it was at the time the card was scanned.
|
||||
|
||||
|
||||
# Important Notes:
|
||||
* The webserver uses Spiffs to manage the HTML files for the web server you need to run ("Upload Filesystem Image" step via serial or use the OTA upgrade and run "Upload Filesystem Image OTA" step and uplaod it.)
|
30
data/index.html
Normal file
30
data/index.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE HTML><html>
|
||||
<head>
|
||||
<title>ESP Web Server</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="data:,">
|
||||
<style>
|
||||
html {font-family: Arial; display: inline-block; text-align: center;}
|
||||
h2 {font-size: 3.0rem;}
|
||||
p {font-size: 3.0rem;}
|
||||
body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
|
||||
.switch {position: relative; display: inline-block; width: 120px; height: 68px}
|
||||
.switch input {display: none}
|
||||
.slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 6px}
|
||||
.slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
|
||||
input:checked+.slider {background-color: #b30000}
|
||||
input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>ESP Web Server</h2>
|
||||
%BUTTONPLACEHOLDER%
|
||||
<script>function toggleCheckbox(element) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
if(element.checked){ xhr.open("GET", "/gpio?output="+element.id+"&state=1", true); }
|
||||
else { xhr.open("GET", "/gpio?output="+element.id+"&state=0", true); }
|
||||
xhr.send();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
44
include/ACL.h
Normal file
44
include/ACL.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef ACL_H
|
||||
#define ACL_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <initializer_list>
|
||||
#include <EEPROM.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
#ifdef WEB_SERVER
|
||||
#ifdef WEB_SERIAL_DEBUG
|
||||
#include <WebSerial.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct User {
|
||||
String cardId;
|
||||
String desc;
|
||||
};
|
||||
|
||||
class ACL {
|
||||
public:
|
||||
ACL();
|
||||
ACL(std::initializer_list<User> userList);
|
||||
~ACL();
|
||||
|
||||
|
||||
void addUser(const String& cardId, const String& desc);
|
||||
void updateUser(const String& cardId, const String& newCardId, const String& newdesc);
|
||||
bool removeUser(const String& cardId);
|
||||
bool validateAccess(const String& cardId);
|
||||
int getACLSize() const;
|
||||
|
||||
// Additional EEPROM functions
|
||||
void saveToEEPROM();
|
||||
void loadFromEEPROM();
|
||||
|
||||
const User* getACL() const { return acl; } // Getter for acl
|
||||
|
||||
private:
|
||||
User* acl;
|
||||
int aclSize;
|
||||
};
|
||||
|
||||
#endif
|
39
include/README
Normal file
39
include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
11
include/buzzer_ctl.h
Normal file
11
include/buzzer_ctl.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef BUZZER_CTL_H
|
||||
#define BUZZER_CTL_H
|
||||
|
||||
#include "hardware.h"
|
||||
|
||||
void short_beep();
|
||||
void granted_beep();
|
||||
void denied_beep();
|
||||
void clear_states();
|
||||
|
||||
#endif
|
37
include/hardware.h
Normal file
37
include/hardware.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef HARDWARE_H
|
||||
#define HARDWARE_H
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef WEB_SERVER
|
||||
#ifdef WEB_SERIAL_DEBUG
|
||||
#include <WebSerial.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef BOARD1
|
||||
#define TAMPER_PIN 26
|
||||
#define LEDCTL_PIN 32
|
||||
#define DATA0_PIN 34
|
||||
#define DATA1_PIN 36
|
||||
#define RELAY1_PIN 32
|
||||
#define RELAY2_PIN 33
|
||||
#define RELAY_DELAY 3000
|
||||
#endif
|
||||
|
||||
#ifdef BOARD2
|
||||
#define TAMPER_PIN 0
|
||||
#define ALARM_PIN 33
|
||||
#define WIEGAND_PIN 32
|
||||
#define LEDCTL_PIN 25
|
||||
#define DATA0_PIN 26
|
||||
#define DATA1_PIN 27
|
||||
#define RELAY1_PIN 25
|
||||
#define RELAY2_PIN 19
|
||||
#define RELAY_DELAY 3000
|
||||
#endif
|
||||
|
||||
void controlRelay();
|
||||
void unlockDoor(bool silent);
|
||||
void lockDoor();
|
||||
void toggleDoor();
|
||||
#endif
|
26
include/main.h
Normal file
26
include/main.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#include "hardware.h"
|
||||
#include <Arduino.h>
|
||||
#include "secrets.h"
|
||||
|
||||
#ifdef WIFI
|
||||
#include "WiFi.h"
|
||||
#endif
|
||||
|
||||
#ifdef WEB_SERVER
|
||||
#include "web_server.h"
|
||||
#ifdef WEB_SERIAL_DEBUG
|
||||
#include <WebSerial.h>
|
||||
#endif
|
||||
#ifdef WEB_OTA_UPDATE
|
||||
#include <AsyncElegantOTA.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef BUZZER
|
||||
#include "buzzer_ctl.h"
|
||||
#endif
|
||||
|
||||
#endif
|
25
include/secrets.h.dist
Normal file
25
include/secrets.h.dist
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef SECRETS_H
|
||||
#define SECRETS_H
|
||||
|
||||
#ifdef WIFI
|
||||
const char* ssid = "WIFI";
|
||||
const char* password = "123";
|
||||
#endif
|
||||
|
||||
#ifdef WEB_SERVER
|
||||
const char* http_username = "admin";
|
||||
const char* http_password = "admin";
|
||||
#endif
|
||||
|
||||
#ifdef LOCAL_ACL
|
||||
#include "ACL.h"
|
||||
|
||||
ACL acl = {
|
||||
{"1234", "Card 1"},
|
||||
{"4567", "Card 2"},
|
||||
{"7891", "Card 3"}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
19
include/web_server.h
Normal file
19
include/web_server.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef WEB_SERVER_H
|
||||
#define WEB_SERVER_H
|
||||
|
||||
#include <AsyncTCP.h>
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "SPIFFS.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
|
||||
// Set web server port number to 80
|
||||
AsyncWebServer server(80);
|
||||
|
||||
#ifdef LOCAL_ACL
|
||||
#include "ACL.h"
|
||||
ACL acl = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
46
lib/README
Normal file
46
lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
34
platformio.ini
Normal file
34
platformio.ini
Normal file
|
@ -0,0 +1,34 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:esp32-evb]
|
||||
platform = espressif32
|
||||
board = esp32-evb
|
||||
framework = arduino
|
||||
build_flags = -DBOARD1 -DRELAY1 -DWIFI -DWEB_SERVER -DNET_FAIL_SAFE -DWEB_OTA_UPDATE -DLOCAL_ACL -DLOCAL_ACL_API -DLATCH_DOOR
|
||||
lib_deps =
|
||||
ottowinter/ESPAsyncWebServer-esphome@^3.0.0
|
||||
ayushsharma82/WebSerial@^1.4.0
|
||||
ayushsharma82/AsyncElegantOTA@^2.2.7
|
||||
bblanchon/ArduinoJson@^6.21.2
|
||||
|
||||
[env:mcu-esp32s]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = nodemcu-32s
|
||||
build_flags = -DBOARD2 -DBUZZER -DRELAY1 -DWIFI -DWEB_SERVER -DNET_FAIL_SAFE -DWEB_OTA_UPDATE -DLOCAL_ACL -DLOCAL_ACL_API -DLATCH_DOOR
|
||||
lib_deps =
|
||||
ottowinter/ESPAsyncWebServer-esphome@^3.0.0
|
||||
ayushsharma82/WebSerial@^1.4.0
|
||||
ayushsharma82/AsyncElegantOTA@^2.2.7
|
||||
bblanchon/ArduinoJson@^6.21.2
|
||||
extra_scripts = platformio_upload.py
|
||||
extra_configs = upload_params.ini
|
||||
|
53
platformio_upload.py
Normal file
53
platformio_upload.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Allows PlatformIO to upload directly to AsyncElegantOTA
|
||||
#
|
||||
# To use:
|
||||
# - copy this script into the same folder as your platformio.ini
|
||||
# - set the following for your project in platformio.ini:
|
||||
#
|
||||
# extra_scripts = platformio_upload.py
|
||||
# upload_protocol = custom
|
||||
# upload_url = <your upload URL>
|
||||
#
|
||||
# An example of an upload URL:
|
||||
# upload_URL = http://192.168.1.123/update
|
||||
|
||||
import requests
|
||||
import hashlib
|
||||
Import('env')
|
||||
|
||||
try:
|
||||
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
|
||||
from tqdm import tqdm
|
||||
except ImportError:
|
||||
env.Execute("$PYTHONEXE -m pip install requests_toolbelt")
|
||||
env.Execute("$PYTHONEXE -m pip install tqdm")
|
||||
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
|
||||
from tqdm import tqdm
|
||||
|
||||
def on_upload(source, target, env):
|
||||
firmware_path = str(source[0])
|
||||
upload_url = env.GetProjectOption('upload_url')
|
||||
|
||||
with open(firmware_path, 'rb') as firmware:
|
||||
md5 = hashlib.md5(firmware.read()).hexdigest()
|
||||
firmware.seek(0)
|
||||
encoder = MultipartEncoder(fields={
|
||||
'MD5': md5,
|
||||
'firmware': ('firmware', firmware, 'application/octet-stream')}
|
||||
)
|
||||
|
||||
bar = tqdm(desc='Upload Progress',
|
||||
total=encoder.len,
|
||||
dynamic_ncols=True,
|
||||
unit='B',
|
||||
unit_scale=True,
|
||||
unit_divisor=1024
|
||||
)
|
||||
|
||||
monitor = MultipartEncoderMonitor(encoder, lambda monitor: bar.update(monitor.bytes_read - bar.n))
|
||||
|
||||
response = requests.post(upload_url, data=monitor, headers={'Content-Type': monitor.content_type})
|
||||
bar.close()
|
||||
print(response,response.text)
|
||||
|
||||
env.Replace(UPLOADCMD=on_upload)
|
133
src/ACL.cpp
Normal file
133
src/ACL.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
#include "ACL.h"
|
||||
|
||||
ACL::ACL() {
|
||||
acl = nullptr;
|
||||
aclSize = 0;
|
||||
}
|
||||
|
||||
ACL::ACL(std::initializer_list<User> userList) {
|
||||
aclSize = userList.size();
|
||||
acl = new User[aclSize];
|
||||
|
||||
int i = 0;
|
||||
for (const User& user : userList) {
|
||||
acl[i] = user;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
ACL::~ACL() {
|
||||
if (acl != nullptr) {
|
||||
delete[] acl;
|
||||
}
|
||||
}
|
||||
|
||||
void ACL::addUser(const String& cardId, const String& desc) {
|
||||
User newUser;
|
||||
newUser.cardId = cardId;
|
||||
newUser.desc = desc;
|
||||
|
||||
User* newACL = new User[aclSize + 1];
|
||||
for (int i = 0; i < aclSize; i++) {
|
||||
newACL[i] = acl[i];
|
||||
}
|
||||
newACL[aclSize] = newUser;
|
||||
|
||||
aclSize++;
|
||||
acl = newACL;
|
||||
}
|
||||
|
||||
void ACL::updateUser(const String& cardId, const String& newCardId, const String& newdesc) {
|
||||
for (int i = 0; i < aclSize; i++) {
|
||||
if (acl[i].cardId == cardId) {
|
||||
acl[i].cardId = newCardId;
|
||||
acl[i].desc = newdesc;
|
||||
return; // Exit the loop and method if user is found and updated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ACL::removeUser(const String& cardId) {
|
||||
int userIndex = -1;
|
||||
for (int i = 0; i < aclSize; i++) {
|
||||
if (acl[i].cardId == cardId) {
|
||||
userIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (userIndex == -1) {
|
||||
return false; // User not found
|
||||
}
|
||||
|
||||
User* newACL = new User[aclSize - 1];
|
||||
int j = 0;
|
||||
for (int i = 0; i < aclSize; i++) {
|
||||
if (i != userIndex) {
|
||||
newACL[j] = acl[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
aclSize--;
|
||||
acl = newACL;
|
||||
return true; // User removed successfully
|
||||
}
|
||||
|
||||
bool ACL::validateAccess(const String& cardId) {
|
||||
for (int i = 0; i < aclSize; i++) {
|
||||
|
||||
#ifdef SERIAL_DEBUG
|
||||
Serial.println("Checking ACL match for Card: " + String(cardId) + " Against Card: " + String(acl[i].cardId) + " desc: " + String(acl[i].desc));
|
||||
#endif
|
||||
#ifdef WEB_SERIAL_DEBUG
|
||||
WebSerial.println("Checking ACL match for Card: " + String(cardId) + " Against Card: " + String(acl[i].cardId) + " desc: " + String(acl[i].desc));
|
||||
#endif
|
||||
if (acl[i].cardId.equals(cardId)) {
|
||||
#ifdef SERIAL_DEBUG
|
||||
Serial.println("Card matched and authenticated.");
|
||||
#endif
|
||||
#ifdef WEB_SERIAL_DEBUG
|
||||
WebSerial.println("Card matched and authenticated.");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#ifdef SERIAL_DEBUG
|
||||
Serial.println("Card did not match.");
|
||||
#endif
|
||||
#ifdef WEB_SERIAL_DEBUG
|
||||
WebSerial.println("Card did not match.");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
Preferences preferences;
|
||||
|
||||
|
||||
int ACL::getACLSize() const {
|
||||
return aclSize;
|
||||
}
|
||||
|
||||
void ACL::loadFromEEPROM() {
|
||||
preferences.begin("acl", true);
|
||||
aclSize = preferences.getUInt("aclSize", 0);
|
||||
acl = new User[aclSize];
|
||||
for (int i = 0; i < aclSize; i++) {
|
||||
String key = "user_" + String(i);
|
||||
acl[i].cardId = preferences.getString((key + "_cardId").c_str(), "");
|
||||
acl[i].desc = preferences.getString((key + "_desc").c_str(), "");
|
||||
}
|
||||
preferences.end();
|
||||
}
|
||||
|
||||
void ACL::saveToEEPROM() {
|
||||
preferences.begin("acl", false);
|
||||
preferences.putUInt("aclSize", aclSize);
|
||||
for (int i = 0; i < aclSize; i++) {
|
||||
String key = "user_" + String(i);
|
||||
preferences.putString((key + "_cardId").c_str(), acl[i].cardId.c_str());
|
||||
preferences.putString((key + "_desc").c_str(), acl[i].desc.c_str());
|
||||
}
|
||||
preferences.end();
|
||||
}
|
40
src/buzzer_ctl.cpp
Normal file
40
src/buzzer_ctl.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "buzzer_ctl.h"
|
||||
|
||||
void short_beep()
|
||||
{
|
||||
#ifdef ALARM_PIN
|
||||
digitalWrite(ALARM_PIN, LOW);
|
||||
delay(50);
|
||||
digitalWrite(ALARM_PIN, HIGH);
|
||||
#endif
|
||||
}
|
||||
|
||||
void granted_beep()
|
||||
{
|
||||
#ifdef ALARM_PIN
|
||||
digitalWrite(ALARM_PIN, LOW);
|
||||
delay(50);
|
||||
digitalWrite(ALARM_PIN, HIGH);
|
||||
delay(50);
|
||||
digitalWrite(ALARM_PIN, LOW);
|
||||
delay(50);
|
||||
digitalWrite(ALARM_PIN, HIGH);
|
||||
delay(50);
|
||||
digitalWrite(ALARM_PIN, LOW);
|
||||
delay(50);
|
||||
digitalWrite(ALARM_PIN, HIGH);
|
||||
#endif
|
||||
}
|
||||
|
||||
void denied_beep(void)
|
||||
{
|
||||
#ifdef ALARM_PIN
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
delay(4);
|
||||
digitalWrite(ALARM_PIN, LOW);
|
||||
delay(4);
|
||||
digitalWrite(ALARM_PIN, HIGH);
|
||||
}
|
||||
#endif
|
||||
}
|
50
src/hardware.cpp
Normal file
50
src/hardware.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include "hardware.h"
|
||||
#include "buzzer_ctl.h"
|
||||
void controlRelay(int relayPin, bool on) {
|
||||
digitalWrite(relayPin, on ? HIGH : LOW); // Turn the relay on or off
|
||||
}
|
||||
|
||||
void unlockDoor(bool silent) {
|
||||
#ifdef BUZZER
|
||||
if (!silent) {
|
||||
granted_beep();
|
||||
}
|
||||
#endif
|
||||
#ifdef RELAY1
|
||||
controlRelay(RELAY1_PIN,false);
|
||||
#endif
|
||||
#ifdef SERIAL_DEBUG
|
||||
Serial.println("door unlocked");
|
||||
#endif
|
||||
|
||||
#ifdef WEB_SERIAL_DEBUG
|
||||
WebSerial.println("door unlocked");
|
||||
#endif
|
||||
}
|
||||
|
||||
void lockDoor() {
|
||||
#ifdef RELAY1
|
||||
controlRelay(RELAY1_PIN,true);
|
||||
#endif
|
||||
#ifdef SERIAL_DEBUG
|
||||
Serial.println("door locked");
|
||||
#endif
|
||||
|
||||
#ifdef WEB_SERIAL_DEBUG
|
||||
WebSerial.println("door locked");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void toggleDoor() {
|
||||
#ifdef RELAY1
|
||||
int relayState = digitalRead(RELAY1_PIN);
|
||||
// Toggle the relay based on the current state
|
||||
if (relayState == LOW) {
|
||||
lockDoor();
|
||||
} else {
|
||||
unlockDoor(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
477
src/main.cpp
Normal file
477
src/main.cpp
Normal file
|
@ -0,0 +1,477 @@
|
|||
#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
|
||||
|
||||
}
|
||||
}
|
3
upload_params.ini.dist
Normal file
3
upload_params.ini.dist
Normal file
|
@ -0,0 +1,3 @@
|
|||
[env]
|
||||
upload_protocol = custom
|
||||
upload_url = http://admin:admin@remotehost/update
|
Loading…
Reference in a new issue