From 0ce45a133b966de247769ce20de15cc722858741 Mon Sep 17 00:00:00 2001 From: BotoX Date: Fri, 12 Dec 2025 08:35:20 +0100 Subject: [PATCH] update --- app/FingerLogic.cpp | 88 +++-- app/FingerLogic.h | 14 +- app/FingerPrint.h | 34 ++ app/FingerPrint_API.cpp | 25 ++ app/FingerPrint_AsyncAPI.cpp | 34 ++ app/Settings.cpp | 24 +- app/Settings.h | 6 + app/application.cpp | 49 ++- app/main.cpp | 357 ++++++++++++++---- app/main.h | 27 +- app/utils.cpp | 29 ++ app/utils.h | 18 + component.mk | 11 +- files/favicon.ico | Bin 1150 -> 0 bytes files/favicon.ico.gz | Bin 0 -> 368 bytes files/index.html | 25 -- files/index.html.gz | Bin 0 -> 663 bytes files/main.css.gz | Bin 653 -> 499 bytes files/main.js.gz | Bin 742 -> 4350 bytes files/otadone.html.gz | Bin 0 -> 231 bytes files/settings.conf | 10 +- files/templates.js.gz | Bin 4065 -> 0 bytes .../{wifi-sprites.png => wifi-sprites.png.gz} | Bin 1815 -> 1855 bytes 23 files changed, 616 insertions(+), 135 deletions(-) delete mode 100644 files/favicon.ico create mode 100644 files/favicon.ico.gz delete mode 100644 files/index.html create mode 100644 files/index.html.gz create mode 100644 files/otadone.html.gz delete mode 100644 files/templates.js.gz rename files/{wifi-sprites.png => wifi-sprites.png.gz} (96%) diff --git a/app/FingerLogic.cpp b/app/FingerLogic.cpp index c0ac909..1e94ee1 100644 --- a/app/FingerLogic.cpp +++ b/app/FingerLogic.cpp @@ -16,7 +16,9 @@ CFingerLogic::CFingerLogic(CMain *pMain) memset(&m_FingerParams, 0, sizeof(m_FingerParams)); memset(m_aFingerSlots, 0, sizeof(m_aFingerSlots)); m_FingerSlotsAdded = 0; + m_ErrorCounter = 0; + m_InitTimer.initializeMs(100, TimerDelegate(&CFingerLogic::InitFinger, this)); m_PowerOffTimer.initializeMs(1000, TimerDelegate(&CFingerLogic::PowerOff, this)); } @@ -31,8 +33,13 @@ void CFingerLogic::SetState(FingerLogicState state) { m_State = state; + if(m_State == STATE_ERROR) + m_ErrorCounter++; + if(m_State == STATE_READY || m_State == STATE_ERROR) - m_PowerOffTimer.start(false); + { + m_PowerOffTimer.startOnce(); + } else { m_PowerOffTimer.stop(); @@ -47,9 +54,8 @@ void CFingerLogic::PowerOff() debugf("PowerOff()"); m_Power = false; - Main().FingerEnable(false); - wifi_set_sleep_type(LIGHT_SLEEP_T); - system_soft_wdt_feed(); + m_ErrorCounter = 0; + Main().FingerPower(false); } void CFingerLogic::PowerOn() @@ -59,14 +65,15 @@ void CFingerLogic::PowerOn() debugf("PowerOn()"); m_Power = true; - Main().FingerEnable(true); - wifi_set_sleep_type(MODEM_SLEEP_T); + Main().FingerPower(true); + system_soft_wdt_feed(); delayMilliseconds(100); + system_soft_wdt_feed(); } -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) @@ -122,15 +129,20 @@ void CFingerLogic::InitFinger() if(m_State > STATE_INIT_VERIFYPASSWORD) return; + if(m_ErrorCounter > 100) + return; + SetState(STATE_INIT_VERIFYPASSWORD); FingerPrint().AsyncVerifyPassword((VerifyPasswordCallback)InitFinger_OnVerifyPassword, this); - m_Timer.initializeMs(100, TimerDelegate(&CFingerLogic::InitFinger, this)).start(); + m_InitTimer.start(); + m_ErrorCounter++; } void CFingerLogic::InitFinger_OnVerifyPassword(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) { - pThis->m_Timer.stop(); + pThis->m_ErrorCounter--; + pThis->m_InitTimer.stop(); debugf("InitFinger_OnVerifyPassword: (%d) %s", error, errorStr); if(error == ERROR_OK) @@ -157,11 +169,38 @@ 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); + pThis->m_InitTimer.start(); + } +} + +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); } else + { pThis->SetState(STATE_ERROR); + pThis->m_InitTimer.start(); + } } void CFingerLogic::InitFinger_OnReadTemplateMap(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, uint8_t *pData, uint16_t dataLen) @@ -241,7 +280,10 @@ void CFingerLogic::InitFinger_OnLoadTemplate(CFingerLogic *pThis, FingerPrintErr pThis->FingerPrint().AsyncDownloadCharacteristics((DownloadCharacteristicsCallback)InitFinger_OnDownloadCharacteristics, pThis, 0x01); } else + { pThis->SetState(STATE_ERROR); + pThis->m_InitTimer.start(); + } } void CFingerLogic::InitFinger_OnDownloadCharacteristics(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int8_t *pChar, uint16_t charLen) @@ -282,7 +324,10 @@ void CFingerLogic::InitFinger_OnDownloadCharacteristics(CFingerLogic *pThis, Fin pThis->FingerPrint().AsyncLoadTemplate((LoadTemplateCallback)InitFinger_OnLoadTemplate, pThis, position, 0x01); } else + { pThis->SetState(STATE_ERROR); + pThis->m_InitTimer.start(); + } } @@ -537,9 +582,7 @@ void CFingerLogic::EnrollFinger_OnCreateTemplate(CFingerLogic *pThis, FingerPrin pThis->FingerPrint().AsyncStoreTemplate((StoreTemplateCallback)EnrollFinger_OnStoreTemplate, pThis, pThis->m_iBuffer, 0x01); } else - { pThis->SetState(STATE_READY); - } } void CFingerLogic::EnrollFinger_OnStoreTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, uint16_t positionNumber) @@ -554,9 +597,7 @@ void CFingerLogic::EnrollFinger_OnStoreTemplate(CFingerLogic *pThis, FingerPrint pThis->FingerPrint().AsyncLoadTemplate((LoadTemplateCallback)EnrollFinger_OnLoadTemplate, pThis, positionNumber, 0x01); } else - { pThis->SetState(STATE_READY); - } } void CFingerLogic::EnrollFinger_OnLoadTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) @@ -570,9 +611,7 @@ void CFingerLogic::EnrollFinger_OnLoadTemplate(CFingerLogic *pThis, FingerPrintE pThis->FingerPrint().AsyncDownloadCharacteristics((DownloadCharacteristicsCallback)EnrollFinger_OnDownloadCharacteristics, pThis, 0x01); } else - { pThis->SetState(STATE_READY); - } } void CFingerLogic::EnrollFinger_OnDownloadCharacteristics(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int8_t *pChar, uint16_t charLen) @@ -586,13 +625,20 @@ void CFingerLogic::EnrollFinger_OnDownloadCharacteristics(CFingerLogic *pThis, F ctx.update(pChar, charLen); uint8_t *digest = ctx.getHash().data(); - Serial1.printf("NEW Finger %d hexdigest: ", pThis->m_iBuffer); - for(uint8_t i = 0; i < sizeof(digest); i++) - Serial1.printf("%x", digest[i]); - Serial1.printf("\n"); - pThis->Main().OnFingerEnrolled(pThis->m_iBuffer, digest); } 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); +} diff --git a/app/FingerLogic.h b/app/FingerLogic.h index ddc5081..83a13d0 100644 --- a/app/FingerLogic.h +++ b/app/FingerLogic.h @@ -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; } @@ -84,11 +91,13 @@ private: void PowerOff(); void PowerOn(); void SetState(FingerLogicState state); + Timer m_InitTimer; Timer m_PowerOffTimer; CMain *m_pMain; CFingerPrint *m_pFingerPrint; FingerLogicState m_State; + int m_ErrorCounter; bool m_Finger; CFingerSystemParameters m_FingerParams; @@ -97,7 +106,6 @@ private: int32_t m_iBuffer; - Timer m_Timer; void *m_fnUserCallback; void *m_pUserData; }; diff --git a/app/FingerPrint.h b/app/FingerPrint.h index 2bccf4b..feb9898 100644 --- a/app/FingerPrint.h +++ b/app/FingerPrint.h @@ -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); diff --git a/app/FingerPrint_API.cpp b/app/FingerPrint_API.cpp index a2ab40b..473fce3 100644 --- a/app/FingerPrint_API.cpp +++ b/app/FingerPrint_API.cpp @@ -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[] = { diff --git a/app/FingerPrint_AsyncAPI.cpp b/app/FingerPrint_AsyncAPI.cpp index 8fcbe50..40e42b8 100644 --- a/app/FingerPrint_AsyncAPI.cpp +++ b/app/FingerPrint_AsyncAPI.cpp @@ -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[] = { diff --git a/app/Settings.cpp b/app/Settings.cpp index 4a7fa0d..7044fdc 100644 --- a/app/Settings.cpp +++ b/app/Settings.cpp @@ -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(); m_Gateway = network["gateway"].as(); + 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); diff --git a/app/Settings.h b/app/Settings.h index d5a71fb..c08060e 100644 --- a/app/Settings.h +++ b/app/Settings.h @@ -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 m_FingerPrints; + + int m_SecurityLevel; }; #endif diff --git a/app/application.cpp b/app/application.cpp index 7dc4bbb..856d3b6 100644 --- a/app/application.cpp +++ b/app/application.cpp @@ -1,27 +1,60 @@ #include #include "FingerPrint.h" +#include "utils.h" #include "main.h" HardwareSerial Serial1(UART_ID_1); NtpClient ntpClient("at.pool.ntp.org", 3600); -void IRAM_ATTR OnFingerInterrupt() +static SimpleTimer procTimer; +volatile uint8_t procTimerLoops = 0; +void OnProcTimer() { - // LOW = FINGER, HIGH = NO FINGER - bool status = digitalRead(FINGER_DETECT_PIN); + const bool finger = !digitalRead(FINGER_DETECT_PIN); + const bool unlocked = !digitalRead(SAFELOCK_DETECT_PIN); + const bool opened = digitalRead(DOOR_DETECT_PIN); + static Debounce Finger(finger); + static Debounce Unlocked(unlocked); + static Debounce Opened(opened); - g_Main.OnFingerInterrupt(!status); + int8_t fingerEdge = Finger.debounce(finger); + if(fingerEdge) + g_Main.OnFinger(fingerEdge & 1); + + int8_t unlockedEdge = Unlocked.debounce(unlocked); + if(unlockedEdge) + g_Main.OnLock(unlockedEdge & 1); + + int8_t openedEdge = Finger.debounce(opened); + if(openedEdge) + g_Main.OnDoor(openedEdge & 1); + + procTimerLoops++; + if(procTimerLoops > 25) // 250ms + procTimer.stop(); +} + +void IRAM_ATTR OnLevelChangeISR() +{ + procTimerLoops = 0; + procTimer.start(); } void ready() { debugf("READY!"); - gpio_pin_wakeup_enable(GPIO_ID_PIN(FINGER_DETECT_PIN), GPIO_PIN_INTR_LOLEVEL); - g_Main.Init(Serial); - attachInterrupt(FINGER_DETECT_PIN, OnFingerInterrupt, CHANGE); + 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_HILEVEL); + + attachInterrupt(FINGER_DETECT_PIN, OnLevelChangeISR, CHANGE); + attachInterrupt(SAFELOCK_DETECT_PIN, OnLevelChangeISR, CHANGE); + attachInterrupt(DOOR_DETECT_PIN, OnLevelChangeISR, CHANGE); + + procTimer.initializeMs<10>(OnProcTimer); } void init() @@ -45,7 +78,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(); diff --git a/app/main.cpp b/app/main.cpp index 3fd98fd..3f7bd84 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,6 +1,10 @@ #include +#include #include #include +#include +#include +#include #include "utils.h" #include "main.h" @@ -10,17 +14,98 @@ 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 + if(!m_StationConnected) + 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,27 +130,61 @@ 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)); m_FingerPrint.Init(serial, 0xFFFFFFFF, 0x00000000); m_FingerLogic.Init(&m_FingerPrint); + + m_LightSleepTimer.initializeMs(60 * 1000, TimerDelegate(&CMain::EnterLightSleep, this)); +} + +static void wakeupCallback() { + debugf("Wakeing up @ %lu", millis()); + debugf("Wakeing up @ %s", SystemClock.getSystemTimeString().c_str()); + wifi_fpm_close(); + wifi_set_opmode(STATION_MODE); + wifi_station_connect(); + system_soft_wdt_feed(); +} + +void CMain::EnterLightSleep() +{ + debugf("Going to sleep @ %lu", millis()); + debugf("Going to sleep @ %s", SystemClock.getSystemTimeString().c_str()); + wifi_station_disconnect(); + wifi_set_opmode(NULL_MODE); + wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); + wifi_fpm_open(); + wifi_fpm_set_wakeup_cb(wakeupCallback); + wifi_fpm_do_sleep(0xFFFFFFF); + system_soft_wdt_feed(); } bool CMain::HttpAuthorized(HttpRequest &request, HttpResponse &response) @@ -124,7 +243,6 @@ void CMain::HttpOnApi(HttpRequest &request, HttpResponse &response) return; } - HttpStatus status = HandleApi(request.method, endpoint, jsonReq, jsonResp); if(status != HTTP_STATUS_OK) { @@ -140,73 +258,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(); + 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 +365,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 +382,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 +391,7 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re { if(req["cancel"].as() && m_Enrolling) { - resp["success"] = FingerLogic().EnrollFinger(false); + FingerLogic().EnrollFinger(false); m_Enrolling = false; m_Enrolled = false; } @@ -336,15 +473,100 @@ void CMain::HttpOnFile(HttpRequest &request, HttpResponse &response) response.sendFile(file); } -void CMain::OnFingerInterrupt(bool finger) +int CMain::HttpOnUpload(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) { - m_FingerLogic.OnFingerInterrupt(finger); + if(!HttpAuthorized(request, response)) + return 1; + + ReadWriteStream* file = request.files["firmware"]; + auto otaStream = static_cast(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::FingerEnable(bool enable) +void CMain::CheckSleep() +{ + if(!m_FingerPlaced && + !m_LockUnlocked && + !m_DoorOpened) + { + m_LightSleepTimer.startOnce(); + } + else + { + m_LightSleepTimer.stop(); + } +} + +void CMain::OnFinger(bool finger) +{ + m_FingerPlaced = finger; + m_FingerLogic.OnFinger(finger); + + if(finger) + MqttSendMessage("safeweb/finger/placed", "true"); + else + MqttSendMessage("safeweb/finger/placed", "false"); + CheckSleep(); +} + +void CMain::OnLock(bool unlocked) +{ + m_LockUnlocked = unlocked; + + if(unlocked) + MqttSendMessage("safeweb/lock/unlocked", "true"); + else + MqttSendMessage("safeweb/lock/unlocked", "false"); + CheckSleep(); +} + +void CMain::OnDoor(bool opened) +{ + m_DoorOpened = opened; + + if(opened) + MqttSendMessage("safeweb/door/opened", "true"); + else + MqttSendMessage("safeweb/door/opened", "false"); + CheckSleep(); +} + +void CMain::FingerPower(bool enable) { const int pin = FINGER_ENABLE_PIN; digitalWrite(pin, !enable); + + if(enable) + { + wifi_set_sleep_level(MAX_SLEEP_T); + wifi_set_listen_interval(3); + wifi_set_sleep_type(MODEM_SLEEP_T); + } + else + { + wifi_set_sleep_type(LIGHT_SLEEP_T); + } } void CMain::LockSendBytes(uint8_t *pBytes, uint8_t len) @@ -443,11 +665,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; diff --git a/app/main.h b/app/main.h index 3e6143a..4f6569d 100644 --- a/app/main.h +++ b/app/main.h @@ -19,10 +19,19 @@ class CMain public: CMain(); void Init(HardwareSerial &serial); + void CheckSleep(); + void EnterLightSleep(); - 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 FingerEnable(bool enable); + void OnFinger(bool finger); + void OnLock(bool unlocked); + void OnDoor(bool opened); + + void FingerPower(bool enable); void LockUnlock(); void EnrollMessage(const char *msg, bool error=false); @@ -38,9 +47,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 +65,21 @@ private: FtpServer m_FTP; HttpServer m_HttpServer; + MqttClient m_Mqtt; + Timer m_MqttTimer; + Timer m_LightSleepTimer; String m_EnrollMessage; String m_EnrollLabel; CSettings::CFingerPrint m_EnrolledFinger; bool m_Enrolling; bool m_Enrolled; + + bool m_StationConnected; + + bool m_FingerPlaced; + bool m_LockUnlocked; + bool m_DoorOpened; }; extern CMain g_Main; diff --git a/app/utils.cpp b/app/utils.cpp index d082b36..c42e715 100644 --- a/app/utils.cpp +++ b/app/utils.cpp @@ -73,3 +73,32 @@ int Rssi2Quality(sint8 rssi) else return 4; } + +Debounce::Debounce() : state(0xFF) {} +Debounce::Debounce(bool signal) { init(signal); } +void Debounce::init(bool signal) +{ + if(signal) + state = 0xFF; + else + state = 0x00; +} + +int8_t Debounce::debounce(bool signal) +{ + const uint8_t current = state & UPPER; + const uint8_t history = (state << 1) & LOWER; + + state = current | history | signal; + + // Events are triggered when the history is saturated + // with the opposite of the active switch state. + // As a result, the events are guaranteed to alternate. + + switch (state) { + case UPPER: state = 0x00; return -1; // LOW/False (falling edge) + case LOWER: state = 0xFF; return 1; // HIGH/True (rising edge) + } + + return 0;//state & UPPER; +} diff --git a/app/utils.h b/app/utils.h index d0ed894..8d9697d 100644 --- a/app/utils.h +++ b/app/utils.h @@ -5,4 +5,22 @@ int hex2bytes(const char *str, uint8_t *bytes, int32_t length); void bytes2hex(const uint8_t *bytes, int32_t length, char *str, int32_t strLength); int Rssi2Quality(sint8 rssi); +class Debounce +{ + static constexpr uint8_t UPPER = 0b10000000; + static constexpr uint8_t LOWER = 0b01111111; +public: + Debounce(); + Debounce(bool signal); + void init(bool signal); + /* Return value: + * 0: no change + * -1: LOW/False (falling edge) + * 1: HIGH/True (rising edge) + */ + int8_t debounce(bool signal); +private: + uint8_t state; +}; + #endif diff --git a/component.mk b/component.mk index dcb4953..cfff8c6 100644 --- a/component.mk +++ b/component.mk @@ -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 diff --git a/files/favicon.ico b/files/favicon.ico deleted file mode 100644 index 23ceec5c4cd0158aafbd691c5c778a7da013ab6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmZQzU}Ruq5D);-3Je)63=Con3=A3!3=9Gc3=9ek5OD?&U;z=R_~FBcvR$1WyM6#Tt}>aP7b!e7cN{dg6aSM{X3Ve zjMOV)^@G$kH8!-t^q)L=(u#wf^)E;q8RPOlHy6jR1@q^n!}K3Ne$1YqkLMe*UJx6X z{+#TrIWWC2x~r?R4x|Se((HcJ{W!J;zeU*y&yI&{TtS=3y0~0(Pz(|wE*cs z#<=uHM1-yX`}Z#+OfQHoEh(OYtQW+_rJs|7?N45A&di%PZ))GYdsnTgv7rMLrXW4Y z7?*yS8c;d_VQg^+QUlUoR9G+p#3l#V)z!A#zI|Jzsu}ja-x1X+|b7J<9gXezDm$`Ex;QEaD)2$_A zV*(o9Z{9KEgmsFJj>-qg*jQiRQfaflsi{kA|NsA=?r`k$vvVhv!uIc*ckXTc{<@7y zulxJDV%*o%`SESrFRmA3(Dd@}zrWVg*YsS`PWswh5S;8R?eqWSWc9TTN%fsq&!0W| zG5g@3%9jRzkN3-Oe*XONPq#Jy=Db;B`QqXAt5+l4*Zf->y`630>-{Ak7a4B+Wxgg} zyl>tAH#awn9eLH?!4duc`}=sel)p<=j|O{m?6 - - - - - - Safe control panel - - - - - - - -
- - - - - - - - - - diff --git a/files/index.html.gz b/files/index.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..a7a989f7bfa02ea843bd573efe23c9984066df2c GIT binary patch literal 663 zcmb2|=3oE;Cg!b^Zx`J%5NL}(`f%EfLu^bXIcY^1mz-qSGCWSsosb%>;O!LUJZJg( z#tSJyFAA$R&-;6A_pkE0llGk|jZT5utv%&`eYaSMc{;dWKd+I{y(B`K%aL#6-MU1k zbdw0fU134Zu1grS6jrQ>WXTk`D5`wrp4xIBgM=Bk+YY$iB`HtlP^#Qfne0~rGZk%)`*&=f>9`?{ zA>_8P=D~(zT&8no1v?*+_%hd0QrOQ^CoFGz2AgYuq2{+)>EGT~?*1GTTk@z<=v#Zn z#vC5DxD}7TwH!8iobjnJMpkvJ#n&vyqe@Xy%BJ2kI5tdJbRs3MZj(z}IeYovjjQ=;Ih@n!C`^gg%6jA2>Pvi}?+KJp&6dOh)i)I66?i$#lU>$P`HzQJr( z{m3=q{HB`FW6NH+U&@J{m3I61tuJk5)9+q2yEQH7Hh;lF?&h~CttHPoBRDpF3H#3C z(Izt+4@tTYnJwGpK$mVtKb`sr;N>u4~E5gePn-jk}&A@Bi$R zQZY6qafKq~n=84lFJct0OKP)Gj?C)taGl$nC^z|u+Zlmp6AUIiW6V5kktI>jTpNCA JjT0*a0|13+@#_Ep literal 653 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 I!Op+{02@qdNdN!< diff --git a/files/otadone.html.gz b/files/otadone.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..cbe8466de036ab5343c6b87b8a6f74678791bc3a GIT binary patch literal 231 zcmb2|=HRgMw#;N=E=nyfNh~VS%P7gsVX&QWFz=9ofXn-uqX*bc9!2fkDY7Go)8fmH zBhpC^Z%o{)`09SSFUNA(%xAr2*VmLEfB*UYdAYe?_QlOKdZ2k{pP7u=S1tqD$+}KU zEk4G~uM*F**m1>wip2DTlM0Nkz7bmzB~je_Ex&DRM)dxLA319unV9iDUvs3alUJW{ zjIJDzu3OzrX3>#0{~4TYeN74 literal 0 HcmV?d00001 diff --git a/files/settings.conf b/files/settings.conf index 99c40d7..e4ad423 100644 --- a/files/settings.conf +++ b/files/settings.conf @@ -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 } \ No newline at end of file diff --git a/files/templates.js.gz b/files/templates.js.gz deleted file mode 100644 index ff7a6ba78b3d1a23c1ff88e12b6c6580a73216ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4065 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 YxoT_Qy_Y^`V*Zc4(D03BOcVnH0D}YA8~^|S diff --git a/files/wifi-sprites.png b/files/wifi-sprites.png.gz similarity index 96% rename from files/wifi-sprites.png rename to files/wifi-sprites.png.gz index 863d0d7f28d60e96bcc36f55a461e79816598c21..53e078f9534afc47811202e7310c6ddc96735ef7 100644 GIT binary patch delta 49 zcmbQvx1UczzMF%?qN^~GiMc#8EmOC+peVB>wOFqpFP(u=oc+a*4dUz^>r+e)inB8? F004ro4>|w< delta 8 PcmdnbH=S>zf;c+>4T}O|