This commit is contained in:
2025-12-12 08:35:20 +01:00
committed by example
parent 356d7de2cf
commit 0ce45a133b
23 changed files with 616 additions and 135 deletions

View File

@@ -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,10 +582,8 @@ 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,10 +597,8 @@ 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,10 +611,8 @@ 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);
}

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; }
@@ -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;
};

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

@@ -1,27 +1,60 @@
#include <SmingCore.h>
#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();

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,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,28 +258,17 @@ 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 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 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;
if(endpoint == "dashboard")
{
JsonObject network = resp.createNestedObject("network");
if(WifiStation.isEnabled())
{
@@ -170,7 +277,7 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
network["channel"] = WifiStation.getChannel();
network["dhcp"] = WifiStation.isEnabledDHCP();
network["rssi"] = WifiStation.getRssi();
network["signal"] = Rssi2Quality(WifiStation.getRssi());
network["signal"] = Rssi2Quality(network["rssi"]);
network["address"] = WifiStation.getIP().toString();
}
else
@@ -182,17 +289,31 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
network["address"] = WifiAccessPoint.getIP().toString();
}
}
}
else if(endpoint == "unlock")
{
if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED;
if(!req.containsKey("unlock"))
return HTTP_STATUS_BAD_REQUEST;
if(req["unlock"])
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")
{
if(method != HTTP_GET) return HTTP_STATUS_METHOD_NOT_ALLOWED;
if(method == HTTP_GET)
{
JsonArray fingerprints = resp.createNestedArray("fingerprints");
uint16_t tmp = Settings().m_FingerPrints.count();
for(uint16_t i = 0; i < tmp; i++)
@@ -207,6 +328,26 @@ HttpStatus CMain::HandleApi(HttpMethod method, String endpoint, JsonDocument &re
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 +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<bool>() && 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<OtaUpgradeStream*>(file);
if(otaStream == nullptr) {
debug_e("Something went wrong with the file upload");
return 1;
}
void CMain::FingerEnable(bool enable)
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::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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

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