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; m_State = state;
if(m_State == STATE_READY || m_State == STATE_ERROR) if(m_State == STATE_READY || m_State == STATE_ERROR)
m_PowerOffTimer.start(false); m_PowerOffTimer.startOnce();
else else
{ {
m_PowerOffTimer.stop(); m_PowerOffTimer.stop();
@@ -64,9 +64,9 @@ void CFingerLogic::PowerOn()
delayMilliseconds(100); 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; m_Finger = finger;
if(finger) if(finger)
@@ -157,6 +157,27 @@ void CFingerLogic::InitFinger_OnReadSystemParameters(CFingerLogic *pThis, Finger
debugf("packetLength: %d", param->packetLength); debugf("packetLength: %d", param->packetLength);
debugf("baudRate: %d", param->baudRate); 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->SetState(STATE_INIT_READTEMPLATEMAP);
pThis->FingerPrint().AsyncReadTemplateMap((ReadTemplateMapCallback)InitFinger_OnReadTemplateMap, pThis, 0); pThis->FingerPrint().AsyncReadTemplateMap((ReadTemplateMapCallback)InitFinger_OnReadTemplateMap, pThis, 0);
} }
@@ -596,3 +617,15 @@ void CFingerLogic::EnrollFinger_OnDownloadCharacteristics(CFingerLogic *pThis, F
pThis->SetState(STATE_READY); 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_VERIFYPASSWORD,
STATE_INIT_READSYSTEMPARAMETERS, STATE_INIT_READSYSTEMPARAMETERS,
STATE_INIT_SETSYSTEM_SECURITYLEVEL,
STATE_INIT_READTEMPLATEMAP, STATE_INIT_READTEMPLATEMAP,
STATE_INIT_VERIFYTEMPLATES, STATE_INIT_VERIFYTEMPLATES,
@@ -33,7 +34,9 @@ enum FingerLogicState
STATE_ENROLL_CREATETEMPLATE, STATE_ENROLL_CREATETEMPLATE,
STATE_ENROLL_STORETEMPLATE, STATE_ENROLL_STORETEMPLATE,
STATE_ENROLL_LOADTEMPLATE, STATE_ENROLL_LOADTEMPLATE,
STATE_ENROLL_DOWNLOADCHARACTERISTICS STATE_ENROLL_DOWNLOADCHARACTERISTICS,
STATE_SETSYSTEM_SECURITYLEVEL
}; };
class CFingerLogic class CFingerLogic
@@ -41,7 +44,7 @@ class CFingerLogic
public: public:
CFingerLogic(class CMain *pMain); CFingerLogic(class CMain *pMain);
void Init(CFingerPrint *pFingerPrint); void Init(CFingerPrint *pFingerPrint);
void OnFingerInterrupt(bool finger); void OnFinger(bool finger);
bool FingerSlot(uint16_t index); bool FingerSlot(uint16_t index);
bool FingerSlot(uint16_t index, bool value); bool FingerSlot(uint16_t index, bool value);
@@ -50,6 +53,7 @@ public:
void InitFinger(); void InitFinger();
static void InitFinger_OnVerifyPassword(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); 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_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_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); static void InitFinger_OnReadTemplateMap(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, uint8_t *pData, uint16_t dataLen);
void InitFinger_VerifyTemplates(); void InitFinger_VerifyTemplates();
@@ -76,6 +80,9 @@ public:
static void EnrollFinger_OnLoadTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); 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); 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; } CMain &Main() { return *m_pMain; }
CFingerPrint &FingerPrint() { return *m_pFingerPrint; } CFingerPrint &FingerPrint() { return *m_pFingerPrint; }

View File

