first commit
This commit is contained in:
		
							
								
								
									
										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 | ||||
		Reference in New Issue
	
	Block a user