commit 356d7de2cfbe325322d0f4d10cd44494025b64c4 Author: BotoX Date: Tue Jun 6 19:34:45 2023 +0200 first commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ff51b6c --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +##################################################################### +#### Please don't change this file. Use component.mk instead #### +##################################################################### + +ifndef SMING_HOME +$(error SMING_HOME is not set: please configure it as an environment variable) +endif + +include $(SMING_HOME)/project.mk diff --git a/app/FingerLogic.cpp b/app/FingerLogic.cpp new file mode 100644 index 0000000..c0ac909 --- /dev/null +++ b/app/FingerLogic.cpp @@ -0,0 +1,598 @@ +#include +#include +#include "utils.h" +#include "main.h" +#include "FingerPrint.h" +#include "FingerLogic.h" + +CFingerLogic::CFingerLogic(CMain *pMain) +{ + m_pFingerPrint = NULL; + m_State = STATE_INITIAL; + m_Finger = false; + m_Power = false; + m_pMain = pMain; + + memset(&m_FingerParams, 0, sizeof(m_FingerParams)); + memset(m_aFingerSlots, 0, sizeof(m_aFingerSlots)); + m_FingerSlotsAdded = 0; + + m_PowerOffTimer.initializeMs(1000, TimerDelegate(&CFingerLogic::PowerOff, this)); +} + +void CFingerLogic::Init(CFingerPrint *pFingerPrint) +{ + m_pFingerPrint = pFingerPrint; + + InitFinger(); +} + +void CFingerLogic::SetState(FingerLogicState state) +{ + m_State = state; + + if(m_State == STATE_READY || m_State == STATE_ERROR) + m_PowerOffTimer.start(false); + else + { + m_PowerOffTimer.stop(); + PowerOn(); + } +} + +void CFingerLogic::PowerOff() +{ + if(!m_Power) + return; + + debugf("PowerOff()"); + m_Power = false; + Main().FingerEnable(false); + wifi_set_sleep_type(LIGHT_SLEEP_T); + system_soft_wdt_feed(); +} + +void CFingerLogic::PowerOn() +{ + if(m_Power) + return; + + debugf("PowerOn()"); + m_Power = true; + Main().FingerEnable(true); + wifi_set_sleep_type(MODEM_SLEEP_T); + delayMilliseconds(100); +} + +void CFingerLogic::OnFingerInterrupt(bool finger) +{ + debugf("OnFingerInterrupt: %s", finger ? "DOWN" : "UP"); + m_Finger = finger; + + if(finger) + { + if(m_State == STATE_READY) + VerifyFinger(); + else if(m_State == STATE_ENROLL_WAITFINGER1 || m_State == STATE_ENROLL_WAITFINGER2) + EnrollFinger_OnFinger(); + } +} + +bool CFingerLogic::FingerSlot(uint16_t index) +{ + if(index > m_FingerSlotsAdded) + return false; + + return m_aFingerSlots[index / 8] & (1 << (index % 8)); +} + +bool CFingerLogic::FingerSlot(uint16_t index, bool value) +{ + if(index > m_FingerSlotsAdded) + return false; + + if(value) + m_aFingerSlots[index / 8] |= (1 << (index % 8)); + else + m_aFingerSlots[index / 8] &= ~(1 << (index % 8)); + return true; +} + +int CFingerLogic::FirstFreeFingerSlot() +{ + for(int i = 0; i < m_FingerSlotsAdded / 8; i++) + { + if(m_aFingerSlots[i] != 0xFF) + { + uint8_t val = m_aFingerSlots[i]; + for(int j = 0; j < 8; j++) + { + if(!(val & (1 << j))) + return (i * 8) + j; + } + } + } + + return -1; +} + + +void CFingerLogic::InitFinger() +{ + if(m_State > STATE_INIT_VERIFYPASSWORD) + return; + + SetState(STATE_INIT_VERIFYPASSWORD); + FingerPrint().AsyncVerifyPassword((VerifyPasswordCallback)InitFinger_OnVerifyPassword, this); + + m_Timer.initializeMs(100, TimerDelegate(&CFingerLogic::InitFinger, this)).start(); +} + +void CFingerLogic::InitFinger_OnVerifyPassword(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + pThis->m_Timer.stop(); + debugf("InitFinger_OnVerifyPassword: (%d) %s", error, errorStr); + + if(error == ERROR_OK) + { + pThis->SetState(STATE_INIT_READSYSTEMPARAMETERS); + pThis->FingerPrint().AsyncReadSystemParameters((ReadSystemParametersCallback)InitFinger_OnReadSystemParameters, pThis); + } + else + pThis->SetState(STATE_ERROR); +} + +void CFingerLogic::InitFinger_OnReadSystemParameters(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, CFingerSystemParameters *param) +{ + debugf("InitFinger_OnReadSystemParameters: (%d) %s", error, errorStr); + + if(error == ERROR_OK) + { + memcpy(&pThis->m_FingerParams, param, sizeof(CFingerSystemParameters)); + debugf("statusRegister: %d", param->statusRegister); + debugf("systemID: %d", param->systemID); + debugf("storageCapacity: %d", param->storageCapacity); + debugf("securityLevel: %d", param->securityLevel); + debugf("deviceAddress: %X", param->deviceAddress); + debugf("packetLength: %d", param->packetLength); + debugf("baudRate: %d", param->baudRate); + + pThis->SetState(STATE_INIT_READTEMPLATEMAP); + pThis->FingerPrint().AsyncReadTemplateMap((ReadTemplateMapCallback)InitFinger_OnReadTemplateMap, pThis, 0); + } + else + pThis->SetState(STATE_ERROR); +} + +void CFingerLogic::InitFinger_OnReadTemplateMap(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, uint8_t *pData, uint16_t dataLen) +{ + debugf("InitFinger_OnReadTemplateMap: (%d) %s (%p, %d)", error, errorStr, pData, dataLen); + + if(error == ERROR_OK) + { + memcpy(&pThis->m_aFingerSlots[pThis->m_FingerSlotsAdded/8], pData, dataLen); + pThis->m_FingerSlotsAdded += dataLen * 8; + + if(pThis->m_FingerSlotsAdded < pThis->m_FingerParams.storageCapacity) + { + uint8_t page = pThis->m_FingerSlotsAdded / 8 / dataLen; + pThis->FingerPrint().AsyncReadTemplateMap((ReadTemplateMapCallback)InitFinger_OnReadTemplateMap, pThis, page); + } + else + { + pThis->InitFinger_VerifyTemplates(); + } + } +} + +void CFingerLogic::InitFinger_VerifyTemplates() +{ + SetState(STATE_INIT_VERIFYTEMPLATES); + + HashMap &fingerMap = Main().Settings().m_FingerPrints; + uint16_t fingerCount = fingerMap.count(); + + // Check consistency (1) + for(uint16_t i = 0; i < fingerCount; i++) + { + uint16_t id = fingerMap.keyAt(i); + if(!FingerSlot(id)) + { + SetState(STATE_ERROR); + FingerPrint().EmptyDatabase(); + fingerMap.clear(); + Main().Settings().Save(); + debugf("InitFinger_VerifyTemplates: INCONSITENCY(1) AT SLOT %d !!!", id); + return; + } + } + + // Check consistency (2) + for(uint16_t id = 0; id < m_FingerSlotsAdded; id++) + { + if(FingerSlot(id) && !fingerMap.contains(id)) + { + SetState(STATE_ERROR); + FingerPrint().EmptyDatabase(); + fingerMap.clear(); + Main().Settings().Save(); + debugf("InitFinger_VerifyTemplates: INCONSITENCY(2) AT SLOT %d !!!", id); + return; + } + } + + if(!fingerCount) + { + SetState(STATE_READY); + return; + } + + m_iBuffer = 0; + uint16_t position = fingerMap.keyAt(m_iBuffer); + FingerPrint().AsyncLoadTemplate((LoadTemplateCallback)InitFinger_OnLoadTemplate, this, position, 0x01); +} + +void CFingerLogic::InitFinger_OnLoadTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + debugf("InitFinger_OnLoadTemplate: (%d) %s", error, errorStr); + + if(error == ERROR_OK) + { + pThis->FingerPrint().AsyncDownloadCharacteristics((DownloadCharacteristicsCallback)InitFinger_OnDownloadCharacteristics, pThis, 0x01); + } + else + pThis->SetState(STATE_ERROR); +} + +void CFingerLogic::InitFinger_OnDownloadCharacteristics(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int8_t *pChar, uint16_t charLen) +{ + debugf("InitFinger_OnDownloadCharacteristics: (%d) %s (%p, %d)", error, errorStr, pChar, charLen); + + if(error == ERROR_OK) + { + Crypto::Sha256 ctx; + ctx.update(pChar, charLen); + uint8_t *digest = ctx.getHash().data(); + + const CSettings::CFingerPrint &finger = pThis->Main().Settings().m_FingerPrints.valueAt(pThis->m_iBuffer); + + char aHexDigest1[SHA256_SIZE*2+1]; + char aHexDigest2[SHA256_SIZE*2+1]; + bytes2hex(digest, sizeof(digest), aHexDigest1, sizeof(aHexDigest1)); + bytes2hex(finger.m_aDigest, sizeof(finger.m_aDigest), aHexDigest2, sizeof(aHexDigest2)); + + debugf("Index: %d -> Num: %d \"%s\" (%s ?= %s)", pThis->m_iBuffer, finger.m_FingerNum, finger.m_aLabel, aHexDigest1, aHexDigest2); + + if(memcmp(digest, finger.m_aDigest, SHA256_SIZE) != 0) + { + pThis->SetState(STATE_ERROR); + debugf("InitFinger_VerifyTemplates: DIVERGENT DIGEST AT SLOT %d !!!", pThis->m_iBuffer); + return; + } + + pThis->m_iBuffer++; + if(pThis->m_iBuffer >= pThis->Main().Settings().m_FingerPrints.count()) + { + debugf("InitFinger_VerifyTemplates: DONE! Verified %d templates.", pThis->m_iBuffer); + pThis->SetState(STATE_READY); + return; + } + + uint16_t position = pThis->Main().Settings().m_FingerPrints.keyAt(pThis->m_iBuffer); + pThis->FingerPrint().AsyncLoadTemplate((LoadTemplateCallback)InitFinger_OnLoadTemplate, pThis, position, 0x01); + } + else + pThis->SetState(STATE_ERROR); +} + + +void CFingerLogic::VerifyFinger() +{ + if(m_State != STATE_READY) + return; + + SetState(STATE_VERIFY_READIMAGE); + FingerPrint().AsyncReadImage((ReadImageCallback)VerifyFinger_OnReadImage, this); +} + +void CFingerLogic::VerifyFinger_OnReadImage(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + debugf("VerifyFinger_OnReadImage: (%d) %s", error, errorStr); + + if(error == ERROR_OK) + { + pThis->SetState(STATE_VERIFY_CONVERTIMAGE); + pThis->FingerPrint().AsyncConvertImage((ConvertImageCallback)VerifyFinger_OnConvertImage, pThis, 0x01); + } + else + { + pThis->SetState(STATE_READY); + if(pThis->m_Finger) + pThis->VerifyFinger(); + } +} + +void CFingerLogic::VerifyFinger_OnConvertImage(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + debugf("VerifyFinger_OnConvertImage: (%d) %s", error, errorStr); + + if(error == ERROR_OK) + { + pThis->SetState(STATE_VERIFY_SEARCHTEMPLATE); + pThis->FingerPrint().AsyncSearchTemplate((SearchTemplateCallback)VerifyFinger_OnSearchTemplate, pThis, 0x01, 0, pThis->m_FingerParams.storageCapacity); + } + else + { + pThis->SetState(STATE_READY); + if(pThis->m_Finger) + pThis->VerifyFinger(); + } +} + +void CFingerLogic::VerifyFinger_OnSearchTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int16_t position, int16_t score) +{ + debugf("VerifyFinger_OnSearchTemplate: (%d) %s (%d, %d)", error, errorStr, position, score); + + if(error == ERROR_OK) + { + pThis->m_iBuffer = position; + pThis->SetState(STATE_VERIFY_LOADTEMPLATE); + pThis->FingerPrint().AsyncLoadTemplate((LoadTemplateCallback)VerifyFinger_OnLoadTemplate, pThis, position, 0x01); + } + else + pThis->SetState(STATE_READY); +} + +void CFingerLogic::VerifyFinger_OnLoadTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + debugf("VerifyFinger_OnLoadTemplate: (%d) %s", error, errorStr); + + if(error == ERROR_OK) + { + pThis->SetState(STATE_VERIFY_DOWNLOADCHARACTERISTICS); + pThis->FingerPrint().AsyncDownloadCharacteristics((DownloadCharacteristicsCallback)VerifyFinger_OnDownloadCharacteristics, pThis, 0x01); + } + else + pThis->SetState(STATE_READY); +} + +void CFingerLogic::VerifyFinger_OnDownloadCharacteristics(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int8_t *pChar, uint16_t charLen) +{ + debugf("VerifyFinger_OnDownloadCharacteristics: (%d) %s (%p, %d)", error, errorStr, pChar, charLen); + + if(error == ERROR_OK) + { + Crypto::Sha256 ctx; + ctx.update(pChar, charLen); + uint8_t *digest = ctx.getHash().data(); + + char aHexDigest[SHA256_SIZE*2+1]; + bytes2hex(digest, sizeof(digest), aHexDigest, sizeof(aHexDigest)); + + debugf("Finger hexdigest: %s", aHexDigest); + + pThis->Main().OnFingerVerified(pThis->m_iBuffer, digest); + } + + pThis->SetState(STATE_READY); +} + + +bool CFingerLogic::EnrollFinger(bool cancel) +{ + if(cancel) + { + if(m_State < STATE_ENROLL_WAITFINGER1 || m_State > STATE_ENROLL_DOWNLOADCHARACTERISTICS) + return false; + + SetState(STATE_READY); + return true; + } + + if(m_State != STATE_READY) + return false; + + SetState(STATE_ENROLL_WAITFINGER1); + if(m_Finger) + EnrollFinger_OnFinger(); + + return true; +} + +void CFingerLogic::EnrollFinger_OnFinger() +{ + if(m_State == STATE_ENROLL_WAITFINGER1) + { + SetState(STATE_ENROLL_READIMAGE1); + FingerPrint().AsyncReadImage((ReadImageCallback)EnrollFinger_OnReadImage1, this); + } + else if(m_State == STATE_ENROLL_WAITFINGER2) + { + SetState(STATE_ENROLL_READIMAGE2); + FingerPrint().AsyncReadImage((ReadImageCallback)EnrollFinger_OnReadImage2, this); + } +} + +void CFingerLogic::EnrollFinger_OnReadImage1(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + debugf("EnrollFinger_OnReadImage1: (%d) %s", error, errorStr); + if(pThis->m_State != STATE_ENROLL_READIMAGE1) return; + + if(error == ERROR_OK) + { + pThis->SetState(STATE_ENROLL_CONVERTIMAGE1); + pThis->FingerPrint().AsyncConvertImage((ConvertImageCallback)EnrollFinger_OnConvertImage1, pThis, 0x01); + } + else + { + pThis->SetState(STATE_ENROLL_WAITFINGER1); + char aBuf[512]; + m_snprintf(aBuf, sizeof(aBuf), "Error while scanning finger: %s
Lift your finger and try again.", errorStr); + pThis->Main().EnrollMessage(aBuf); + } +} + +void CFingerLogic::EnrollFinger_OnConvertImage1(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + debugf("EnrollFinger_OnConvertImage1: (%d) %s", error, errorStr); + if(pThis->m_State != STATE_ENROLL_CONVERTIMAGE1) return; + + if(error == ERROR_OK) + { + pThis->SetState(STATE_ENROLL_SEARCHTEMPLATE); + pThis->FingerPrint().AsyncSearchTemplate((SearchTemplateCallback)EnrollFinger_OnSearchTemplate, pThis, 0x01, 0, pThis->m_FingerParams.storageCapacity); + } + else + { + pThis->SetState(STATE_ENROLL_WAITFINGER1); + char aBuf[512]; + m_snprintf(aBuf, sizeof(aBuf), "Error while analyzing finger: %s
Lift your finger and try again.", errorStr); + pThis->Main().EnrollMessage(aBuf); + } +} + +void CFingerLogic::EnrollFinger_OnSearchTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int16_t position, int16_t score) +{ + debugf("EnrollFinger_OnSearchTemplate: (%d) %s (%d, %d)", error, errorStr, position, score); + if(pThis->m_State != STATE_ENROLL_SEARCHTEMPLATE) return; + + if(error == ERROR_OK) + { + pThis->SetState(STATE_READY); + pThis->Main().EnrollMessage("Aborting: This finger is already enrolled!", true); + } + else + { + pThis->SetState(STATE_ENROLL_WAITFINGER2); + pThis->Main().EnrollMessage("Finger scanned. Lift finger and place it on the sensor again to enroll."); + } +} + +void CFingerLogic::EnrollFinger_OnReadImage2(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + debugf("EnrollFinger_OnReadImage2: (%d) %s", error, errorStr); + if(pThis->m_State != STATE_ENROLL_READIMAGE2) return; + + if(error == ERROR_OK) + { + pThis->SetState(STATE_ENROLL_CONVERTIMAGE2); + pThis->FingerPrint().AsyncConvertImage((ConvertImageCallback)EnrollFinger_OnConvertImage2, pThis, 0x02); + } + else + { + pThis->SetState(STATE_ENROLL_WAITFINGER2); + char aBuf[512]; + m_snprintf(aBuf, sizeof(aBuf), "Error while scanning finger: %s
Lift your finger and try again.", errorStr); + pThis->Main().EnrollMessage(aBuf); + } +} + +void CFingerLogic::EnrollFinger_OnConvertImage2(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + debugf("EnrollFinger_OnConvertImage2: (%d) %s", error, errorStr); + if(pThis->m_State != STATE_ENROLL_CONVERTIMAGE2) return; + + if(error == ERROR_OK) + { + pThis->SetState(STATE_ENROLL_COMPARE); + pThis->FingerPrint().AsyncCompareCharacteristics((CompareCharacteristicsCallback)EnrollFinger_OnCompareCharacteristics, pThis); + } + else + { + pThis->SetState(STATE_ENROLL_WAITFINGER2); + char aBuf[512]; + m_snprintf(aBuf, sizeof(aBuf), "Error while analyzing finger: %s
Lift your finger and try again.", errorStr); + pThis->Main().EnrollMessage(aBuf); + } +} + +void CFingerLogic::EnrollFinger_OnCompareCharacteristics(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int16_t score) +{ + debugf("EnrollFinger_OnCompareCharacteristics: (%d) %s (%d)", error, errorStr, score); + if(pThis->m_State != STATE_ENROLL_COMPARE) return; + + if(error == ERROR_OK) + { + pThis->SetState(STATE_ENROLL_CREATETEMPLATE); + pThis->FingerPrint().AsyncCreateTemplate((CreateTemplateCallback)EnrollFinger_OnCreateTemplate, pThis); + } + else + { + pThis->SetState(STATE_ENROLL_WAITFINGER1); + char aBuf[512]; + m_snprintf(aBuf, sizeof(aBuf), "Error while comparing fingers: %s
Lift your finger and try again.", errorStr); + pThis->Main().EnrollMessage(aBuf); + } +} + +void CFingerLogic::EnrollFinger_OnCreateTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr) +{ + debugf("EnrollFinger_OnCreateTemplate: (%d) %s", error, errorStr); + if(pThis->m_State != STATE_ENROLL_CREATETEMPLATE) return; + + if(error == ERROR_OK) + { + pThis->SetState(STATE_ENROLL_STORETEMPLATE); + pThis->m_iBuffer = pThis->FirstFreeFingerSlot(); + 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) +{ + debugf("EnrollFinger_OnStoreTemplate: (%d) %s (%d)", error, errorStr, positionNumber); + if(pThis->m_State != STATE_ENROLL_STORETEMPLATE) return; + + if(error == ERROR_OK) + { + pThis->FingerSlot(positionNumber, true); + pThis->SetState(STATE_ENROLL_LOADTEMPLATE); + 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) +{ + debugf("EnrollFinger_OnLoadTemplate: (%d) %s", error, errorStr); + if(pThis->m_State != STATE_ENROLL_LOADTEMPLATE) return; + + if(error == ERROR_OK) + { + pThis->SetState(STATE_ENROLL_DOWNLOADCHARACTERISTICS); + 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) +{ + debugf("EnrollFinger_OnDownloadCharacteristics: (%d) %s (%p, %d)", error, errorStr, pChar, charLen); + if(pThis->m_State != STATE_ENROLL_DOWNLOADCHARACTERISTICS) return; + + if(error == ERROR_OK) + { + Crypto::Sha256 ctx; + 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); +} diff --git a/app/FingerLogic.h b/app/FingerLogic.h new file mode 100644 index 0000000..ddc5081 --- /dev/null +++ b/app/FingerLogic.h @@ -0,0 +1,105 @@ +#ifndef FINGERLOGIC_H +#define FINGERLOGIC_H + +#include +#include "FingerPrint.h" + +enum FingerLogicState +{ + STATE_ERROR = -1, + STATE_INITIAL = 0, + + STATE_INIT_VERIFYPASSWORD, + STATE_INIT_READSYSTEMPARAMETERS, + STATE_INIT_READTEMPLATEMAP, + STATE_INIT_VERIFYTEMPLATES, + + STATE_READY, + + STATE_VERIFY_READIMAGE, + STATE_VERIFY_CONVERTIMAGE, + STATE_VERIFY_SEARCHTEMPLATE, + STATE_VERIFY_LOADTEMPLATE, + STATE_VERIFY_DOWNLOADCHARACTERISTICS, + + STATE_ENROLL_WAITFINGER1, + STATE_ENROLL_READIMAGE1, + STATE_ENROLL_CONVERTIMAGE1, + STATE_ENROLL_SEARCHTEMPLATE, + STATE_ENROLL_WAITFINGER2, + STATE_ENROLL_READIMAGE2, + STATE_ENROLL_CONVERTIMAGE2, + STATE_ENROLL_COMPARE, + STATE_ENROLL_CREATETEMPLATE, + STATE_ENROLL_STORETEMPLATE, + STATE_ENROLL_LOADTEMPLATE, + STATE_ENROLL_DOWNLOADCHARACTERISTICS +}; + +class CFingerLogic +{ +public: + CFingerLogic(class CMain *pMain); + void Init(CFingerPrint *pFingerPrint); + void OnFingerInterrupt(bool finger); + + bool FingerSlot(uint16_t index); + bool FingerSlot(uint16_t index, bool value); + int FirstFreeFingerSlot(); + + 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_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(); + static void InitFinger_OnLoadTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); + static void InitFinger_OnDownloadCharacteristics(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int8_t *pChar, uint16_t charLen); + + void VerifyFinger(); + static void VerifyFinger_OnReadImage(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); + static void VerifyFinger_OnConvertImage(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); + static void VerifyFinger_OnSearchTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int16_t position, int16_t score); + static void VerifyFinger_OnLoadTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); + static void VerifyFinger_OnDownloadCharacteristics(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int8_t *pChar, uint16_t charLen); + + bool EnrollFinger(bool cancel=false); + static void EnrollFinger_OnReadImage1(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); + static void EnrollFinger_OnConvertImage1(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); + static void EnrollFinger_OnSearchTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int16_t position, int16_t score); + void EnrollFinger_OnFinger(); + static void EnrollFinger_OnReadImage2(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); + static void EnrollFinger_OnConvertImage2(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); + static void EnrollFinger_OnCompareCharacteristics(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, int16_t score); + static void EnrollFinger_OnCreateTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr); + static void EnrollFinger_OnStoreTemplate(CFingerLogic *pThis, FingerPrintError error, const char *errorStr, uint16_t positionNumber); + 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); + + CMain &Main() { return *m_pMain; } + CFingerPrint &FingerPrint() { return *m_pFingerPrint; } + +private: + bool m_Power; + void PowerOff(); + void PowerOn(); + void SetState(FingerLogicState state); + Timer m_PowerOffTimer; + + CMain *m_pMain; + CFingerPrint *m_pFingerPrint; + FingerLogicState m_State; + bool m_Finger; + + CFingerSystemParameters m_FingerParams; + uint8_t m_aFingerSlots[1792/8]; + uint16_t m_FingerSlotsAdded; + + int32_t m_iBuffer; + + Timer m_Timer; + void *m_fnUserCallback; + void *m_pUserData; +}; + +#endif diff --git a/app/FingerPrint.cpp b/app/FingerPrint.cpp new file mode 100644 index 0000000..5509565 --- /dev/null +++ b/app/FingerPrint.cpp @@ -0,0 +1,267 @@ +#include +#include "FingerPrint.h" + +static struct CFingerErrorsExplain +{ + uint8_t error; + const char *str; +} gs_FingerErrors[] = { + {ERROR_OK, "ERROR_OK: command execution complete"}, + {ERROR_COMMUNICATION, "ERROR_COMMUNICATION: error when receiving data package"}, + {ERROR_NOFINGER, "ERROR_NOFINGER: no finger on the sensor"}, + {ERROR_READIMAGE, "ERROR_READIMAGE: fail to enroll the finger"}, + {ERROR_MESSYIMAGE, "ERROR_MESSYIMAGE: fail to generate character file due to the over-disorderly fingerprint image"}, + {ERROR_FEWFEATUREPOINTS, "ERROR_FEWFEATUREPOINTS: fail to generate character file due to lackness of character point or over-smallness of fingerprint image"}, + {ERROR_NOTMATCHING, "ERROR_NOTMATCHING: finger doesn't match"}, + {ERROR_NOTEMPLATEFOUND, "ERROR_NOTEMPLATEFOUND: fail to find the matching finger"}, + {ERROR_CHARACTERISTICSMISMATCH, "ERROR_CHARACTERISTICSMISMATCH: fail to combine the character files"}, + {ERROR_INVALIDPOSITION, "ERROR_INVALIDPOSITION: addressing PageID is beyond the finger library"}, + {ERROR_LOADTEMPLATE, "ERROR_LOADTEMPLATE: error when reading template from library or the template is invalid"}, + {ERROR_UPLOADTEMPLATE, "ERROR_UPLOADTEMPLATE: error when uploading template"}, + {ERROR_PACKETRESPONSEFAIL, "ERROR_PACKETRESPONSEFAIL: Module can't receive the following data packages"}, + {ERROR_UPLOADIMAGE, "ERROR_UPLOADIMAGE: error when uploading image"}, + {ERROR_DELETETEMPLATE, "ERROR_DELETETEMPLATE: fail to delete the template"}, + {ERROR_CLEARDATABASE, "ERROR_CLEARDATABASE: fail to clear finger library"}, + {ERROR_WRONGPASSWORD, "ERROR_WRONGPASSWORD: wrong password"}, + {ERROR_INVALIDIMAGE, "ERROR_INVALIDIMAGE: fail to generate the image for the lackness of valid primary image"}, + {ERROR_FLASH, "ERROR_FLASH: error when writing flash"}, + {ERROR_NODEF, "ERROR_NODEF: No definition error"}, + {ERROR_INVALIDREGISTER, "ERROR_INVALIDREGISTER: invalid register number"}, + {ERROR_INCORRECTREGISTERCONF, "ERROR_INCORRECTREGISTERCONF: incorrect configuration of register"}, + {ERROR_INVALIDNOTEPADPAGE, "ERROR_INVALIDNOTEPADPAGE: wrong notepad page number"}, + {ERROR_COMMUNICATIONPORT, "ERROR_COMMUNICATIONPORT: fail to operate the communication port"} +}; + +const char *CFingerPrint::ExplainFingerError(uint8_t error) +{ + CFingerErrorsExplain *pFound = NULL; + for(int i = 0; i < sizeof(gs_FingerErrors) / sizeof(*gs_FingerErrors); i++) + { + if(error == gs_FingerErrors[i].error) + { + pFound = &gs_FingerErrors[i]; + break; + } + } + + if(pFound) + return pFound->str; + + return "Unknown error."; +} + +CFingerPrint::CFingerPrint() +{ + m_RecvState = RECV_DROP; +} + +void CFingerPrint::Init(HardwareSerial &serial, uint32_t address, uint32_t password) +{ + if(m_pSerial) + return; + + m_pSerial = &serial; + m_Address = address; + m_Password = password; + + m_pSerial->onDataReceived(StreamDataReceivedDelegate(&CFingerPrint::OnData, this)); +} + +void CFingerPrint::OnData(Stream &stream, char arrivedChar, unsigned short availableCharsCount) +{ + if(m_RecvState == RECV_DONE) + return; + + if(m_RecvState == RECV_DROP) + { + while(stream.available()) + stream.read(); + + return; + } + + // RECV_WAITING + while(stream.available()) + { + uint8_t cur = stream.read(); + + if(m_RecvIndex == 0 && cur != 0xEF || m_RecvIndex == 1 && cur != 0x01) + { + debugf("skip garbage header at %d: %X", m_RecvIndex, cur); + m_RecvIndex = 0; + continue; + } + + m_aRecvBuffer[m_RecvIndex++] = cur; + + // Packet could be complete (the minimal packet size is 12 bytes) + if(m_RecvIndex >= 12) + { + // Check the packet header (redundant) + uint16_t header = m_aRecvBuffer[0] << 8 | m_aRecvBuffer[1]; + if(header != 0xEF01) + { + debugf("wrong header: %X", header); + m_RecvIndex = 0; + continue; + } + + // Calculate packet payload length + uint16_t length = m_aRecvBuffer[7] << 8 | m_aRecvBuffer[8]; + + // Check if the packet is still fully received + // Condition: index counter < packet payload length + packet frame + if(m_RecvIndex < length + 9) + continue; + + // At this point the packet should be fully received + uint8_t ident = m_aRecvBuffer[6]; + + // Calculate checksum: + // Checksum = packet type (1 byte) + packet length RAW!! (2 bytes) + packet payload (n bytes) + uint16_t calcChecksum = ident; + const uint16_t tmp = 7 + length; + for(uint16_t i = 7; i < tmp; i++) + calcChecksum += m_aRecvBuffer[i]; + + // Checksum in received data package + uint16_t recvChecksum = m_aRecvBuffer[m_RecvIndex - 2] << 8 | m_aRecvBuffer[m_RecvIndex - 1]; + + if(calcChecksum != recvChecksum) + { + debugf("checksum!!: %X != %X", calcChecksum, recvChecksum); + m_RecvIndex = 0; + continue; + } + + m_RecvIndex = 0; + m_RecvState = RECV_DONE; + + (this->*m_fnRecvCallback)((FingerPrintIdent)ident, &m_aRecvBuffer[9], length - 2); + } + } +} + +int CFingerPrint::Recv(FingerPrintIdent *pIdent, uint8_t *pData, uint16_t maxLength, const int maxTime) +{ + m_RecvState = RECV_DONE; + m_RecvIndex = 0; + + debugf("Start manual recv:"); + int Ret = -9999; + + int timeout = maxTime; + while(true) + { + while(!m_pSerial->available()) + { + if(--timeout == 0) + return Ret; + delay(1); + } + + uint8_t cur = m_pSerial->read(); + m_aRecvBuffer[m_RecvIndex++] = cur; + + // Packet could be complete (the minimal packet size is 12 bytes) + if(m_RecvIndex >= 12) + { + // Check the packet header + uint16_t header = m_aRecvBuffer[0] << 8 | m_aRecvBuffer[1]; + if(header != 0xEF01) + { + debugf("wrong header: %X", header); + m_RecvIndex = 0; + return Ret; + } + + // Calculate packet payload length + uint16_t length = m_aRecvBuffer[7] << 8 | m_aRecvBuffer[8]; + + // Check if the packet is still fully received + // Condition: index counter < packet payload length + packet frame + if(m_RecvIndex < length + 9) + continue; + + // At this point the packet should be fully received + uint8_t ident = m_aRecvBuffer[6]; + + // Calculate checksum: + // Checksum = packet type (1 byte) + packet length RAW! (2 bytes) + packet payload (n bytes) + uint16_t calcChecksum = ident + length; + uint16_t tmp = 9 + length - 2; + for(uint16_t i = 9; i < tmp; i++) + calcChecksum += m_aRecvBuffer[i]; + + // Checksum in received data package + uint16_t recvChecksum = m_aRecvBuffer[m_RecvIndex - 2] << 8 | m_aRecvBuffer[m_RecvIndex - 1]; + + if(calcChecksum != recvChecksum) + { + debugf("checksum!!: %X != %X", calcChecksum, recvChecksum); + m_RecvIndex = 0; + return Ret; + } + + length -= 2; + Ret = 0; + + if(pIdent) + *(uint8_t *)pIdent = ident; + + if(pData) + { + memcpy(pData, &m_aRecvBuffer[9], min(length, maxLength)); + Ret = (int)maxLength - length; // >= 0 = OK + } + + break; + } + } + + debugf("End recv. (%dms)", maxTime - timeout); + m_RecvIndex = 0; + + return Ret; +} + +int CFingerPrint::Write(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_WAITING; + + // Header + const uint16_t Header = 0xEF01; + m_pSerial->write(Header >> 8 & 0xFF); + m_pSerial->write(Header >> 0 & 0xFF); + + // Address + m_pSerial->write(m_Address >> 24 & 0xFF); + m_pSerial->write(m_Address >> 16 & 0xFF); + m_pSerial->write(m_Address >> 8 & 0xFF); + m_pSerial->write(m_Address >> 0 & 0xFF); + + // Package identifier + m_pSerial->write(ident); + + // Package length + length += 2; // Checksum + m_pSerial->write(length >> 8 & 0xFF); + m_pSerial->write(length >> 0 & 0xFF); + + // Checksum + uint16_t Checksum = ident + length; + + // Data + length -= 2; + for(uint16_t i = 0; i < length; i++) + { + m_pSerial->write(pData[i]); + Checksum += pData[i]; + } + + // Checksum + m_pSerial->write(Checksum >> 8 & 0xFF); + m_pSerial->write(Checksum >> 0 & 0xFF); + + return length; +} diff --git a/app/FingerPrint.h b/app/FingerPrint.h new file mode 100644 index 0000000..2bccf4b --- /dev/null +++ b/app/FingerPrint.h @@ -0,0 +1,163 @@ +#ifndef FINGERPRINT_H +#define FINGERPRINT_H + +// Data package identifier +enum FingerPrintIdent +{ + IDENT_COMMAND = 0x01, // Command packet + IDENT_DATA = 0x02, // Data packet; + // Data packet shall not appear alone in executing processs, + // must follow command packet or acknowledge packet. + IDENT_ACK = 0x07, // Acknowledge packet + IDENT_ENDOFDATA = 0x08, // End of Data packet +}; + +enum FingerPrintCommand +{ + COMMAND_GENIMG = 0x01, // Collect finger image + COMMAND_IMG2TZ = 0x02, // To generate character file from image + COMMAND_MATCH = 0x03, // Carry out precise matching of two templates + COMMAND_SEARCH = 0x04, // Search library finger + COMMAND_REGMODEL = 0x05, // To combine character files and generate template + COMMAND_STORE = 0x06, // To store template; + COMMAND_LOADCHAR = 0x07, // to read/load template + COMMAND_UPCHAR = 0x08, // to upload template + COMMAND_DOWNCHR = 0x09, // to download template + COMMAND_UPIMAGE = 0x0A, // To upload image + COMMAND_DOWNIMAGE = 0x0B, // To download image + COMMAND_DELETCHAR = 0x0C, // to delete tempates + COMMAND_EMPTY = 0x0D, // to empty the library + COMMAND_SETSYSPARA = 0x0E, // To set system Parameter + COMMAND_READSYSPARA = 0x0F, // To read Parameter + COMMAND_SETPWD = 0x12, // To set password + COMMAND_VFYPWD = 0x13, // To verify password + COMMAND_GETRANDOMCODE = 0x14, // to get random code + COMMAND_SETADDER = 0x15, // To set device address + COMMAND_CONTROL = 0x17, // Port control + COMMAND_WRITENOTEPAD = 0x18, // to write note pad + COMMAND_READNOTEPAD = 0x19, // To read note pad + COMMAND_HISPEEDSEARCH = 0x1B, // Search the library fastly + COMMAND_TEMPLATENUM = 0x1D, // To read finger template numbers + COMMAND_READCONLIST = 0x1F, // To read finger template index table +}; + +enum FingerPrintError +{ + ERROR_OK = 0x00, // command execution complete + ERROR_COMMUNICATION = 0x01, // error when receiving data package + ERROR_NOFINGER = 0x02, // no finger on the sensor + ERROR_READIMAGE = 0x03, // fail to enroll the finger + ERROR_MESSYIMAGE = 0x06, // fail to generate character file due to the over-disorderly fingerprint image + ERROR_FEWFEATUREPOINTS = 0x07, // fail to generate character file due to lackness of character point or over-smallness of fingerprint image + ERROR_NOTMATCHING = 0x08, // finger doesn't match + ERROR_NOTEMPLATEFOUND = 0x09, // fail to find the matching finger + ERROR_CHARACTERISTICSMISMATCH = 0x0A, // fail to combine the character files + ERROR_INVALIDPOSITION = 0x0B, // addressing PageID is beyond the finger library + ERROR_LOADTEMPLATE = 0x0C, // error when reading template from library or the template is invalid + ERROR_UPLOADTEMPLATE = 0x0D, // error when uploading template + ERROR_PACKETRESPONSEFAIL = 0x0E, // Module can't receive the following data packages + ERROR_UPLOADIMAGE = 0x0F, // error when uploading image + ERROR_DELETETEMPLATE = 0x10, // fail to delete the template + ERROR_CLEARDATABASE = 0x11, // fail to clear finger library + ERROR_WRONGPASSWORD = 0x13, // wrong password + ERROR_INVALIDIMAGE = 0x15, // fail to generate the image for the lackness of valid primary image + ERROR_FLASH = 0x18, // error when writing flash + ERROR_NODEF = 0x19, // No definition error + ERROR_INVALIDREGISTER = 0x1A, // invalid register number + ERROR_INCORRECTREGISTERCONF = 0x1B, // incorrect configuration of register + ERROR_INVALIDNOTEPADPAGE = 0x1C, // wrong notepad page number + ERROR_COMMUNICATIONPORT = 0x1D, // fail to operate the communication port +}; + +enum RecvStates +{ + RECV_DONE = 0, + RECV_WAITING = 1, + RECV_DROP = 2 +}; + +struct CFingerSystemParameters +{ + uint16_t statusRegister; + uint16_t systemID; + uint16_t storageCapacity; + uint16_t securityLevel; + uint32_t deviceAddress; + uint16_t packetLength; + uint16_t baudRate; +}; + +class CFingerPrint; +typedef void (CFingerPrint::*RecvCallback)(FingerPrintIdent ident, uint8_t *pData, uint16_t length); + +typedef void (*VerifyPasswordCallback)(void *pUser, FingerPrintError error, const char *errorStr); +typedef void (*ReadSystemParametersCallback)(void *pUser, FingerPrintError error, const char *errorStr, CFingerSystemParameters *param); +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); +typedef void (*CompareCharacteristicsCallback)(void *pUser, FingerPrintError error, const char *errorStr, int16_t score); +typedef void (*CreateTemplateCallback)(void *pUser, FingerPrintError error, const char *errorStr); +typedef void (*DownloadCharacteristicsCallback)(void *pUser, FingerPrintError error, const char *errorStr, int8_t *pChar, uint16_t charLen); +typedef void (*LoadTemplateCallback)(void *pUser, FingerPrintError error, const char *errorStr); +typedef void (*StoreTemplateCallback)(void *pUser, FingerPrintError error, const char *errorStr, uint16_t positionNumber); +typedef void (*ReadTemplateMapCallback)(void *pUser, FingerPrintError error, const char *errorStr, uint8_t *pData, uint16_t dataLen); + +class CFingerPrint +{ +public: + CFingerPrint(); + void Init(HardwareSerial &serial, uint32_t address, uint32_t password); + + static const char *ExplainFingerError(uint8_t error); + + int VerifyPassword(); + int ReadSystemParameters(uint8_t aResponse[17]); + int DeleteTemplate(uint16_t positionStart, uint16_t count); + int EmptyDatabase(); + + int AsyncVerifyPassword(VerifyPasswordCallback fnCallback, void *pUser); + int AsyncReadSystemParameters(ReadSystemParametersCallback fnCallback, void *pUser); + 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); + int AsyncCompareCharacteristics(CompareCharacteristicsCallback fnCallback, void *pUser); + int AsyncCreateTemplate(CreateTemplateCallback fnCallback, void *pUser); + int AsyncDownloadCharacteristics(DownloadCharacteristicsCallback fnCallback, void *pUser, uint8_t numCharBuffer); + int AsyncLoadTemplate(LoadTemplateCallback fnCallback, void *pUser, uint16_t positionNumber, uint8_t numCharBuffer); + int AsyncStoreTemplate(StoreTemplateCallback fnCallback, void *pUser, uint16_t positionNumber, uint8_t numCharBuffer); + int AsyncReadTemplateMap(ReadTemplateMapCallback fnCallback, void *pUser, uint8_t numPage); + +private: + void OnAsyncVerifyPassword(FingerPrintIdent ident, uint8_t *pData, uint16_t length); + void OnAsyncReadSystemParameters(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); + void OnAsyncCompareCharacteristics(FingerPrintIdent ident, uint8_t *pData, uint16_t length); + void OnAsyncCreateTemplate(FingerPrintIdent ident, uint8_t *pData, uint16_t length); + void OnAsyncDownloadCharacteristics(FingerPrintIdent ident, uint8_t *pData, uint16_t length); + void OnAsyncLoadTemplate(FingerPrintIdent ident, uint8_t *pData, uint16_t length); + void OnAsyncStoreTemplate(FingerPrintIdent ident, uint8_t *pData, uint16_t length); + void OnAsyncReadTemplateMap(FingerPrintIdent ident, uint8_t *pData, uint16_t length); + + void OnData(Stream &stream, char arrivedChar, unsigned short availableCharsCount); + int Write(FingerPrintIdent ident, uint8_t *pData, uint16_t length); + int Recv(FingerPrintIdent *pIdent, uint8_t *pData, uint16_t maxLength, const int maxTime=100); + + uint32_t m_Address; + uint32_t m_Password; + HardwareSerial *m_pSerial; + + uint8_t m_aRecvBuffer[2+4+1+2+256+2]; + uint16_t m_RecvIndex; + + volatile RecvStates m_RecvState; + RecvCallback m_fnRecvCallback; + void *m_fnUserCallback; + void *m_pUserData; + + uint8_t m_aBuffer[1024]; + int32_t m_iBuffer; +}; + +#endif diff --git a/app/FingerPrint_API.cpp b/app/FingerPrint_API.cpp new file mode 100644 index 0000000..a2ab40b --- /dev/null +++ b/app/FingerPrint_API.cpp @@ -0,0 +1,101 @@ +#include +#include "FingerPrint.h" + +int CFingerPrint::VerifyPassword() +{ + uint8_t aPayload[] = { + COMMAND_VFYPWD, + (uint8_t)(m_Password >> 24 & 0xFF), + (uint8_t)(m_Password >> 16 & 0xFF), + (uint8_t)(m_Password >> 8 & 0xFF), + (uint8_t)(m_Password >> 0 & 0xFF), + }; + + 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::ReadSystemParameters(uint8_t aResponse[17]) +{ + uint8_t aPayload[] = { + COMMAND_READSYSPARA, + }; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + FingerPrintIdent ident; + int ret = Recv(&ident, aResponse, 17); + 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[] = { + COMMAND_DELETCHAR, + (uint8_t)(positionStart >> 8 & 0xFF), + (uint8_t)(positionStart >> 0 & 0xFF), + (uint8_t)(count >> 8 & 0xFF), + (uint8_t)(count >> 0 & 0xFF), + }; + + 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::EmptyDatabase() +{ + uint8_t aPayload[] = { + COMMAND_EMPTY, + }; + + 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; +} diff --git a/app/FingerPrint_AsyncAPI.cpp b/app/FingerPrint_AsyncAPI.cpp new file mode 100644 index 0000000..8fcbe50 --- /dev/null +++ b/app/FingerPrint_AsyncAPI.cpp @@ -0,0 +1,510 @@ +#include +#include "FingerPrint.h" + +int CFingerPrint::AsyncVerifyPassword(VerifyPasswordCallback fnCallback, void *pUser) +{ + uint8_t aPayload[] = { + COMMAND_VFYPWD, + (uint8_t)(m_Password >> 24 & 0xFF), + (uint8_t)(m_Password >> 16 & 0xFF), + (uint8_t)(m_Password >> 8 & 0xFF), + (uint8_t)(m_Password >> 0 & 0xFF), + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncVerifyPassword; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncVerifyPassword(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + if(ident != IDENT_ACK) + { + ((VerifyPasswordCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!"); + return; + } + + if(length != 1) + { + ((VerifyPasswordCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Incorrect data length!"); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + ((VerifyPasswordCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr); +} + + +int CFingerPrint::AsyncReadSystemParameters(ReadSystemParametersCallback fnCallback, void *pUser) +{ + uint8_t aPayload[] = { + COMMAND_READSYSPARA, + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncReadSystemParameters; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncReadSystemParameters(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + if(ident != IDENT_ACK) + { + ((ReadSystemParametersCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!", NULL); + return; + } + + if(length != 1 + sizeof(CFingerSystemParameters)) + { + ((ReadSystemParametersCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Incorrect data length!", NULL); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + CFingerSystemParameters *param = (CFingerSystemParameters *)m_aBuffer; + param->statusRegister = pData[1] << 8 | pData[2]; + param->systemID = pData[3] << 8 | pData[4]; + param->storageCapacity = pData[5] << 8 | pData[6]; + param->securityLevel = pData[7] << 8 | pData[8]; + param->deviceAddress = pData[9] << 24 | pData[10] << 16 | pData[11] << 8 | pData[12]; + param->packetLength = pData[13] << 8 | pData[14]; + param->baudRate = pData[15] << 8 | pData[16]; + + ((ReadSystemParametersCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr, param); +} + + +int CFingerPrint::AsyncReadImage(ReadImageCallback fnCallback, void *pUser) +{ + uint8_t aPayload[] = { + COMMAND_GENIMG, + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncReadImage; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncReadImage(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + if(ident != IDENT_ACK) + { + ((ReadImageCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!"); + return; + } + + if(length != 1) + { + ((ReadImageCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Incorrect data length!"); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + ((ReadImageCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr); +} + + +int CFingerPrint::AsyncConvertImage(ConvertImageCallback fnCallback, void *pUser, uint8_t numCharBuffer) +{ + if(numCharBuffer != 0x01 && numCharBuffer != 0x02) + return -1; + + uint8_t aPayload[] = { + COMMAND_IMG2TZ, + numCharBuffer, + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncConvertImage; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncConvertImage(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + if(ident != IDENT_ACK) + { + ((ConvertImageCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!"); + return; + } + + if(length != 1) + { + ((ConvertImageCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Incorrect data length!"); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + ((ConvertImageCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr); +} + + +int CFingerPrint::AsyncSearchTemplate(SearchTemplateCallback fnCallback, void *pUser, uint8_t numCharBuffer, uint16_t positionStart, uint16_t numTemplates) +{ + if(numCharBuffer != 0x01 && numCharBuffer != 0x02) + return -1; + + uint8_t aPayload[] = { + COMMAND_SEARCH, + numCharBuffer, + (uint8_t)(positionStart >> 8 & 0xFF), + (uint8_t)(positionStart >> 0 & 0xFF), + (uint8_t)(numTemplates >> 8 & 0xFF), + (uint8_t)(numTemplates >> 0 & 0xFF), + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncSearchTemplate; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncSearchTemplate(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + int16_t position = -1; + int16_t score = -1; + + if(ident != IDENT_ACK) + { + ((SearchTemplateCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!", position, score); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + if(error == ERROR_OK) + { + if(length != 5) + { + ((SearchTemplateCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Incorrect data length!", position, score); + return; + } + + position = pData[1] << 8 | pData[2]; + score = pData[3] << 8 | pData[4]; + } + + ((SearchTemplateCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr, position, score); +} + + +int CFingerPrint::AsyncCompareCharacteristics(CompareCharacteristicsCallback fnCallback, void *pUser) +{ + uint8_t aPayload[] = { + COMMAND_MATCH, + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncCompareCharacteristics; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncCompareCharacteristics(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + int16_t score = -1; + + if(ident != IDENT_ACK) + { + ((CompareCharacteristicsCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!", score); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + if(error == ERROR_OK) + { + if(length != 3) + { + ((CompareCharacteristicsCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Incorrect data length!", score); + return; + } + + score = pData[1] << 8 | pData[2]; + } + + ((CompareCharacteristicsCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr, score); +} + + +int CFingerPrint::AsyncCreateTemplate(CreateTemplateCallback fnCallback, void *pUser) +{ + uint8_t aPayload[] = { + COMMAND_REGMODEL, + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncCreateTemplate; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncCreateTemplate(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + if(ident != IDENT_ACK) + { + ((CreateTemplateCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!"); + return; + } + + if(length != 1) + { + ((CreateTemplateCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Incorrect data length!"); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + ((CreateTemplateCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr); +} + + +int CFingerPrint::AsyncDownloadCharacteristics(DownloadCharacteristicsCallback fnCallback, void *pUser, uint8_t numCharBuffer) +{ + if(numCharBuffer != 0x01 && numCharBuffer != 0x02) + return -1; + + uint8_t aPayload[] = { + COMMAND_UPCHAR, + numCharBuffer, + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncDownloadCharacteristics; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + m_iBuffer = -1; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncDownloadCharacteristics(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + if(m_iBuffer == -1) + { + if(ident != IDENT_ACK) + { + m_RecvState = RECV_DROP; + ((DownloadCharacteristicsCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!", NULL, 0); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + if(error != ERROR_OK) + { + m_RecvState = RECV_DROP; + ((DownloadCharacteristicsCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr, NULL, 0); + return; + } + + m_iBuffer = 0; + m_RecvState = RECV_WAITING; + return; + } + + if(ident != IDENT_DATA && ident != IDENT_ENDOFDATA) + { + m_RecvState = RECV_DROP; + ((DownloadCharacteristicsCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no data packet", NULL, 0); + return; + } + + if(sizeof(m_aBuffer) - m_iBuffer < length) + { + m_RecvState = RECV_DROP; + ((DownloadCharacteristicsCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Not enough space to store received data", NULL, 0); + return; + } + + memcpy(&m_aBuffer[m_iBuffer], pData, length); + m_iBuffer += length; + + if(ident == IDENT_ENDOFDATA) + { + m_RecvState = RECV_DROP; + ((DownloadCharacteristicsCallback)m_fnUserCallback)(m_pUserData, ERROR_OK, ExplainFingerError(ERROR_OK), (int8_t *)m_aBuffer, m_iBuffer); + return; + } + + m_RecvState = RECV_WAITING; +} + + +int CFingerPrint::AsyncLoadTemplate(LoadTemplateCallback fnCallback, void *pUser, uint16_t positionNumber, uint8_t numCharBuffer) +{ + if(numCharBuffer != 0x01 && numCharBuffer != 0x02) + return -1; + + uint8_t aPayload[] = { + COMMAND_LOADCHAR, + numCharBuffer, + (uint8_t)(positionNumber >> 8 & 0xFF), + (uint8_t)(positionNumber >> 0 & 0xFF), + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncLoadTemplate; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncLoadTemplate(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + if(ident != IDENT_ACK) + { + ((LoadTemplateCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!"); + return; + } + + if(length != 1) + { + ((LoadTemplateCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Incorrect data length!"); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + ((LoadTemplateCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr); +} + + +int CFingerPrint::AsyncStoreTemplate(StoreTemplateCallback fnCallback, void *pUser, uint16_t positionNumber, uint8_t numCharBuffer) +{ + if(numCharBuffer != 0x01 && numCharBuffer != 0x02) + return -1; + + uint8_t aPayload[] = { + COMMAND_STORE, + numCharBuffer, + (uint8_t)(positionNumber >> 8 & 0xFF), + (uint8_t)(positionNumber >> 0 & 0xFF), + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncStoreTemplate; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + m_iBuffer = positionNumber; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncStoreTemplate(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + if(ident != IDENT_ACK) + { + ((StoreTemplateCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!", m_iBuffer); + return; + } + + if(length != 1) + { + ((StoreTemplateCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "Incorrect data length!", m_iBuffer); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + ((StoreTemplateCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr, m_iBuffer); +} + + +int CFingerPrint::AsyncReadTemplateMap(ReadTemplateMapCallback fnCallback, void *pUser, uint8_t numPage) +{ + uint8_t aPayload[] = { + COMMAND_READCONLIST, + numPage + }; + + m_fnRecvCallback = &CFingerPrint::OnAsyncReadTemplateMap; + m_fnUserCallback = (void *)fnCallback; + m_pUserData = pUser; + + Write(IDENT_COMMAND, aPayload, sizeof(aPayload)); + + return 0; +} + +void CFingerPrint::OnAsyncReadTemplateMap(FingerPrintIdent ident, uint8_t *pData, uint16_t length) +{ + m_RecvState = RECV_DROP; + + if(ident != IDENT_ACK) + { + ((ReadTemplateMapCallback)m_fnUserCallback)(m_pUserData, ERROR_COMMUNICATION, "The received packet is no ack packet!", NULL, 0); + return; + } + + uint8_t error = pData[0]; + const char *errorStr = ExplainFingerError(error); + + if(error != ERROR_OK) + { + ((ReadTemplateMapCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr, NULL, 0); + return; + } + + ((ReadTemplateMapCallback)m_fnUserCallback)(m_pUserData, (FingerPrintError)error, errorStr, &pData[1], length - 1); +} diff --git a/app/Settings.cpp b/app/Settings.cpp new file mode 100644 index 0000000..4a7fa0d --- /dev/null +++ b/app/Settings.cpp @@ -0,0 +1,118 @@ +#include +#include +#include "utils.h" +#include "Settings.h" + +CSettings::CSettings() +{ + strcpy(m_aUsername, "admin"); + strcpy(m_aPassword, "admin"); + strcpy(m_aSSID, ""); + 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); + memset(m_aLockCode, 0, sizeof(m_aLockCode)); +} + +bool CSettings::Exists() +{ + return fileExist(APP_SETTINGS_FILE); +} + +bool CSettings::Load() +{ + if(!Exists()) + { + Save(); + return true; + } + + uint32_t size = fileGetSize(APP_SETTINGS_FILE); + char *pData = new char[size + 1]; + fileGetContent(APP_SETTINGS_FILE, pData, size + 1); + + DynamicJsonDocument doc(1024); + if(deserializeJson(doc, pData)) + { + delete[] pData; + return false; + } + + strncpy(m_aUsername, doc["username"], sizeof(m_aUsername)); + strncpy(m_aPassword, doc["password"], sizeof(m_aPassword)); + + JsonObject network = doc["network"]; + strncpy(m_aSSID, network["ssid"], sizeof(m_aSSID)); + strncpy(m_aPSK, network["passphrase"], sizeof(m_aPSK)); + m_DHCP = network["dhcp"]; + strncpy(m_aHostname, network["hostname"], sizeof(m_aHostname)); + m_Address = network["address"].as(); + m_Netmask = network["netmask"].as(); + m_Gateway = network["gateway"].as(); + + JsonArray lockCode = doc["lock_code"]; + uint8_t tmp = min(lockCode.size(), sizeof(m_aLockCode)); + for(uint8_t i = 0; i < tmp; i++) + m_aLockCode[i] = lockCode[i]; + + JsonArray fingerprints = doc["fingerprints"]; + uint16_t sz = fingerprints.size(); + m_FingerPrints.allocate(sz); + for(uint16_t i = 0; i < sz; i++) + { + JsonObject obj = fingerprints[i]; + CFingerPrint finger; + + finger.m_FingerNum = obj["num"]; + strncpy(finger.m_aLabel, obj["label"], sizeof(finger.m_aLabel)); + hex2bytes(obj["digest"].as(), finger.m_aDigest, sizeof(finger.m_aDigest)); + + m_FingerPrints[finger.m_FingerNum] = finger; + } + + delete[] pData; + return true; +} + +void CSettings::Save() +{ + DynamicJsonDocument doc(1024); + + doc["username"] = m_aUsername; + doc["password"] = m_aPassword; + + JsonObject network = doc.createNestedObject("network"); + network["ssid"] = m_aSSID; + network["passphrase"] = m_aPSK; + network["dhcp"] = m_DHCP; + network["hostname"] = m_aHostname; + network["address"] = m_Address.toString(); + network["netmask"] = m_Netmask.toString(); + network["gateway"] = m_Gateway.toString(); + + JsonArray lockCode = doc.createNestedArray("lock_code"); + for(uint8_t i = 0; i < sizeof(m_aLockCode); i++) + lockCode.add(m_aLockCode[i]); + + JsonArray fingerprints = doc.createNestedArray("fingerprints"); + uint16_t tmp = m_FingerPrints.count(); + for(uint16_t i = 0; i < tmp; i++) + { + JsonObject obj = fingerprints.createNestedObject(); + const CFingerPrint &finger = m_FingerPrints.valueAt(i); + + 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); + } + + String docString; + serializeJsonPretty(doc, docString); + fileSetContent(APP_SETTINGS_FILE, docString); +} diff --git a/app/Settings.h b/app/Settings.h new file mode 100644 index 0000000..d5a71fb --- /dev/null +++ b/app/Settings.h @@ -0,0 +1,38 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#define APP_SETTINGS_FILE "settings.conf" + +class CSettings +{ +public: + CSettings(); + + bool Exists(); + bool Load(); + void Save(); + + char m_aUsername[32]; + char m_aPassword[32]; + + char m_aSSID[32+1]; + char m_aPSK[64+1]; + + bool m_DHCP; + char m_aHostname[64]; + IpAddress m_Address; + IpAddress m_Netmask; + IpAddress m_Gateway; + + uint8_t m_aLockCode[8]; + + struct CFingerPrint + { + uint16_t m_FingerNum; + char m_aLabel[32]; + uint8_t m_aDigest[SHA256_SIZE]; + }; + HashMap m_FingerPrints; +}; + +#endif diff --git a/app/application.cpp b/app/application.cpp new file mode 100644 index 0000000..7dc4bbb --- /dev/null +++ b/app/application.cpp @@ -0,0 +1,54 @@ +#include +#include "FingerPrint.h" +#include "main.h" + +HardwareSerial Serial1(UART_ID_1); +NtpClient ntpClient("at.pool.ntp.org", 3600); + +void IRAM_ATTR OnFingerInterrupt() +{ + // LOW = FINGER, HIGH = NO FINGER + bool status = digitalRead(FINGER_DETECT_PIN); + + g_Main.OnFingerInterrupt(!status); +} + +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); +} + +void init() +{ + // for fingerprint module + Serial.systemDebugOutput(false); + Serial.begin(115200, SERIAL_8N1, SERIAL_FULL); + Serial.flush(); // TODO: Full flush? + + // for debug messages + Serial1.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY); + Serial1.systemDebugOutput(true); + + // p-channel FET to turn on fingerprint module + pinMode(FINGER_ENABLE_PIN, OUTPUT_OPEN_DRAIN); + digitalWrite(FINGER_ENABLE_PIN, 1); + + // communication with safe lock + pinMode(SAFELOCK_DATA_PIN, OUTPUT_OPEN_DRAIN); + digitalWrite(SAFELOCK_DATA_PIN, 1); + + pinMode(FINGER_DETECT_PIN, INPUT); + pinMode(SAFELOCK_DETECT_PIN, INPUT); + pinMode(DOOR_DETECT_PIN, INPUT); + + // mount spiffs + spiffs_mount(); + + System.onReady(ready); +} diff --git a/app/main.cpp b/app/main.cpp new file mode 100644 index 0000000..3fd98fd --- /dev/null +++ b/app/main.cpp @@ -0,0 +1,462 @@ +#include +#include +#include +#include "utils.h" +#include "main.h" + +CMain g_Main; + +CMain::CMain() : m_FingerLogic(this) +{ +} + +static void STAGotIP(IpAddress ip, IpAddress mask, IpAddress gateway) +{ + debugf("GOTIP - IP: %s, MASK: %s, GW: %s\n", ip.toString().c_str(), mask.toString().c_str(), + gateway.toString().c_str()); +} + +static void STADisconnect(const String& ssid, MacAddress bssid, WifiDisconnectReason reason) +{ + debugf("DISCONNECT - SSID: %s, REASON: %d", ssid.c_str(), WifiEvents.getDisconnectReasonDesc(reason).c_str()); +} + +void CMain::Init(HardwareSerial &serial) +{ + m_Settings.Load(); + //m_Settings.Save(); + + WifiAccessPoint.enable(false); + if(m_Settings.m_aSSID[0]) + { + debugf("Station: %s", m_Settings.m_aSSID); + WifiStation.enable(true); + WifiStation.config(m_Settings.m_aSSID, m_Settings.m_aPSK); + if(m_Settings.m_DHCP && m_Settings.m_aHostname[0]) + WifiStation.setHostname(m_Settings.m_aHostname); + if(!m_Settings.m_DHCP && !m_Settings.m_Address.isNull()) + WifiStation.setIP(m_Settings.m_Address, m_Settings.m_Netmask, m_Settings.m_Gateway); + WifiStation.connect(); + } + else + { + debugf("Access Point 'admin': %s", m_Settings.m_aPassword); + WifiAccessPoint.config("safeweb", m_Settings.m_aPassword, AUTH_WPA2_PSK); + WifiAccessPoint.enable(true); + } + + WifiEvents.onStationGotIP(STAGotIP); + WifiEvents.onStationDisconnect(STADisconnect); + + m_FTP.listen(21); + m_FTP.addUser(m_Settings.m_aUsername, m_Settings.m_aPassword); + + m_HttpServer.listen(80); + m_HttpServer.setBodyParser("application/json", bodyToStringParser); + + m_HttpServer.paths.set("/api", 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/unlock", HttpPathDelegate(&CMain::HttpOnApi, this)); + + m_HttpServer.paths.setDefault(HttpPathDelegate(&CMain::HttpOnFile, this)); + + m_FingerPrint.Init(serial, 0xFFFFFFFF, 0x00000000); + m_FingerLogic.Init(&m_FingerPrint); +} + +bool CMain::HttpAuthorized(HttpRequest &request, HttpResponse &response) +{ + String auth = request.getHeader("Authorization"); + if(auth.startsWith("Basic ")) + { + int headerLength = auth.length() - 6; + if(headerLength <= 64) + { + auth = base64_decode(auth.c_str() + 6, headerLength); + if(auth) + { + int sep = auth.indexOf(':'); + if(sep != -1) + { + String username = auth.substring(0, sep); + String password = auth.substring(sep + 1); + if(username == m_Settings.m_aUsername && password == m_Settings.m_aPassword) + return true; + } + } + } + } + + response.code = HTTP_STATUS_UNAUTHORIZED; + response.setHeader("WWW-Authenticate", "Basic realm=\"safeweb\""); + response.setHeader("401 Wrong credentials", "Authentication required"); + response.setHeader("Connection", "close"); + return false; +} + +void CMain::HttpOnApi(HttpRequest &request, HttpResponse &response) +{ + if(!HttpAuthorized(request, response)) + return; + + String path = request.uri.Path; + if(path.length() < 6) + { + response.code = HTTP_STATUS_NOT_FOUND; + return; + } + String endpoint = path.substring(5); + + DynamicJsonDocument jsonReq(1024); + deserializeJson(jsonReq, request.getBody()); + DynamicJsonDocument jsonResp(1024); + + response.setAllowCrossDomainOrigin("*"); + response.setHeader("Access-Control-Allow-Headers", "Content-Type"); + + if(request.method == HTTP_OPTIONS) + { + response.code = HTTP_STATUS_OK; + return; + } + + + HttpStatus status = HandleApi(request.method, endpoint, jsonReq, jsonResp); + if(status != HTTP_STATUS_OK) + { + response.code = status; + return; + } + + String respString; + serializeJson(jsonResp, respString); + response.setContentType(MIME_JSON); + response.sendString(respString); +} + +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(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 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(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(); + } + } + else if(endpoint == "unlock") + { + if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED; + + LockUnlock(); + resp["success"] = "true"; + } + 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++) + { + JsonObject obj = fingerprints.createNestedObject(); + const CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(i); + + 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); + } + } + else if(endpoint == "fingerprint/label") + { + if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED; + + if(!req.containsKey("index") || !req.containsKey("label")) + return HTTP_STATUS_BAD_REQUEST; + + int index = req["index"].as() - 1; + String label = req["label"]; + + if(index < 0 || index >= Settings().m_FingerPrints.count()) + return HTTP_STATUS_BAD_REQUEST; + + 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") + { + if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED; + + if(!req.containsKey("index")) + return HTTP_STATUS_BAD_REQUEST; + + int index = req["index"].as() - 1; + + if(index < 0 || index >= Settings().m_FingerPrints.count()) + return HTTP_STATUS_BAD_REQUEST; + + const CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(index); + FingerPrint().DeleteTemplate(finger.m_FingerNum, 1); + Settings().m_FingerPrints.removeAt(index); + Settings().Save(); + + resp["success"] = true; + } + else if(endpoint == "fingerprint/enroll") + { + if(method != HTTP_POST) return HTTP_STATUS_METHOD_NOT_ALLOWED; + + if(req.containsKey("cancel")) + { + if(req["cancel"].as() && m_Enrolling) + { + resp["success"] = FingerLogic().EnrollFinger(false); + m_Enrolling = false; + m_Enrolled = false; + } + } + else + { + if(!req.containsKey("enrolling")) + return HTTP_STATUS_BAD_REQUEST; + + bool enrolling = req["enrolling"]; + if(enrolling && !m_Enrolling) + { + if(m_Enrolled) + { + resp["done"] = true; + resp["message"] = m_EnrollMessage; + m_Enrolled = false; + } + else + { + resp["error"] = m_EnrollMessage; + } + } + else if(enrolling && m_Enrolling) + { + resp["done"] = false; + resp["message"] = m_EnrollMessage; + } + else // if(!enrolling) + { + if(!req.containsKey("label")) + return HTTP_STATUS_BAD_REQUEST; + + if(!m_Enrolling) + { + FingerLogic().EnrollFinger(); + m_Enrolling = true; + m_Enrolled = false; + m_EnrollLabel = String(req["label"].as()); + resp["done"] = false; + m_EnrollMessage = "Started enroll process. Please place your finger on the sensor."; + resp["message"] = m_EnrollMessage; + } + else + { + resp["done"] = true; + m_Enrolled = false; + resp["message"] = m_EnrollMessage; + } + } + } + } + else + { + return HTTP_STATUS_NOT_FOUND; + } + + return HTTP_STATUS_OK; +} + +void CMain::HttpOnFile(HttpRequest &request, HttpResponse &response) +{ + if(!HttpAuthorized(request, response)) + return; + + String file = request.uri.Path; + + if(file == "/") + file = "index.html"; + if(file[0] == '/') + file = file.substring(1); + if(file[0] == '.') + { + response.code = HTTP_STATUS_FORBIDDEN; + return; + } + + response.setCache(86400, true); // It's important to use cache for better performance. + response.sendFile(file); +} + +void CMain::OnFingerInterrupt(bool finger) +{ + m_FingerLogic.OnFingerInterrupt(finger); +} + +void CMain::FingerEnable(bool enable) +{ + const int pin = FINGER_ENABLE_PIN; + digitalWrite(pin, !enable); +} + +void CMain::LockSendBytes(uint8_t *pBytes, uint8_t len) +{ + const int pin = SAFELOCK_DATA_PIN; + + // Init + digitalWrite(pin, 0); + delayMicroseconds(10 * 1000); + digitalWrite(pin, 1); + delayMicroseconds(10 * 1000); + + // Send data, calculate checksum and send checksum + uint8_t chk = 0x00; + for(uint8_t i = 0; i <= len; i++) + { + uint8_t byte; + if(i == len) + { + byte = chk; + } + else + { + byte = pBytes[i]; + chk ^= byte; + } + + for(int8_t j = 0; j < 8; j++) + { + digitalWrite(pin, 0); + delayMicroseconds(100); + digitalWrite(pin, 1); + + uint8_t val = byte & (1 << j); + if(val) + delayMicroseconds(300); + else + delayMicroseconds(100); + } + + digitalWrite(pin, 0); + delayMicroseconds(100); + digitalWrite(pin, 1); + delayMicroseconds(8); + } +} + +void CMain::LockSendCode(uint8_t pass[8]) +{ + uint8_t packet[9] = {0x51}; + memcpy(&packet[1], pass, 8); + LockSendBytes(packet, sizeof(packet)); +} + +void CMain::LockUnlock() +{ + LockSendCode(Settings().m_aLockCode); +} + +void CMain::EnrollMessage(const char *msg, bool error) +{ + m_EnrollMessage = msg; + if(error) + m_Enrolling = false; +} + +void CMain::OnFingerVerified(uint16_t fingerNum, uint8_t digest[SHA256_SIZE]) +{ + int fingerIndex = Settings().m_FingerPrints.indexOf(fingerNum); + if(fingerIndex == -1) + { + debugf("OnFingerVerified: fingerIndex == -1"); + return; + } + + const CSettings::CFingerPrint &finger = Settings().m_FingerPrints.valueAt(fingerIndex); + if(memcmp(digest, finger.m_aDigest, SHA256_SIZE) != 0) + { + debugf("OnFingerVerified: SHA256 mismatch"); + return; + } + + LockUnlock(); + + debugf("OnFingerVerified: OK!!!"); +} + +void CMain::OnFingerEnrolled(uint16_t fingerNum, uint8_t digest[SHA256_SIZE]) +{ + CSettings::CFingerPrint finger; + finger.m_FingerNum = fingerNum; + strncpy(finger.m_aLabel, m_EnrollLabel.c_str(), sizeof(finger.m_aLabel)); + memcpy(finger.m_aDigest, digest, SHA256_SIZE); + + char aHexDigest[SHA256_SIZE*2+1]; + 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; + m_Enrolling = false; + m_EnrolledFinger = finger; + + char aBuf[512]; + m_snprintf(aBuf, sizeof(aBuf), "Successfully enrolled new finger \"%s\".", finger.m_aLabel); + EnrollMessage(aBuf); + + debugf("OnFingerEnrolled: OK!!!"); +} diff --git a/app/main.h b/app/main.h new file mode 100644 index 0000000..3e6143a --- /dev/null +++ b/app/main.h @@ -0,0 +1,65 @@ +#ifndef MAIN_H +#define MAIN_H + +#define FINGER_DETECT_PIN 4 +#define FINGER_ENABLE_PIN 5 + +#define SAFELOCK_DATA_PIN 12 +#define SAFELOCK_DETECT_PIN 14 + +#define DOOR_DETECT_PIN 13 + +#include +#include "Settings.h" +#include "FingerPrint.h" +#include "FingerLogic.h" + +class CMain +{ +public: + CMain(); + void Init(HardwareSerial &serial); + + void OnFingerInterrupt(bool finger); + + void FingerEnable(bool enable); + + void LockUnlock(); + void EnrollMessage(const char *msg, bool error=false); + + void OnFingerVerified(uint16_t fingerNum, uint8_t digest[SHA256_SIZE]); + void OnFingerEnrolled(uint16_t fingerNum, uint8_t digest[SHA256_SIZE]); + + CSettings &Settings() { return m_Settings; } + CFingerPrint &FingerPrint() { return m_FingerPrint; } + CFingerLogic &FingerLogic() { return m_FingerLogic; } + +private: + bool HttpAuthorized(HttpRequest &request, HttpResponse &response); + void HttpOnApi(HttpRequest &request, HttpResponse &response); + void HttpOnFile(HttpRequest &request, HttpResponse &response); + + HttpStatus HandleApi(HttpMethod method, String endpoint, JsonDocument &req, JsonDocument &resp); + +private: + void LockSendBytes(uint8_t *pBytes, uint8_t len); + void LockSendCode(uint8_t code[8]); + + CSettings m_Settings; + CFingerPrint m_FingerPrint; + CFingerLogic m_FingerLogic; + + FtpServer m_FTP; + HttpServer m_HttpServer; + + String m_EnrollMessage; + String m_EnrollLabel; + CSettings::CFingerPrint m_EnrolledFinger; + bool m_Enrolling; + bool m_Enrolled; +}; + +extern CMain g_Main; +extern HardwareSerial Serial1; + +#endif diff --git a/app/utils.cpp b/app/utils.cpp new file mode 100644 index 0000000..d082b36 --- /dev/null +++ b/app/utils.cpp @@ -0,0 +1,75 @@ +#include +#include "utils.h" + +static inline uint8_t _char2byte(char c) +{ + if('0' <= c && c <= '9') return (uint8_t)(c - '0'); + if('A' <= c && c <= 'F') return (uint8_t)(c - 'A' + 10); + if('a' <= c && c <= 'f') return (uint8_t)(c - 'a' + 10); + return 0xFF; +} + +int hex2bytes(const char *str, uint8_t *bytes, int32_t length) +{ + int result; + if(!str || !bytes || length <= 0) + return -1; + + for(result = 0; *str; result++) + { + uint8_t msn = _char2byte(*str++); + if(msn == 0xFF) return -1; + + uint8_t lsn = _char2byte(*str++); + if(lsn == 0xFF) return -1; + + uint8_t bin = (msn << 4) + lsn; + + if(length-- <= 0) + return -1; + + *bytes++ = bin; + } + + return result; +} + +void bytes2hex(const uint8_t *bytes, int32_t length, char *str, int32_t strLength) +{ + const char binHex[] = "0123456789ABCDEF"; + + if(!str || strLength < 3) + return; + *str = 0; + + if(!bytes || length <= 0 || strLength <= 2 * length) + { + strncpy(str, "ERR", strLength); + return; + } + + for(; length > 0; length--, strLength -= 2) + { + uint8_t byte = *bytes++; + + *str++ = binHex[(byte >> 4) & 0x0F]; + *str++ = binHex[byte & 0x0F]; + } + + if(strLength-- <= 0) + return; + + *str++ = 0; +} + +int Rssi2Quality(sint8 rssi) +{ + if(rssi >= -100 && rssi <= -80) + return 1; + else if(rssi > -80 && rssi <= -65) + return 2; + else if(rssi > -65 && rssi < -50) + return 3; + else + return 4; +} diff --git a/app/utils.h b/app/utils.h new file mode 100644 index 0000000..d0ed894 --- /dev/null +++ b/app/utils.h @@ -0,0 +1,8 @@ +#ifndef UTILS_H +#define UTILS_H + +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); + +#endif diff --git a/component.mk b/component.mk new file mode 100644 index 0000000..dcb4953 --- /dev/null +++ b/component.mk @@ -0,0 +1,4 @@ +ARDUINO_LIBRARIES := ArduinoJson6 + +HWCONFIG = spiffs +SPIFF_FILES = files diff --git a/files/.gitkeep b/files/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/files/bootstrap.min.css.gz b/files/bootstrap.min.css.gz new file mode 100644 index 0000000..71634cb Binary files /dev/null and b/files/bootstrap.min.css.gz differ diff --git a/files/bootstrap.min.js.gz b/files/bootstrap.min.js.gz new file mode 100644 index 0000000..d01045c Binary files /dev/null and b/files/bootstrap.min.js.gz differ diff --git a/files/favicon.ico b/files/favicon.ico new file mode 100644 index 0000000..23ceec5 Binary files /dev/null and b/files/favicon.ico differ diff --git a/files/index.html b/files/index.html new file mode 100644 index 0000000..1c18dbe --- /dev/null +++ b/files/index.html @@ -0,0 +1,25 @@ + + + + + + + Safe control panel + + + + + + + +
+ + + + + + + + + + diff --git a/files/jquery-3.2.1.min.js.gz b/files/jquery-3.2.1.min.js.gz new file mode 100644 index 0000000..269d2de Binary files /dev/null and b/files/jquery-3.2.1.min.js.gz differ diff --git a/files/main.css.gz b/files/main.css.gz new file mode 100644 index 0000000..772ab62 Binary files /dev/null and b/files/main.css.gz differ diff --git a/files/main.js.gz b/files/main.js.gz new file mode 100644 index 0000000..8044855 Binary files /dev/null and b/files/main.js.gz differ diff --git a/files/nunjucks.min.js.gz b/files/nunjucks.min.js.gz new file mode 100644 index 0000000..ff6749f Binary files /dev/null and b/files/nunjucks.min.js.gz differ diff --git a/files/popper.min.js.gz b/files/popper.min.js.gz new file mode 100644 index 0000000..78a60d7 Binary files /dev/null and b/files/popper.min.js.gz differ diff --git a/files/settings.conf b/files/settings.conf new file mode 100644 index 0000000..99c40d7 --- /dev/null +++ b/files/settings.conf @@ -0,0 +1,25 @@ +{ + "username": "admin", + "password": "password", + "network": { + "ssid": "", + "passphrase": "", + "dhcp": true, + "hostname": "safeweb", + "address": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway": "0.0.0.0" + }, + "lock_code": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "fingerprints": [ + ] +} \ No newline at end of file diff --git a/files/templates.js.gz b/files/templates.js.gz new file mode 100644 index 0000000..ff7a6ba Binary files /dev/null and b/files/templates.js.gz differ diff --git a/files/wifi-sprites.png b/files/wifi-sprites.png new file mode 100644 index 0000000..863d0d7 Binary files /dev/null and b/files/wifi-sprites.png differ diff --git a/out/.gitignore b/out/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/out/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore