This commit is contained in:
2026-05-21 08:07:16 +08:00
parent 90908cf018
commit 84d8874a38
32 changed files with 271 additions and 27 deletions
+23
View File
@@ -32,6 +32,7 @@ static AttendanceRecord attendance_records[MAX_ATTENDANCE_RECORDS];
static uint16_t record_count = 0;
static uint32_t total_attendance_count = 0; // 总考勤人次
/* 函数作用:根据当前小时和分钟查找所处的考勤时间段。 */
static int8_t Attendance_FindTimeSlot(uint8_t hour, uint8_t minute)
{
uint16_t current_minutes = hour * 60 + minute;
@@ -57,6 +58,7 @@ static int8_t Attendance_FindTimeSlot(uint8_t hour, uint8_t minute)
/**
* @brief 初始化考勤模块
*/
/* 函数作用:初始化考勤记录、时间段配置和今日已签到缓存。 */
void Attendance_Init(void)
{
record_count = 0;
@@ -81,6 +83,7 @@ void Attendance_Init(void)
/**
* @brief 清空今日签到缓存
*/
/* 函数作用:清空当前时段的服务器已签到学号缓存。 */
void Attendance_ClearCache(void)
{
cached_id_count = 0;
@@ -93,6 +96,7 @@ void Attendance_ClearCache(void)
/**
* @brief 添加学号到缓存
*/
/* 函数作用:把指定学号加入当前考勤时段的本地查重缓存。 */
void Attendance_AddToCache(const char* student_id)
{
int8_t current_slot = Attendance_GetCurrentTimeSlot();
@@ -132,6 +136,7 @@ void Attendance_AddToCache(const char* student_id)
* @brief 在本地缓存中查重
* @retval 1=在缓存中找到(重复), 0=未找到
*/
/* 函数作用:检查指定学号是否已存在于当前时段缓存中。 */
uint8_t Attendance_IsDuplicateInCache(const char* student_id)
{
int8_t current_slot = Attendance_GetCurrentTimeSlot();
@@ -158,6 +163,7 @@ uint8_t Attendance_IsDuplicateInCache(const char* student_id)
* @brief 从服务器加载今日已签到记录到缓存
* @retval 加载的记录数量
*/
/* 函数作用:下载当前时段已签到学号列表并写入本地查重缓存。 */
uint8_t Attendance_LoadTodayRecordsFromServer(void)
{
char slot_start[8];
@@ -221,6 +227,7 @@ uint8_t Attendance_LoadTodayRecordsFromServer(void)
* @param slots 新的时间段配置数组
* @param count 时间段数量
*/
/* 函数作用:用服务端下发的新时间段数组替换本地时间段配置。 */
void Attendance_UpdateTimeSlots(TimeSlot* slots, uint8_t count)
{
if (count > MAX_TIME_SLOT_COUNT) {
@@ -249,6 +256,7 @@ void Attendance_UpdateTimeSlots(TimeSlot* slots, uint8_t count)
* @param json_str JSON字符串(格式:{"time_slots":[{"enabled":true,"start_time":"08:00",...}]})
* @retval 成功解析的时间段数量
*/
/* 函数作用:从服务端同步 JSON 中解析时间段配置并应用到本地。 */
uint8_t Attendance_ParseTimeSlotsJSON(const char* json_str)
{
TimeSlot temp_slots[MAX_TIME_SLOT_COUNT];
@@ -386,6 +394,7 @@ uint8_t Attendance_ParseTimeSlotsJSON(const char* json_str)
* @param hour 小时 (0-23)
* @param minute 分钟 (0-59)
*/
/* 函数作用:更新考勤模块当前时间,并在跨时段时清空查重缓存。 */
void Attendance_SetCurrentTime(uint8_t hour, uint8_t minute)
{
int8_t old_slot = Attendance_FindTimeSlot(current_hour, current_minute);
@@ -405,6 +414,7 @@ void Attendance_SetCurrentTime(uint8_t hour, uint8_t minute)
* @brief 获取当前所处的考勤时段
* @retval 时段编号 (0-N),如果不在任何时段内则返回 -1
*/
/* 函数作用:返回当前时间对应的考勤时段索引。 */
int8_t Attendance_GetCurrentTimeSlot(void)
{
return Attendance_FindTimeSlot(current_hour, current_minute);
@@ -415,6 +425,7 @@ int8_t Attendance_GetCurrentTimeSlot(void)
* @param start_time 输出:开始时间(格式"HH:MM")
* @param end_time 输出:结束时间(格式"HH:MM")
*/
/* 函数作用:输出当前考勤时段的开始和结束时间字符串。 */
void Attendance_GetCurrentSlotTime(char* start_time, char* end_time)
{
int8_t current_slot = Attendance_GetCurrentTimeSlot();
@@ -438,6 +449,7 @@ void Attendance_GetCurrentSlotTime(char* start_time, char* end_time)
* @param student_id 学生学号
* @retval 1=服务器显示已签到(重复), 0=未签到或查询失败
*/
/* 函数作用:向服务器查询指定学生在当前时段是否已经签到。 */
uint8_t Attendance_IsDuplicateOnServer(const char* student_id)
{
char slot_start[8];
@@ -458,6 +470,7 @@ uint8_t Attendance_IsDuplicateOnServer(const char* student_id)
* @param student_id 学生学号
* @retval 1=重复, 0=不重复
*/
/* 函数作用:检查指定学生是否已在当前时段产生本地重复签到。 */
uint8_t Attendance_IsDuplicate(const char* student_id)
{
int8_t current_slot = Attendance_GetCurrentTimeSlot();
@@ -485,6 +498,7 @@ uint8_t Attendance_IsDuplicate(const char* student_id)
* @param result 人脸识别结果
* @retval 考勤状态
*/
/* 函数作用:校验识别结果、查重并生成一条待同步考勤记录。 */
AttendanceStatus Attendance_ProcessFaceResult(FaceRecognitionResult* result)
{
// 1. 检查识别是否成功
@@ -598,6 +612,7 @@ AttendanceStatus Attendance_ProcessFaceResult(FaceRecognitionResult* result)
/**
* @brief 清空所有考勤记录
*/
/* 函数作用:清空当前缓存的考勤记录,但保留累计统计值。 */
void Attendance_ClearRecords(void)
{
record_count = 0;
@@ -608,6 +623,7 @@ void Attendance_ClearRecords(void)
/**
* @brief 获取累计考勤总数
*/
/* 函数作用:返回系统启动以来累计处理的成功考勤次数。 */
uint16_t Attendance_GetTotalCount(void)
{
return total_attendance_count;
@@ -616,6 +632,7 @@ uint16_t Attendance_GetTotalCount(void)
/**
* @brief 获取当前缓存中的记录数
*/
/* 函数作用:返回当前本地缓存中的考勤记录数量。 */
uint16_t Attendance_GetActiveCount(void)
{
return record_count;
@@ -625,6 +642,7 @@ uint16_t Attendance_GetActiveCount(void)
* @brief 获取待同步记录的数量(PENDING或FAILED状态)
* @retval 待同步记录数
*/
/* 函数作用:统计当前等待上传或需要重试上传的记录数量。 */
uint16_t Attendance_GetPendingCount(void)
{
uint16_t count = 0;
@@ -644,6 +662,7 @@ uint16_t Attendance_GetPendingCount(void)
* @param index 输出参数,返回记录的索引
* @retval 记录指针,如果没有待同步记录则返回NULL
*/
/* 函数作用:按优先级取出下一条可上传或可重试的考勤记录。 */
AttendanceRecord* Attendance_GetNextPendingRecord(uint16_t* index)
{
uint32_t current_time = Get_Time_ms();
@@ -690,6 +709,7 @@ AttendanceRecord* Attendance_GetNextPendingRecord(uint16_t* index)
* @param index 记录索引
* @param status 新的同步状态
*/
/* 函数作用:更新指定考勤记录的服务器同步状态。 */
void Attendance_MarkSyncStatus(uint16_t index, SyncStatus status)
{
if (index < record_count)
@@ -711,6 +731,7 @@ void Attendance_MarkSyncStatus(uint16_t index, SyncStatus status)
* @brief 增加记录的重试计数
* @param index 记录索引
*/
/* 函数作用:增加指定记录的上传重试次数。 */
void Attendance_IncrementRetry(uint16_t index)
{
if (index < record_count)
@@ -724,6 +745,7 @@ void Attendance_IncrementRetry(uint16_t index)
/**
* @brief 将所有FAILED记录重置为PENDING(用于网络恢复后)
*/
/* 函数作用:网络恢复后把失败记录重新置为待同步状态。 */
void Attendance_ResetFailedRecords(void)
{
uint16_t reset_count = 0;
@@ -748,6 +770,7 @@ void Attendance_ResetFailedRecords(void)
* @param index 记录索引
* @retval 记录指针,如果索引无效则返回NULL
*/
/* 函数作用:根据数组索引返回对应考勤记录指针。 */
AttendanceRecord* Attendance_GetRecordByIndex(uint16_t index)
{
if (index < record_count)
+40 -18
View File
@@ -51,29 +51,51 @@ typedef struct {
} TimeSlot;
// 函数声明
// 函数作用:初始化考勤模块的记录缓存、时间段配置和运行状态。
void Attendance_Init(void);
void Attendance_SetCurrentTime(uint8_t hour, uint8_t minute); // 设置当前时间
void Attendance_UpdateTimeSlots(TimeSlot* slots, uint8_t count); // 更新时间段配置(从服务器下载后调用)
uint8_t Attendance_ParseTimeSlotsJSON(const char* json_str); // 从JSON字符串解析时间段配置
// 函数作用:设置考勤模块当前使用的小时和分钟,用于判断当前是否处于考勤时段。
void Attendance_SetCurrentTime(uint8_t hour, uint8_t minute);
// 函数作用:使用服务器下发的时间段数组更新本地考勤时间段配置。
void Attendance_UpdateTimeSlots(TimeSlot* slots, uint8_t count);
// 函数作用:从服务器返回的 JSON 字符串中解析时间段配置并更新本地列表。
uint8_t Attendance_ParseTimeSlotsJSON(const char* json_str);
// 函数作用:处理 K210 人脸识别结果,判断是否可打卡并生成考勤记录。
AttendanceStatus Attendance_ProcessFaceResult(FaceRecognitionResult* result);
// 函数作用:检查指定学号在当前考勤时段内是否已经打卡。
uint8_t Attendance_IsDuplicate(const char* student_id);
uint8_t Attendance_IsDuplicateOnServer(const char* student_id); // 服务器端查重
void Attendance_GetCurrentSlotTime(char* start_time, char* end_time); // 获取当前时段时间
uint8_t Attendance_LoadTodayRecordsFromServer(void); // 新增:从服务器加载今日签到记录
uint8_t Attendance_IsDuplicateInCache(const char* student_id); // 新增:在本地缓存中查重
void Attendance_AddToCache(const char* student_id); // 新增:添加学号到缓存
void Attendance_ClearCache(void); // 新增:清空缓存
// 函数作用:通过服务器接口检查指定学号在当前时段是否已经打卡。
uint8_t Attendance_IsDuplicateOnServer(const char* student_id);
// 函数作用:获取当前考勤时段的开始和结束时间字符串。
void Attendance_GetCurrentSlotTime(char* start_time, char* end_time);
// 函数作用:从服务器加载今日已签到记录,用于设备重启后恢复去重缓存。
uint8_t Attendance_LoadTodayRecordsFromServer(void);
// 函数作用:在本地今日签到缓存中检查指定学号是否已存在。
uint8_t Attendance_IsDuplicateInCache(const char* student_id);
// 函数作用:把指定学号加入本地今日签到缓存。
void Attendance_AddToCache(const char* student_id);
// 函数作用:清空本地今日签到缓存。
void Attendance_ClearCache(void);
// 函数作用:清空本地保存的考勤记录和同步状态。
void Attendance_ClearRecords(void);
uint16_t Attendance_GetTotalCount(void); // 获取累计考勤总数
uint16_t Attendance_GetActiveCount(void); // 获取当前缓存记录数
int8_t Attendance_GetCurrentTimeSlot(void); // 获取当前时段编号(-1表示不在考勤时段)
// 函数作用:返回累计考勤记录总数。
uint16_t Attendance_GetTotalCount(void);
// 函数作用:返回当前本地缓存中的有效记录数量。
uint16_t Attendance_GetActiveCount(void);
// 函数作用:返回当前所在考勤时段编号,-1 表示不在任意考勤时段。
int8_t Attendance_GetCurrentTimeSlot(void);
// 数据同步相关函数
uint16_t Attendance_GetPendingCount(void); // 获取待同步记录数量(PENDING状态)
AttendanceRecord* Attendance_GetNextPendingRecord(uint16_t* index); // 获取下一个待同步记录
void Attendance_MarkSyncStatus(uint16_t index, SyncStatus status); // 更新记录的同步状态
void Attendance_IncrementRetry(uint16_t index); // 增加重试计数
AttendanceRecord* Attendance_GetRecordByIndex(uint16_t index); // 按索引获取记录
void Attendance_ResetFailedRecords(void); // 将所有FAILED记录重置为PENDING
// 函数作用:统计当前处于待同步状态的考勤记录数量。
uint16_t Attendance_GetPendingCount(void);
// 函数作用:查找下一条待同步记录,并通过 index 返回其在缓存中的位置。
AttendanceRecord* Attendance_GetNextPendingRecord(uint16_t* index);
// 函数作用:按缓存索引更新指定考勤记录的同步状态。
void Attendance_MarkSyncStatus(uint16_t index, SyncStatus status);
// 函数作用:按缓存索引增加指定考勤记录的同步重试次数。
void Attendance_IncrementRetry(uint16_t index);
// 函数作用:按缓存索引返回指定考勤记录指针。
AttendanceRecord* Attendance_GetRecordByIndex(uint16_t index);
// 函数作用:将所有同步失败记录重置为待同步,便于后续重新上传。
void Attendance_ResetFailedRecords(void);
#endif
+1
View File
@@ -1,5 +1,6 @@
#include "Buzzer_LED.h"
/* 函数作用:初始化蜂鸣器、成功指示灯和板载状态 LED 对应 GPIO。 */
void Buzzer_LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
+1
View File
@@ -14,6 +14,7 @@
#define LED_STATUS_OFF() GPIO_SetBits(GPIOC, GPIO_Pin_13) // 就绪后:熄灭
// 声明初始化函数
// 函数作用:初始化成功指示灯、状态灯和蜂鸣器相关 GPIO 输出状态。
void Buzzer_LED_Init(void);
#endif
+5
View File
@@ -23,6 +23,7 @@ static const uint8_t AES_IV[16] = {
static struct AES_ctx aes_ctx;
/* 函数作用:初始化 AES 加密上下文。 */
void Crypto_Init(void)
{
// 初始化AES上下文(使用CBC模式)
@@ -33,6 +34,7 @@ void Crypto_Init(void)
* @brief PKCS#7填充
* 将数据填充到16字节的倍数
*/
/* 函数作用:为 JSON 明文追加 PKCS#7 填充。 */
static uint32_t pkcs7_pad(uint8_t* data, uint32_t data_len, uint32_t buffer_size)
{
// 计算需要填充的字节数
@@ -52,6 +54,7 @@ static uint32_t pkcs7_pad(uint8_t* data, uint32_t data_len, uint32_t buffer_size
return data_len + padding_len;
}
/* 函数作用:移除解密结果中的 PKCS#7 填充。 */
static uint32_t pkcs7_unpad(uint8_t* data, uint32_t data_len)
{
uint8_t padding_len;
@@ -75,6 +78,7 @@ static uint32_t pkcs7_unpad(uint8_t* data, uint32_t data_len)
return data_len - padding_len;
}
/* 函数作用:将 JSON 字符串 AES-CBC 加密并 Base64 编码输出。 */
uint32_t Crypto_EncryptJSON(const char* json_data, char* encrypted_output)
{
uint32_t json_len = strlen(json_data);
@@ -121,6 +125,7 @@ uint32_t Crypto_EncryptJSON(const char* json_data, char* encrypted_output)
return encoded_len;
}
/* 函数作用:将 Base64 密文解码并 AES-CBC 解密为 JSON 字符串。 */
uint32_t Crypto_DecryptJSON(const char* encrypted_base64, char* plain_output, uint32_t output_size)
{
uint32_t encrypted_len;
+3
View File
@@ -12,6 +12,7 @@
*
* 注意:encrypted_output缓冲区至少需要 (strlen(json_data) / 16 + 1) * 16 * 4/3 + 10 字节
*/
// 函数作用:对 JSON 字符串执行 AES-CBC 加密和 Base64 编码,生成可上传服务端的密文。
uint32_t Crypto_EncryptJSON(const char* json_data, char* encrypted_output);
/**
@@ -21,6 +22,7 @@ uint32_t Crypto_EncryptJSON(const char* json_data, char* encrypted_output);
* @param output_size 输出缓冲区大小
* @return 明文字节数,失败返回0
*/
// 函数作用:对 Base64 编码的 AES-CBC 密文进行解码和解密,输出 JSON 明文。
uint32_t Crypto_DecryptJSON(const char* encrypted_base64, char* plain_output, uint32_t output_size);
/**
@@ -34,6 +36,7 @@ uint32_t Crypto_DecryptJSON(const char* encrypted_base64, char* plain_output, ui
* @brief 初始化加密模块
* 设置密钥和IV(初始化向量)
*/
// 函数作用:初始化加密模块使用的固定密钥、IV 和底层 AES 上下文。
void Crypto_Init(void);
#endif // _CRYPTO_H_
+9
View File
@@ -33,6 +33,7 @@
#define DEC_TO_BCD(dec) ((((dec) / 10) << 4) | ((dec) % 10))
// 微秒延时
/* 函数作用:为 DS1302 时序提供简短的微秒级软件延时。 */
static void delay_us(uint32_t us)
{
volatile uint32_t i;
@@ -44,6 +45,7 @@ static void delay_us(uint32_t us)
/**
* @brief 配置IO口为输出
*/
/* 函数作用:把 DS1302 数据引脚配置为推挽输出模式。 */
static void DS1302_DATAOUT_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
@@ -56,6 +58,7 @@ static void DS1302_DATAOUT_init(void)
/**
* @brief 配置IO口为输入
*/
/* 函数作用:把 DS1302 数据引脚配置为浮空输入模式。 */
static void DS1302_DATAINPUT_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
@@ -67,6 +70,7 @@ static void DS1302_DATAINPUT_init(void)
/**
* @brief DS1302初始化
*/
/* 函数作用:初始化 DS1302 的时钟、数据和使能引脚。 */
void DS1302_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
@@ -92,6 +96,7 @@ void DS1302_Init(void)
* @brief 向DS1302发送一字节数据(根据demo代码)
* @param data 要发送的数据
*/
/* 函数作用:按 DS1302 时序向芯片写入一个字节。 */
static void DS1302_write_onebyte(uint8_t data)
{
uint8_t count = 0;
@@ -119,6 +124,7 @@ static void DS1302_write_onebyte(uint8_t data)
* @param address 寄存器地址
* @param data 数据
*/
/* 函数作用:向 DS1302 指定寄存器写入一个字节数据。 */
void DS1302_WriteByte(uint8_t address, uint8_t data)
{
CE_L;
@@ -141,6 +147,7 @@ void DS1302_WriteByte(uint8_t address, uint8_t data)
* @param address 寄存器地址
* @retval 读取到的数据
*/
/* 函数作用:从 DS1302 指定寄存器读取一个字节数据。 */
uint8_t DS1302_ReadByte(uint8_t address)
{
uint8_t count = 0;
@@ -184,6 +191,7 @@ uint8_t DS1302_ReadByte(uint8_t address)
* @brief 设置DS1302时间
* @param time 时间结构体指针
*/
/* 函数作用:把时间结构体转换为 BCD 后写入 DS1302。 */
void DS1302_SetTime(DS1302_Time* time)
{
// 关闭写保护
@@ -206,6 +214,7 @@ void DS1302_SetTime(DS1302_Time* time)
* @brief 读取DS1302时间
* @param time 时间结构体指针
*/
/* 函数作用:读取 DS1302 时间寄存器并转换为十进制时间结构体。 */
void DS1302_GetTime(DS1302_Time* time)
{
uint8_t read_time[7];
+5
View File
@@ -15,10 +15,15 @@ typedef struct {
} DS1302_Time;
// 函数声明
// 函数作用:初始化 DS1302 时钟芯片使用的 GPIO 引脚和通信状态。
void DS1302_Init(void);
// 函数作用:把指定时间写入 DS1302 时钟芯片。
void DS1302_SetTime(DS1302_Time* time);
// 函数作用:从 DS1302 时钟芯片读取当前日期和时间。
void DS1302_GetTime(DS1302_Time* time);
// 函数作用:从 DS1302 指定寄存器地址读取一个字节数据。
uint8_t DS1302_ReadByte(uint8_t addr);
// 函数作用:向 DS1302 指定寄存器地址写入一个字节数据。
void DS1302_WriteByte(uint8_t addr, uint8_t dat);
#endif
+4
View File
@@ -11,6 +11,7 @@ extern volatile uint8_t g_k210_rx_flag;
// 全局变量:存储解析后的人脸识别结果
FaceRecognitionResult K210_FaceResult;
/* 函数作用:初始化与 K210 通信的 USART3 串口和接收中断。 */
void K210_USART_Init(void)
{
// 根据连接图.md: K210使用USART3 (PB10/PB11)
@@ -60,6 +61,7 @@ void K210_USART_Init(void)
* K210数据格式示例: "ID:2021001,NAME:Zhang_San,CONF:0.95"
* 或者: "UNKNOWN" (未识别到人脸)
*/
/* 函数作用:解析 K210 串口发送的人脸识别字符串并填充结果结构体。 */
uint8_t K210_ParseFaceData(const char* buffer, FaceRecognitionResult* result)
{
DEBUG_PRINT("[K210] Raw data: %s\r\n", buffer);
@@ -109,6 +111,7 @@ uint8_t K210_ParseFaceData(const char* buffer, FaceRecognitionResult* result)
return 0; // 解析失败
}
/* 函数作用:检查 K210 接收完成标志,并在有新数据时解析识别结果。 */
uint8_t K210_GetRxFlag(void)
{
if (g_k210_rx_flag)
@@ -129,6 +132,7 @@ uint8_t K210_GetRxFlag(void)
return 0;
}
/* 函数作用:清除 K210 串口接收完成标志和计数器。 */
void K210_ClearRxFlag(void)
{
g_k210_rx_flag = 0;
+4
View File
@@ -10,9 +10,13 @@
extern FaceRecognitionResult K210_FaceResult;
// 声明公共函数
// 函数作用:初始化 STM32 与 K210 通信用的 USART3 接口和接收中断。
void K210_USART_Init(void);
// 函数作用:返回 K210 串口接收完成标志,用于主循环判断是否有新识别结果。
uint8_t K210_GetRxFlag(void);
// 函数作用:清除 K210 串口接收完成标志,准备接收下一帧数据。
void K210_ClearRxFlag(void);
// 函数作用:解析 K210 发来的识别结果字符串,填充人脸识别结果结构体。
uint8_t K210_ParseFaceData(const char* buffer, FaceRecognitionResult* result);
#endif
+4
View File
@@ -6,6 +6,7 @@ static uint8_t g_fill_light_on = 0;
/**
* @brief 初始化光敏电阻ADC和补光灯GPIO
*/
/* 函数作用:初始化光敏电阻 ADC 输入和补光灯 GPIO 输出。 */
void LightSensor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
@@ -60,6 +61,7 @@ void LightSensor_Init(void)
* @brief 获取光敏电阻ADC值
* @return ADC值 (0-4095),值越大表示光线越暗(电阻越大)
*/
/* 函数作用:采集一次光敏电阻 ADC 数值。 */
uint16_t LightSensor_GetValue(void)
{
// 启动ADC转换
@@ -76,6 +78,7 @@ uint16_t LightSensor_GetValue(void)
* @brief 更新补光灯状态(在主循环中定期调用)
* 使用迟滞控制避免灯光频繁开关
*/
/* 函数作用:根据环境光强自动打开或关闭补光灯。 */
void LightSensor_Update(void)
{
uint16_t light_value = LightSensor_GetValue();
@@ -100,6 +103,7 @@ void LightSensor_Update(void)
* @brief 获取补光灯当前状态
* @return 1=亮,0=灭
*/
/* 函数作用:返回补光灯当前开关状态。 */
uint8_t LightSensor_IsLightOn(void)
{
return g_fill_light_on;
+8 -4
View File
@@ -22,9 +22,13 @@
// 使用迟滞区间避免频繁开关
// 函数声明
void LightSensor_Init(void); // 初始化光敏电阻ADC和补光灯GPIO
uint16_t LightSensor_GetValue(void); // 获取光敏电阻ADC值
void LightSensor_Update(void); // 更新补光灯状态(主循环中调用)
uint8_t LightSensor_IsLightOn(void); // 获取补光灯当前状态
// 函数作用:初始化光敏电阻 ADC 输入和补光灯 GPIO 输出。
void LightSensor_Init(void);
// 函数作用:读取光敏电阻 ADC 当前采样值。
uint16_t LightSensor_GetValue(void);
// 函数作用:根据当前光照强度和迟滞阈值更新补光灯开关状态。
void LightSensor_Update(void);
// 函数作用:返回补光灯当前是否处于点亮状态。
uint8_t LightSensor_IsLightOn(void);
#endif
+17
View File
@@ -12,6 +12,7 @@
/* ... 请确保您文件里这部分内容是完整的 ... */
/*引脚初始化*/
/* 函数作用:初始化 OLED 软件 I2C 使用的 SCL 和 SDA GPIO。 */
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
@@ -28,6 +29,7 @@ void OLED_I2C_Init(void)
OLED_W_SDA(1);
}
/* 函数作用:产生 OLED 软件 I2C 起始信号。 */
void OLED_I2C_Start(void)
{
OLED_W_SDA(1);
@@ -36,6 +38,7 @@ void OLED_I2C_Start(void)
OLED_W_SCL(0);
}
/* 函数作用:产生 OLED 软件 I2C 停止信号。 */
void OLED_I2C_Stop(void)
{
OLED_W_SDA(0);
@@ -43,6 +46,7 @@ void OLED_I2C_Stop(void)
OLED_W_SDA(1);
}
/* 函数作用:通过软件 I2C 向 OLED 发送一个字节。 */
void OLED_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
@@ -56,6 +60,7 @@ void OLED_I2C_SendByte(uint8_t Byte)
OLED_W_SCL(0);
}
/* 函数作用:向 OLED 写入一条控制命令。 */
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
@@ -65,6 +70,7 @@ void OLED_WriteCommand(uint8_t Command)
OLED_I2C_Stop();
}
/* 函数作用:向 OLED 写入一个显示数据字节。 */
void OLED_WriteData(uint8_t Data)
{
OLED_I2C_Start();
@@ -74,6 +80,7 @@ void OLED_WriteData(uint8_t Data)
OLED_I2C_Stop();
}
/* 函数作用:设置 OLED 显存写入光标位置。 */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y);
@@ -81,6 +88,7 @@ void OLED_SetCursor(uint8_t Y, uint8_t X)
OLED_WriteCommand(0x00 | (X & 0x0F));
}
/* 函数作用:清空 OLED 屏幕显示内容。 */
void OLED_Clear(void)
{
uint8_t i, j;
@@ -94,6 +102,7 @@ void OLED_Clear(void)
}
}
/* 函数作用:在指定行列显示一个 ASCII 字符。 */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
@@ -109,6 +118,7 @@ void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
}
}
/* 函数作用:从指定行列开始显示字符串。 */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
@@ -118,6 +128,7 @@ void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
}
}
/* 函数作用:计算整数 X 的 Y 次方,用于数字拆位显示。 */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
@@ -128,6 +139,7 @@ uint32_t OLED_Pow(uint32_t X, uint32_t Y)
return Result;
}
/* 函数作用:在 OLED 上显示指定长度的无符号十进制数字。 */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
@@ -137,6 +149,7 @@ void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
}
}
/* 函数作用:在 OLED 上显示带正负号的十进制数字。 */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
@@ -157,6 +170,7 @@ void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Le
}
}
/* 函数作用:在 OLED 上显示指定长度的十六进制数字。 */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
@@ -174,6 +188,7 @@ void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Leng
}
}
/* 函数作用:在 OLED 上显示指定长度的二进制数字。 */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
@@ -191,6 +206,7 @@ void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Leng
* @param num 要显示的汉字在OLED_F16x16_CN字模数组中的索引
* @retval 无
*/
/* 函数作用:按照字模索引在 OLED 上显示一个 16x16 汉字。 */
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t num)
{
uint8_t i;
@@ -219,6 +235,7 @@ void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t num)
/* ... OLED_Init() 函数保持不变 ... */
/* 函数作用:初始化 OLED 控制器参数并清屏。 */
void OLED_Init(void)
{
uint32_t i, j;
+9
View File
@@ -6,16 +6,25 @@
#include "stm32f10x.h"
// 函数作用:初始化 OLED 屏幕通信接口并完成显示控制器基础配置。
void OLED_Init(void);
// 函数作用:清空 OLED 显示缓冲区或屏幕内容。
void OLED_Clear(void);
// 函数作用:在指定行列显示单个 ASCII 字符。
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
// 函数作用:从指定行列开始显示字符串。
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
// 函数作用:在指定位置显示无符号十进制数字。
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
// 函数作用:在指定位置显示带符号十进制数字。
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
// 函数作用:在指定位置显示十六进制数字。
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
// 函数作用:在指定位置显示二进制数字。
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
// 添加显示汉字的函数声明
// 函数作用:在指定行列显示字库中编号对应的汉字。
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t num);
#endif
+5
View File
@@ -7,6 +7,7 @@ char RxBuffer[RX_BUFFER_SIZE];
uint16_t RxCounter = 0;
// 初始化ESP8266所用的串口 (根据连接图.md: USART1 PA9/PA10)
/* 函数作用:初始化与 ESP8266 通信的 USART1 串口和接收中断。 */
void Serial_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
@@ -46,6 +47,7 @@ void Serial_Init(void)
}
// 发送字符串
/* 函数作用:通过 USART1 向 ESP8266 发送字符串。 */
void Serial_SendString(const char* String)
{
for (uint32_t i = 0; String[i] != '\0'; i++)
@@ -56,12 +58,14 @@ void Serial_SendString(const char* String)
}
// 获取接收缓冲区
/* 函数作用:返回 USART1 接收缓冲区指针。 */
char* Serial_GetRxBuffer(void)
{
return RxBuffer;
}
// 清空接收缓冲区
/* 函数作用:清空 USART1 接收缓冲区和接收计数。 */
void Serial_ClearRxBuffer(void)
{
memset(RxBuffer, 0, RX_BUFFER_SIZE);
@@ -69,6 +73,7 @@ void Serial_ClearRxBuffer(void)
}
// USART1中断服务函数 (ESP8266)
/* 函数作用:接收 ESP8266 串口返回数据并写入环形缓冲区。 */
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
+11 -5
View File
@@ -4,14 +4,20 @@
#include <stdio.h>
#include "stm32f10x.h"
void Serial_Init(void); // 初始化串口
void Serial_SendByte(uint8_t Byte); // 发送一个字节
void Serial_SendString(const char* String); // 发送字符串
char* Serial_GetRxBuffer(void); // 获取接收缓冲区指针
void Serial_ClearRxBuffer(void); // 清空接收缓冲区
// 函数作用:初始化调试/通信串口及其接收缓冲区。
void Serial_Init(void);
// 函数作用:通过串口发送一个字节。
void Serial_SendByte(uint8_t Byte);
// 函数作用:通过串口连续发送以空字符结尾的字符串。
void Serial_SendString(const char* String);
// 函数作用:返回串口接收缓冲区指针,供主循环解析接收到的数据。
char* Serial_GetRxBuffer(void);
// 函数作用:清空串口接收缓冲区并复位接收状态。
void Serial_ClearRxBuffer(void);
// 注意:Get_Time_ms() 函数与串口无关,应该在 `Delay.h` 中声明。
// 我们暂时将它与串口放一起,但更规范的做法是分离。
// 函数作用:返回系统启动以来经过的毫秒数。
uint32_t Get_Time_ms(void);
#endif
+18
View File
@@ -54,6 +54,7 @@ static const uint8_t Rcon[11] = {
#define getSBoxInvert(num) (rsbox[(num)])
// 密钥扩展
/* 函数作用:根据 AES-128 主密钥生成每轮加密使用的轮密钥。 */
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
@@ -102,6 +103,7 @@ static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
}
// AddRoundKey
/* 函数作用:将当前状态矩阵与指定轮密钥进行异或。 */
static void AddRoundKey(uint8_t round, uint8_t* state, const uint8_t* RoundKey)
{
uint8_t i, j;
@@ -113,6 +115,7 @@ static void AddRoundKey(uint8_t round, uint8_t* state, const uint8_t* RoundKey)
}
// SubBytes
/* 函数作用:使用 AES S 盒对状态矩阵进行字节替换。 */
static void SubBytes(uint8_t* state)
{
uint8_t i;
@@ -122,6 +125,7 @@ static void SubBytes(uint8_t* state)
}
// ShiftRows
/* 函数作用:执行 AES 加密流程中的行移位变换。 */
static void ShiftRows(uint8_t* state)
{
uint8_t temp;
@@ -150,12 +154,14 @@ static void ShiftRows(uint8_t* state)
}
// xtime (用于MixColumns)
/* 函数作用:在 AES 有限域中执行乘以 2 的运算。 */
static uint8_t xtime(uint8_t x)
{
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
// MixColumns
/* 函数作用:执行 AES 加密流程中的列混淆变换。 */
static void MixColumns(uint8_t* state)
{
uint8_t i;
@@ -184,6 +190,7 @@ static void MixColumns(uint8_t* state)
}
// Cipher - AES加密核心
/* 函数作用:对 16 字节状态块执行完整 AES 加密。 */
static void Cipher(uint8_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
@@ -205,6 +212,7 @@ static void Cipher(uint8_t* state, const uint8_t* RoundKey)
AddRoundKey(10, state, RoundKey);
}
/* 函数作用:在 AES 有限域中执行两个字节的乘法。 */
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
@@ -214,6 +222,7 @@ static uint8_t Multiply(uint8_t x, uint8_t y)
(((y >> 4) & 1) * xtime(xtime(xtime(xtime(x))))));
}
/* 函数作用:使用 AES 逆 S 盒对状态矩阵进行逆字节替换。 */
static void InvSubBytes(uint8_t* state)
{
uint8_t i;
@@ -222,6 +231,7 @@ static void InvSubBytes(uint8_t* state)
}
}
/* 函数作用:执行 AES 解密流程中的逆行移位变换。 */
static void InvShiftRows(uint8_t* state)
{
uint8_t temp;
@@ -245,6 +255,7 @@ static void InvShiftRows(uint8_t* state)
state[15] = temp;
}
/* 函数作用:执行 AES 解密流程中的逆列混淆变换。 */
static void InvMixColumns(uint8_t* state)
{
uint8_t i;
@@ -263,6 +274,7 @@ static void InvMixColumns(uint8_t* state)
}
}
/* 函数作用:对 16 字节状态块执行完整 AES 解密。 */
static void InvCipher(uint8_t* state, const uint8_t* RoundKey)
{
uint8_t round;
@@ -282,22 +294,26 @@ static void InvCipher(uint8_t* state, const uint8_t* RoundKey)
// ========== 公开API函数 ==========
/* 函数作用:初始化 AES 上下文并生成轮密钥。 */
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
/* 函数作用:初始化 AES 上下文并设置 CBC 模式 IV。 */
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
/* 函数作用:对单个 16 字节数据块执行 AES-ECB 加密。 */
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
Cipher(buf, ctx->RoundKey);
}
/* 函数作用:使用 AES-CBC 模式加密指定长度的数据缓冲区。 */
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
size_t i;
@@ -317,11 +333,13 @@ void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
}
}
/* 函数作用:对单个 16 字节数据块执行 AES-ECB 解密。 */
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
InvCipher(buf, ctx->RoundKey);
}
/* 函数作用:使用 AES-CBC 模式解密指定长度的数据缓冲区。 */
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
size_t i;
+6
View File
@@ -20,6 +20,7 @@ struct AES_ctx
* @param ctx AES上下文结构
* @param key 16字节密钥
*/
// 函数作用:根据 16 字节密钥初始化 AES 上下文并生成轮密钥。
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
/**
@@ -27,6 +28,7 @@ void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
* @param ctx AES上下文
* @param buf 要加密的数据(16字节块)
*/
// 函数作用:使用 AES-ECB 模式加密单个 16 字节数据块。
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
/**
@@ -34,6 +36,7 @@ void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
* @param ctx AES上下文
* @param buf 要解密的数据(16字节块)
*/
// 函数作用:使用 AES-ECB 模式解密单个 16 字节数据块。
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
/**
@@ -43,6 +46,7 @@ void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
* @param length 数据长度(必须是16的倍数)
* @param iv 初始化向量(16字节)
*/
// 函数作用:使用上下文中的 IV 按 AES-CBC 模式加密指定长度的缓冲区。
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
/**
@@ -52,6 +56,7 @@ void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
* @param length 数据长度(必须是16的倍数)
* @param iv 初始化向量(16字节)
*/
// 函数作用:使用上下文中的 IV 按 AES-CBC 模式解密指定长度的缓冲区。
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
/**
@@ -60,6 +65,7 @@ void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
* @param key 16字节密钥
* @param iv 16字节初始化向量
*/
// 函数作用:使用 16 字节密钥和 16 字节 IV 初始化 AES-CBC 上下文。
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
#endif // _AES_H_
+13
View File
@@ -34,6 +34,7 @@ static const uint8_t Rcon[11] = {
#define getSBoxValue(num) (sbox[(num)])
// 密钥扩展
/* 函数作用:根据 AES-128 主密钥生成每轮加密使用的轮密钥。 */
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
@@ -82,6 +83,7 @@ static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
}
// AddRoundKey
/* 函数作用:将当前状态矩阵与指定轮密钥进行异或。 */
static void AddRoundKey(uint8_t round, uint8_t* state, const uint8_t* RoundKey)
{
uint8_t i, j;
@@ -93,6 +95,7 @@ static void AddRoundKey(uint8_t round, uint8_t* state, const uint8_t* RoundKey)
}
// SubBytes
/* 函数作用:使用 AES S 盒对状态矩阵进行字节替换。 */
static void SubBytes(uint8_t* state)
{
uint8_t i;
@@ -102,6 +105,7 @@ static void SubBytes(uint8_t* state)
}
// ShiftRows
/* 函数作用:执行 AES 加密流程中的行移位变换。 */
static void ShiftRows(uint8_t* state)
{
uint8_t temp;
@@ -130,12 +134,14 @@ static void ShiftRows(uint8_t* state)
}
// xtime (用于MixColumns)
/* 函数作用:在 AES 有限域中执行乘以 2 的运算。 */
static uint8_t xtime(uint8_t x)
{
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
// MixColumns
/* 函数作用:执行 AES 加密流程中的列混淆变换。 */
static void MixColumns(uint8_t* state)
{
uint8_t i;
@@ -164,6 +170,7 @@ static void MixColumns(uint8_t* state)
}
// Cipher - AES加密核心
/* 函数作用:对 16 字节状态块执行完整 AES 加密。 */
static void Cipher(uint8_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
@@ -187,22 +194,26 @@ static void Cipher(uint8_t* state, const uint8_t* RoundKey)
// ========== 公开API函数 ==========
/* 函数作用:初始化 AES 上下文并生成轮密钥。 */
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
/* 函数作用:初始化 AES 上下文并设置 CBC 模式 IV。 */
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
/* 函数作用:对单个 16 字节数据块执行 AES-ECB 加密。 */
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
Cipher(buf, ctx->RoundKey);
}
/* 函数作用:使用 AES-CBC 模式加密指定长度的数据缓冲区。 */
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
size_t i;
@@ -223,12 +234,14 @@ void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
}
// STM32端不需要解密功能
/* 函数作用:对单个 16 字节数据块执行 AES-ECB 解密。 */
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
(void)ctx;
(void)buf;
}
/* 函数作用:使用 AES-CBC 模式解密指定长度的数据缓冲区。 */
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
(void)ctx;
+6
View File
@@ -20,6 +20,7 @@ struct AES_ctx
* @param ctx AES上下文结构
* @param key 16字节密钥
*/
// 函数作用:根据 16 字节密钥初始化 AES 上下文并生成轮密钥。
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
/**
@@ -28,6 +29,7 @@ void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
* @param key 16字节密钥
* @param iv 16字节初始化向量
*/
// 函数作用:使用 16 字节密钥和 16 字节 IV 初始化 AES-CBC 上下文。
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
/**
@@ -35,6 +37,7 @@ void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
* @param ctx AES上下文
* @param buf 要加密的数据(16字节块)
*/
// 函数作用:使用 AES-ECB 模式加密单个 16 字节数据块。
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
/**
@@ -42,6 +45,7 @@ void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
* @param ctx AES上下文
* @param buf 要解密的数据(16字节块)
*/
// 函数作用:使用 AES-ECB 模式解密单个 16 字节数据块。
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
/**
@@ -50,6 +54,7 @@ void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
* @param buf 要加密的数据
* @param length 数据长度(必须是16的倍数)
*/
// 函数作用:使用上下文中的 IV 按 AES-CBC 模式加密指定长度的缓冲区。
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
/**
@@ -58,6 +63,7 @@ void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
* @param buf 要解密的数据
* @param length 数据长度(必须是16的倍数)
*/
// 函数作用:使用上下文中的 IV 按 AES-CBC 模式解密指定长度的缓冲区。
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
#endif // _AES_H_
+3
View File
@@ -9,6 +9,7 @@
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/* 函数作用:将二进制数据编码为 Base64 字符串。 */
uint32_t base64_encode(const uint8_t* input, uint32_t input_length, char* output)
{
uint32_t i, j;
@@ -41,6 +42,7 @@ uint32_t base64_encode(const uint8_t* input, uint32_t input_length, char* output
return output_length;
}
/* 函数作用:把 Base64 字符转换为 6 位数值。 */
static uint8_t base64_char_value(char c)
{
if (c >= 'A' && c <= 'Z') return c - 'A';
@@ -51,6 +53,7 @@ static uint8_t base64_char_value(char c)
return 0;
}
/* 函数作用:将 Base64 字符串解码为二进制数据。 */
uint32_t base64_decode(const char* input, uint32_t input_length, uint8_t* output)
{
uint32_t i, j;
+2
View File
@@ -13,6 +13,7 @@
*
* 注意:output缓冲区大小至少需要 ((input_length + 2) / 3) * 4 + 1 字节
*/
// 函数作用:将二进制输入数据编码为 Base64 字符串并返回输出长度。
uint32_t base64_encode(const uint8_t* input, uint32_t input_length, char* output);
/**
@@ -22,6 +23,7 @@ uint32_t base64_encode(const uint8_t* input, uint32_t input_length, char* output
* @param output 输出缓冲区
* @return 解码后的数据长度
*/
// 函数作用:将 Base64 字符串解码为二进制数据并返回解码长度。
uint32_t base64_decode(const char* input, uint32_t input_length, uint8_t* output);
/**
+15
View File
@@ -25,11 +25,13 @@ extern const char* HTTP_API_TOKEN;
extern uint8_t g_server_connected;
/* --- 私有辅助函数 --- */
/* 函数作用:向 ESP8266 串口发送一条 AT 指令并追加回车换行。 */
static void ESP8266_SendCmd(const char* cmd) {
Serial_SendString(cmd);
Serial_SendString("\r\n");
}
/* 函数作用:在指定超时时间内等待 ESP8266 返回期望关键字。 */
static uint8_t ESP8266_WaitForAck(const char* ack, uint32_t timeout) {
uint32_t time_start = Get_Time_ms();
uint32_t time_elapsed;
@@ -57,11 +59,13 @@ static uint8_t ESP8266_WaitForAck(const char* ack, uint32_t timeout) {
return ESP8266_FAIL;
}
/* 函数作用:判断指定年份是否为闰年。 */
static uint8_t IsLeapYear(uint16_t year)
{
return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 1 : 0;
}
/* 函数作用:返回指定年份月份的天数。 */
static uint8_t DaysInMonth(uint16_t year, uint8_t month)
{
static const uint8_t days_table[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
@@ -69,6 +73,7 @@ static uint8_t DaysInMonth(uint16_t year, uint8_t month)
return days_table[month - 1];
}
/* 函数作用:把 Unix 时间戳转换为北京时间结构体。 */
static void EpochToDateTimeUTC8(uint32_t epoch_seconds, ESP8266_DateTime* datetime)
{
// 转为北京时间(UTC+8)
@@ -99,6 +104,7 @@ static void EpochToDateTimeUTC8(uint32_t epoch_seconds, ESP8266_DateTime* dateti
datetime->day = (uint8_t)(days + 1U);
}
/* 函数作用:从 HTTP 响应中复制正文,并在响应加密时完成解密。 */
static uint8_t ESP8266_CopyDecryptedBody(char* response,
char* body_start,
uint16_t body_len,
@@ -137,6 +143,7 @@ static uint8_t ESP8266_CopyDecryptedBody(char* response,
/* --- 公共接口函数实现 --- */
/* 函数作用:初始化 ESP8266 AT 模式、WiFi 模式和 TCP 单连接模式。 */
uint8_t ESP8266_Setup(void)
{
ESP8266_SendCmd("+++");
@@ -159,6 +166,7 @@ uint8_t ESP8266_Setup(void)
return ESP8266_SUCCESS;
}
/* 函数作用:连接指定 SSID 和密码的 WiFi 热点。 */
uint8_t ESP8266_ConnectWiFi(const char* ssid, const char* password)
{
char cmd_buffer[128];
@@ -172,6 +180,7 @@ uint8_t ESP8266_ConnectWiFi(const char* ssid, const char* password)
/**
* @brief 连接到HTTP服务器(新增)
*/
/* 函数作用:与服务端建立 TCP 连接,为后续 HTTP 请求做准备。 */
uint8_t ESP8266_ConnectHTTPServer(const char* ip, const char* port)
{
char cmd_buffer[100];
@@ -215,6 +224,7 @@ uint8_t ESP8266_ConnectHTTPServer(const char* ip, const char* port)
/**
* @brief 发送加密数据(HTTP POST请求)
*/
/* 函数作用:通过 HTTP POST 上传 AES 加密后的考勤 JSON 数据。 */
uint8_t ESP8266_SendEncryptedData(const char* encrypted_data)
{
char http_request[1024];
@@ -353,6 +363,7 @@ uint8_t ESP8266_SendEncryptedData(const char* encrypted_data)
return ESP8266_FAIL;
}
/* 函数作用:从服务端时间接口获取 Unix 时间戳并转换为本地时间。 */
uint8_t ESP8266_GetNetworkTime(ESP8266_DateTime* datetime)
{
char response_buffer[256];
@@ -405,6 +416,7 @@ uint8_t ESP8266_GetNetworkTime(ESP8266_DateTime* datetime)
return ESP8266_SUCCESS;
}
/* 函数作用:查询 ESP8266 当前 TCP/WiFi 连接状态。 */
uint8_t ESP8266_CheckConnection(void)
{
char* response;
@@ -442,6 +454,7 @@ uint8_t ESP8266_CheckConnection(void)
/**
* @brief 发送HTTP GET请求并接收响应
*/
/* 函数作用:发送带 Token 的 HTTP GET 请求,并返回解密后的响应正文。 */
uint8_t ESP8266_HttpGet(const char* path, char* response_buffer, uint16_t buffer_size)
{
char http_request[320];
@@ -592,6 +605,7 @@ uint8_t ESP8266_HttpGet(const char* path, char* response_buffer, uint16_t buffer
* @param slot_end 时段结束时间(格式"HH:MM")
* @retval 1 表示已签到(重复), 0 表示未签到
*/
/* 函数作用:向服务器查询指定学生在给定时段内是否重复签到。 */
uint8_t ESP8266_CheckDuplicate(const char* student_id, const char* slot_start, const char* slot_end)
{
char path[128];
@@ -639,6 +653,7 @@ uint8_t ESP8266_CheckDuplicate(const char* student_id, const char* slot_start, c
* @param buffer_size 缓冲区大小
* @retval 成功下载的学号数量
*/
/* 函数作用:下载指定时间段内今日已签到学生列表,用于本地查重缓存。 */
uint8_t ESP8266_DownloadTodayRecords(const char* slot_start, const char* slot_end, char* student_ids, uint16_t buffer_size)
{
char path[128];
+9
View File
@@ -25,6 +25,7 @@ typedef struct {
* @brief 对ESP8266模块进行基础设置
* @retval 1 表示成功, 0 表示失败
*/
// 函数作用:初始化 ESP8266 模块并配置基础 AT 工作模式。
uint8_t ESP8266_Setup(void);
/**
@@ -33,6 +34,7 @@ uint8_t ESP8266_Setup(void);
* @param password WiFi的密码
* @retval 1 表示成功, 0 表示失败
*/
// 函数作用:控制 ESP8266 连接指定 WiFi 网络。
uint8_t ESP8266_ConnectWiFi(const char* ssid, const char* password);
/**
@@ -41,6 +43,7 @@ uint8_t ESP8266_ConnectWiFi(const char* ssid, const char* password);
* @param port 服务器的端口号
* @retval 1 表示成功, 0 表示失败
*/
// 函数作用:控制 ESP8266 与指定 HTTP 服务器建立 TCP 连接。
uint8_t ESP8266_ConnectHTTPServer(const char* ip, const char* port);
/**
@@ -48,12 +51,14 @@ uint8_t ESP8266_ConnectHTTPServer(const char* ip, const char* port);
* @param encrypted_data 加密后的Base64数据
* @retval 1 表示成功, 0 表示失败
*/
// 函数作用:通过 ESP8266 发送加密后的考勤 HTTP POST 数据。
uint8_t ESP8266_SendEncryptedData(const char* encrypted_data);
/**
* @brief 检查TCP连接是否正常
* @retval 1 表示已连接, 0 表示未连接
*/
// 函数作用:检查 ESP8266 当前 TCP/网络连接是否仍然可用。
uint8_t ESP8266_CheckConnection(void);
/**
@@ -61,6 +66,7 @@ uint8_t ESP8266_CheckConnection(void);
* @param datetime 时间结构体指针,用于存储获取的时间
* @retval 1 表示成功, 0 表示失败
*/
// 函数作用:从网络时间服务或服务端接口获取当前日期时间。
uint8_t ESP8266_GetNetworkTime(ESP8266_DateTime* datetime);
/**
@@ -70,6 +76,7 @@ uint8_t ESP8266_GetNetworkTime(ESP8266_DateTime* datetime);
* @param buffer_size 缓冲区大小
* @retval 1 表示成功, 0 表示失败
*/
// 函数作用:通过 ESP8266 发起 HTTP GET 请求并把响应写入缓冲区。
uint8_t ESP8266_HttpGet(const char* path, char* response_buffer, uint16_t buffer_size);
/**
@@ -79,6 +86,7 @@ uint8_t ESP8266_HttpGet(const char* path, char* response_buffer, uint16_t buffer
* @param slot_end 时段结束时间(格式"HH:MM")
* @retval 1 表示已签到(重复), 0 表示未签到
*/
// 函数作用:调用服务端查重接口,判断学生在指定时段是否已签到。
uint8_t ESP8266_CheckDuplicate(const char* student_id, const char* slot_start, const char* slot_end);
/**
@@ -89,6 +97,7 @@ uint8_t ESP8266_CheckDuplicate(const char* student_id, const char* slot_start, c
* @param buffer_size 缓冲区大小
* @retval 成功下载的学号数量
*/
// 函数作用:下载指定时段内今日已签到学生列表,用于本地去重缓存恢复。
uint8_t ESP8266_DownloadTodayRecords(const char* slot_start, const char* slot_end, char* student_ids, uint16_t buffer_size);
#endif