This commit is contained in:
2025-12-12 08:35:20 +01:00
parent 356d7de2cf
commit 8a97b01622
21 changed files with 445 additions and 110 deletions

View File

@@ -32,7 +32,7 @@ void CFingerLogic::SetState(FingerLogicState state)
m_State = state;
if(m_State == STATE_READY || m_State == STATE_ERROR)
m_PowerOffTimer.start(false);
m_PowerOffTimer.startOnce();
else
{
m_PowerOffTimer.stop();
@@ -64,9 +64,9 @@ void CFingerLogic::PowerOn()
delayMilliseconds(100);
}
void CFingerLogic::OnFingerInterrupt(bool finger)
void CFingerLogic::OnFinger(bool finger)
{
debugf("OnFingerInterrupt: %s", finger ? "DOWN" : "UP");
debugf("OnFinger: %s", finger ? "DOWN" : "UP");
m_Finger = finger;
if(finger)
@@ -157,6 +157,27 @@ void CFingerLogic::InitFinger_OnReadSystemParameters(CFingerLogic *pThis, Finger
debugf("packetLength: %d", param->packetLength);
debugf("baudRate: %d", param->baudRate);
if(param->securityLevel != pThis->Main().Settings().m_SecurityLevel)
{
pThis->SetState(STATE_INIT_SETSYSTEM_SECURITYLEVEL);
pThis->FingerPrint().AsyncSetSystemParameter((SetSystemParameterCallback)InitFinger_OnSetSystemSecurityLevel, pThis, FINGERPRINT_SECURITY_REG_ADDR, pThis->Main().Settings().m_SecurityLevel);
}
else
{
pThis->SetState(STATE_INIT_READTEMPLATEMAP);
pThis->FingerPrint().AsyncReadTemplateMap((ReadTemplateMapCallback)InitFinger_OnReadTemplateMap, pThis, 0);
}
}
else
pThis->SetState(STATE_ERROR);
}
void CFingerLogic::InitFinger_OnSetSystemSecurityLevel(CFingerLogic *pThis, FingerPrintError error, const char *errorStr)
{
debugf("InitFinger_OnSetSystemSecurityLevel: (%d) %s", error, errorStr);
if(error == ERROR_OK)
{
pThis->SetState(STATE_INIT_READTEMPLATEMAP);
pThis->FingerPrint().AsyncReadTemplateMap((ReadTemplateMapCallback)InitFinger_OnReadTemplateMap, pThis, 0);
}
@@ -596,3 +617,15 @@ void CFingerLogic::EnrollFinger_OnDownloadCharacteristics(CFingerLogic *pThis, F
pThis->SetState(STATE_READY);
}
void CFingerLogic::SetSecurityLevel(int securityLevel)
{
SetState(STATE_SETSYSTEM_SECURITYLEVEL);
FingerPrint().AsyncSetSystemParameter((SetSystemParameterCallback)SetSecurityLevel_OnSetSystemSecurityLevel, this, FINGERPRINT_SECURITY_REG_ADDR, securityLevel);
}
void CFingerLogic::SetSecurityLevel_OnSetSystemSecurityLevel(CFingerLogic *pThis, FingerPrintError error, const char *errorStr)
{
debugf("SetSecurityLevel_OnSetSystemSecurityLevel: (%d) %s", error, errorStr);
pThis->SetState(STATE_READY);
}

View File

@@ -11,6 +11,7 @@ enum FingerLogicState
STATE_INIT_VERIFYPASSWORD,
STATE_INIT_READSYSTEMPARAMETERS,
STATE_INIT_SETSYSTEM_SECURITYLEVEL,
STATE_INIT_READTEMPLATEMAP,
STATE_INIT_VERIFYTEMPLATES,
@@ -33,7 +34,9 @@ enum FingerLogicState
STATE_ENROLL_CREATETEMPLATE,
STATE_ENROLL_STORETEMPLATE,
STATE_ENROLL_LOADTEMPLATE,
STATE_ENROLL_DOWNLOADCHARACTERISTICS
STATE_ENROLL_DOWNLOADCHARACTERISTICS,
STATE_SETSYSTEM_SECURITYLEVEL
};
class CFingerLogic
@@ -41,7 +44,7 @@ class CFingerLogic
public:
CFingerLogic(class CMain *pMain);
void Init(CFingerPrint *pFingerPrint);
void OnFingerInterrupt(bool finger);
void OnFinger(bool finger);
bool FingerSlot(uint16_t index);
bool FingerSlot(uint16_t index, bool value);
@@ -50,6 +53,7 @@ public:
void InitFinger();
static void InitFinger_OnVerifyPassword(CFingerLogic *pThis, FingerPrintError error, const char *errorStr);
static void InitFinger_OnReadSystemParameters(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, CFingerSystemParameters *param);
static void InitFinger_OnSetSystemSecurityLevel(CFingerLogic *pThis, FingerPrintError error, const char *errorStr);
static void InitFinger_OnGetTemplates(CFingerLogic *pThis, FingerPrintError error, const char *errorStr);
static void InitFinger_OnReadTemplateMap(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, uint8_t *pData, uint16_t dataLen);
void InitFinger_VerifyTemplates();
@@ -76,6 +80,9 @@ public:
static void EnrollFinger_OnLoadTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr);
static void EnrollFinger_OnDownloadCharacteristics(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int8_t *pChar, uint16_t charLen);
void SetSecurityLevel(int securityLevel);
static void SetSecurityLevel_OnSetSystemSecurityLevel(CFingerLogic *pThis, FingerPrintError error, const char *errorStr);
CMain &Main() { return *m_pMain; }
CFingerPrint &FingerPrint() { return *m_pFingerPrint; }

