Compare commits
14 Commits
74da018c99
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 1bcbd3b8e3 | |||
| c6ad3f2259 | |||
| 156e095d4d | |||
| 8caffd50b2 | |||
| dc995d6e7e | |||
| 6d57da3402 | |||
| a21706f328 | |||
| 0b216e2e83 | |||
| d27a42c7e8 | |||
| e81509a295 | |||
| bbdef13516 | |||
| bb3e4fa27c | |||
| 8f2444caeb | |||
| 2719314506 |
@@ -25,3 +25,25 @@
|
|||||||
### 19.02.2022
|
### 19.02.2022
|
||||||
+ Stable and fast csv database implemented.
|
+ Stable and fast csv database implemented.
|
||||||
+ Basic REST API: read, create and delete users / read and drop csv db
|
+ Basic REST API: read, create and delete users / read and drop csv db
|
||||||
|
|
||||||
|
### 19.03.2022
|
||||||
|
+ Settings pos
|
||||||
|
+ WifiSSID - terminator 0x00 - Pos.: 0-31 (00 - 1F)
|
||||||
|
+ WifiPassword - terminator 0x00 - Pos.: 32-63 (20 - 3F)
|
||||||
|
+ Device IP - 40-43 uint32
|
||||||
|
+ Subnet 44 - 47
|
||||||
|
+ Gateway 48 - 4B
|
||||||
|
+ Mode 0 - station; 1 - client 4C uint8
|
||||||
|
+ AuthError Timeout (secs) 4D
|
||||||
|
+ OpenLock hold (secs) 4E
|
||||||
|
### 20.03.2022
|
||||||
|
+ settings API implemented and tested
|
||||||
|
+ TODO: factory reset
|
||||||
|
|
||||||
|
### 10.04.2022
|
||||||
|
+ Toni:
|
||||||
|
+ Reed Contact -> Door closed?
|
||||||
|
+ Tracking in->out
|
||||||
|
+ Stats
|
||||||
|
+ Sensors
|
||||||
|
|
||||||
@@ -5,9 +5,14 @@ Adress 0x21
|
|||||||
### 1.1 Wiring
|
### 1.1 Wiring
|
||||||
* Red: 3.3V
|
* Red: 3.3V
|
||||||
* Black GND
|
* Black GND
|
||||||
* Green SDA -> D3
|
* Green SDA -> D2
|
||||||
* Grey SCL -> D4
|
* Grey SCL -> D1
|
||||||
|
|
||||||
## 2.0 LCD
|
## 2.0 LCD
|
||||||
The LCD display is driven on the same i2c bus as the keypad.
|
The LCD display is driven on the same i2c bus as the keypad.
|
||||||
|
|
||||||
|
nterface Definition
|
||||||
|
BYTE BIT
|
||||||
|
7 (MSB) 6 5 4 3 2 1 0 (LSB)
|
||||||
|
I2C slave address L H L L A2 A1 A0 R/W
|
||||||
|
I/O data bus P7 P6 P5 P4 P3 P2 P1 P0
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -1,7 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"NAME":"Toni",
|
|
||||||
"PASSWORD":"geheim",
|
|
||||||
"PIN":1231239
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"THEMEID": 1,
|
|
||||||
"SSID":"DoorLock",
|
|
||||||
"PASS":"geheim123"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 25.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.2" baseProfile="tiny" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
x="0px" y="0px" viewBox="0 0 500 500" overflow="visible" xml:space="preserve">
|
||||||
|
<polyline fill="#CCCCCC" stroke="#000000" stroke-miterlimit="10" points="181.5,395.5 86.5,395.5 86.5,300.5 "/>
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<rect x="355.37" y="40.63" transform="matrix(0.7071 -0.7071 0.7071 0.7071 31.9147 294.7168)" fill="#CCCCCC" stroke="#000000" stroke-width="1" stroke-miterlimit="9.9999" width="32.69" height="136.4"/>
|
||||||
|
|
||||||
|
<rect x="94.59" y="168.11" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -95.5641 241.9096)" fill="#CCCCCC" stroke="#000000" stroke-width="1" stroke-miterlimit="9.9999" width="299.29" height="136.4"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 881 B |
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 25.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.2" baseProfile="tiny" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
x="0px" y="0px" viewBox="0 0 500 500" overflow="visible" xml:space="preserve">
|
||||||
|
<path fill="#CCCCCC" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" d="M349.5,421.5h-199c-8.28,0-15-6.72-15-15
|
||||||
|
v-255h229v255C364.5,414.78,357.78,421.5,349.5,421.5z"/>
|
||||||
|
<path fill="#CCCCCC" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" d="M386.81,99.5H314.5V80.38
|
||||||
|
c0-9.88-8.01-17.88-17.88-17.88h-92.24c-9.88,0-17.88,8.01-17.88,17.88V99.5h-73.31c-5.35,0-9.69,4.34-9.69,9.69v20.62
|
||||||
|
c0,5.35,4.34,9.69,9.69,9.69h273.62c5.35,0,9.69-4.34,9.69-9.69v-20.62C396.5,103.84,392.16,99.5,386.81,99.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 874 B |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>doorlock_pwa</title><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-capable" content="yes"><link rel="apple-touch-icon" href="/assets/icons/apple-touch-icon.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#673ab8"><style>*{box-sizing:border-box}html{font-family:Helvetica,sans-serif;font-size:16px}body,html{height:100%}body{background-color:#fff;margin:0;padding:0;width:100%}</style><link href="/bundle.3bf6c.css" rel="stylesheet" media="only x" onload="this.media='all'"><noscript><link rel="stylesheet" href="/bundle.3bf6c.css"></noscript></head><body><script defer="defer" src="/bundle.4bdf3.js"></script><script nomodule="" src="/polyfills.914a6.js"></script></body></html>
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Document</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Error 404: Page not found.</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Document</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Welcome on ESP8266</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"GREET":"Hallo $name",
|
|
||||||
"FAIL":"PIN Incorrect",
|
|
||||||
"HOME":"Willkommen!|Pin oder Karte"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include "AdminAuth.h"
|
||||||
|
using namespace webconsole;
|
||||||
|
bool AdminAuth::isAuth(const char *token)
|
||||||
|
{
|
||||||
|
return tokenbuffer->exists(token) != -1;
|
||||||
|
}
|
||||||
|
bool AdminAuth::logout(const char *token)
|
||||||
|
{
|
||||||
|
int16_t tid = tokenbuffer->exists(token);
|
||||||
|
tokenbuffer->setnull(tid);
|
||||||
|
return tid != -1;
|
||||||
|
}
|
||||||
|
char *AdminAuth::login(const String& username, const String& password)
|
||||||
|
{
|
||||||
|
char *res = nullptr;
|
||||||
|
File adminfile = LittleFS.open("admin", "r");
|
||||||
|
if (username.equals(adminfile.readStringUntil('\0')) && password.equals(adminfile.readStringUntil('\0')))
|
||||||
|
res = tokenbuffer->newToken();
|
||||||
|
adminfile.close();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
bool AdminAuth::setAuth(const String& username, const String& password)
|
||||||
|
{
|
||||||
|
if (username.length() == 0 || username.length() > MAX_USERNAMEPASSWORD_LENGTH || password.length()==0 || password.length() > MAX_USERNAMEPASSWORD_LENGTH)
|
||||||
|
return false;
|
||||||
|
File adminfile = LittleFS.open("admin", "w+");
|
||||||
|
adminfile.print(username);
|
||||||
|
adminfile.print('\0');
|
||||||
|
adminfile.print(password);
|
||||||
|
adminfile.print('\0');
|
||||||
|
delay(1);
|
||||||
|
adminfile.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "LittleFS.h"
|
||||||
|
#define TOKENBUFFERCAPACITY 3
|
||||||
|
#define TOKENLENGHT 10
|
||||||
|
#define MAX_USERNAMEPASSWORD_LENGTH 25
|
||||||
|
namespace webconsole
|
||||||
|
{
|
||||||
|
|
||||||
|
struct TokenBuffer
|
||||||
|
{
|
||||||
|
|
||||||
|
TokenBuffer()
|
||||||
|
{
|
||||||
|
for (uint16_t j = 0; j < TOKENBUFFERCAPACITY; j++)
|
||||||
|
setnull(j);
|
||||||
|
}
|
||||||
|
char *newToken()
|
||||||
|
{
|
||||||
|
if (next >= TOKENBUFFERCAPACITY)
|
||||||
|
next = 0;
|
||||||
|
for (uint16_t i = 0; i < TOKENLENGHT; i++)
|
||||||
|
token[next][i] = randomChar();
|
||||||
|
token[next][TOKENLENGHT] = 0x00;
|
||||||
|
|
||||||
|
return &token[next++][0];
|
||||||
|
}
|
||||||
|
int16_t exists(const char *cmp)
|
||||||
|
{
|
||||||
|
for (uint16_t j = 0; j < TOKENBUFFERCAPACITY; j++)
|
||||||
|
{
|
||||||
|
bool match = true;
|
||||||
|
for (uint16_t i = 0; match && i < TOKENLENGHT; i++)
|
||||||
|
if (token[j][i] != cmp[i])
|
||||||
|
match = false;
|
||||||
|
if (match)
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
void setnull(uint16_t index)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < TOKENBUFFERCAPACITY)
|
||||||
|
for (uint16_t i = 0; i < TOKENLENGHT; i++)
|
||||||
|
token[index][i] = 0x00; // initialize with nullbytes
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t next = 0;
|
||||||
|
char token[TOKENBUFFERCAPACITY][TOKENLENGHT + 1];
|
||||||
|
char randomChar()
|
||||||
|
{
|
||||||
|
auto charrype = random(0, 3);
|
||||||
|
switch (charrype)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return random(0x30, 0x3A); // Numbers
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
return random(0x41, 0x5B); // Capital letters
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
return random(0x61, 0x7B); // Lowercase letters
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0x21;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class AdminAuth
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//char *login(const char *username, const char *password);
|
||||||
|
char *login(const String &username,const String &password);
|
||||||
|
bool isAuth(const char *token);
|
||||||
|
bool logout(const char *token);
|
||||||
|
bool setAuth(const String &username, const String &password);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TokenBuffer *tokenbuffer = new TokenBuffer();
|
||||||
|
};
|
||||||
|
}
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
Config::Config(const char* filepath): _filepath(filepath)
|
||||||
|
{
|
||||||
|
if (!LittleFS.begin())
|
||||||
|
{
|
||||||
|
Serial.println("An Error has occurred while mounting LittleFS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Config::~Config()
|
||||||
|
{
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
bool Config::setFilePath(const char* filepath){
|
||||||
|
this->_filepath = filepath;
|
||||||
|
return loadBin();
|
||||||
|
}
|
||||||
|
bool Config::loadBin()
|
||||||
|
{
|
||||||
|
File config_file = LittleFS.open(_filepath, "r");
|
||||||
|
//if (buffer == nullptr) // Allocate only once
|
||||||
|
// buffer = (uint8_t *)malloc(CONFIG_SIZE);
|
||||||
|
if (config_file.available())
|
||||||
|
{
|
||||||
|
config_file.read(buffer, CONFIG_SIZE);
|
||||||
|
buffer[OFFSET_SSID + 0x1F] = 0x00; // ensure ssid and password are terminated with a null character
|
||||||
|
buffer[OFFSET_PASS + 0x1F] = 0x00;
|
||||||
|
}
|
||||||
|
config_file.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Config::saveBin()
|
||||||
|
{
|
||||||
|
File config_file = LittleFS.open(_filepath, "w");
|
||||||
|
config_file.write(buffer, CONFIG_SIZE);
|
||||||
|
delay(100);
|
||||||
|
config_file.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool Config::setSSID(const char *ssid)
|
||||||
|
{
|
||||||
|
size_t offset = 0;
|
||||||
|
bool copy = true;
|
||||||
|
for (int i = 0; i < 31; i++)
|
||||||
|
{
|
||||||
|
if (ssid[i] == 0x00)
|
||||||
|
copy = false;
|
||||||
|
buffer[offset + i] = copy ? ssid[i] : 0x00;
|
||||||
|
}
|
||||||
|
buffer[offset + 31] = '\n';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool Config::setPASS(const char *pass)
|
||||||
|
{
|
||||||
|
size_t offset = 0x20;
|
||||||
|
bool copy = true;
|
||||||
|
for (int i = 0; i < 31; i++)
|
||||||
|
{
|
||||||
|
if (pass[i] == 0x00)
|
||||||
|
copy = false;
|
||||||
|
buffer[offset + i] = copy ? pass[i] : 0x00;
|
||||||
|
}
|
||||||
|
buffer[offset + 31] = '\n';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void Config::print()
|
||||||
|
{
|
||||||
|
Serial.print("BufferHEX ");
|
||||||
|
for (int i = 0; i < CONFIG_SIZE; i++)
|
||||||
|
{
|
||||||
|
Serial.print(i, HEX);
|
||||||
|
Serial.print(">");
|
||||||
|
Serial.print(buffer[i], HEX);
|
||||||
|
Serial.print("|");
|
||||||
|
}
|
||||||
|
Serial.print("SSID: ");
|
||||||
|
Serial.println(SSID);
|
||||||
|
Serial.print("PASS: ");
|
||||||
|
Serial.println(PASS);
|
||||||
|
Serial.print("IP");
|
||||||
|
Serial.print(*((uint8_t *)&ip), DEC);
|
||||||
|
Serial.print(".");
|
||||||
|
Serial.print(* (((uint8_t *)&ip + 1)), DEC);
|
||||||
|
Serial.print(".");
|
||||||
|
Serial.print(* ((uint8_t *)&ip + 2), DEC);
|
||||||
|
Serial.print(".");
|
||||||
|
Serial.println( * ((uint8_t *)&ip + 3), DEC);
|
||||||
|
Serial.print("GW");
|
||||||
|
Serial.print( * ((uint8_t *)&gw), DEC);
|
||||||
|
Serial.print(".");
|
||||||
|
Serial.print( * ((uint8_t *)&gw + 1), DEC);
|
||||||
|
Serial.print(".");
|
||||||
|
Serial.print( * ((uint8_t *)&gw + 2), DEC);
|
||||||
|
Serial.print(".");
|
||||||
|
Serial.println( * ((uint8_t *)&gw + 3), DEC);
|
||||||
|
Serial.print("SUBNET");
|
||||||
|
Serial.print( * ((uint8_t *)&subnet), DEC);
|
||||||
|
Serial.print(".");
|
||||||
|
Serial.print( * ((uint8_t *)&subnet + 1), DEC);
|
||||||
|
Serial.print(".");
|
||||||
|
Serial.print( * ((uint8_t *)&subnet + 2), DEC);
|
||||||
|
Serial.print(".");
|
||||||
|
Serial.println( * ((uint8_t *)&subnet + 3), DEC);
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "LittleFS.h"
|
||||||
|
#include "ArduinoJson.h"
|
||||||
|
#define OFFSET_SSID 0x00
|
||||||
|
#define OFFSET_PASS 0x20
|
||||||
|
#define OFFSET_IP 0x40
|
||||||
|
#define OFFSET_SUBNET 0x44
|
||||||
|
#define OFFSET_GW 0x48
|
||||||
|
#define OFFSET_MODE 0x4C
|
||||||
|
#define OFFSET_FAIL_TIMEOUT 0x4D
|
||||||
|
#define OFFSET_HOLD_TIME 0x4E
|
||||||
|
#define CONFIG_SIZE 0x4F
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
uint8_t *buffer = (uint8_t *)malloc(CONFIG_SIZE);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Config(const char* filepath);
|
||||||
|
~Config();
|
||||||
|
const char *SSID = (char *)(buffer + OFFSET_SSID);
|
||||||
|
const char *PASS = (char *)(buffer + OFFSET_PASS);
|
||||||
|
uint32_t &ip = *((uint32_t*)(buffer+OFFSET_IP));
|
||||||
|
uint32_t &subnet = *((uint32_t*)(buffer+OFFSET_SUBNET));
|
||||||
|
uint32_t &gw = *((uint32_t*)(buffer+OFFSET_GW));
|
||||||
|
uint8_t &mode = *(buffer + OFFSET_MODE);
|
||||||
|
uint8_t &fail_timeout = *(buffer+OFFSET_FAIL_TIMEOUT);
|
||||||
|
uint8_t &hold_time = *(buffer+OFFSET_HOLD_TIME);
|
||||||
|
const char* _filepath;
|
||||||
|
bool setFilePath(const char* filepath);
|
||||||
|
bool loadConfig();
|
||||||
|
bool loadBin();
|
||||||
|
bool saveBin();
|
||||||
|
void print();
|
||||||
|
bool setSSID(const char *ssid);
|
||||||
|
bool setPASS(const char *pass);
|
||||||
|
};
|
||||||
+2
-4
@@ -1,8 +1,6 @@
|
|||||||
#include "Keyboard.h"
|
#include "Keyboard.h"
|
||||||
|
|
||||||
//#define DEBUG
|
|
||||||
#define PIN_WIRE_SDA D3
|
|
||||||
#define PIN_WIRE_SCL D4
|
|
||||||
Keyboard::Keyboard(uint8_t _debounce)
|
Keyboard::Keyboard(uint8_t _debounce)
|
||||||
{
|
{
|
||||||
this->keybind.insert({
|
this->keybind.insert({
|
||||||
@@ -23,7 +21,7 @@ Keyboard::Keyboard(uint8_t _debounce)
|
|||||||
}
|
}
|
||||||
void Keyboard::begin(TwoWire *databus)
|
void Keyboard::begin(TwoWire *databus)
|
||||||
{
|
{
|
||||||
pcf8574 = new PCF8574(databus, 0x21, PIN_WIRE_SDA, PIN_WIRE_SCL);
|
pcf8574 = new PCF8574(databus, 0x21);
|
||||||
pcf8574->pinMode(0, OUTPUT);
|
pcf8574->pinMode(0, OUTPUT);
|
||||||
for (int i = 1; i < 8; i++)
|
for (int i = 1; i < 8; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
#include "Persistence.h"
|
|
||||||
|
|
||||||
Persistence::Persistence()
|
|
||||||
{
|
|
||||||
if (!LittleFS.begin())
|
|
||||||
{
|
|
||||||
Serial.println("An Error has occurred while mounting LittleFS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String Persistence::TestRead()
|
|
||||||
{
|
|
||||||
File file = LittleFS.open("/users.json", "r");
|
|
||||||
String result = "";
|
|
||||||
if (!file)
|
|
||||||
{
|
|
||||||
Serial.println("Failed to open file for reading");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
Serial.println("File Content:");
|
|
||||||
while (file.available())
|
|
||||||
{
|
|
||||||
result += (char)file.read();
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Persistence::Configuration Persistence::loadConfig()
|
|
||||||
{
|
|
||||||
StaticJsonDocument<1024> doc;
|
|
||||||
File config_file = LittleFS.open("/config.json","r");
|
|
||||||
deserializeJson(doc, config_file);
|
|
||||||
Configuration res;
|
|
||||||
res.SSID=doc["SSID"].as<const char*>();
|
|
||||||
res.PASS=doc["PASS"].as<const char*>();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
DynamicJsonDocument Persistence::readUsers(){
|
|
||||||
File user_file = LittleFS.open("/users.json","r");
|
|
||||||
size_t buffersize = user_file.size()+512;
|
|
||||||
Serial.printf("Reserved %i for reading UserDB File", buffersize);
|
|
||||||
DynamicJsonDocument doc(user_file.size()+512);
|
|
||||||
deserializeJson(doc, user_file);
|
|
||||||
user_file.close();
|
|
||||||
return doc;
|
|
||||||
}
|
|
||||||
bool Persistence::saveUsers(DynamicJsonDocument &doc){
|
|
||||||
File user_file = LittleFS.open("/users.json","w");
|
|
||||||
serializeJson(doc, user_file);
|
|
||||||
user_file.close();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "LittleFS.h"
|
|
||||||
#include "ArduinoJson.h"
|
|
||||||
class Persistence
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Persistence();
|
|
||||||
String TestRead();
|
|
||||||
typedef struct Configuration{
|
|
||||||
const char* SSID;
|
|
||||||
const char* PASS;
|
|
||||||
int THEME;
|
|
||||||
} config;
|
|
||||||
Configuration loadConfig();
|
|
||||||
DynamicJsonDocument readUsers();
|
|
||||||
bool saveUsers(DynamicJsonDocument &doc);
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "Relais.h"
|
||||||
|
|
||||||
|
Relais::Relais(uint8_t pin)
|
||||||
|
{
|
||||||
|
pinMode(pin, OUTPUT);
|
||||||
|
digitalWrite(_pin, 1);
|
||||||
|
_pin = pin;
|
||||||
|
}
|
||||||
|
void Relais::cylce()
|
||||||
|
{
|
||||||
|
if (!_state)
|
||||||
|
return;
|
||||||
|
if (millis() > _call_time)
|
||||||
|
{
|
||||||
|
digitalWrite(_pin, 1);
|
||||||
|
_state = false;
|
||||||
|
Serial.println("Relay released.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Relais::activate(uint8_t seconds)
|
||||||
|
{
|
||||||
|
Serial.println("Relay activated for "+String(seconds)+" Seconds.");
|
||||||
|
digitalWrite(_pin, 0);
|
||||||
|
_state = true;
|
||||||
|
_call_time = millis() + seconds * 1000;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
class Relais
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Relais(uint8_t pin);
|
||||||
|
void cylce();
|
||||||
|
void activate(uint8_t seconds);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned long _call_time = 0;
|
||||||
|
bool _state = false;
|
||||||
|
uint8_t _pin;
|
||||||
|
};
|
||||||
+4
-3
@@ -1,8 +1,8 @@
|
|||||||
#include "Rfid.h"
|
#include "Rfid.h"
|
||||||
#define SS_PIN D8
|
#define SS_PIN D8
|
||||||
#define RST_PIN D1
|
#define RST_PIN D0
|
||||||
#define RFID_TIMEOUT 3000
|
#define RFID_TIMEOUT 3000
|
||||||
Rfid::Rfid(/* args */) : _mfrc522(SS_PIN)
|
Rfid::Rfid(/* args */) : _mfrc522(SS_PIN, RST_PIN)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,11 +33,12 @@ void Rfid::scan()
|
|||||||
{
|
{
|
||||||
_status = 1;
|
_status = 1;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
Serial.print(this->_rfid);
|
_mfrc522.PICC_DumpToSerial(&(_mfrc522.uid));
|
||||||
#endif
|
#endif
|
||||||
this->_lastRfid = this->_rfid;
|
this->_lastRfid = this->_rfid;
|
||||||
this->_lastRfidScan = millis();
|
this->_lastRfidScan = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Persistence.h"
|
#include "Config.h"
|
||||||
#include "LittleFS.h"
|
#include "LittleFS.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
@@ -46,6 +46,8 @@ namespace userdb
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserDb
|
class UserDb
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -148,7 +150,8 @@ namespace userdb
|
|||||||
{
|
{
|
||||||
close_file();
|
close_file();
|
||||||
}
|
}
|
||||||
void close_file(){
|
void close_file()
|
||||||
|
{
|
||||||
db_file.close();
|
db_file.close();
|
||||||
}
|
}
|
||||||
bool has_next()
|
bool has_next()
|
||||||
|
|||||||
-118
@@ -1,118 +0,0 @@
|
|||||||
#include "Users.h"
|
|
||||||
|
|
||||||
|
|
||||||
Users::Users(Persistence &p): persistence(p){
|
|
||||||
}
|
|
||||||
Users::~Users()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
bool Users::ImportFromPersistence(){
|
|
||||||
Serial.println("Importing Users from Persistence");
|
|
||||||
DynamicJsonDocument user_persistence = persistence.readUsers();
|
|
||||||
JsonArray array = user_persistence.as<JsonArray>();
|
|
||||||
for(JsonObject userdata : array){
|
|
||||||
User *imported = new User();
|
|
||||||
imported->uid= userdata["ID"].as<unsigned long>();
|
|
||||||
if(_userdb.count(*imported))
|
|
||||||
continue;
|
|
||||||
imported->first_name = userdata["FIRST_NAME"].as<String>();
|
|
||||||
imported->last_name = userdata["LAST_NAME"].as<String>();
|
|
||||||
imported->rfid_uid = userdata["RFID_UID"].as<String>();
|
|
||||||
imported->user_pin = userdata["USER_PIN"].as<String>();
|
|
||||||
imported->enabled = userdata["ENABLED"].as<bool>();
|
|
||||||
this->_userdb.insert(*imported);
|
|
||||||
}
|
|
||||||
Serial.println("User import is done!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Users::ExportToPersistence(){
|
|
||||||
Serial.println("Exporting Users to Persistence");
|
|
||||||
size_t user_amount = _userdb.size()+2;
|
|
||||||
Serial.print( " User Amount: " );
|
|
||||||
Serial.print(user_amount);
|
|
||||||
size_t capacity = JSON_ARRAY_SIZE(user_amount) + user_amount*JSON_OBJECT_SIZE(6);
|
|
||||||
Serial.print(" JSON Capacity");
|
|
||||||
Serial.print(capacity);
|
|
||||||
Serial.print(" Free Heap: ");
|
|
||||||
Serial.print(ESP.getFreeHeap());
|
|
||||||
//DynamicJsonDocument<CAPACITY> doc;
|
|
||||||
DynamicJsonDocument doc(capacity);
|
|
||||||
|
|
||||||
for(Users::User u : _userdb){
|
|
||||||
JsonObject exported_user = doc.createNestedObject();
|
|
||||||
exported_user["ID"] = u.uid;
|
|
||||||
exported_user["FIRST_NAME"] = u.first_name;
|
|
||||||
exported_user["LAST_NAME"] = u.last_name;
|
|
||||||
exported_user["RFID_UID"] = u.rfid_uid;
|
|
||||||
exported_user["USER_PIN"] = u.user_pin;
|
|
||||||
exported_user["ENABLED"] = u.enabled;
|
|
||||||
}
|
|
||||||
persistence.saveUsers(doc);
|
|
||||||
Serial.println("User export is done!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool Users::checkPin(String pin_code, std::vector<Users::User> *logon_users)
|
|
||||||
{
|
|
||||||
std::copy_if(this->_userdb.begin(), this->_userdb.end(), inserter(*logon_users, logon_users->end()), [=](user_account user)
|
|
||||||
{ return pin_code == user.user_pin&&user.enabled==true; });
|
|
||||||
return (logon_users->size() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Users::checkRfid(String rfid_code, std::vector<Users::User> *logon_users)
|
|
||||||
{
|
|
||||||
std::copy_if(this->_userdb.begin(), this->_userdb.end(), inserter(*logon_users, logon_users->end()), [=](user_account user)
|
|
||||||
{ return rfid_code == user.rfid_uid&&user.enabled==true; });
|
|
||||||
return (logon_users->size() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long Users::addUser(String first_name, String last_name, String rifd_uid, String user_pin)
|
|
||||||
{
|
|
||||||
Serial.println("Adding User:" + first_name);
|
|
||||||
unsigned long new_id = this->_userdb.rbegin()->uid + 1;
|
|
||||||
this->_userdb.insert(user_account{.uid = new_id, .first_name = first_name, .last_name = last_name, .rfid_uid = rifd_uid, .user_pin = user_pin});
|
|
||||||
return new_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Users::delUser(unsigned long id)
|
|
||||||
{
|
|
||||||
return this->_userdb.erase(user_account{.uid = id})>0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Users::updateUser(unsigned long id, ATTRIBUTES attr, String value)
|
|
||||||
{
|
|
||||||
auto modify = this->_userdb.find(user_account{.uid = id});
|
|
||||||
switch (attr)
|
|
||||||
{
|
|
||||||
case FIRST_NAME:
|
|
||||||
modify->first_name = value;
|
|
||||||
break;
|
|
||||||
case LAST_NAME:
|
|
||||||
modify->last_name = value;
|
|
||||||
break;
|
|
||||||
case RFID_UID:
|
|
||||||
modify->rfid_uid = value;
|
|
||||||
break;
|
|
||||||
case USER_PIN:
|
|
||||||
modify->user_pin = value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Users::countUsers(){
|
|
||||||
return _userdb.size();
|
|
||||||
}
|
|
||||||
String Users::toString(User *user)
|
|
||||||
{
|
|
||||||
return "UID: " + String(user->uid) + " Name: " + user->first_name + " " + user->last_name;
|
|
||||||
}
|
|
||||||
void Users::PrintAllToSerial(){
|
|
||||||
Serial.println("#All Users:");
|
|
||||||
for(Users::User u : _userdb){
|
|
||||||
Serial.println(toString(&u));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <set>
|
|
||||||
#include <vector>
|
|
||||||
#include "Persistence.h"
|
|
||||||
class Users
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum ATTRIBUTES{
|
|
||||||
FIRST_NAME,
|
|
||||||
LAST_NAME,
|
|
||||||
RFID_UID,
|
|
||||||
USER_PIN
|
|
||||||
};
|
|
||||||
typedef struct User
|
|
||||||
{
|
|
||||||
//User(unsigned long new_uid): uid(new_uid){}
|
|
||||||
unsigned long uid;
|
|
||||||
mutable String first_name;
|
|
||||||
mutable String last_name;
|
|
||||||
mutable String rfid_uid;
|
|
||||||
mutable String user_pin;
|
|
||||||
mutable bool enabled = true;
|
|
||||||
bool operator < (const User &o) const { return uid <o.uid; }
|
|
||||||
bool operator == (const User &o) const { return uid ==o.uid; }
|
|
||||||
|
|
||||||
} user_account;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::set<User> _userdb;
|
|
||||||
Persistence &persistence;
|
|
||||||
/* data */
|
|
||||||
public:
|
|
||||||
Users(Persistence &persistence);
|
|
||||||
~Users();
|
|
||||||
bool checkRfid(String rfid_code, std::vector<Users::User> *logon_users);
|
|
||||||
bool checkPin(String pin_code, std::vector<Users::User> *logon_users);
|
|
||||||
unsigned long addUser(String first_name, String last_name, String rifd_uid, String user_pin);
|
|
||||||
bool delUser(unsigned long id);
|
|
||||||
bool updateUser(unsigned long id, ATTRIBUTES attr, String value);
|
|
||||||
String toString(User *user);
|
|
||||||
bool ImportFromPersistence();
|
|
||||||
bool ExportToPersistence();
|
|
||||||
void PrintAllToSerial();
|
|
||||||
size_t countUsers();
|
|
||||||
};
|
|
||||||
+258
-47
@@ -2,29 +2,52 @@
|
|||||||
|
|
||||||
using namespace webconsole;
|
using namespace webconsole;
|
||||||
|
|
||||||
WebConsole::WebConsole() {}
|
WebConsole::WebConsole()
|
||||||
|
{
|
||||||
|
}
|
||||||
WebConsole::~WebConsole()
|
WebConsole::~WebConsole()
|
||||||
{
|
{
|
||||||
_server->close();
|
_server->close();
|
||||||
delete (_server);
|
delete (_server);
|
||||||
}
|
}
|
||||||
bool WebConsole::init(userdb::UserDb *userdb)
|
bool WebConsole::init(Config *config, userdb::UserDb *userdb)
|
||||||
{
|
{
|
||||||
_server = new ESP8266WebServer(80);
|
|
||||||
this->userdb = userdb;
|
this->userdb = userdb;
|
||||||
|
this->_config = config;
|
||||||
|
this->userdb = userdb;
|
||||||
|
// Wifi Setup
|
||||||
|
this->_dnsServer = new DNSServer;
|
||||||
|
WiFi.mode(WIFI_AP);
|
||||||
|
Serial.print("\t1. Network config... ");
|
||||||
|
Serial.println(WiFi.softAPConfig(_config->ip, _config->gw, _config->subnet) ? "Ready" : "Failed!");
|
||||||
|
Serial.print("\t2. DNS config... ");
|
||||||
|
Serial.println(_dnsServer->start(53, "*", _config->ip) ? "Ready" : "Failed!");
|
||||||
|
Serial.print("\t3 AP setup SSID:\"" + String(_config->SSID) + "\"... ");
|
||||||
|
if (strlen(_config->PASS) > 0)
|
||||||
|
Serial.println(WiFi.softAP(_config->SSID, _config->PASS) ? "Ready" : "Failed!");
|
||||||
|
else
|
||||||
|
Serial.println(WiFi.softAP(_config->SSID) ? "Ready" : "Failed!");
|
||||||
|
WiFi.hostname("Doorlock");
|
||||||
|
Serial.println("Please connect via http://" + WiFi.softAPIP().toString() + "/");
|
||||||
|
|
||||||
|
// Webserver Setup
|
||||||
|
this->_server = new ESP8266WebServer(80);
|
||||||
|
const char *headerkeys[] = {"Authentification"};
|
||||||
|
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char *);
|
||||||
|
_server->collectHeaders(headerkeys, headerkeyssize);
|
||||||
_server->begin();
|
_server->begin();
|
||||||
|
_server->on("/api/auth", HTTPMethod::HTTP_POST, std::bind(&WebConsole::_auth, this));
|
||||||
_server->on("/api/userdb", HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_dropUserDb, this));
|
_server->on("/api/userdb", HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_dropUserDb, this));
|
||||||
_server->on("/api/userdb", HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUserDb, this));
|
_server->on("/api/userdb", HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUserDb, this));
|
||||||
_server->on("/api/rfid", std::bind(&WebConsole::_catchRFID, this));
|
_server->on("/api/rfid", HTTPMethod::HTTP_GET, std::bind(&WebConsole::_catchRFID, this));
|
||||||
_server->on("/api/debug/printfile", std::bind(&WebConsole::_print_db_raw, this));
|
_server->on("/api/debug/printfile", std::bind(&WebConsole::_print_db_raw, this));
|
||||||
_server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_deleteUser, this));
|
_server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_deleteUser, this));
|
||||||
_server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUser, this));
|
_server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUser, this));
|
||||||
_server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_PUT, std::bind(&WebConsole::_createUser, this));
|
_server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_PUT, std::bind(&WebConsole::_createUser, this));
|
||||||
_server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_POST, std::bind(&WebConsole::_updateUser, this));
|
_server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_POST, std::bind(&WebConsole::_updateUser, this));
|
||||||
_server->on(UriBraces("/api/config/{}"), std::bind(&WebConsole::_deleteUser, this));
|
_server->on(UriBraces("/api/config"), HTTPMethod::HTTP_POST, std::bind(&WebConsole::_settings, this));
|
||||||
//_server->on("/bypin",std::bind(&WebConsole::_findPin,this));
|
_server->serveStatic("/", LittleFS, "/s/");
|
||||||
_server->serveStatic("/", LittleFS, "/static/index.html");
|
_server->onNotFound(std::bind(&WebConsole::_handleUnknown, this));
|
||||||
//_server->onNotFound(std::bind(&WebConsole::_handleStatic, this));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void WebConsole::attachRfid(Rfid *rfid)
|
void WebConsole::attachRfid(Rfid *rfid)
|
||||||
@@ -33,51 +56,221 @@ void WebConsole::attachRfid(Rfid *rfid)
|
|||||||
}
|
}
|
||||||
void WebConsole::serve()
|
void WebConsole::serve()
|
||||||
{
|
{
|
||||||
if (catch_rfid && rfid != nullptr && rfid->available())
|
_dnsServer->processNextRequest();
|
||||||
|
if (catch_rfid)
|
||||||
|
{
|
||||||
|
if (millis() - catch_rfid_millis > 2000)
|
||||||
|
{
|
||||||
|
catch_rfid = 0;
|
||||||
|
}
|
||||||
|
else if (catch_rfid > 1 && rfid != nullptr && rfid->available())
|
||||||
{
|
{
|
||||||
rfid_buffer = rfid->getID();
|
rfid_buffer = rfid->getID();
|
||||||
catch_rfid_updated = true;
|
catch_rfid = 1;
|
||||||
catch_rfid = false;
|
}
|
||||||
}
|
}
|
||||||
_server->handleClient();
|
_server->handleClient();
|
||||||
}
|
}
|
||||||
bool WebConsole::isInterceptingRfid()
|
uint8_t WebConsole::isInterceptingRfid()
|
||||||
{
|
{
|
||||||
return catch_rfid;
|
if (catch_rfid == 3)
|
||||||
}
|
|
||||||
void WebConsole::_handleStatic()
|
|
||||||
{
|
{
|
||||||
String path = path_prefix + _server->uri();
|
catch_rfid = 2;
|
||||||
Serial.print("Request " + path);
|
return 3;
|
||||||
File src = LittleFS.open(path, "r");
|
|
||||||
if (src.isDirectory())
|
|
||||||
{
|
|
||||||
path += "index.html";
|
|
||||||
src.close();
|
|
||||||
src = LittleFS.open(path, "r");
|
|
||||||
}
|
|
||||||
if (!src)
|
|
||||||
{
|
|
||||||
path = "/static/error404.html";
|
|
||||||
src.close();
|
|
||||||
src = LittleFS.open(path, "r");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.println(" resolved to " + path);
|
return catch_rfid;
|
||||||
if (src)
|
}
|
||||||
|
bool WebConsole::_isAuth()
|
||||||
{
|
{
|
||||||
String content_type = "text/html";
|
if (_server->method() == HTTPMethod::HTTP_OPTIONS)
|
||||||
_server->streamFile(src, content_type);
|
{
|
||||||
src.close();
|
_server->send(204);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_server->hasHeader("Authentification"))
|
||||||
|
{
|
||||||
|
_server->send(401, "text/plain", "Error 401: Unauthorized (missing auth token)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const char *token = _server->header("Authentification").c_str();
|
||||||
|
bool res = auth.isAuth(token);
|
||||||
|
if (!res)
|
||||||
|
_server->send(401, "text/plain", "Error 401: Unauthorized (missing auth token)");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
void WebConsole::_auth()
|
||||||
|
{
|
||||||
|
_sendCORS();
|
||||||
|
String action = _server->arg("action");
|
||||||
|
if (action.equals("check"))
|
||||||
|
{
|
||||||
|
const char *token = _server->arg("token").c_str();
|
||||||
|
bool res = auth.isAuth(token);
|
||||||
|
_server->send(200, "text/plain", res ? "valid" : "invalid");
|
||||||
|
}
|
||||||
|
else if (action.equals("login"))
|
||||||
|
{
|
||||||
|
char *token = auth.login(_server->arg("username"), _server->arg("password"));
|
||||||
|
if (token == nullptr)
|
||||||
|
_server->send(401, "text/plain", "login_failed");
|
||||||
|
else
|
||||||
|
_server->send(200, "text/plain", token);
|
||||||
|
}
|
||||||
|
else if (action.equals("logout"))
|
||||||
|
{
|
||||||
|
const char *token = _server->arg("token").c_str();
|
||||||
|
bool res = auth.logout(token);
|
||||||
|
_server->send(200, "text/plain", res ? "success" : "failed");
|
||||||
|
}
|
||||||
|
else if (action.equals("update"))
|
||||||
|
{
|
||||||
|
// if (!_isAuth())
|
||||||
|
// return;
|
||||||
|
bool res = auth.setAuth(_server->arg("username"), _server->arg("password"));
|
||||||
|
_server->send(200, "text/plain", res ? "success" : "failed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_server->send(404, "text/plain", "unknown action");
|
||||||
|
}
|
||||||
|
void WebConsole::_settings()
|
||||||
|
{
|
||||||
|
_sendCORS();
|
||||||
|
if (!_isAuth())
|
||||||
|
return;
|
||||||
|
String action = _server->arg("action");
|
||||||
|
if (action.equals("update"))
|
||||||
|
{
|
||||||
|
uint8_t error = 0b00000000;
|
||||||
|
uint8_t sucess = 0b00000000;
|
||||||
|
if (_server->hasArg("SSID"))
|
||||||
|
{
|
||||||
|
_config->setSSID(_server->arg("SSID").c_str());
|
||||||
|
int length = strlen(_config->SSID);
|
||||||
|
if (length == 0 || length > 31)
|
||||||
|
error |= 0b00000001;
|
||||||
|
else
|
||||||
|
sucess |= 0b00000001;
|
||||||
|
}
|
||||||
|
if (_server->hasArg("PASS"))
|
||||||
|
{
|
||||||
|
_config->setSSID(_server->arg("PASS").c_str());
|
||||||
|
int length = strlen(_config->SSID);
|
||||||
|
if (length == 0 || length > 31)
|
||||||
|
error |= 0b00000010;
|
||||||
|
else
|
||||||
|
sucess |= 0b00000010;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_server->hasArg("ip"))
|
||||||
|
{
|
||||||
|
IPAddress temp;
|
||||||
|
if (temp.fromString(_server->arg("ip")))
|
||||||
|
{
|
||||||
|
_config->ip = temp.v4();
|
||||||
|
sucess |= 0b00000100;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error |= 0b00000100;
|
||||||
|
}
|
||||||
|
if (_server->hasArg("subnet"))
|
||||||
|
{
|
||||||
|
IPAddress temp;
|
||||||
|
if (temp.fromString(_server->arg("subnet")))
|
||||||
|
{
|
||||||
|
_config->subnet = temp.v4();
|
||||||
|
sucess |= 0b00001000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error |= 0b00001000;
|
||||||
|
}
|
||||||
|
if (_server->hasArg("gw"))
|
||||||
|
{
|
||||||
|
IPAddress temp;
|
||||||
|
if (temp.fromString(_server->arg("gw")))
|
||||||
|
{
|
||||||
|
_config->gw = temp.v4();
|
||||||
|
sucess |= 0b00010000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error |= 0b00010000;
|
||||||
|
}
|
||||||
|
if (_server->hasArg("mode"))
|
||||||
|
{
|
||||||
|
_config->mode = _server->arg("mode").toInt();
|
||||||
|
if (_config->mode >= 0 && _config->mode <= 1)
|
||||||
|
sucess |= 0b00100000;
|
||||||
|
else
|
||||||
|
error |= 0b00100000;
|
||||||
|
}
|
||||||
|
if (_server->hasArg("fail_timeout"))
|
||||||
|
{
|
||||||
|
_config->fail_timeout = _server->arg("fail_timeout").toInt();
|
||||||
|
if (_config->fail_timeout >= 0 && _config->fail_timeout <= 250)
|
||||||
|
sucess |= 0b01000000;
|
||||||
|
else
|
||||||
|
error |= 0b01000000;
|
||||||
|
}
|
||||||
|
if (_server->hasArg("hold_time"))
|
||||||
|
{
|
||||||
|
_config->hold_time = _server->arg("hold_time").toInt();
|
||||||
|
if (_config->hold_time > 0 && _config->hold_time <= 250)
|
||||||
|
sucess |= 0b10000000;
|
||||||
|
else
|
||||||
|
error |= 0b10000000;
|
||||||
|
}
|
||||||
|
if (error == 0 && sucess > 0)
|
||||||
|
{ // Save
|
||||||
|
_config->saveBin();
|
||||||
|
_server->send(200, "text/json", "{\"status\":\"ok\", \"sucess\":\"" + String(sucess) + "\"}");
|
||||||
|
}
|
||||||
|
else if (error > 0)
|
||||||
|
{ // Rollback
|
||||||
|
_config->loadBin();
|
||||||
|
_server->send(200, "text/json", "{\"status\":\"failed\", \"sucess\":\"" + String(sucess) + "\", \"error\":\"" + String(error) + "\"}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_server->send(200, "text/json", "{\"status\":\"noting_todo\"}");
|
||||||
|
}
|
||||||
|
else if (action.equals("get"))
|
||||||
|
{
|
||||||
|
_server->send(200, "text/json", "{\"SSID\":\"" + String(_config->SSID) + "\", \"PASS\":\"" + String(_config->PASS) + "\", \"ip\":\"" + IPAddress(_config->ip).toString() + "\", \"subnet\":\"" + IPAddress(_config->subnet).toString() + "\", \"gw\":\"" + IPAddress(_config->gw).toString() + "\", \"mode\":\"" + String(_config->mode) + "\", \"fail_timeout\":\"" + String(_config->fail_timeout) + "\", \"hold_time\":\"" + String(_config->hold_time) + "\"}");
|
||||||
|
}
|
||||||
|
else if (action.equals("apply"))
|
||||||
|
{
|
||||||
|
_server->send(200, "text/json", "{\"status\":\"restarting\"}");
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_server->send(404, "text/plain", "unknown action");
|
||||||
|
}
|
||||||
|
void WebConsole::_sendCORS()
|
||||||
|
{
|
||||||
|
_server->sendHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
_server->sendHeader("Access-Control-Max-Age", "10000");
|
||||||
|
_server->sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS,DELETE");
|
||||||
|
_server->sendHeader("Access-Control-Allow-Headers", "*");
|
||||||
|
}
|
||||||
|
void WebConsole::_handleUnknown()
|
||||||
|
{
|
||||||
|
_sendCORS();
|
||||||
|
if (_server->method() == HTTP_OPTIONS)
|
||||||
|
{
|
||||||
|
_server->send(204);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_server->send(500, "text/plain", "Internal error 500");
|
File src = LittleFS.open("s/index.html", "r");
|
||||||
|
_server->streamFile(src, "text/html");
|
||||||
|
src.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebConsole::_getUserDb()
|
void WebConsole::_getUserDb()
|
||||||
{
|
{
|
||||||
|
_sendCORS();
|
||||||
|
if (!_isAuth())
|
||||||
|
return;
|
||||||
File src = LittleFS.open("userdb.csv", "r");
|
File src = LittleFS.open("userdb.csv", "r");
|
||||||
if (src)
|
if (src)
|
||||||
{
|
{
|
||||||
@@ -87,6 +280,9 @@ void WebConsole::_getUserDb()
|
|||||||
}
|
}
|
||||||
void WebConsole::_deleteUser()
|
void WebConsole::_deleteUser()
|
||||||
{
|
{
|
||||||
|
_sendCORS();
|
||||||
|
if (!_isAuth())
|
||||||
|
return;
|
||||||
if (userdb == nullptr)
|
if (userdb == nullptr)
|
||||||
{
|
{
|
||||||
_server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}");
|
_server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}");
|
||||||
@@ -107,6 +303,9 @@ void WebConsole::_deleteUser()
|
|||||||
|
|
||||||
void WebConsole::_getUser()
|
void WebConsole::_getUser()
|
||||||
{
|
{
|
||||||
|
_sendCORS();
|
||||||
|
if (!_isAuth())
|
||||||
|
return;
|
||||||
if (userdb == nullptr)
|
if (userdb == nullptr)
|
||||||
{
|
{
|
||||||
_server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}");
|
_server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}");
|
||||||
@@ -125,6 +324,9 @@ void WebConsole::_getUser()
|
|||||||
}
|
}
|
||||||
void WebConsole::_updateUser()
|
void WebConsole::_updateUser()
|
||||||
{
|
{
|
||||||
|
_sendCORS();
|
||||||
|
if (!_isAuth())
|
||||||
|
return;
|
||||||
userdb::User updated;
|
userdb::User updated;
|
||||||
String body = _server->arg("plain");
|
String body = _server->arg("plain");
|
||||||
const int capacity = 256;
|
const int capacity = 256;
|
||||||
@@ -171,11 +373,14 @@ void WebConsole::_updateUser()
|
|||||||
updated.user_pin = doc["user_pin"].as<String>();
|
updated.user_pin = doc["user_pin"].as<String>();
|
||||||
if (doc.containsKey("enabled"))
|
if (doc.containsKey("enabled"))
|
||||||
updated.enabled = doc["enabled"].as<bool>();
|
updated.enabled = doc["enabled"].as<bool>();
|
||||||
bool res = userdb->update_user(&updated);
|
userdb->update_user(&updated);
|
||||||
_server->send(200, "text/json", updated.toJSONString().c_str());
|
_server->send(200, "text/json", updated.toJSONString().c_str());
|
||||||
}
|
}
|
||||||
void WebConsole::_createUser()
|
void WebConsole::_createUser()
|
||||||
{
|
{
|
||||||
|
_sendCORS();
|
||||||
|
if (!_isAuth())
|
||||||
|
return;
|
||||||
userdb::User created;
|
userdb::User created;
|
||||||
String body = _server->arg("plain");
|
String body = _server->arg("plain");
|
||||||
const int capacity = 1024;
|
const int capacity = 1024;
|
||||||
@@ -210,6 +415,9 @@ void WebConsole::_createUser()
|
|||||||
}
|
}
|
||||||
void WebConsole::_dropUserDb()
|
void WebConsole::_dropUserDb()
|
||||||
{
|
{
|
||||||
|
_sendCORS();
|
||||||
|
if (!_isAuth())
|
||||||
|
return;
|
||||||
if (userdb->drop())
|
if (userdb->drop())
|
||||||
_server->send(500, "text/json", "{\"ok\":\"UserDb dropped.\"}");
|
_server->send(500, "text/json", "{\"ok\":\"UserDb dropped.\"}");
|
||||||
else
|
else
|
||||||
@@ -217,24 +425,27 @@ void WebConsole::_dropUserDb()
|
|||||||
}
|
}
|
||||||
void WebConsole::_catchRFID()
|
void WebConsole::_catchRFID()
|
||||||
{
|
{
|
||||||
|
_sendCORS();
|
||||||
|
if (!_isAuth())
|
||||||
|
return;
|
||||||
if (rfid == nullptr)
|
if (rfid == nullptr)
|
||||||
{
|
{
|
||||||
_server->send(500, "text/json", "{\"error\":\"RFID not attached.\"}");
|
_server->send(500, "text/json", "{\"error\":\"RFID not attached.\",\"state\":\"" + String(catch_rfid) + "\"}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (catch_rfid_updated)
|
if (catch_rfid == 1)
|
||||||
{
|
{
|
||||||
String response = "{\"rfid_uid\":\"" + rfid_buffer + "\"}";
|
String response = "{\"rfid_uid\":\"" + rfid_buffer + "\",\"state\":\"" + String(catch_rfid) + "\"}";
|
||||||
_server->send(500, "text/json", response);
|
_server->send(200, "text/json", response);
|
||||||
catch_rfid_updated = false;
|
catch_rfid = 0;
|
||||||
}
|
|
||||||
else if (catch_rfid)
|
|
||||||
{
|
|
||||||
_server->send(500, "text/json", "{\"ok\":\"already activated\"}");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
catch_rfid = true;
|
catch_rfid = 3; // 3 - Start listening
|
||||||
_server->send(500, "text/json", "{\"ok\":\"now activated\"}");
|
catch_rfid_millis = millis();
|
||||||
|
_server->send(200, "text/json", "{\"ok\":\"listening\",\"state\":\"" + String(catch_rfid) + "\"}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void WebConsole::_updateAdmin()
|
||||||
|
{
|
||||||
|
}
|
||||||
+22
-6
@@ -1,4 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <ESP8266mDNS.h>
|
||||||
|
#include <DNSServer.h>
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
#include <uri/UriBraces.h>
|
#include <uri/UriBraces.h>
|
||||||
@@ -7,9 +9,12 @@
|
|||||||
#include "ArduinoJson.h"
|
#include "ArduinoJson.h"
|
||||||
#include "UserDb.h"
|
#include "UserDb.h"
|
||||||
#include "Rfid.h"
|
#include "Rfid.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "AdminAuth.h"
|
||||||
|
#include "Interface.h"
|
||||||
namespace webconsole
|
namespace webconsole
|
||||||
{
|
{
|
||||||
static const char path_prefix[] PROGMEM = "/static";
|
static const char path_prefix[] PROGMEM = "/s";
|
||||||
static ESP8266WebServer _server(80);
|
static ESP8266WebServer _server(80);
|
||||||
class WebConsole
|
class WebConsole
|
||||||
{
|
{
|
||||||
@@ -17,13 +22,17 @@ namespace webconsole
|
|||||||
WebConsole();
|
WebConsole();
|
||||||
~WebConsole();
|
~WebConsole();
|
||||||
|
|
||||||
bool init(userdb::UserDb *userdb);
|
bool init(Config *config, userdb::UserDb *userdb);
|
||||||
void attachRfid(Rfid *rfid);
|
void attachRfid(Rfid *rfid);
|
||||||
void serve();
|
void serve();
|
||||||
bool isInterceptingRfid();
|
uint8_t isInterceptingRfid();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _handleStatic();
|
void _sendCORS();
|
||||||
|
void _settings();
|
||||||
|
void _auth();
|
||||||
|
bool _isAuth();
|
||||||
|
void _handleUnknown();
|
||||||
void _getUserDb();
|
void _getUserDb();
|
||||||
void _deleteUser();
|
void _deleteUser();
|
||||||
void _getUser();
|
void _getUser();
|
||||||
@@ -31,21 +40,28 @@ namespace webconsole
|
|||||||
void _createUser();
|
void _createUser();
|
||||||
void _dropUserDb();
|
void _dropUserDb();
|
||||||
void _catchRFID();
|
void _catchRFID();
|
||||||
|
void _updateAdmin();
|
||||||
void _print_db_raw()
|
void _print_db_raw()
|
||||||
{
|
{
|
||||||
File f = LittleFS.open("userdb.csv", "r");
|
File f = LittleFS.open("userdb.csv", "r");
|
||||||
while (f.available()){
|
while (f.available())
|
||||||
|
{
|
||||||
f.sendAvailable(Serial);
|
f.sendAvailable(Serial);
|
||||||
}
|
}
|
||||||
_server->send(200, "text/plain", "printed to serial");
|
_server->send(200, "text/plain", "printed to serial");
|
||||||
f.close();
|
f.close();
|
||||||
}
|
}
|
||||||
|
Config *_config;
|
||||||
|
DNSServer *_dnsServer;
|
||||||
ESP8266WebServer *_server;
|
ESP8266WebServer *_server;
|
||||||
userdb::UserDb *userdb = nullptr;
|
userdb::UserDb *userdb = nullptr;
|
||||||
bool catch_rfid = false;
|
unsigned long catch_rfid_millis = 0;
|
||||||
|
uint8_t catch_rfid = 0;
|
||||||
bool catch_rfid_updated = false;
|
bool catch_rfid_updated = false;
|
||||||
String rfid_buffer;
|
String rfid_buffer;
|
||||||
Rfid *rfid = nullptr;
|
Rfid *rfid = nullptr;
|
||||||
|
Interface *iface = nullptr;
|
||||||
|
AdminAuth auth;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
+19
-30
@@ -7,45 +7,30 @@
|
|||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include "WebConsole.h"
|
#include "WebConsole.h"
|
||||||
#include "UserDb.h"
|
#include "UserDb.h"
|
||||||
#include "Persistence.h"
|
#include "Config.h"
|
||||||
// File persistence
|
#include "Relais.h"
|
||||||
Persistence persistence;
|
|
||||||
userdb::UserDb userdatabase("userdb.csv");
|
// File config
|
||||||
|
Config config("/settings");
|
||||||
|
userdb::UserDb userdatabase("/userdb.csv");
|
||||||
webconsole::WebConsole web;
|
webconsole::WebConsole web;
|
||||||
// Rfid
|
// Rfid
|
||||||
Rfid rfid;
|
Rfid rfid;
|
||||||
// i2C Bus
|
// i2C Bus
|
||||||
#define PIN_WIRE_SDA D3
|
#define PIN_WIRE_SDA D1
|
||||||
#define PIN_WIRE_SCL D4
|
#define PIN_WIRE_SCL D2
|
||||||
|
Relais relay(D4);
|
||||||
Keyboard keyboard(200);
|
Keyboard keyboard(200);
|
||||||
|
|
||||||
Interface iface;
|
Interface iface;
|
||||||
// Wifi control
|
|
||||||
IPAddress local_IP(192, 168, 4, 22);
|
|
||||||
IPAddress gateway(192, 168, 4, 9);
|
|
||||||
IPAddress subnet(255, 255, 255, 0);
|
|
||||||
|
|
||||||
// User DB
|
|
||||||
// Users users(persistence);
|
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
Persistence::Configuration config = persistence.loadConfig();
|
config.loadBin();
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Serial.println("Starting System");
|
Serial.println("Starting System...");
|
||||||
Serial.print("\t1. Network config ->");
|
|
||||||
Serial.println(WiFi.softAPConfig(local_IP, gateway, subnet) ? "Ready" : "Failed!");
|
|
||||||
Serial.print("\t2 AP setup " + String(config.SSID)+ " -> ");
|
|
||||||
if (strlen(config.PASS) > 0)
|
|
||||||
Serial.println(WiFi.softAP(config.SSID, config.PASS) ? "Ready" : "Failed!");
|
|
||||||
else
|
|
||||||
Serial.println(WiFi.softAP(config.SSID) ? "Ready" : "Failed!");
|
|
||||||
delay(150);
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
userdatabase.print_to_serial();
|
userdatabase.print_to_serial();
|
||||||
#endif
|
#endif
|
||||||
web.init(&userdatabase);
|
web.init(&config, &userdatabase);
|
||||||
keyboard.begin(&Wire);
|
keyboard.begin(&Wire);
|
||||||
rfid.begin();
|
rfid.begin();
|
||||||
web.attachRfid(&rfid);
|
web.attachRfid(&rfid);
|
||||||
@@ -54,15 +39,18 @@ void setup()
|
|||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
|
relay.cylce();
|
||||||
rfid.scan();
|
rfid.scan();
|
||||||
web.serve();
|
web.serve();
|
||||||
keyboard.scanAsync();
|
keyboard.scanAsync();
|
||||||
userdb::User login_user;
|
userdb::User login_user;
|
||||||
if(web.isInterceptingRfid()&&iface.getState()==0){
|
if (web.isInterceptingRfid() == 3)
|
||||||
iface.showMessage("WebUI connected", "Awaiting RFID", 3000);
|
iface.showMessage("WebUI connected", "Awaiting RFID", 2000);
|
||||||
}
|
else if (web.isInterceptingRfid() == 1)
|
||||||
|
iface.showMessage("WebUI connected", "RFID Found", 1000);
|
||||||
else if (iface.pinAvailable() && iface.getState() != Interface::GREET)
|
else if (iface.pinAvailable() && iface.getState() != Interface::GREET)
|
||||||
{
|
{
|
||||||
|
|
||||||
unsigned long delta = millis();
|
unsigned long delta = millis();
|
||||||
login_user = userdatabase.user_by_pin(iface.getPin());
|
login_user = userdatabase.user_by_pin(iface.getPin());
|
||||||
Serial.println("Query duration: " + String(millis() - delta));
|
Serial.println("Query duration: " + String(millis() - delta));
|
||||||
@@ -88,6 +76,7 @@ void loop()
|
|||||||
{
|
{
|
||||||
iface.greetUser(login_user.first_name + " " + login_user.last_name);
|
iface.greetUser(login_user.first_name + " " + login_user.last_name);
|
||||||
Serial.println("Logon from User " + login_user.toString());
|
Serial.println("Logon from User " + login_user.toString());
|
||||||
|
relay.activate(config.hold_time);
|
||||||
}
|
}
|
||||||
iface.render();
|
iface.render();
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user