@@ -69,6 +69,36 @@ enum FingerPrintError
ERROR_COMMUNICATIONPORT = 0x1D, // fail to operate the communication port 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 enum RecvStates
{ {
RECV_DONE = 0, 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 (*VerifyPasswordCallback)(void *pUser, FingerPrintError error, const char *errorStr);
typedef void (*ReadSystemParametersCallback)(void *pUser, FingerPrintError error, const char *errorStr, CFingerSystemParameters *param); 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 (*ReadImageCallback)(void *pUser, FingerPrintError error, const char *errorStr);
typedef void (*ConvertImageCallback)(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); typedef void (*SearchTemplateCallback)(void *pUser, FingerPrintError error, const char *errorStr, int16_t position, int16_t score);
@@ -112,11 +143,13 @@ public:
int VerifyPassword(); int VerifyPassword();
int ReadSystemParameters(uint8_t aResponse[17]); int ReadSystemParameters(uint8_t aResponse[17]);
int SetSystemParameter(uint8_t regAddr, uint8_t value);
int DeleteTemplate(uint16_t positionStart, uint16_t count); int DeleteTemplate(uint16_t positionStart, uint16_t count);
int EmptyDatabase(); int EmptyDatabase();
int AsyncVerifyPassword(VerifyPasswordCallback fnCallback, void *pUser); int AsyncVerifyPassword(VerifyPasswordCallback fnCallback, void *pUser);
int AsyncReadSystemParameters(ReadSystemParametersCallback 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 AsyncReadImage(ReadImageCallback fnCallback, void *pUser);
int AsyncConvertImage(ConvertImageCallback fnCallback, void *pUser, uint8_t numCharBuffer); int AsyncConvertImage(ConvertImageCallback fnCallback, void *pUser, uint8_t numCharBuffer);
int AsyncSearchTemplate(SearchTemplateCallback fnCallback, void *pUser, uint8_t numCharBuffer, uint16_t positionStart, uint16_t numTemplates); int AsyncSearchTemplate(SearchTemplateCallback fnCallback, void *pUser, uint8_t numCharBuffer, uint16_t positionStart, uint16_t numTemplates);
@@ -130,6 +163,7 @@ public:
private: private:
void OnAsyncVerifyPassword(FingerPrintIdent ident, uint8_t *pData, uint16_t length); void OnAsyncVerifyPassword(FingerPrintIdent ident, uint8_t *pData, uint16_t length);
void OnAsyncReadSystemParameters(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 OnAsyncReadImage(FingerPrintIdent ident, uint8_t *pData, uint16_t length);
void OnAsyncConvertImage(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); 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; 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) int CFingerPrint::DeleteTemplate(uint16_t positionStart, uint16_t count)
{ {
uint8_t aPayload[] = { 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) int CFingerPrint::AsyncReadImage(ReadImageCallback fnCallback, void *pUser)
{ {
uint8_t aPayload[] = { uint8_t aPayload[] = {

View File

@@ -11,10 +11,11 @@ CSettings::CSettings()
strcpy(m_aPSK, ""); strcpy(m_aPSK, "");
m_DHCP = true; m_DHCP = true;
strcpy(m_aHostname, "safeweb"); strcpy(m_aHostname, "safeweb");
m_Address = IPAddress(0, 0, 0, 0); m_Address = IpAddress(0, 0, 0, 0);
m_Netmask = IPAddress(0, 0, 0, 0); m_Netmask = IpAddress(0, 0, 0, 0);
m_Gateway = IPAddress(0, 0, 0, 0); m_Gateway = IpAddress(0, 0, 0, 0);
memset(m_aLockCode, 0, sizeof(m_aLockCode)); memset(m_aLockCode, 0, sizeof(m_aLockCode));
m_SecurityLevel = 4;
} }
bool CSettings::Exists() bool CSettings::Exists()
@@ -53,6 +54,11 @@ bool CSettings::Load()
m_Netmask = network["netmask"].as<const char *>(); m_Netmask = network["netmask"].as<const char *>();
m_Gateway = network["gateway"].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"]; JsonArray lockCode = doc["lock_code"];
uint8_t tmp = min(lockCode.size(), sizeof(m_aLockCode)); uint8_t tmp = min(lockCode.size(), sizeof(m_aLockCode));
for(uint8_t i = 0; i < tmp; i++) for(uint8_t i = 0; i < tmp; i++)
@@ -73,6 +79,8 @@ bool CSettings::Load()
m_FingerPrints[finger.m_FingerNum] = finger; m_FingerPrints[finger.m_FingerNum] = finger;
} }
m_SecurityLevel = min(max(int(doc["security_level"]), 1), 5);
delete[] pData; delete[] pData;
return true; return true;
} }
@@ -93,6 +101,14 @@ void CSettings::Save()
network["netmask"] = m_Netmask.toString(); network["netmask"] = m_Netmask.toString();
network["gateway"] = m_Gateway.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"); JsonArray lockCode = doc.createNestedArray("lock_code");
for(uint8_t i = 0; i < sizeof(m_aLockCode); i++) for(uint8_t i = 0; i < sizeof(m_aLockCode); i++)
lockCode.add(m_aLockCode[i]); lockCode.add(m_aLockCode[i]);
@@ -112,6 +128,8 @@ void CSettings::Save()
obj["digest"] = String(aHexDigest); obj["digest"] = String(aHexDigest);
} }
doc["security_level"] = m_SecurityLevel;
String docString; String docString;
serializeJsonPretty(doc, docString); serializeJsonPretty(doc, docString);
fileSetContent(APP_SETTINGS_FILE, docString); fileSetContent(APP_SETTINGS_FILE, docString);

View File

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

View File

@@ -5,12 +5,32 @@
HardwareSerial Serial1(UART_ID_1); HardwareSerial Serial1(UART_ID_1);
NtpClient ntpClient("at.pool.ntp.org", 3600); NtpClient ntpClient("at.pool.ntp.org", 3600);
void IRAM_ATTR OnFingerInterrupt() void IRAM_ATTR OnFingerISR()
{ {
detachInterrupt(FINGER_DETECT_PIN);
// LOW = FINGER, HIGH = NO FINGER // 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() void ready()
@@ -18,10 +38,14 @@ void ready()
debugf("READY!"); debugf("READY!");
gpio_pin_wakeup_enable(GPIO_ID_PIN(FINGER_DETECT_PIN), GPIO_PIN_INTR_LOLEVEL); 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); 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() void init()
@@ -45,7 +69,7 @@ void init()
pinMode(FINGER_DETECT_PIN, INPUT); pinMode(FINGER_DETECT_PIN, INPUT);
pinMode(SAFELOCK_DETECT_PIN, INPUT); pinMode(SAFELOCK_DETECT_PIN, INPUT);
pinMode(DOOR_DETECT_PIN, INPUT); pinMode(DOOR_DETECT_PIN, INPUT_PULLUP);
// mount spiffs // mount spiffs
spiffs_mount(); spiffs_mount();

View File

@@ -1,6 +1,10 @@
#include <SmingCore.h> #include <SmingCore.h>
#include <Network/Mqtt/MqttBuffer.h>
#include <Data/WebHelpers/base64.h> #include <Data/WebHelpers/base64.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <MultipartParser.h>
#include <HttpMultipartResource.h>
#include <OtaUpgradeStream.h>
#include "utils.h" #include "utils.h"
#include "main.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(), files["firmware"] = new OtaUpgradeStream;
gateway.toString().c_str());
} }
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()); 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) void CMain::Init(HardwareSerial &serial)
{ {
m_Settings.Load(); m_Settings.Load();
@@ -45,22 +129,32 @@ void CMain::Init(HardwareSerial &serial)
WifiAccessPoint.enable(true); WifiAccessPoint.enable(true);
} }
WifiEvents.onStationGotIP(STAGotIP); WifiEvents.onStationGotIP(StationGotIPDelegate(&CMain::OnStationGotIP, this));
WifiEvents.onStationDisconnect(STADisconnect); WifiEvents.onStationDisconnect(StationDisconnectDelegate(&CMain::OnStationDisconnect, this));
m_FTP.listen(21); m_FTP.listen(21);
m_FTP.addUser(m_Settings.m_aUsername, m_Settings.m_aPassword); 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.listen(80);
m_HttpServer.setBodyParser("application/json", bodyToStringParser);
m_HttpServer.paths.set("/api", HttpPathDelegate(&CMain::HttpOnApi, this)); 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/dashboard", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/fingerprint", 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/label", HttpPathDelegate(&CMain::HttpOnApi, this));
m_HttpServer.paths.set("/api/fingerprint/delete", 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/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/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)); m_HttpServer.paths.setDefault(HttpPathDelegate(&CMain::HttpOnFile, this));
@@ -124,7 +218,6 @@ void CMain::HttpOnApi(HttpRequest &request, HttpResponse &response)
return; return;
} }
HttpStatus status = HandleApi(request.method, endpoint, jsonReq, jsonResp); HttpStatus status = HandleApi(request.method, endpoint, jsonReq, jsonResp);
if(status != HTTP_STATUS_OK) if(status != HTTP_STATUS_OK)
{ {
@@ -140,28 +233,17 @@ void CMain::HttpOnApi(HttpRequest &request, HttpResponse &response)
HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &req, JsonDocument &resp) HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &req, JsonDocument &resp)
{ {
String test; if(endpoint == "state" || endpoint == "dashboard")
serializeJsonPretty(req, test);
debugf("request: %s\n", test.c_str());
if(endpoint == "dashboard")
{ {
if(method != HTTP_GET) return HTTP_STATUS_METHOD_NOT_ALLOWED; if(method != HTTP_GET) return HTTP_STATUS_METHOD_NOT_ALLOWED;
JsonObject lock = resp.createNestedObject("lock"); JsonObject state = resp.createNestedObject("state");
lock["locked"] = true; state["unlocked"] = (bool)!digitalRead(SAFELOCK_DETECT_PIN);
state["opened"] = (bool)digitalRead(DOOR_DETECT_PIN);
JsonObject door = resp.createNestedObject("door"); state["battery"] = (system_adc_read() / 1024.f) / 0.237;
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";
if(endpoint == "dashboard")
{
JsonObject network = resp.createNestedObject("network"); JsonObject network = resp.createNestedObject("network");
if(WifiStation.isEnabled()) if(WifiStation.isEnabled())
{ {
@@ -170,7 +252,7 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
network["channel"] = WifiStation.getChannel(); network["channel"] = WifiStation.getChannel();
network["dhcp"] = WifiStation.isEnabledDHCP(); network["dhcp"] = WifiStation.isEnabledDHCP();
network["rssi"] = WifiStation.getRssi(); network["rssi"] = WifiStation.getRssi();
network["signal"] = Rssi2Quality(WifiStation.getRssi()); network["signal"] = Rssi2Quality(network["rssi"]);
network["address"] = WifiStation.getIP().toString(); network["address"] = WifiStation.getIP().toString();
} }
else else
@@ -182,17 +264,31 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
network["address"] = WifiAccessPoint.getIP().toString(); network["address"] = WifiAccessPoint.getIP().toString();
} }
} }
}
else if(endpoint == "unlock") else if(endpoint == "unlock")
{ {
if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED; if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED;
if(!req.containsKey("unlock"))
return HTTP_STATUS_BAD_REQUEST;
if(req["unlock"])
LockUnlock(); LockUnlock();
resp["success"] = "true"; }
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") else if(endpoint == "fingerprint")
{ {
if(method != HTTP_GET) return HTTP_STATUS_METHOD_NOT_ALLOWED; if(method == HTTP_GET)
{
JsonArray fingerprints = resp.createNestedArray("fingerprints"); JsonArray fingerprints = resp.createNestedArray("fingerprints");
uint16_t tmp = Settings().m_FingerPrints.count(); uint16_t tmp = Settings().m_FingerPrints.count();
for(uint16_t i = 0; i < tmp; i++) for(uint16_t i = 0; i < tmp; i++)
@@ -207,6 +303,26 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest, sizeof(aHexDigest)); bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest, sizeof(aHexDigest));
obj["digest"] = String(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") 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); CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(index);
strncpy(finger.m_aLabel, label.c_str(), sizeof(finger.m_aLabel)); strncpy(finger.m_aLabel, label.c_str(), sizeof(finger.m_aLabel));
Settings().Save(); Settings().Save();
resp["success"] = true;
} }
else if(endpoint == "fingerprint/delete") else if(endpoint == "fingerprint/delete")
{ {
@@ -243,8 +357,6 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
FingerPrint().DeleteTemplate(finger.m_FingerNum, 1); FingerPrint().DeleteTemplate(finger.m_FingerNum, 1);
Settings().m_FingerPrints.removeAt(index); Settings().m_FingerPrints.removeAt(index);
Settings().Save(); Settings().Save();
resp["success"] = true;
} }
else if(endpoint == "fingerprint/enroll") 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) if(req["cancel"].as<bool>() && m_Enrolling)
{ {
resp["success"] = FingerLogic().EnrollFinger(false); FingerLogic().EnrollFinger(false);
m_Enrolling = false; m_Enrolling = false;
m_Enrolled = false; m_Enrolled = false;
} }
@@ -336,11 +448,52 @@ void CMain::HttpOnFile(HttpRequest &request, HttpResponse &response)
response.sendFile(file); 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); m_FingerLogic.OnFingerInterrupt(finger);
} }
void CMain::OnLock(bool unlocked);
{
}
void CMain::OnDoor(bool opened);
{
}
void CMain::FingerEnable(bool enable) void CMain::FingerEnable(bool enable)
{ {
const int pin = FINGER_ENABLE_PIN; 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)); bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest, sizeof(aHexDigest));
debugf("OnFingerEnrolled: \"%s\"", 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().m_FingerPrints[fingerNum] = finger;
Settings().Save(); Settings().Save();
m_Enrolled = true; m_Enrolled = true;

View File

@@ -20,7 +20,14 @@ public:
CMain(); CMain();
void Init(HardwareSerial &serial); 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); void FingerEnable(bool enable);
@@ -38,9 +45,14 @@ private:
bool HttpAuthorized(HttpRequest &request, HttpResponse &response); bool HttpAuthorized(HttpRequest &request, HttpResponse &response);
void HttpOnApi(HttpRequest &request, HttpResponse &response); void HttpOnApi(HttpRequest &request, HttpResponse &response);
void HttpOnFile(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); HttpStatus HandleApi(HttpMethod method, String endpoint, JsonDocument &req, JsonDocument &resp);
void StartMqttClient();
void MqttSendDescription();
void MqttSendMessage(const char *topic, const char *msg);
private: private:
void LockSendBytes(uint8_t *pBytes, uint8_t len); void LockSendBytes(uint8_t *pBytes, uint8_t len);
void LockSendCode(uint8_t code[8]); void LockSendCode(uint8_t code[8]);
@@ -51,12 +63,16 @@ private:
FtpServer m_FTP; FtpServer m_FTP;
HttpServer m_HttpServer; HttpServer m_HttpServer;
MqttClient m_Mqtt;
Timer m_MqttTimer;
String m_EnrollMessage; String m_EnrollMessage;
String m_EnrollLabel; String m_EnrollLabel;
CSettings::CFingerPrint m_EnrolledFinger; CSettings::CFingerPrint m_EnrolledFinger;
bool m_Enrolling; bool m_Enrolling;
bool m_Enrolled; bool m_Enrolled;
bool m_StationConnected;
}; };
extern CMain g_Main; 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 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", "netmask": "0.0.0.0",
"gateway": "0.0.0.0" "gateway": "0.0.0.0"
}, },
"mqtt": {
"client_name": "safeweb",
"host": "",
"port": 0,
"username": "safeweb",
"password": "password"
},
"lock_code": [ "lock_code": [
0, 0,
1, 1,
@@ -21,5 +28,6 @@
7 7
], ],
"fingerprints": [ "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