// ====================================================================== // main.c (人脸识别考勤系统 - AES加密版) // ====================================================================== #include "stm32f10x.h" #include "Delay.h" #include "OLED.h" #include "Serial.h" #include "K210_USART.h" #include "Buzzer_LED.h" #include "esp8266.h" #include "Attendance.h" #include "Debug.h" #include "Crypto.h" // ← 新增:加密模块 #include "DS1302.h" // ← 新增:DS1302 RTC时钟模块 #include "LightSensor.h" // ← 新增:光敏电阻和补光灯模块 #include #include /* --- 用户配置区域 --- */ // 导出配置供ESP8266使用(直接定义字符串常量) // const char* WIFI_SSID = "TP-LINK_AA12"; // const char* WIFI_PASSWORD = "ji13722508517"; const char* WIFI_SSID = "yzfh"; const char* WIFI_PASSWORD = "yzfhroot"; const char* HTTP_SERVER_IP = "27.106.113.205"; // 公网服务器IP const char* HTTP_SERVER_PORT = "8082"; // HTTP端口(8082) /* --- 班级配置 --- */ // 每个设备对应一个班级,修改此处的班级代码 #define CLASS_ID "1" // 班级编号(如 "1", "2", "3" 等) const char* DEVICE_CLASS_ID = CLASS_ID; // 导出给其他模块使用 /* --- 全局变量定义 --- */ volatile char g_k210_rx_buffer[K210_RX_BUFFER_SIZE]; volatile uint16_t g_k210_rx_counter = 0; volatile uint8_t g_k210_rx_flag = 0; uint8_t g_server_connected = 0; uint8_t g_wifi_connected = 0; // WiFi连接状态标志 uint8_t g_esp8266_ready = 0; // ESP8266初始化状态 // 时间相关变量(使用DS1302 RTC) ESP8266_DateTime g_current_time = {1970, 1, 1, 1, 0, 0}; // 默认时间(会被NTP覆盖) uint32_t last_time_sync = 0; uint32_t last_rtc_read = 0; // 上次从DS1302读取时间的时刻 uint8_t g_use_ds1302 = 0; // DS1302可用标志:0=不可用,1=可用 #define TIME_SYNC_INTERVAL_MS 3600000 // NTP同步间隔:1小时 #define RTC_READ_INTERVAL_MS 1000 // DS1302读取间隔:1秒 uint32_t last_heartbeat_time = 0; uint32_t last_reconnect_attempt = 0; uint32_t last_wifi_reconnect_attempt = 0; // WiFi重连尝试时间 uint32_t last_sync_attempt = 0; uint32_t last_timeslot_download = 0; // 上次下载时间段配置的时刻 #define HEARTBEAT_INTERVAL_MS 10000 #define RECONNECT_INTERVAL_MS 5000 #define WIFI_RECONNECT_INTERVAL_MS 30000 // WiFi重连间隔:30秒 #define SYNC_INTERVAL_MS 5000 #define TIMESLOT_DOWNLOAD_INTERVAL_MS 30000 // 时间段配置下载间隔:30秒 #define MAX_RETRY_COUNT 5 #define SYNC_BATCH_THRESHOLD 3 typedef enum { UI_STATE_IDLE, UI_STATE_SHOW_RESULT, } UIState; UIState ui_state = UI_STATE_IDLE; uint32_t ui_state_start_time = 0; #define UI_RESULT_DISPLAY_TIME 2000 typedef enum { BEEP_IDLE, BEEP_SUCCESS, BEEP_DUPLICATE } BeepState; BeepState beep_state = BEEP_IDLE; uint32_t beep_start_time = 0; uint8_t beep_count = 0; char last_student_id[20] = ""; uint32_t last_process_time = 0; #define PROCESS_INTERVAL_MS 2000 /* --- 辅助函数 --- */ void StartBeepSuccess(void) { BUZZER_OFF(); LED_SUCCESS_OFF(); beep_state = BEEP_SUCCESS; beep_start_time = Get_Time_ms(); beep_count = 0; BUZZER_ON(); LED_SUCCESS_ON(); } void StartBeepDuplicate(void) { BUZZER_OFF(); LED_SUCCESS_OFF(); beep_state = BEEP_DUPLICATE; beep_start_time = Get_Time_ms(); beep_count = 0; BUZZER_ON(); LED_SUCCESS_ON(); } void ProcessBeep(void) { if (beep_state == BEEP_IDLE) { BUZZER_OFF(); LED_SUCCESS_OFF(); return; } uint32_t current_time = Get_Time_ms(); uint32_t elapsed = current_time - beep_start_time; if (beep_state == BEEP_SUCCESS) { if (elapsed >= 80) { BUZZER_OFF(); LED_SUCCESS_OFF(); beep_state = BEEP_IDLE; } } else if (beep_state == BEEP_DUPLICATE) { if (beep_count == 0) { if (elapsed >= 80) { BUZZER_OFF(); LED_SUCCESS_OFF(); beep_count = 1; } } else if (beep_count == 1) { if (elapsed >= 160) { BUZZER_ON(); LED_SUCCESS_ON(); beep_count = 2; } } else if (beep_count == 2) { if (elapsed >= 240) { BUZZER_OFF(); LED_SUCCESS_OFF(); beep_state = BEEP_IDLE; } } } } void UpdateIdleScreen(void) { OLED_Clear(); OLED_ShowString(1, 1, "System Ready"); OLED_ShowString(2, 1, "Encrypted Mode"); // ← 显示加密模式 OLED_ShowString(3, 1, "Total:"); OLED_ShowNum(3, 7, Attendance_GetTotalCount(), 3); uint16_t pending = Attendance_GetPendingCount(); if (pending > 0) { OLED_ShowString(4, 1, "Pending:"); OLED_ShowNum(4, 9, pending, 2); } } /** * @brief 发送加密的考勤记录到服务器(新版本) */ uint8_t SendEncryptedAttendance(AttendanceRecord* record) { char json_data[256]; char encrypted_data[512]; // 1. 构造JSON数据(包含班级编号) sprintf(json_data, "{\"student_id\":\"%s\",\"name\":\"%s\",\"confidence\":%.2f,\"status\":\"success\",\"class_id\":\"%s\"}", record->student_id, record->student_name, record->confidence, DEVICE_CLASS_ID); DEBUG_PRINT("[CRYPTO] Original JSON: %s\r\n", json_data); // 2. 加密JSON数据 uint32_t encrypted_len = Crypto_EncryptJSON(json_data, encrypted_data); if (encrypted_len == 0) { DEBUG_ERROR("[CRYPTO] Encryption failed!"); return 0; } DEBUG_PRINT("[CRYPTO] Encrypted length: %u\r\n", encrypted_len); DEBUG_PRINT("[CRYPTO] Encrypted data: %s\r\n", encrypted_data); // 3. 发送加密数据 if (!g_server_connected) { DEBUG_ERROR("[CRYPTO] Server not connected"); return 0; } if (ESP8266_SendEncryptedData(encrypted_data) == ESP8266_SUCCESS) { DEBUG_OK("[CRYPTO] Encrypted data sent successfully"); return 1; } else { DEBUG_ERROR("[CRYPTO] Send failed"); return 0; } } void SyncPendingRecords(void) { uint32_t current_time = Get_Time_ms(); uint16_t pending_count = Attendance_GetPendingCount(); if (pending_count == 0) return; if (!g_server_connected) return; uint8_t should_sync = 0; if (current_time - last_sync_attempt >= SYNC_INTERVAL_MS) { should_sync = 1; DEBUG_PRINT("[Sync] Timed sync: %d records pending\r\n", pending_count); } if (pending_count >= SYNC_BATCH_THRESHOLD) { should_sync = 1; DEBUG_PRINT("[Sync] Batch sync: %d records pending\r\n", pending_count); } if (!should_sync) return; last_sync_attempt = current_time; DEBUG_INFO("[Sync] Starting batch sync (encrypted mode)..."); uint16_t synced_count = 0; uint16_t failed_count = 0; for (uint16_t i = 0; i < pending_count && i < 10; i++) { uint16_t record_index; AttendanceRecord* record = Attendance_GetNextPendingRecord(&record_index); if (record == NULL) break; if (record->retry_count >= MAX_RETRY_COUNT) { DEBUG_PRINT("[Sync] Record %d exceeded max retries\r\n", record_index); Attendance_MarkSyncStatus(record_index, SYNC_FAILED); failed_count++; continue; } Attendance_MarkSyncStatus(record_index, SYNC_SYNCING); DEBUG_PRINT("[Sync] Syncing %d/%d: %s [retry=%d]\r\n", i + 1, pending_count, record->student_id, record->retry_count); // 使用加密发送 if (SendEncryptedAttendance(record) == 1) { Attendance_MarkSyncStatus(record_index, SYNC_SYNCED); synced_count++; } else { Attendance_IncrementRetry(record_index); Attendance_MarkSyncStatus(record_index, SYNC_FAILED); failed_count++; DEBUG_ERROR("[Sync] Send failed, stopping batch sync"); break; } Delay_ms(100); } DEBUG_PRINT("[Sync] Batch complete: %d synced, %d failed\r\n", synced_count, failed_count); } uint8_t SyncNetworkTime(void) { DEBUG_INFO("Syncing time from network..."); if (ESP8266_GetNetworkTime(&g_current_time) == ESP8266_SUCCESS) { last_time_sync = Get_Time_ms(); DEBUG_PRINT("Time synced: %04d-%02d-%02d %02d:%02d:%02d\r\n", g_current_time.year, g_current_time.month, g_current_time.day, g_current_time.hour, g_current_time.minute, g_current_time.second); // 如果DS1302可用,将网络时间写入DS1302 if (g_use_ds1302) { DS1302_Time rtc_time; rtc_time.year = g_current_time.year; rtc_time.month = g_current_time.month; rtc_time.day = g_current_time.day; rtc_time.hour = g_current_time.hour; rtc_time.minute = g_current_time.minute; rtc_time.second = g_current_time.second; rtc_time.week = 1; DS1302_SetTime(&rtc_time); DEBUG_OK("Network time synchronized to DS1302 RTC"); // 延时后验证写入 Delay_ms(100); // 读回验证 DS1302_Time verify_time; DS1302_GetTime(&verify_time); DEBUG_PRINT("[DS1302] Verify: %04d-%02d-%02d %02d:%02d:%02d\r\n", verify_time.year, verify_time.month, verify_time.day, verify_time.hour, verify_time.minute, verify_time.second); // 检查验证结果(允许1-2秒误差) if (verify_time.year == g_current_time.year && verify_time.month == g_current_time.month && verify_time.day == g_current_time.day && verify_time.hour == g_current_time.hour) { DEBUG_OK("[DS1302] Time verified successfully!"); } else { DEBUG_ERROR("[DS1302] Time verification failed! Disabling DS1302."); DEBUG_PRINT("[DS1302] Expected: %04d-%02d-%02d %02d:%02d, Got: %04d-%02d-%02d %02d:%02d\r\n", g_current_time.year, g_current_time.month, g_current_time.day, g_current_time.hour, g_current_time.minute, verify_time.year, verify_time.month, verify_time.day, verify_time.hour, verify_time.minute); g_use_ds1302 = 0; // 禁用DS1302 DEBUG_INFO("[System] Switched to NTP-only mode"); } } else { DEBUG_INFO("[System] Using NTP time (DS1302 disabled)"); } Attendance_SetCurrentTime(g_current_time.hour, g_current_time.minute); return 1; } DEBUG_ERROR("Time sync failed"); return 0; } uint8_t DownloadTimeSlots(void) { char response_buffer[512]; DEBUG_INFO("Downloading time slot configuration from server..."); // 发送HTTP GET请求到 /api/timeslots if (ESP8266_HttpGet("/api/timeslots", response_buffer, sizeof(response_buffer)) == ESP8266_SUCCESS) { DEBUG_OK("Time slots downloaded successfully"); // 解析JSON并更新时间段配置 uint8_t slot_count = Attendance_ParseTimeSlotsJSON(response_buffer); if (slot_count > 0) { DEBUG_PRINT("Time slot configuration updated: %d slots\r\n", slot_count); // 更新下载时间戳 last_timeslot_download = Get_Time_ms(); return 1; } else { DEBUG_ERROR("Failed to parse time slot JSON"); return 0; } } DEBUG_ERROR("Failed to download time slots, using current configuration"); return 0; } void UpdateTimeSlotConfig(void) { uint32_t current_time = Get_Time_ms(); // 如果服务器未连接,不执行下载 if (!g_server_connected) { return; } // 检查是否到了更新间隔(每5分钟) if (current_time - last_timeslot_download >= TIMESLOT_DOWNLOAD_INTERVAL_MS) { DEBUG_INFO("Auto-updating time slot configuration..."); if (DownloadTimeSlots()) { DEBUG_OK("Time slot configuration updated from server"); // 短暂显示更新提示 OLED_ShowString(4, 1, "Config Synced! "); } else { DEBUG_ERROR("Failed to update time slot configuration"); } // 无论成功失败都更新时间戳,避免频繁重试 last_timeslot_download = current_time; } } void UpdateCurrentTime(void) { uint32_t current_ms = Get_Time_ms(); static uint8_t debug_counter = 0; // 调试计数器,每10秒打印一次 // 如果DS1302可用,每秒从DS1302读取时间 if (g_use_ds1302 && current_ms - last_rtc_read >= RTC_READ_INTERVAL_MS) { last_rtc_read = current_ms; DS1302_Time rtc_time; DS1302_GetTime(&rtc_time); // 更新全局时间变量 g_current_time.year = rtc_time.year; g_current_time.month = rtc_time.month; g_current_time.day = rtc_time.day; g_current_time.hour = rtc_time.hour; g_current_time.minute = rtc_time.minute; g_current_time.second = rtc_time.second; // 更新考勤模块的当前时间 Attendance_SetCurrentTime(rtc_time.hour, rtc_time.minute); // 每10秒打印一次当前时间(调试用) debug_counter++; if (debug_counter >= 10) { debug_counter = 0; DEBUG_PRINT("[Time] DS1302: %04d-%02d-%02d %02d:%02d:%02d\r\n", rtc_time.year, rtc_time.month, rtc_time.day, rtc_time.hour, rtc_time.minute, rtc_time.second); } } // 如果DS1302不可用,直接使用NTP时间(每秒更新考勤时间) else if (!g_use_ds1302 && current_ms - last_rtc_read >= RTC_READ_INTERVAL_MS) { last_rtc_read = current_ms; // 直接使用全局NTP时间 Attendance_SetCurrentTime(g_current_time.hour, g_current_time.minute); // 每10秒打印一次当前时间(调试用) debug_counter++; if (debug_counter >= 10) { debug_counter = 0; DEBUG_PRINT("[Time] NTP: %04d-%02d-%02d %02d:%02d\r\n", g_current_time.year, g_current_time.month, g_current_time.day, g_current_time.hour, g_current_time.minute); } } // 定期从网络同步时间(每小时一次,WiFi在线即可) if (current_ms - last_time_sync > TIME_SYNC_INTERVAL_MS) { if (g_wifi_connected) { SyncNetworkTime(); } } } uint8_t TryReconnectWiFi(void) { uint32_t current_time = Get_Time_ms(); // 控制重连频率:30秒尝试一次 if (current_time - last_wifi_reconnect_attempt < WIFI_RECONNECT_INTERVAL_MS) { return 0; } last_wifi_reconnect_attempt = current_time; DEBUG_INFO("Attempting to reconnect WiFi..."); // 尝试连接WiFi if (ESP8266_ConnectWiFi(WIFI_SSID, WIFI_PASSWORD) == ESP8266_SUCCESS) { DEBUG_OK("WiFi reconnected!"); g_wifi_connected = 1; // WiFi已连通时先做NTP对时(不依赖HTTP服务器连接) SyncNetworkTime(); // WiFi连接成功后,立即尝试连接服务器 DEBUG_INFO("Connecting to HTTP server..."); if (ESP8266_ConnectHTTPServer(HTTP_SERVER_IP, HTTP_SERVER_PORT) == ESP8266_SUCCESS) { DEBUG_OK("HTTP server connected!"); g_server_connected = 1; last_heartbeat_time = current_time; // 连接成功提示音 BUZZER_ON(); Delay_ms(100); BUZZER_OFF(); // 同步时间 SyncNetworkTime(); // 下载时间段配置 DownloadTimeSlots(); return 1; } else { DEBUG_ERROR("HTTP server connection failed"); g_server_connected = 0; } return 1; // WiFi已连接,即使服务器未连接 } DEBUG_ERROR("WiFi reconnect failed, will retry in 30s..."); return 0; } uint8_t TryReconnectServer(void) { uint32_t current_time = Get_Time_ms(); if (current_time - last_reconnect_attempt < RECONNECT_INTERVAL_MS) { return 0; } last_reconnect_attempt = current_time; DEBUG_INFO("Attempting to reconnect to HTTP server..."); if (ESP8266_ConnectHTTPServer(HTTP_SERVER_IP, HTTP_SERVER_PORT) == ESP8266_SUCCESS) { DEBUG_OK("Reconnected to HTTP server!"); g_server_connected = 1; last_heartbeat_time = current_time; BUZZER_ON(); Delay_ms(50); BUZZER_OFF(); return 1; } DEBUG_ERROR("Reconnect failed, will retry..."); return 0; } void CheckServerConnection(void) { uint32_t current_time = Get_Time_ms(); // 如果WiFi未连接,尝试重连WiFi if (!g_wifi_connected) { TryReconnectWiFi(); return; // WiFi未连接时,不检查服务器 } // WiFi已连接,检查服务器连接状态 if (current_time - last_heartbeat_time < HEARTBEAT_INTERVAL_MS) { return; } last_heartbeat_time = current_time; if (g_server_connected) { // 检查服务器连接是否仍然有效 if (ESP8266_CheckConnection() == ESP8266_SUCCESS) { DEBUG_INFO("Server connection OK"); } else { DEBUG_ERROR("Server connection lost!"); g_server_connected = 0; // 立即尝试重连服务器 TryReconnectServer(); } } else { // 服务器未连接,尝试重连 TryReconnectServer(); } } // ====================================================================== // 主函数 // ====================================================================== // 主函数 // ====================================================================== int main(void) { AttendanceStatus att_status; uint16_t unsynced_temp; // 1. 硬件和驱动初始化 Debug_Init(); DEBUG_INFO("==========================================="); DEBUG_INFO(" STM32 Face Recognition System"); DEBUG_INFO(" with AES-128-CBC Encryption"); DEBUG_INFO("==========================================="); Delay_Init(); DEBUG_OK("Delay initialized"); DEBUG_INFO("Initializing OLED..."); OLED_Init(); DEBUG_OK("OLED initialized"); DEBUG_INFO("Initializing Buzzer & LED..."); Buzzer_LED_Init(); DEBUG_OK("Buzzer & LED initialized"); DEBUG_INFO("Initializing USART1 PA9/PA10 (ESP8266)..."); Serial_Init(); DEBUG_OK("USART1 PA9/PA10 (ESP8266) initialized"); DEBUG_INFO("Initializing USART3 PB10/PB11 (K210)..."); K210_USART_Init(); DEBUG_OK("USART3 PB10/PB11 (K210) initialized"); DEBUG_INFO("Initializing Attendance module..."); Attendance_Init(); DEBUG_OK("Attendance module initialized"); // ← 新增:初始化加密模块 DEBUG_INFO("Initializing Crypto module..."); Crypto_Init(); DEBUG_OK("Crypto module initialized (AES-128-CBC)"); // ⭐ 新增:初始化DS1302 RTC时钟模块(带硬件检测) DEBUG_INFO("Initializing DS1302 RTC..."); DS1302_Init(); // 尝试读取DS1302,检测硬件是否正常 DS1302_Time rtc_time; DS1302_GetTime(&rtc_time); DEBUG_PRINT("RTC Time: %04d-%02d-%02d %02d:%02d:%02d\r\n", rtc_time.year, rtc_time.month, rtc_time.day, rtc_time.hour, rtc_time.minute, rtc_time.second); // 检测DS1302读取的数据是否合理 if (rtc_time.year >= 2020 && rtc_time.year <= 2100 && rtc_time.month >= 1 && rtc_time.month <= 12 && rtc_time.day >= 1 && rtc_time.day <= 31 && rtc_time.hour <= 23 && rtc_time.minute <= 59 && rtc_time.second <= 59) { // DS1302数据合理,启用DS1302 g_use_ds1302 = 1; DEBUG_OK("DS1302 RTC initialized and verified"); // 将DS1302时间同步到全局时间变量 g_current_time.year = rtc_time.year; g_current_time.month = rtc_time.month; g_current_time.day = rtc_time.day; g_current_time.hour = rtc_time.hour; g_current_time.minute = rtc_time.minute; g_current_time.second = rtc_time.second; // 设置考勤模块的当前时间 Attendance_SetCurrentTime(rtc_time.hour, rtc_time.minute); } else { // DS1302数据异常,禁用DS1302 g_use_ds1302 = 0; DEBUG_ERROR("DS1302 hardware error or not connected!"); DEBUG_INFO("System will use NTP time only"); } // ⭐ 新增:初始化光敏电阻和补光灯模块 DEBUG_INFO("Initializing Light Sensor and Fill Light..."); LightSensor_Init(); DEBUG_OK("Light Sensor initialized (PB0=ADC, PB1=LED)"); last_rtc_read = Get_Time_ms(); // 2. ESP8266网络连接流程(优化:失败后继续运行,后台自动重连) DEBUG_INFO("Starting ESP8266 setup..."); OLED_ShowString(1, 1, "ESP setup..."); if (ESP8266_Setup() == ESP8266_SUCCESS) { DEBUG_OK("ESP8266 setup success"); g_esp8266_ready = 1; DEBUG_PRINT("Connecting to WiFi: %s\r\n", WIFI_SSID); OLED_ShowString(2, 1, "WiFi connecting"); if (ESP8266_ConnectWiFi(WIFI_SSID, WIFI_PASSWORD) == ESP8266_SUCCESS) { DEBUG_OK("WiFi connected!"); g_wifi_connected = 1; // WiFi已连通时先做NTP对时(不依赖HTTP服务器连接) SyncNetworkTime(); OLED_Clear(); OLED_ShowString(1, 1, "WiFi OK!"); OLED_ShowString(2, 1, "HTTP connecting"); // 尝试连接HTTP服务器 if (ESP8266_ConnectHTTPServer(HTTP_SERVER_IP, HTTP_SERVER_PORT) == ESP8266_SUCCESS) { DEBUG_OK("HTTP server connected!"); g_server_connected = 1; last_heartbeat_time = Get_Time_ms(); // 下载时间段配置 DownloadTimeSlots(); // 下载今日已签到记录到本地缓存 OLED_ShowString(2, 1, "Loading records"); uint8_t loaded = Attendance_LoadTodayRecordsFromServer(); DEBUG_PRINT("[Main] Loaded %d today's records from server\r\n", loaded); } else { DEBUG_ERROR("HTTP connection failed!"); DEBUG_INFO("Will retry in background..."); g_server_connected = 0; } } else { DEBUG_ERROR("WiFi connection failed!"); DEBUG_INFO("Will retry in background (every 30s)..."); g_wifi_connected = 0; last_wifi_reconnect_attempt = Get_Time_ms(); // 初始化重连时间 } } else { DEBUG_ERROR("ESP8266 setup failed!"); DEBUG_INFO("WiFi module unavailable, system will run offline"); g_esp8266_ready = 0; g_wifi_connected = 0; g_server_connected = 0; } // 3. 根据连接结果给出反馈(无论成功失败都继续运行) OLED_Clear(); if (g_server_connected) { DEBUG_INFO("================================="); DEBUG_INFO("System Ready! Server connected!"); DEBUG_INFO("Encryption Mode: AES-128-CBC"); DEBUG_INFO("================================="); OLED_ShowString(1, 1, "System Ready"); OLED_ShowString(2, 1, "Online Mode"); // 系统就绪:熄灭PC13状态指示LED LED_STATUS_OFF(); StartBeepSuccess(); Delay_ms(500); } else if (g_wifi_connected) { DEBUG_INFO("================================="); DEBUG_INFO("System Ready! WiFi connected"); DEBUG_INFO("Server reconnecting..."); DEBUG_INFO("================================="); OLED_ShowString(1, 1, "System Ready"); OLED_ShowString(2, 1, "Reconnecting.."); // 系统就绪:熄灭PC13状态指示LED LED_STATUS_OFF(); } else if (g_esp8266_ready) { DEBUG_INFO("================================="); DEBUG_INFO("System Ready! (WiFi reconnecting)"); DEBUG_INFO("Auto-retry every 30 seconds"); DEBUG_INFO("================================="); OLED_ShowString(1, 1, "System Ready"); OLED_ShowString(2, 1, "WiFi retry..."); // 系统就绪:熄灭PC13状态指示LED LED_STATUS_OFF(); } else { DEBUG_ERROR("ESP8266 not available"); DEBUG_INFO("================================="); DEBUG_INFO("System Ready! (Offline mode)"); DEBUG_INFO("Data will be stored locally"); DEBUG_INFO("================================="); OLED_ShowString(1, 1, "System Ready"); OLED_ShowString(2, 1, "Offline Mode"); // 系统就绪:熄灭PC13状态指示LED LED_STATUS_OFF(); StartBeepDuplicate(); Delay_ms(1000); } UpdateIdleScreen(); // 4. 主循环 DEBUG_INFO("Entering main loop..."); while(1) { uint32_t current_time = Get_Time_ms(); ProcessBeep(); UpdateCurrentTime(); CheckServerConnection(); SyncPendingRecords(); UpdateTimeSlotConfig(); // ⭐ 新增:定期更新时间段配置 LightSensor_Update(); // ⭐ 新增:更新补光灯状态(根据光敏电阻) if (K210_GetRxFlag()) { if (K210_FaceResult.success) { DEBUG_PRINT("Face recognized: ID=%s, Name=%s, Conf=%.2f\r\n", K210_FaceResult.student_id, K210_FaceResult.student_name, K210_FaceResult.confidence); // 智能节流 if (strcmp(last_student_id, K210_FaceResult.student_id) == 0) { if (current_time - last_process_time < PROCESS_INTERVAL_MS) { DEBUG_INFO("Same person throttled - skipping"); continue; } } strncpy(last_student_id, K210_FaceResult.student_id, sizeof(last_student_id) - 1); last_process_time = current_time; // 确保蜂鸣器关闭(服务器查重可能耗时较长) BUZZER_OFF(); LED_SUCCESS_OFF(); beep_state = BEEP_IDLE; OLED_Clear(); OLED_ShowString(1, 1, "ID:"); OLED_ShowString(1, 4, K210_FaceResult.student_id); OLED_ShowString(2, 1, K210_FaceResult.student_name); OLED_ShowString(3, 1, "Verifying..."); // 显示正在验证 att_status = Attendance_ProcessFaceResult(&K210_FaceResult); switch(att_status) { case ATTENDANCE_SUCCESS: DEBUG_OK("Attendance SUCCESS! (Encrypted)"); OLED_ShowString(3, 1, "Check-in OK!"); OLED_ShowString(4, 1, "Encrypted"); StartBeepSuccess(); DEBUG_INFO("Record queued for encrypted sync"); break; case ATTENDANCE_DUPLICATE: DEBUG_ERROR("Duplicate attendance detected!"); OLED_ShowString(3, 1, "Already Signed!"); StartBeepDuplicate(); break; case ATTENDANCE_LOW_CONFIDENCE: DEBUG_ERROR("Low confidence rejected"); OLED_ShowString(3, 1, "Low Confidence"); StartBeepDuplicate(); break; case ATTENDANCE_OUT_OF_TIME: DEBUG_ERROR("Not in time slot"); OLED_ShowString(3, 1, "Not Check Time"); StartBeepDuplicate(); break; default: DEBUG_ERROR("Attendance processing error"); OLED_ShowString(3, 1, "Error!"); StartBeepDuplicate(); break; } ui_state = UI_STATE_SHOW_RESULT; ui_state_start_time = current_time; unsynced_temp = Attendance_GetPendingCount(); DEBUG_PRINT("Total: %d, Pending: %d\r\n", Attendance_GetTotalCount(), unsynced_temp); } else { DEBUG_INFO("Unknown face - ignored"); } } if (ui_state == UI_STATE_SHOW_RESULT) { if (current_time - ui_state_start_time >= UI_RESULT_DISPLAY_TIME) { ui_state = UI_STATE_IDLE; UpdateIdleScreen(); } } Delay_ms(10); } }