first commit
This commit is contained in:
commit
356d7de2cf
9
Makefile
Normal file
9
Makefile
Normal file
@ -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
|
598
app/FingerLogic.cpp
Normal file
598
app/FingerLogic.cpp
Normal file
@ -0,0 +1,598 @@
|
|||||||
|
#include <SmingCore.h>
|
||||||
|
#include <Crypto/Sha2.h>
|
||||||
|
#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<uint16_t, CSettings::CFingerPrint> &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<br>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<br>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<br>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<br>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<br>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);
|
||||||
|
}
|
105
app/FingerLogic.h
Normal file
105
app/FingerLogic.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#ifndef FINGERLOGIC_H
|
||||||
|
#define FINGERLOGIC_H
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
|
#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
|
267
app/FingerPrint.cpp
Normal file
267
app/FingerPrint.cpp
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
#include <SmingCore.h>
|
||||||
|
#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;
|
||||||
|
}
|
163
app/FingerPrint.h
Normal file
163
app/FingerPrint.h
Normal file
@ -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
|
101
app/FingerPrint_API.cpp
Normal file
101
app/FingerPrint_API.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include <SmingCore.h>
|
||||||
|
#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;
|
||||||
|
}
|
510
app/FingerPrint_AsyncAPI.cpp
Normal file
510
app/FingerPrint_AsyncAPI.cpp
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
#include <SmingCore.h>
|
||||||
|
#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);
|
||||||
|
}
|
118
app/Settings.cpp
Normal file
118
app/Settings.cpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#include <SmingCore.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#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<const char *>();
|
||||||
|
m_Netmask = network["netmask"].as<const char *>();
|
||||||
|
m_Gateway = network["gateway"].as<const char *>();
|
||||||
|
|
||||||
|
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<const char *>(), 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);
|
||||||
|
}
|
38
app/Settings.h
Normal file
38
app/Settings.h
Normal file
@ -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<uint16_t, CFingerPrint> m_FingerPrints;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
54
app/application.cpp
Normal file
54
app/application.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <SmingCore.h>
|
||||||
|
#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);
|
||||||
|
}
|
462
app/main.cpp
Normal file
462
app/main.cpp
Normal file
@ -0,0 +1,462 @@
|
|||||||
|
#include <SmingCore.h>
|
||||||
|
#include <Data/WebHelpers/base64.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#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<int>() - 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<int>() - 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<bool>() && 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<const char *>());
|
||||||
|
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!!!");
|
||||||
|
}
|
65
app/main.h
Normal file
65
app/main.h
Normal file
@ -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 <ArduinoJson.h>
|
||||||
|
#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
|
75
app/utils.cpp
Normal file
75
app/utils.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include <SmingCore.h>
|
||||||
|
#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;
|
||||||
|
}
|
8
app/utils.h
Normal file
8
app/utils.h
Normal file
@ -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
|
4
component.mk
Normal file
4
component.mk
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
ARDUINO_LIBRARIES := ArduinoJson6
|
||||||
|
|
||||||
|
HWCONFIG = spiffs
|
||||||
|
SPIFF_FILES = files
|
0
files/.gitkeep
Normal file
0
files/.gitkeep
Normal file
BIN
files/bootstrap.min.css.gz
Normal file
BIN
files/bootstrap.min.css.gz
Normal file
Binary file not shown.
BIN
files/bootstrap.min.js.gz
Normal file
BIN
files/bootstrap.min.js.gz
Normal file
Binary file not shown.
BIN
files/favicon.ico
Normal file
BIN
files/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
25
files/index.html
Normal file
25
files/index.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!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/jquery-3.2.1.min.js.gz
Normal file
BIN
files/jquery-3.2.1.min.js.gz
Normal file
Binary file not shown.
BIN
files/main.css.gz
Normal file
BIN
files/main.css.gz
Normal file
Binary file not shown.
BIN
files/main.js.gz
Normal file
BIN
files/main.js.gz
Normal file
Binary file not shown.
BIN
files/nunjucks.min.js.gz
Normal file
BIN
files/nunjucks.min.js.gz
Normal file
Binary file not shown.
BIN
files/popper.min.js.gz
Normal file
BIN
files/popper.min.js.gz
Normal file
Binary file not shown.
25
files/settings.conf
Normal file
25
files/settings.conf
Normal file
@ -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": [
|
||||||
|
]
|
||||||
|
}
|
BIN
files/templates.js.gz
Normal file
BIN
files/templates.js.gz
Normal file
Binary file not shown.
BIN
files/wifi-sprites.png
Normal file
BIN
files/wifi-sprites.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
2
out/.gitignore
vendored
Normal file
2
out/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
Loading…
Reference in New Issue
Block a user