#include "tools.hpp" extern M5Canvas canvas; // 线程控制相关变量 / Thread control related variables TaskHandle_t fingerDetectionTaskHandle = NULL; bool fingerDetectionEnabled = true; // 指纹检测线程是否启用 / Whether finger detection thread is enabled bool touchDetectionEnabled = true; // 触摸检测是否启用 / Whether touch detection is enabled int fingerPresentCount = 0; // 手指存在的连续计数 / Consecutive count of finger presence bool displayFingerprintImage(M5UnitFingerprint2& fp2, uint32_t maxBufferSize, M5Canvas& canvas, int displayX, int displayY, uint8_t rotation) { // 获取指纹图像 / Get fingerprint image if (fp2.PS_GetImage() != FINGERPRINT_OK) { Serial.println("Failed to get fingerprint image."); canvas.setCursor(2, 60); canvas.printf("Failed to get image"); return false; } // 分配内存 / Allocate memory uint8_t* imageBuffer = (uint8_t*)malloc(maxBufferSize); if (imageBuffer == nullptr) { Serial.println("Failed to allocate memory for image buffer."); canvas.setCursor(2, 60); canvas.printf("Memory allocation failed"); return false; } // 上传图像 / Upload image uint32_t actualImageSize = 0; fingerprint_status_t result = fp2.PS_UpImage(imageBuffer, maxBufferSize, actualImageSize); if (result == FINGERPRINT_OK) { Serial.printf("Image uploaded successfully, size: %d bytes\r\n", actualImageSize); // 在画布上显示图像大小信息 / Display image size information on canvas canvas.fillRect(0, 41, 320, 240, 0x0000); canvas.setCursor(2, 60); canvas.printf("Image size: %d bytes", actualImageSize); // 显示指纹图像 (80x208, 4bit per pixel) 支持不同旋转角度 / Display fingerprint image (80x208, 4bit per pixel) with different rotation angles support if (actualImageSize > 0) { const int imageWidth = 80; const int imageHeight = 208; // 遍历图像数据 / Iterate through image data for (int y = 0; y < imageHeight && y < 220; y++) { // 限制高度避免超出屏幕 / Limit height to avoid exceeding screen for (int x = 0; x < imageWidth && x < 200; x++) { // 限制宽度避免超出屏幕 / Limit width to avoid exceeding screen // 计算像素在缓冲区中的位置 (每字节包含2个像素) / Calculate pixel position in buffer (each byte contains 2 pixels) int byteIndex = (y * imageWidth + x) / 2; if (byteIndex < actualImageSize) { // 获取4bit像素值 (0-15) / Get 4bit pixel value (0-15) uint8_t pixelValue; if ((y * imageWidth + x) % 2 == 0) { // 偶数像素取低4位 / Even pixels take low 4 bits pixelValue = imageBuffer[byteIndex] & 0x0F; } else { // 奇数像素取高4位 / Odd pixels take high 4 bits pixelValue = (imageBuffer[byteIndex] & 0xF0) >> 4; } // 将4bit值(0-15)转换为RGB565 (白色到黑色的渐变) / Convert 4bit value (0-15) to RGB565 (white to black gradient) // 0=黑色, 15=白色 / 0=black, 15=white uint8_t grayValue = pixelValue * 17; // 0-255范围 / 0-255 range uint16_t rgb565 = ((grayValue >> 3) << 11) | ((grayValue >> 2) << 5) | (grayValue >> 3); // 根据rotation参数计算旋转后的像素位置 / Calculate rotated pixel position based on rotation parameter int drawX, drawY; switch (rotation) { case 0: // 不旋转 / No rotation drawX = displayX + x; drawY = displayY + y; break; case 1: // 顺时针90度 / Clockwise 90 degrees drawX = displayX + imageHeight - 1 - y; drawY = displayY + x; break; case 2: // 180度 / 180 degrees drawX = displayX + imageWidth - 1 - x; drawY = displayY + imageHeight - 1 - y; break; case 3: // 逆时针90度 (或顺时针270度) / Counterclockwise 90 degrees (or clockwise 270 degrees) drawX = displayX + y; drawY = displayY + imageWidth - 1 - x; break; default: // 默认顺时针90度 / Default clockwise 90 degrees drawX = displayX + imageHeight - 1 - y; drawY = displayY + x; break; } // 在画布上绘制像素 / Draw pixel on canvas canvas.drawPixel(drawX, drawY, rgb565); } } } } // 释放内存 / Free memory free(imageBuffer); return true; } else { Serial.println("Failed to upload image."); canvas.setCursor(2, 60); canvas.printf("Failed to upload image"); // 释放内存 / Free memory free(imageBuffer); return false; } } bool displaySystemParameters(M5UnitFingerprint2& fp2, M5Canvas& canvas, int displayX, int displayY, bool clearArea) { // 读取系统参数 / Read system parameters PS_ReadSysPara_BasicParams sysParams; fingerprint_status_t result = fp2.PS_ReadSysPara(sysParams); if (result != FINGERPRINT_OK) { Serial.println("Failed to read system parameters."); canvas.setCursor(displayX, displayY); canvas.printf("Failed to read sys params"); return false; } // 如果需要清除显示区域 / If need to clear display area if (clearArea) { canvas.fillRect(displayX, displayY, 320 - displayX, 200, 0x0000); // 清除显示区域 / Clear display area } // 显示系统参数 / Display system parameters int lineHeight = 20; // 每行的高度 / Height per line int currentY = displayY; canvas.setCursor(displayX, currentY); canvas.printf("Status Reg: 0x%04X", sysParams.status_register); currentY += lineHeight; canvas.setCursor(displayX, currentY); canvas.printf("Template Size: %d bytes", sysParams.temp_size); currentY += lineHeight; canvas.setCursor(displayX, currentY); canvas.printf("Data Size: %d bytes", sysParams.data_size); currentY += lineHeight; canvas.setCursor(displayX, currentY); canvas.printf("Score Level: %d", sysParams.score_level); currentY += lineHeight; canvas.setCursor(displayX, currentY); canvas.printf("Device Addr: 0x%08X", sysParams.device_addr); currentY += lineHeight; canvas.setCursor(displayX, currentY); // 0:32 bytes, 1:64 bytes, 2:128 bytes, 3:256 bytes canvas.printf("Packet Size: "); switch (sysParams.packet_size) { case 0: canvas.printf("32 bytes"); break; case 1: canvas.printf("64 bytes"); break; case 2: canvas.printf("128 bytes"); break; case 3: canvas.printf("256 bytes"); break; default: canvas.printf("Unknown"); break; } currentY += lineHeight; canvas.setCursor(displayX, currentY); canvas.printf("Baud Rate: %d bps", sysParams.baud_rate * 9600); Serial.println("System parameters displayed successfully."); return true; } bool displayMenu(M5Canvas& canvas, int displayX, int displayY) { // 清除显示区域 canvas.fillRect(displayX, displayY, 320, 240, 0x0000); // 获取可用显示区域的宽度和高度 int availableWidth = canvas.width() - displayX; int availableHeight = canvas.height() - displayY; // 计算菜单布局参数 (2x2布局) int margin = 10; // 边界间隙 int gapX = 15; // 水平方框之间的间隙 int gapY = 15; // 垂直方框之间的间隙 // 计算每个方框的宽度和高度 (考虑边界和间隙) int boxWidth = (availableWidth - 2 * margin - gapX) / 2; int boxHeight = (availableHeight - 2 * margin - gapY) / 2; // 确保方框不会太小 if (boxWidth < 80) { boxWidth = 80; margin = (availableWidth - 2 * boxWidth - gapX) / 2; if (margin < 5) margin = 5; } if (boxHeight < 50) { boxHeight = 50; } // 计算四个方框的位置 int topLeftX = displayX + margin; int topLeftY = displayY + margin; int topRightX = topLeftX + boxWidth + gapX; int topRightY = topLeftY; int bottomLeftX = topLeftX; int bottomLeftY = topLeftY + boxHeight + gapY; int bottomRightX = topRightX; int bottomRightY = bottomLeftY; // 设置文本颜色和背景色 uint16_t boxColor = 0x071F; // 深蓝色边框 uint16_t fillColor = 0x18E3; // 浅蓝色填充 uint16_t textColor = 0xFFFF; // 白色文字 canvas.setTextColor(textColor); canvas.setTextSize(2); canvas.setTextDatum(middle_center); // 绘制左上方框 (Auto Enroll) canvas.fillRoundRect(topLeftX, topLeftY, boxWidth, boxHeight, 8, fillColor); canvas.drawRoundRect(topLeftX, topLeftY, boxWidth, boxHeight, 8, boxColor); canvas.drawString("Auto", topLeftX + boxWidth / 2, topLeftY + boxHeight / 2 - 10); canvas.drawString("Enroll", topLeftX + boxWidth / 2, topLeftY + boxHeight / 2 + 10); // 绘制右上方框 (Auto Identify) canvas.fillRoundRect(topRightX, topRightY, boxWidth, boxHeight, 8, fillColor); canvas.drawRoundRect(topRightX, topRightY, boxWidth, boxHeight, 8, boxColor); canvas.drawString("Auto", topRightX + boxWidth / 2, topRightY + boxHeight / 2 - 10); canvas.drawString("Identify", topRightX + boxWidth / 2, topRightY + boxHeight / 2 + 10); // 绘制左下方框 (Up/Down Template) canvas.fillRoundRect(bottomLeftX, bottomLeftY, boxWidth, boxHeight, 8, fillColor); canvas.drawRoundRect(bottomLeftX, bottomLeftY, boxWidth, boxHeight, 8, boxColor); canvas.drawString("Up/Down", bottomLeftX + boxWidth / 2, bottomLeftY + boxHeight / 2 - 10); canvas.drawString("Template", bottomLeftX + boxWidth / 2, bottomLeftY + boxHeight / 2 + 10); // 绘制右下方框 (System Parameters) canvas.fillRoundRect(bottomRightX, bottomRightY, boxWidth, boxHeight, 8, fillColor); canvas.drawRoundRect(bottomRightX, bottomRightY, boxWidth, boxHeight, 8, boxColor); canvas.drawString("System", bottomRightX + boxWidth / 2, bottomRightY + boxHeight / 2 - 10); canvas.drawString("Parameters", bottomRightX + boxWidth / 2, bottomRightY + boxHeight / 2 + 10); // 重置文本对齐方式 canvas.setTextDatum(top_left); Serial.printf( "Menu displayed: TopLeft(%d,%d), TopRight(%d,%d), BottomLeft(%d,%d), BottomRight(%d,%d), Box size: %dx%d\r\n", topLeftX, topLeftY, topRightX, topRightY, bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, boxWidth, boxHeight); return true; } bool initializeFingerprintSensor(M5UnitFingerprint2& fp2, M5Canvas& canvas, int maxRetries, int retryDelay) { Serial.println("Starting fingerprint sensor initialization..."); int retryCount = 0; while (maxRetries == 0 || retryCount < maxRetries) { // maxRetries为0时无限重试 if (maxRetries == 0) { Serial.printf("Initialization attempt %d (infinite retry mode)\n", retryCount + 1); } else { Serial.printf("Initialization attempt %d of %d\n", retryCount + 1, maxRetries); } // 检查指纹传感器是否连接成功 uint8_t ModuleStatus; if (fp2.PS_GetFingerprintModuleStatus(ModuleStatus) == FINGERPRINT_OK) { Serial.printf("Module status: 0x%02X\n", ModuleStatus); // 启动指纹传感器 if (fp2.PS_ActivateFingerprintModule() == FINGERPRINT_OK) { Serial.println("Fingerprint module activated successfully."); // 获取版本号 uint8_t FwVersion; if (fp2.PS_GetFirmwareVersion(FwVersion) == FINGERPRINT_OK) { Serial.printf("Fp2 is active. -> Firmware FwVersion: 0x%02X\r\n", FwVersion); canvas.setCursor(2, 20); canvas.printf("Fp2 is active. -> FW:0x%02X ", FwVersion); canvas.pushSprite(0, 0); // 将画布内容显示到屏幕上 // 获取芯片序列号 uint8_t ChipSN[32] = {0}; // 创建一个32字节的缓冲区来存储芯片序列号 if (fp2.PS_GetChipSN(ChipSN) == FINGERPRINT_OK) { Serial.print("Chip SN: "); for (int i = 0; i < 32; i++) { Serial.printf("%02X", ChipSN[i]); } Serial.println(""); Serial.println("Chip SN acquired successfully."); } else { Serial.println("Failed to get chip SN."); } // 握手指令 if (fp2.PS_HandShake() == FINGERPRINT_OK) { Serial.println("Handshake successful."); } else { Serial.println("Failed to perform handshake."); } // 校验传感器 if (fp2.PS_CheckSensor() == FINGERPRINT_OK) { Serial.println("Sensor check passed."); } else { Serial.println("Sensor check failed."); } Serial.println("Fingerprint sensor initialization completed successfully!"); return true; // 完成所有初始化检查,返回成功 } else { Serial.println("Failed to get firmware FwVersion."); canvas.drawString("Failed to get fw FwVersion.", 2, 20); canvas.pushSprite(0, 0); } } else { Serial.println("Fp2 sensor is not active."); canvas.drawString("Fp2 sensor is not active.", 2, 20); canvas.pushSprite(0, 0); } } else { Serial.println("Fp2 sensor not connected."); canvas.drawString("Fp2 sensor not connected.", 2, 20); canvas.pushSprite(0, 0); } retryCount++; if (maxRetries == 0 || retryCount < maxRetries) { Serial.printf("Retrying in %d ms...\n", retryDelay); delay(retryDelay); // 等待后重试 } } // 只有在限制重试次数模式下才会到达这里 Serial.printf("Fingerprint sensor initialization failed after %d attempts.\n", maxRetries); canvas.drawString("Fp2 init failed", 2, 20); canvas.pushSprite(0, 0); return false; // 达到最大重试次数,返回失败 } bool PS_AutoEnroll_callback(uint16_t ID, fingerprint_status_t confirmationCode, uint8_t param1, uint8_t param2, int packetCount) { fingerprint_auto_enroll_stage_t stage = (fingerprint_auto_enroll_stage_t)param1; switch (stage) { case FINGERPRINT_AUTO_ENROLL_LEGAL_CHECK: // Serial.printf("AutoEnroll [%d]: Stage LEGAL_CHECK, code=0x%02X\n", packetCount, confirmationCode); canvas.setCursor(2, 60); canvas.fillRect(2, 60, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: LEGAL_CHECK(0x%02X)", packetCount, confirmationCode); break; case FINGERPRINT_AUTO_ENROLL_GET_IMAGE: if (confirmationCode == FINGERPRINT_OK) { // Serial.printf("AutoEnroll [%d]: Image acquired\n", packetCount); canvas.setCursor(2, 80); canvas.fillRect(2, 80, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: T:%d GetImage ok", packetCount, (packetCount + 1) / 3); } else { // Serial.printf("AutoEnroll [%d]: Failed to get image, code=0x%02X\n", packetCount, confirmationCode); canvas.setCursor(2, 80); canvas.fillRect(2, 80, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: T:%d GetImage failed(0x%02X)", packetCount, (packetCount + 1) / 3, confirmationCode); } break; case FINGERPRINT_AUTO_ENROLL_GEN_CHAR: if (confirmationCode == FINGERPRINT_OK) { // Serial.printf("AutoEnroll [%d]: Characteristic generated\n", packetCount); canvas.setCursor(2, 100); canvas.fillRect(2, 100, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: T:%d GenChar ok", packetCount, packetCount / 3); } else { // Serial.printf("AutoEnroll [%d]: GenChar failed, code=0x%02X\n", packetCount, confirmationCode); canvas.setCursor(2, 100); canvas.fillRect(2, 100, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: T:%d GenChar failed(0x%02X)", packetCount, (packetCount + 1) / 3, confirmationCode); } break; case FINGERPRINT_AUTO_ENROLL_CHECK_LIFT: // Serial.printf("AutoEnroll [%d]: Stage CHECK_LIFT, status=0x%02X\n", packetCount, param2); canvas.setCursor(2, 120); canvas.fillRect(2, 120, 320, 20, 0x0000); // 清除之前的内容 if (confirmationCode == FINGERPRINT_OK) { canvas.printf("[#%d]: T:%d CheckLift ok", packetCount, (packetCount - 1) / 3); } else { canvas.printf("[#%d]: T:%d CheckLift failed(0x%02X)", packetCount, (packetCount + 1) / 3, confirmationCode); } break; case FINGERPRINT_AUTO_ENROLL_MERGE_TEMPLATE: if (confirmationCode == FINGERPRINT_OK) { // Serial.printf("AutoEnroll [%d]: Template merged\n", packetCount); canvas.setCursor(2, 140); canvas.fillRect(2, 140, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Merge ok", packetCount); } else { // Serial.printf("AutoEnroll [%d]: Merge failed, code=0x%02X\n", packetCount, confirmationCode); canvas.setCursor(2, 140); canvas.fillRect(2, 140, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Merge failed(0x%02X)", packetCount, confirmationCode); } break; case FINGERPRINT_AUTO_ENROLL_VERIFY: if (confirmationCode == FINGERPRINT_OK) { // Serial.printf("AutoEnroll [%d]: Template verified\n", packetCount); canvas.setCursor(2, 160); canvas.fillRect(2, 160, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Verify ok", packetCount); } else { // Serial.printf("AutoEnroll [%d]: Verify failed, code=0x%02X\n", packetCount, confirmationCode); canvas.setCursor(2, 160); canvas.fillRect(2, 160, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Verify failed(0x%02X)", packetCount, confirmationCode); } break; case FINGERPRINT_AUTO_ENROLL_STORE_TEMPLATE: if (confirmationCode == FINGERPRINT_OK) { // Serial.printf("AutoEnroll complete! ID=%d stored.\n", ID); canvas.setCursor(2, 180); canvas.fillRect(2, 180, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Store ok, ID=%d", packetCount, ID); } else { // Serial.printf("AutoEnroll [%d]: Store failed, code=0x%02X\n", packetCount, confirmationCode); canvas.setCursor(2, 180); canvas.fillRect(2, 180, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Store failed(0x%02X)", packetCount, confirmationCode); } break; default: // Serial.printf("AutoEnroll [%d]: Unknown stage %d\n", packetCount, param1); canvas.setCursor(2, 200); canvas.fillRect(2, 200, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Unknown stage %d", packetCount, param1); break; } canvas.pushSprite(0, 0); // 刷新画布 // 返回 true 继续自动注册流程,false 则中断 return (confirmationCode == FINGERPRINT_OK); } // PS_AutoIdentify 回调函数 bool PS_AutoIdentify_callback(uint8_t securityLevel, fingerprint_status_t confirmationCode, uint8_t param, uint16_t matchedID, uint16_t matchScore, int packetCount) { ps_auto_identify_param_t stage = (ps_auto_identify_param_t)param; switch (stage) { case PS_AUTO_IDENTIFY_PARAM_LEGAL_CHECK: // Serial.printf("AutoIdentify [%d]: Stage LEGAL_CHECK, code=0x%02X\n", packetCount, confirmationCode); canvas.setCursor(2, 60); canvas.fillRect(2, 60, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: LEGAL_CHECK(0x%02X)", packetCount, confirmationCode); break; case PS_AUTO_IDENTIFY_PARAM_GET_IMAGE: if (confirmationCode == FINGERPRINT_OK) { // Serial.printf("AutoIdentify [%d]: Image acquired\n", packetCount); canvas.setCursor(2, 80); canvas.fillRect(2, 80, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: GetImage ok", packetCount); } else { // Serial.printf("AutoIdentify [%d]: Failed to get image, code=0x%02X\n", packetCount, // confirmationCode); canvas.setCursor(2, 80); canvas.fillRect(2, 80, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: GetImage failed(0x%02X)", packetCount, confirmationCode); } break; case PS_AUTO_IDENTIFY_PARAM_VERIFY: if (confirmationCode == FINGERPRINT_OK) { // Serial.printf("AutoIdentify [%d]: Verification successful, ID=%d, Score=%d\n", packetCount, // matchedID, matchScore); canvas.setCursor(2, 100); canvas.fillRect(2, 100, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Match!", packetCount); canvas.setCursor(2, 120); canvas.fillRect(2, 120, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("ID=%d Score=%d", matchedID, matchScore); } else { // Serial.printf("AutoIdentify [%d]: Verification failed, code=0x%02X\n", packetCount, // confirmationCode); canvas.setCursor(2, 100); canvas.fillRect(2, 100, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Verify failed(0x%02X)", packetCount, confirmationCode); } break; default: // Serial.printf("AutoIdentify [%d]: Unknown stage %d\n", packetCount, param); canvas.setCursor(2, 120); canvas.fillRect(2, 120, 320, 20, 0x0000); // 清除之前的内容 canvas.printf("[#%d]: Unknown stage %d", packetCount, param); break; } canvas.pushSprite(0, 0); // 刷新画布 // 返回 true 继续自动识别流程,false 则中断 return (confirmationCode == FINGERPRINT_OK || stage == PS_AUTO_IDENTIFY_PARAM_LEGAL_CHECK); } // 触摸检测函数 void handleTouchInput(M5Canvas& canvas, M5UnitFingerprint2& fp2) { if (!touchDetectionEnabled) { return; // 触摸检测被禁用时直接返回 } // 检查触摸输入 if (M5.Touch.getCount()) { auto touch = M5.Touch.getDetail(); if (touch.wasPressed()) { int touchX = touch.x; int touchY = touch.y; // 计算菜单区域参数 (与displayMenu函数中的计算保持一致) int displayX = 0; int displayY = 45; int availableWidth = canvas.width() - displayX; int availableHeight = canvas.height() - displayY; int margin = 20; // 边界间隙 int gapX = 15; // 水平方框之间的间隙 int gapY = 15; // 垂直方框之间的间隙 // 计算每个方框的宽度和高度 int boxWidth = (availableWidth - 2 * margin - gapX) / 2; int boxHeight = (availableHeight - 2 * margin - gapY) / 2; // 确保方框不会太小 if (boxWidth < 80) { boxWidth = 80; margin = (availableWidth - 2 * boxWidth - gapX) / 2; if (margin < 5) margin = 5; } if (boxHeight < 50) { boxHeight = 50; } // 计算四个方框的位置 int topLeftX = displayX + margin; int topLeftY = displayY + margin; int topRightX = topLeftX + boxWidth + gapX; int topRightY = topLeftY; int bottomLeftX = topLeftX; int bottomLeftY = topLeftY + boxHeight + gapY; int bottomRightX = topRightX; int bottomRightY = bottomLeftY; // 检查是否点击了左上方框 (Auto Enroll) if (touchX >= topLeftX && touchX <= topLeftX + boxWidth && touchY >= topLeftY && touchY <= topLeftY + boxHeight) { Serial.println("Auto Enroll button clicked!"); // 立刻销毁指纹检测线程 if (fingerDetectionTaskHandle != NULL) { Serial.println("Terminating finger detection task due to button press."); vTaskDelete(fingerDetectionTaskHandle); fingerDetectionTaskHandle = NULL; fingerDetectionEnabled = false; fingerPresentCount = 0; } canvas.fillRect(0, 41, 320, 240, 0x0000); // 清除画布 canvas.pushSprite(0, 0); // 刷新画布 // 这里可以添加Auto Enroll的具体功能 // 例如:调用PS_AutoEnroll函数 uint16_t ls_id = 9; uint8_t ls_enrollCount = 3; uint8_t ls_param_1 = 0; uint8_t ls_param_2 = 0; Serial.printf("Starting Auto Enroll with ID: %d, Count: %d\n", ls_id, ls_enrollCount); // 自动录取指纹的过程中不需要抬起手指 // fingerprint_auto_enroll_flags_t flags = // (fingerprint_auto_enroll_flags_t)(FINGERPRINT_AUTO_ENROLL_ALLOW_OVERWRITE_ID | // FINGERPRINT_AUTO_ENROLL_NO_LIFT_REQUIRED); // 自动录取指纹的过程中需要抬起手指 fingerprint_auto_enroll_flags_t flags = (fingerprint_auto_enroll_flags_t)(FINGERPRINT_AUTO_ENROLL_ALLOW_OVERWRITE_ID); fp2.PS_AutoEnroll(ls_id, ls_enrollCount, flags, &ls_param_1, &ls_param_2, PS_AutoEnroll_callback); // 等待5s delay(5000); // 回到菜单 canvas.fillRect(0, 41, 320, 240, 0x0000); // 清除画布 displayMenu(canvas, 0, 45); // 重新显示菜单 canvas.pushSprite(0, 0); // 刷新画布 // 重启指纹检测线程 fingerDetectionEnabled = true; // 重新启用指纹检测 if (fingerDetectionTaskHandle == NULL) { Serial.println("Restarting finger detection task."); FingerDetectionParams params = { .fp2 = &fp2, .canvas = &canvas }; xTaskCreate(fingerDetectionTask, "FingerDetectionTask", 4096, ¶ms, 1, &fingerDetectionTaskHandle); } } // 检查是否点击了右上方框 (Auto Identify) else if (touchX >= topRightX && touchX <= topRightX + boxWidth && touchY >= topRightY && touchY <= topRightY + boxHeight) { Serial.println("Auto Identify button clicked!"); // 立刻销毁指纹检测线程 if (fingerDetectionTaskHandle != NULL) { Serial.println("Terminating finger detection task due to button press."); vTaskDelete(fingerDetectionTaskHandle); fingerDetectionTaskHandle = NULL; fingerDetectionEnabled = false; fingerPresentCount = 0; } canvas.fillRect(0, 41, 320, 240, 0x0000); // 清除画布 canvas.pushSprite(0, 0); // 刷新画布 // Auto Identify 功能 uint16_t matchedPageID = 0; fp2.PS_AutoIdentify(0, 0xFFFF, FINGERPRINT_AUTO_VERIFY_DEFAULT, matchedPageID, PS_AutoIdentify_callback); // 等待5s delay(5000); // 回到菜单 canvas.fillRect(0, 41, 320, 240, 0x0000); // 清除画布 displayMenu(canvas, 0, 45); // 重新显示菜单 canvas.pushSprite(0, 0); // 刷新画布 // 重启指纹检测线程 fingerDetectionEnabled = true; // 重新启用指纹检测 if (fingerDetectionTaskHandle == NULL) { Serial.println("Restarting finger detection task."); FingerDetectionParams params = { .fp2 = &fp2, .canvas = &canvas }; xTaskCreate(fingerDetectionTask, "FingerDetectionTask", 4096, ¶ms, 1, &fingerDetectionTaskHandle); } } // 检查是否点击了左下方框 (but3) else if (touchX >= bottomLeftX && touchX <= bottomLeftX + boxWidth && touchY >= bottomLeftY && touchY <= bottomLeftY + boxHeight) { // 立刻销毁指纹检测线程 if (fingerDetectionTaskHandle != NULL) { Serial.println("Terminating finger detection task due to button press."); vTaskDelete(fingerDetectionTaskHandle); fingerDetectionTaskHandle = NULL; fingerDetectionEnabled = false; fingerPresentCount = 0; } canvas.fillRect(0, 41, 320, 240, 0x0000); // 清除画布 canvas.pushSprite(0, 0); // 刷新画布 // Serial.println("=== Demo: Upload Template Auto (Automatic) ==="); uint8_t* templateBuffer = (uint8_t*)malloc(16384); if (templateBuffer != nullptr) { uint32_t totalSize = 0; // 实际接收的总大小 // 先加载模板到缓冲区 fp2.PS_LoadChar(2, 9); Serial.println("Starting automatic template upload..."); canvas.setCursor(2, 40); canvas.printf("Auto uploading template..."); canvas.pushSprite(0, 0); // 使用自动上传函数 fingerprint_status_t result = fp2.PS_UploadTemplateAuto(templateBuffer, // 缓冲区 16384, // 缓冲区大小 totalSize, // 返回的实际大小 templateUploadProgressCallback // 进度回调函数 ); // 画一条横线 canvas.drawLine(0, 130, 320, 130, 0xFFFF); // 在画布上画一条横线 canvas.setCursor(2, 140); canvas.printf("Auto download template..."); canvas.pushSprite(0, 0); result = fp2.PS_DownloadTemplateAuto(templateBuffer, // 缓冲区 totalSize, // 返回的实际大小 templateDownloadProgressCallback // 进度回调函数 ); result = fp2.PS_StoreChar(1, 10); free(templateBuffer); } else { Serial.println("Memory allocation failed for template buffer"); canvas.setCursor(2, 160); canvas.printf("Memory allocation failed"); canvas.pushSprite(0, 0); } delay(5000); // 回到菜单 canvas.fillRect(0, 41, 320, 240, 0x0000); // 清除画布 displayMenu(canvas, 0, 45); // 重新显示菜单 canvas.pushSprite(0, 0); // 刷新画布 // 重启指纹检测线程 fingerDetectionEnabled = true; // 重新启用指纹检测 if (fingerDetectionTaskHandle == NULL) { Serial.println("Restarting finger detection task."); FingerDetectionParams params = { .fp2 = &fp2, .canvas = &canvas }; xTaskCreate(fingerDetectionTask, "FingerDetectionTask", 4096, ¶ms, 1, &fingerDetectionTaskHandle); } } // 检查是否点击了右下方框 (but4) else if (touchX >= bottomRightX && touchX <= bottomRightX + boxWidth && touchY >= bottomRightY && touchY <= bottomRightY + boxHeight) { // 立刻销毁指纹检测线程 if (fingerDetectionTaskHandle != NULL) { Serial.println("Terminating finger detection task due to button press."); vTaskDelete(fingerDetectionTaskHandle); fingerDetectionTaskHandle = NULL; fingerDetectionEnabled = false; fingerPresentCount = 0; } canvas.fillRect(0, 41, 320, 240, 0x0000); // 清除画布 canvas.pushSprite(0, 0); // 刷新画布 displaySystemParameters(fp2, canvas, 2, 60); canvas.pushSprite(0, 0); // 刷新画布 delay(5000); // 回到菜单 canvas.fillRect(0, 41, 320, 240, 0x0000); // 清除画布 displayMenu(canvas, 0, 45); // 重新显示菜单 canvas.pushSprite(0, 0); // 刷新画布 // 重启指纹检测线程 fingerDetectionEnabled = true; // 重新启用指纹检测 if (fingerDetectionTaskHandle == NULL) { Serial.println("Restarting finger detection task."); FingerDetectionParams params = { .fp2 = &fp2, .canvas = &canvas }; xTaskCreate(fingerDetectionTask, "FingerDetectionTask", 4096, ¶ms, 1, &fingerDetectionTaskHandle); } } // 延时防止重复触发 delay(200); } } } // 指纹检测线程函数 void fingerDetectionTask(void* parameter) { Serial.println("Finger detection task started."); // 从参数中提取指纹传感器和画布对象 FingerDetectionParams* params = (FingerDetectionParams*)parameter; M5UnitFingerprint2* fp2 = params->fp2; M5Canvas* canvas = params->canvas; while (fingerDetectionEnabled) { // 使用PS_GetImage检测手指是否存在 fingerprint_status_t result = fp2->PS_GetImage(); if (result == FINGERPRINT_OK) { fingerPresentCount++; Serial.printf("Finger detected! Count: %d/5\n", fingerPresentCount); // 如果持续5秒检测到手指存在 if (fingerPresentCount >= 5) { Serial.println( "Finger present for 5 seconds, entering displayFingerprintImage and disabling touch detection."); // 禁用触摸检测 touchDetectionEnabled = false; // 进入指纹图像显示 if (displayFingerprintImage(*fp2, 1024 * 16, *canvas, 56, 100, 1)) { Serial.println("Fingerprint image displayed successfully."); } else { Serial.println("Failed to display fingerprint image."); } // 重置计数器并重新启用触摸检测 fingerPresentCount = 0; // touchDetectionEnabled = true; // 重新显示菜单 // displayMenu(*canvas, 0, 45); // 刷新屏幕 canvas->pushSprite(0, 0); } } else { // 没有检测到手指,重置计数器 if (fingerPresentCount > 0) { Serial.println("Finger removed, resetting count."); fingerPresentCount = 0; } } // 每1秒检测一次 vTaskDelay(pdMS_TO_TICKS(1000)); } Serial.println("Finger detection task terminated."); fingerDetectionTaskHandle = NULL; vTaskDelete(NULL); } // 模板上传进度回调函数 bool templateUploadProgressCallback(uint16_t currentSize, uint32_t totalSize, int packetCount, bool isComplete) { // 进度回调函数 if (!isComplete) { // 更新显示进度 canvas.setCursor(2, 60); canvas.fillRect(2, 60, 320, 20, 0x0000); canvas.printf("Packet #%3.d: %d bytes", packetCount, currentSize); canvas.setCursor(2, 80); canvas.fillRect(2, 80, 320, 20, 0x0000); canvas.printf("total:%d", totalSize); canvas.pushSprite(0, 0); Serial.printf("Progress: Packet #%d, current: %d bytes, total: %d bytes\n", packetCount, currentSize, totalSize); } else { // 上传完成 canvas.setCursor(2, 100); canvas.fillRect(2, 100, 320, 20, 0x0000); canvas.printf("Up ALL: %d bytes", totalSize); canvas.pushSprite(0, 0); Serial.println("Upload completed!"); } return true; // 继续上传 } // 模板下载进度回调函数 bool templateDownloadProgressCallback(uint16_t currentSize, uint32_t totalSize, int packetCount, bool isComplete) { // 进度回调函数 if (!isComplete) { // 更新显示进度 canvas.setCursor(2, 160); canvas.fillRect(2, 160, 320, 20, 0x0000); canvas.printf("Packet #%3.d: %d bytes", packetCount, currentSize); canvas.setCursor(2, 180); canvas.fillRect(2, 180, 320, 20, 0x0000); canvas.printf("total:%d", totalSize); canvas.pushSprite(0, 0); Serial.printf("Progress: Packet #%d, current: %d bytes, total: %d bytes\n", packetCount, currentSize, totalSize); } else { // 下载完成 canvas.setCursor(2, 200); canvas.fillRect(2, 200, 320, 20, 0x0000); canvas.printf("Down ALL: %d bytes", totalSize); canvas.pushSprite(0, 0); Serial.println("Download completed!"); } return true; // 继续下载 }