View File

@@ -69,6 +69,36 @@ enum FingerPrintError
ERROR_COMMUNICATIONPORT = 0x1D, // fail to operate the communication port
};
#define FINGERPRINT_BAUD_REG_ADDR 0x4 //!< BAUDRATE register address
#define FINGERPRINT_BAUDRATE_9600 0x1 //!< UART baud 9600
#define FINGERPRINT_BAUDRATE_19200 0x2 //!< UART baud 19200
#define FINGERPRINT_BAUDRATE_28800 0x3 //!< UART baud 28800
#define FINGERPRINT_BAUDRATE_38400 0x4 //!< UART baud 38400
#define FINGERPRINT_BAUDRATE_48000 0x5 //!< UART baud 48000
#define FINGERPRINT_BAUDRATE_57600 0x6 //!< UART baud 57600
#define FINGERPRINT_BAUDRATE_67200 0x7 //!< UART baud 67200
#define FINGERPRINT_BAUDRATE_76800 0x8 //!< UART baud 76800
#define FINGERPRINT_BAUDRATE_86400 0x9 //!< UART baud 86400
#define FINGERPRINT_BAUDRATE_96000 0xA //!< UART baud 96000
#define FINGERPRINT_BAUDRATE_105600 0xB //!< UART baud 105600
#define FINGERPRINT_BAUDRATE_115200 0xC //!< UART baud 115200
#define FINGERPRINT_SECURITY_REG_ADDR 0x5 //!< Security level register address
// The safety level is 1 The highest rate of false recognition , The rejection
// rate is the lowest . The safety level is 5 The lowest tate of false
// recognition, The rejection rate is the highest .
#define FINGERPRINT_SECURITY_LEVEL_1 0X1 //!< Security level 1
#define FINGERPRINT_SECURITY_LEVEL_2 0X2 //!< Security level 2
#define FINGERPRINT_SECURITY_LEVEL_3 0X3 //!< Security level 3
#define FINGERPRINT_SECURITY_LEVEL_4 0X4 //!< Security level 4
#define FINGERPRINT_SECURITY_LEVEL_5 0X5 //!< Security level 5
#define FINGERPRINT_PACKET_REG_ADDR 0x6 //!< Packet size register address
#define FINGERPRINT_PACKET_SIZE_32 0X0 //!< Packet size is 32 Byte
#define FINGERPRINT_PACKET_SIZE_64 0X1 //!< Packet size is 64 Byte
#define FINGERPRINT_PACKET_SIZE_128 0X2 //!< Packet size is 128 Byte
#define FINGERPRINT_PACKET_SIZE_256 0X3 //!< Packet size is 256 Byte
enum RecvStates
{
RECV_DONE = 0,
@@ -92,6 +122,7 @@ typedef void (CFingerPrint::*RecvCallback)(FingerPrintIdent ident, uint8_t *pDat
typedef void (*VerifyPasswordCallback)(void *pUser, FingerPrintError error, const char *errorStr);
typedef void (*ReadSystemParametersCallback)(void *pUser, FingerPrintError error, const char *errorStr, CFingerSystemParameters *param);
typedef void (*SetSystemParameterCallback)(void *pUser, FingerPrintError error, const char *errorStr);
typedef void (*ReadImageCallback)(void *pUser, FingerPrintError error, const char *errorStr);
typedef void (*ConvertImageCallback)(void *pUser, FingerPrintError error, const char *errorStr);
typedef void (*SearchTemplateCallback)(void *pUser, FingerPrintError error, const char *errorStr, int16_t position, int16_t score);
@@ -112,11 +143,13 @@ public:
int VerifyPassword();
int ReadSystemParameters(uint8_t aResponse[17]);
int SetSystemParameter(uint8_t regAddr, uint8_t value);
int DeleteTemplate(uint16_t positionStart, uint16_t count);
int EmptyDatabase();
int AsyncVerifyPassword(VerifyPasswordCallback fnCallback, void *pUser);
int AsyncReadSystemParameters(ReadSystemParametersCallback fnCallback, void *pUser);
int AsyncSetSystemParameter(SetSystemParameterCallback fnCallback, void *pUser, uint8_t regAddr, uint8_t value);
int AsyncReadImage(ReadImageCallback fnCallback, void *pUser);
int AsyncConvertImage(ConvertImageCallback fnCallback, void *pUser, uint8_t numCharBuffer);
int AsyncSearchTemplate(SearchTemplateCallback fnCallback, void *pUser, uint8_t numCharBuffer, uint16_t positionStart, uint16_t numTemplates);
@@ -130,6 +163,7 @@ public:
private:
void OnAsyncVerifyPassword(FingerPrintIdent ident, uint8_t *pData, uint16_t length);
void OnAsyncReadSystemParameters(FingerPrintIdent ident, uint8_t *pData, uint16_t length);
void OnAsyncSetSystemParameter(FingerPrintIdent ident, uint8_t *pData, uint16_t length);
void OnAsyncReadImage(FingerPrintIdent ident, uint8_t *pData, uint16_t length);
void OnAsyncConvertImage(FingerPrintIdent ident, uint8_t *pData, uint16_t length);
void OnAsyncSearchTemplate(FingerPrintIdent ident, uint8_t *pData, uint16_t length);

View File

@@ -50,6 +50,31 @@ int CFingerPrint::ReadSystemParameters(uint8_t aResponse[17])
return error;
}
int CFingerPrint::SetSystemParameter(uint8_t regAddr, uint8_t value)
{
uint8_t aPayload[] = {
COMMAND_SETSYSPARA,
regAddr,
value
};
Write(IDENT_COMMAND, aPayload, sizeof(aPayload));
FingerPrintIdent ident;
uint8_t aResponse[1];
int ret = Recv(&ident, aResponse, sizeof(aResponse));
m_RecvState = RECV_DROP;
if(ret < 0)
return ret;
if(ident != IDENT_ACK)
return ERROR_COMMUNICATION;
uint8_t error = aResponse[0];
return error;
}
int CFingerPrint::DeleteTemplate(uint16_t positionStart, uint16_t count)
{
uint8_t aPayload[] = {

View File

@@ -90,6 +90,40 @@ void CFingerPrint::OnAsyncReadSystemParameters(FingerPrintIdent ident, uint8_t *
}
int CFingerPrint::AsyncSetSystemParameter(SetSystemParameterCallback fnCallback, void *pUser, uint8_t regAddr, uint8_t value)
{
uint8_t aPayload[] = {
COMMAND_SETSYSPARA,
regAddr,
value
};
m_fnRecvCallback = &CFingerPrint::OnAsyncSetSystemParameter;
m_fnUserCallback = (void *)fnCallback;
m_pUserData = pUser;
Write(IDENT_COMMAND, aPayload, sizeof(aPayload));
return 0;
}
void CFingerPrint::OnAsyncSetSystemParameter(FingerPrintIdent ident, uint8_t *pData, uint16_t length)
{
m_RecvState = RECV_DROP;
if(ident != IDENT_ACK)
{
((SetSystemParameterCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!");
return;
}
uint8_t error = pData[0];
const char *errorStr = ExplainFingerError(error);
((SetSystemParameterCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr);
}
int CFingerPrint::AsyncReadImage(ReadImageCallback fnCallback, void *pUser)
{
uint8_t aPayload[] = {

View File

@@ -11,10 +11,11 @@ CSettings::CSettings()
strcpy(m_aPSK, "");
m_DHCP = true;
strcpy(m_aHostname, "safeweb");
m_Address = IPAddress(0, 0, 0, 0);
m_Netmask = IPAddress(0, 0, 0, 0);
m_Gateway = IPAddress(0, 0, 0, 0);
m_Address = IpAddress(0, 0, 0, 0);
m_Netmask = IpAddress(0, 0, 0, 0);
m_Gateway = IpAddress(0, 0, 0, 0);
memset(m_aLockCode, 0, sizeof(m_aLockCode));
m_SecurityLevel = 4;
}
bool CSettings::Exists()
@@ -53,6 +54,11 @@ bool CSettings::Load()
m_Netmask = network["netmask"].as<const char *>();
m_Gateway = network["gateway"].as<const char *>();
JsonObject mqtt = doc["mqtt"];
strncpy(m_aMqttName, mqtt["client_name"], sizeof(m_aMqttName));
m_MqttURL = Url("mqtt", mqtt["username"], mqtt["password"], mqtt["host"], mqtt["port"]);
m_MqttUnlock = bool(mqtt["unlock"]);
JsonArray lockCode = doc["lock_code"];
uint8_t tmp = min(lockCode.size(), sizeof(m_aLockCode));
for(uint8_t i = 0; i < tmp; i++)
@@ -73,6 +79,8 @@ bool CSettings::Load()
m_FingerPrints[finger.m_FingerNum] = finger;
}
m_SecurityLevel = min(max(int(doc["security_level"]), 1), 5);
delete[] pData;
return true;
}
@@ -93,6 +101,14 @@ void CSettings::Save()
network["netmask"] = m_Netmask.toString();
network["gateway"] = m_Gateway.toString();
JsonObject mqtt = doc.createNestedObject("mqtt");
mqtt["client_name"] = m_aMqttName;
mqtt["host"] = m_MqttURL.Host;
mqtt["port"] = m_MqttURL.getPort();
mqtt["username"] = m_MqttURL.User;
mqtt["password"] = m_MqttURL.Password;
mqtt["unlock"] = m_MqttUnlock;
JsonArray lockCode = doc.createNestedArray("lock_code");
for(uint8_t i = 0; i < sizeof(m_aLockCode); i++)
lockCode.add(m_aLockCode[i]);
@@ -112,6 +128,8 @@ void CSettings::Save()
obj["digest"] = String(aHexDigest);
}
doc["security_level"] = m_SecurityLevel;
String docString;
serializeJsonPretty(doc, docString);
fileSetContent(APP_SETTINGS_FILE, docString);

View File

@@ -24,6 +24,10 @@ public:
IpAddress m_Netmask;
IpAddress m_Gateway;
Url m_MqttURL;
char m_aMqttName[64];
bool m_MqttUnlock;
uint8_t m_aLockCode[8];
struct CFingerPrint
@@ -33,6 +37,8 @@ public:
uint8_t m_aDigest[SHA256_SIZE];
};
HashMap<uint16_t, CFingerPrint> m_FingerPrints;
int m_SecurityLevel;
};
#endif

View File

@@ -5,12 +5,32 @@
HardwareSerial Serial1(UART_ID_1);
NtpClient ntpClient("at.pool.ntp.org", 3600);
void IRAM_ATTR OnFingerInterrupt()
void IRAM_ATTR OnFingerISR()
{
detachInterrupt(FINGER_DETECT_PIN);
// LOW = FINGER, HIGH = NO FINGER
bool status = digitalRead(FINGER_DETECT_PIN);
const bool finger = !digitalRead(FINGER_DETECT_PIN);
g_Main.OnFingerInterrupt(!status);
// TODO: disable interrupt and start timer
g_Main.OnFinger(finger);
}
void IRAM_ATTR OnLockISR()
{
detachInterrupt(SAFELOCK_DETECT_PIN);
// LOW = UNLOCKED, HIGH = LOCKED
const bool unlocked = !digitalRead(SAFELOCK_DETECT_PIN);
g_Main.OnLock(unlocked);
}
void IRAM_ATTR OnDoorISR()
{
detachInterrupt(DOOR_DETECT_PIN);
// LOW = OPEN, HIGH = CLOSED
const bool opened = digitalRead(DOOR_DETECT_PIN);
g_Main.OnDoor(opened);
}
void ready()
@@ -18,10 +38,14 @@ void ready()
debugf("READY!");
gpio_pin_wakeup_enable(GPIO_ID_PIN(FINGER_DETECT_PIN), GPIO_PIN_INTR_LOLEVEL);
gpio_pin_wakeup_enable(GPIO_ID_PIN(SAFELOCK_DETECT_PIN), GPIO_PIN_INTR_LOLEVEL);
gpio_pin_wakeup_enable(GPIO_ID_PIN(DOOR_DETECT_PIN), GPIO_PIN_INTR_LOLEVEL);
g_Main.Init(Serial);
attachInterrupt(FINGER_DETECT_PIN, OnFingerInterrupt, CHANGE);
attachInterrupt(FINGER_DETECT_PIN, OnFingerISR, CHANGE);
attachInterrupt(SAFELOCK_DETECT_PIN, OnLockISR, CHANGE);
attachInterrupt(DOOR_DETECT_PIN, OnDoorISR, CHANGE);
}
void init()
@@ -45,7 +69,7 @@ void init()
pinMode(FINGER_DETECT_PIN, INPUT);
pinMode(SAFELOCK_DETECT_PIN, INPUT);
pinMode(DOOR_DETECT_PIN, INPUT);
pinMode(DOOR_DETECT_PIN, INPUT_PULLUP);
// mount spiffs
spiffs_mount();

View File

@@ -1,6 +1,10 @@
#include <SmingCore.h>
#include <Network/Mqtt/MqttBuffer.h>
#include <Data/WebHelpers/base64.h>
#include <ArduinoJson.h>
#include <MultipartParser.h>
#include <HttpMultipartResource.h>
#include <OtaUpgradeStream.h>
#include "utils.h"
#include "main.h"
@@ -10,17 +14,97 @@ CMain::CMain() : m_FingerLogic(this)
{
}
static void STAGotIP(IpAddress ip, IpAddress mask, IpAddress gateway)
static void fileUploadMapper(HttpFiles& files)
{
debugf("GOTIP - IP: %s, MASK: %s, GW: %s\n", ip.toString().c_str(), mask.toString().c_str(),
gateway.toString().c_str());
files["firmware"] = new OtaUpgradeStream;
}
static void STADisconnect(const String& ssid, MacAddress bssid, WifiDisconnectReason reason)
static int onMessageDelivered(MqttClient& client, mqtt_message_t* message)
{
Serial1 << _F("Message with id ") << message->puback.message_id << _F(" and QoS ") << message->puback.qos
<< _F(" was delivered successfully.") << endl;
return 0;
}
// Callback for messages, arrived from MQTT server
static int onMessageReceived(MqttClient& client, mqtt_message_t* message)
{
Serial1 << _F("Received: ") << MqttBuffer(message->publish.topic_name) << ':' << endl;
Serial1 << '\t' << MqttBuffer(message->publish.content) << endl;
return 0;
}
void CMain::OnStationGotIP(IpAddress ip, IpAddress mask, IpAddress gateway)
{
m_StationConnected = true;
debugf("GOTIP - IP: %s, MASK: %s, GW: %s\n", ip.toString().c_str(), mask.toString().c_str(), gateway.toString().c_str());
StartMqttClient();
}
void CMain::OnStationDisconnect(const String& ssid, MacAddress bssid, WifiDisconnectReason reason)
{
m_StationConnected = false;
debugf("DISCONNECT - SSID: %s, REASON: %d", ssid.c_str(), WifiEvents.getDisconnectReasonDesc(reason).c_str());
}
int CMain::MqttOnConnect(MqttClient& client, mqtt_message_t* message)
{
debugf("MQTT Connected!");
MqttSendMessage("safeweb/status", "online");
MqttSendDescription();
return 0;
}
void CMain::MqttOnDisconnect(TcpClient& client, bool flag)
{
if(flag == true)
debugf("MQTT Broker Disconnected!!");
else
debugf("MQTT Broker Unreachable!!");
// Restart connection attempt after few seconds
m_MqttTimer.initializeMs(2 * 1000, TimerDelegate(&CMain::StartMqttClient, this)).start();
}
void CMain::MqttSendDescription()
{
if(m_Mqtt.getConnectionState() != eTCS_Connected) {
StartMqttClient(); // Auto reconnect
}
}
void CMain::MqttSendMessage(const char *topic, const char *msg)
{
if(m_Mqtt.getConnectionState() != eTCS_Connected) {
StartMqttClient(); // Auto reconnect
}
m_Mqtt.publish(topic, msg);
}
void CMain::StartMqttClient()
{
m_MqttTimer.stop();
if(!m_StationConnected || !Settings().m_MqttURL.Port || !Settings().m_MqttURL.Host)
return;
if(!m_Mqtt.setWill("safeweb/status", "offline", MqttClient::getFlags(MQTT_QOS_AT_LEAST_ONCE, MQTT_RETAIN_TRUE))) {
debugf("Unable to mqtt.setWill");
}
m_Mqtt.setEventHandler(MQTT_TYPE_PUBACK, onMessageDelivered);
m_Mqtt.setConnectedHandler(MqttDelegate(&CMain::MqttOnConnect, this));
m_Mqtt.setCompleteDelegate(TcpClientCompleteDelegate(&CMain::MqttOnDisconnect, this));
m_Mqtt.setMessageHandler(onMessageReceived);
m_Mqtt.connect(Settings().m_MqttURL, Settings().m_aMqttName);
}
void CMain::Init(HardwareSerial &serial)
{
m_Settings.Load();
@@ -45,22 +129,32 @@ void CMain::Init(HardwareSerial &serial)
WifiAccessPoint.enable(true);
}
WifiEvents.onStationGotIP(STAGotIP);
WifiEvents.onStationDisconnect(STADisconnect);
WifiEvents.onStationGotIP(StationGotIPDelegate(&CMain::OnStationGotIP, this));
WifiEvents.onStationDisconnect(StationDisconnectDelegate(&CMain::OnStationDisconnect, this));
m_FTP.listen(21);
m_FTP.addUser(m_Settings.m_aUsername, m_Settings.m_aPassword);
HttpServerSettings settings;
settings.closeOnContentError = false;
settings.keepAliveSeconds = 5;
m_HttpServer.configure(settings);
m_HttpServer.setBodyParser(MIME_JSON, bodyToStringParser);
m_HttpServer.setBodyParser(MIME_FORM_MULTIPART, formMultipartParser);
m_HttpServer.listen(80);
m_HttpServer.setBodyParser("application/json", bodyToStringParser);
m_HttpServer.paths.set("/api", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/state", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/dashboard", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/fingerprint", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/fingerprint/label", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/fingerprint/delete", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/fingerprint/enroll", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/fingerprint/security", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/unlock", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/reset", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/upgrade", new HttpMultipartResource(fileUploadMapper, HttpResourceDelegate(&CMain::HttpOnUpload, this)));
m_HttpServer.paths.setDefault(HttpPathDelegate(&CMain::HttpOnFile, this));
@@ -124,7 +218,6 @@ void CMain::HttpOnApi(HttpRequest &request, HttpResponse &response)
return;
}
HttpStatus status = HandleApi(request.method, endpoint, jsonReq, jsonResp);
if(status != HTTP_STATUS_OK)
{
@@ -140,73 +233,96 @@ void CMain::HttpOnApi(HttpRequest &request, HttpResponse &response)
HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &req, JsonDocument &resp)
{
String test;
serializeJsonPretty(req, test);
debugf("request: %s\n", test.c_str());
if(endpoint == "dashboard")
if(endpoint == "state" || endpoint == "dashboard")
{
if(method != HTTP_GET) return HTTP_STATUS_METHOD_NOT_ALLOWED;
JsonObject lock = resp.createNestedObject("lock");
lock["locked"] = true;
JsonObject state = resp.createNestedObject("state");
state["unlocked"] = (bool)!digitalRead(SAFELOCK_DETECT_PIN);
state["opened"] = (bool)digitalRead(DOOR_DETECT_PIN);
state["battery"] = (system_adc_read() / 1024.f) / 0.237;
JsonObject door = resp.createNestedObject("door");
door["closed"] = true;
JsonObject battery = resp.createNestedObject("battery");
debugf("adc: %d\n", system_adc_read());
battery["voltage"] = (system_adc_read() / 1024.f) / 0.23;
battery["color"] = "success";
if(battery["voltage"] < 3.6f)
battery["color"] = "warning";
JsonObject network = resp.createNestedObject("network");
if(WifiStation.isEnabled())
if(endpoint == "dashboard")
{
network["type"] = "Station";
network["ssid"] = WifiStation.getSSID();
network["channel"] = WifiStation.getChannel();
network["dhcp"] = WifiStation.isEnabledDHCP();
network["rssi"] = WifiStation.getRssi();
network["signal"] = Rssi2Quality(WifiStation.getRssi());
network["address"] = WifiStation.getIP().toString();
}
else
{
network["type"] = "Access Point";
network["ssid"] = WifiAccessPoint.getSSID();
network["channel"] = WifiStation.getChannel();
network["dhcp"] = true;
network["address"] = WifiAccessPoint.getIP().toString();
JsonObject network = resp.createNestedObject("network");
if(WifiStation.isEnabled())
{
network["type"] = "Station";
network["ssid"] = WifiStation.getSSID();
network["channel"] = WifiStation.getChannel();
network["dhcp"] = WifiStation.isEnabledDHCP();
network["rssi"] = WifiStation.getRssi();
network["signal"] = Rssi2Quality(network["rssi"]);
network["address"] = WifiStation.getIP().toString();
}
else
{
network["type"] = "Access Point";
network["ssid"] = WifiAccessPoint.getSSID();
network["channel"] = WifiStation.getChannel();
network["dhcp"] = true;
network["address"] = WifiAccessPoint.getIP().toString();
}
}
}
else if(endpoint == "unlock")
{
if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED;
LockUnlock();
resp["success"] = "true";
if(!req.containsKey("unlock"))
return HTTP_STATUS_BAD_REQUEST;
if(req["unlock"])
LockUnlock();
}
else if(endpoint == "reset")
{
if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED;
if(!req.containsKey("reset"))
return HTTP_STATUS_BAD_REQUEST;
if(req["reset"])
System.restart(500);
}
else if(endpoint == "fingerprint")
{
if(method != HTTP_GET) return HTTP_STATUS_METHOD_NOT_ALLOWED;
JsonArray fingerprints = resp.createNestedArray("fingerprints");
uint16_t tmp = Settings().m_FingerPrints.count();
for(uint16_t i = 0; i < tmp; i++)
if(method == HTTP_GET)
{
JsonObject obj = fingerprints.createNestedObject();
const CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(i);
JsonArray fingerprints = resp.createNestedArray("fingerprints");
uint16_t tmp = Settings().m_FingerPrints.count();
for(uint16_t i = 0; i < tmp; i++)
{
JsonObject obj = fingerprints.createNestedObject();
const CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(i);
obj["num"] = finger.m_FingerNum;
obj["label"] = String(finger.m_aLabel);
obj["num"] = finger.m_FingerNum;
obj["label"] = String(finger.m_aLabel);
char aHexDigest[SHA256_SIZE*2+1];
bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest, sizeof(aHexDigest));
obj["digest"] = String(aHexDigest);
char aHexDigest[SHA256_SIZE*2+1];
bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest, sizeof(aHexDigest));
obj["digest"] = String(aHexDigest);
}
resp["securityLevel"] = Settings().m_SecurityLevel;
}
else if(method == HTTP_POST)
{
if(!req.containsKey("securityLevel"))
return HTTP_STATUS_BAD_REQUEST;
int newLevel = req["securityLevel"].as<int>();
if(newLevel > FINGERPRINT_SECURITY_LEVEL_5 || newLevel < FINGERPRINT_SECURITY_LEVEL_1)
return HTTP_STATUS_BAD_REQUEST;
if(newLevel != Settings().m_SecurityLevel)
{
Settings().m_SecurityLevel = newLevel;
Settings().Save();
FingerLogic().SetSecurityLevel(newLevel);
}
}
else
return HTTP_STATUS_METHOD_NOT_ALLOWED;
}
else if(endpoint == "fingerprint/label")
{
@@ -224,8 +340,6 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(index);
strncpy(finger.m_aLabel, label.c_str(), sizeof(finger.m_aLabel));
Settings().Save();
resp["success"] = true;
}
else if(endpoint == "fingerprint/delete")
{
@@ -243,8 +357,6 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
FingerPrint().DeleteTemplate(finger.m_FingerNum, 1);
Settings().m_FingerPrints.removeAt(index);
Settings().Save();
resp["success"] = true;
}
else if(endpoint == "fingerprint/enroll")
{
@@ -254,7 +366,7 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
{
if(req["cancel"].as<bool>() && m_Enrolling)
{
resp["success"] = FingerLogic().EnrollFinger(false);
FingerLogic().EnrollFinger(false);
m_Enrolling = false;
m_Enrolled = false;
}
@@ -336,11 +448,52 @@ void CMain::HttpOnFile(HttpRequest &request, HttpResponse &response)
response.sendFile(file);
}
void CMain::OnFingerInterrupt(bool finger)
int CMain::HttpOnUpload(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response)
{
if(!HttpAuthorized(request, response))
return 1;
ReadWriteStream* file = request.files["firmware"];
auto otaStream = static_cast<OtaUpgradeStream*>(file);
if(otaStream == nullptr) {
debug_e("Something went wrong with the file upload");
return 1;
}
if(response.isSuccess() && !otaStream->hasError()) {
// defer the reboot by 1000 milliseconds to give time to the web server to return the response
System.restart(1000);
response.sendFile("otadone.html");
response.headers[HTTP_HEADER_CONNECTION] = "close";
return 0;
}
response.code = HTTP_STATUS_BAD_REQUEST;
response.setContentType(MIME_HTML);
String html = toString(otaStream->errorCode);
response.headers[HTTP_HEADER_CONTENT_LENGTH] = html.length();
response.sendString(html);
return 0;
}
void CMain::OnFinger(bool finger)
{
m_FingerLogic.OnFingerInterrupt(finger);
}
void CMain::OnLock(bool unlocked);
{
}
void CMain::OnDoor(bool opened);
{
}
void CMain::FingerEnable(bool enable)
{
const int pin = FINGER_ENABLE_PIN;
@@ -443,11 +596,6 @@ void CMain::OnFingerEnrolled(uint16_t fingerNum, uint8_t digest[SHA256_SIZE])
bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest, sizeof(aHexDigest));
debugf("OnFingerEnrolled: \"%s\"", aHexDigest);
Serial1.printf("(sz: %d) Finger hexdigest: ", sizeof(finger.m_aDigest));
for(uint8_t i = 0; i < sizeof(finger.m_aDigest); i++)
Serial1.printf("%x", finger.m_aDigest[i]);
Serial1.printf("\n");
Settings().m_FingerPrints[fingerNum] = finger;
Settings().Save();
m_Enrolled = true;

View File

@@ -20,7 +20,14 @@ public:
CMain();
void Init(HardwareSerial &serial);
void OnFingerInterrupt(bool finger);
void OnStationGotIP(IpAddress ip, IpAddress mask, IpAddress gateway);
void OnStationDisconnect(const String& ssid, MacAddress bssid, WifiDisconnectReason reason);
int MqttOnConnect(MqttClient& client, mqtt_message_t* message);
void MqttOnDisconnect(TcpClient& client, bool flag);
void OnFinger(bool finger);
void OnLock(bool unlocked);
void OnDoor(bool opened);
void FingerEnable(bool enable);
@@ -38,9 +45,14 @@ private:
bool HttpAuthorized(HttpRequest &request, HttpResponse &response);
void HttpOnApi(HttpRequest &request, HttpResponse &response);
void HttpOnFile(HttpRequest &request, HttpResponse &response);
int HttpOnUpload(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response);
HttpStatus HandleApi(HttpMethod method, String endpoint, JsonDocument &req, JsonDocument &resp);
void StartMqttClient();
void MqttSendDescription();
void MqttSendMessage(const char *topic, const char *msg);
private:
void LockSendBytes(uint8_t *pBytes, uint8_t len);
void LockSendCode(uint8_t code[8]);
@@ -51,12 +63,16 @@ private:
FtpServer m_FTP;
HttpServer m_HttpServer;
MqttClient m_Mqtt;
Timer m_MqttTimer;
String m_EnrollMessage;
String m_EnrollLabel;
CSettings::CFingerPrint m_EnrolledFinger;
bool m_Enrolling;
bool m_Enrolled;
bool m_StationConnected;
};
extern CMain g_Main;

