初始化

This commit is contained in:
2026-05-01 11:13:45 +08:00
commit bb1d10615e
99 changed files with 48378 additions and 0 deletions
+888
View File
@@ -0,0 +1,888 @@
// ======================================================================
// 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 <stdio.h>
#include <string.h>
/* --- 用户配置区域 --- */
// 导出配置供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);
}
}