Files
bishe-STM32/User/main.c
T
2026-05-01 11:15:54 +08:00

889 lines
29 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ======================================================================
// 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);
}
}