View File

@@ -1,4 +1,11 @@
ARDUINO_LIBRARIES := ArduinoJson6
ARDUINO_LIBRARIES := ArduinoJson6 MultipartParser OtaUpgrade
HWCONFIG = spiffs
HWCONFIG = spiffs-two-roms
SPIFF_FILES = files
ifndef MAKE_DOCS
web-upload: spiffs-image-update
$(call WriteFlash,$(SPIFF_START_ADDR)=$(SPIFF_BIN_OUT))
endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
files/favicon.ico.gz Normal file

Binary file not shown.

View File

@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Safe control panel">
<title>Safe control panel</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha256-LA89z+k9fjgMKQ/kq4OO2Mrf8VltYml/VES+Rg0fh20=" crossorigin="anonymous" />
<link rel="stylesheet" href="main.css">
</head>
<body>
<div class="spinner"><div class="rect1"></div> <div class="rect2"></div> <div class="rect3"></div> <div class="rect4"></div> <div class="rect5"></div></div>
<noscript><center><h1>Your browser does not support JavaScript!</h1></center></noscript>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nunjucks/3.0.1/nunjucks.min.js" integrity="sha256-sh9FYQZVVLprCQB3/IcNyCRrZwu9hZ+xLHhUszDfsK4=" crossorigin="anonymous"></script>
<script src="templates.js"></script>
<script src="main.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha256-pS96pU17yq+gVu4KBQJi38VpSuKN7otMrDQprzf/DWY=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha256-5+02zu5UULQkO7w1GIr6vftCgMfFdZcAHeDtFnKZsBs=" crossorigin="anonymous"></script>
</html>

BIN
files/index.html.gz Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
files/otadone.html.gz Normal file

Binary file not shown.

View File

@@ -10,6 +10,13 @@
"netmask": "0.0.0.0",
"gateway": "0.0.0.0"
},
"mqtt": {
"client_name": "safeweb",
"host": "",
"port": 0,
"username": "safeweb",
"password": "password"
},
"lock_code": [
0,
1,
@@ -21,5 +28,6 @@
7
],
"fingerprints": [
]
],
"security_level": 4
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB