初始化毕业设计项目
This commit is contained in:
@@ -0,0 +1,624 @@
|
||||
/*
|
||||
* ESP8266 WiFi模块驱动(支持HTTP加密传输)
|
||||
* 版本:v2.0 - 加密方案C
|
||||
* 修改:
|
||||
* 1. 添加HTTP POST请求支持
|
||||
* 2. 添加加密数据传输
|
||||
* 3. 保留原有TCP兼容接口
|
||||
*/
|
||||
|
||||
#include "esp8266.h"
|
||||
#include "Serial.h"
|
||||
#include "Delay.h"
|
||||
#include "Debug.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h> // ← 添加atoi函数声明
|
||||
|
||||
// 外部配置(在main.c中定义)
|
||||
extern const char* WIFI_SSID;
|
||||
extern const char* WIFI_PASSWORD;
|
||||
extern const char* HTTP_SERVER_IP;
|
||||
extern const char* HTTP_SERVER_PORT;
|
||||
extern const char* HTTP_API_TOKEN;
|
||||
extern uint8_t g_server_connected;
|
||||
|
||||
/* --- 私有辅助函数 --- */
|
||||
static void ESP8266_SendCmd(const char* cmd) {
|
||||
Serial_SendString(cmd);
|
||||
Serial_SendString("\r\n");
|
||||
}
|
||||
|
||||
static uint8_t ESP8266_WaitForAck(const char* ack, uint32_t timeout) {
|
||||
uint32_t time_start = Get_Time_ms();
|
||||
uint32_t time_elapsed;
|
||||
Serial_ClearRxBuffer();
|
||||
|
||||
DEBUG_PRINT("[ESP8266] Waiting for: %s (timeout: %dms)\r\n", ack, timeout);
|
||||
|
||||
while(1) {
|
||||
time_elapsed = Get_Time_ms() - time_start;
|
||||
if (time_elapsed > timeout) {
|
||||
DEBUG_PRINT("[ESP8266] Timeout after %dms\r\n", time_elapsed);
|
||||
break;
|
||||
}
|
||||
|
||||
char* response = Serial_GetRxBuffer();
|
||||
if (response && strstr(response, ack) != NULL) {
|
||||
DEBUG_OK("[ESP8266] Received expected response");
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
|
||||
Delay_ms(10);
|
||||
}
|
||||
|
||||
DEBUG_PRINT("[ESP8266] Failed! Response: %s\r\n", Serial_GetRxBuffer());
|
||||
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};
|
||||
if (month == 2 && IsLeapYear(year)) return 29;
|
||||
return days_table[month - 1];
|
||||
}
|
||||
|
||||
static void EpochToDateTimeUTC8(uint32_t epoch_seconds, ESP8266_DateTime* datetime)
|
||||
{
|
||||
// 转为北京时间(UTC+8)
|
||||
uint32_t total = epoch_seconds + 8U * 3600U;
|
||||
uint32_t days = total / 86400U;
|
||||
uint32_t sec_of_day = total % 86400U;
|
||||
|
||||
datetime->hour = sec_of_day / 3600U;
|
||||
datetime->minute = (sec_of_day % 3600U) / 60U;
|
||||
datetime->second = sec_of_day % 60U;
|
||||
|
||||
datetime->year = 1970;
|
||||
while (1) {
|
||||
uint16_t ydays = IsLeapYear(datetime->year) ? 366 : 365;
|
||||
if (days < ydays) break;
|
||||
days -= ydays;
|
||||
datetime->year++;
|
||||
}
|
||||
|
||||
datetime->month = 1;
|
||||
while (1) {
|
||||
uint8_t mdays = DaysInMonth(datetime->year, datetime->month);
|
||||
if (days < mdays) break;
|
||||
days -= mdays;
|
||||
datetime->month++;
|
||||
}
|
||||
|
||||
datetime->day = (uint8_t)(days + 1U);
|
||||
}
|
||||
|
||||
/* --- 公共接口函数实现 --- */
|
||||
|
||||
uint8_t ESP8266_Setup(void)
|
||||
{
|
||||
ESP8266_SendCmd("+++");
|
||||
Delay_ms(500);
|
||||
|
||||
ESP8266_SendCmd("AT");
|
||||
if (!ESP8266_WaitForAck("OK", 1000)) return ESP8266_FAIL;
|
||||
|
||||
ESP8266_SendCmd("AT+CWMODE=1");
|
||||
if (!ESP8266_WaitForAck("OK", 2000)) return ESP8266_FAIL;
|
||||
|
||||
// 先关闭可能存在的连接(避免 "link is builded" 错误)
|
||||
ESP8266_SendCmd("AT+CIPCLOSE");
|
||||
Delay_ms(500); // 等待连接关闭
|
||||
|
||||
// 设置单连接模式
|
||||
ESP8266_SendCmd("AT+CIPMUX=0");
|
||||
ESP8266_WaitForAck("OK", 1000);
|
||||
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t ESP8266_ConnectWiFi(const char* ssid, const char* password)
|
||||
{
|
||||
char cmd_buffer[128];
|
||||
|
||||
sprintf(cmd_buffer, "AT+CWJAP=\"%s\",\"%s\"", ssid, password);
|
||||
ESP8266_SendCmd(cmd_buffer);
|
||||
|
||||
return ESP8266_WaitForAck("WIFI GOT IP", 15000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 连接到HTTP服务器(新增)
|
||||
*/
|
||||
uint8_t ESP8266_ConnectHTTPServer(const char* ip, const char* port)
|
||||
{
|
||||
char cmd_buffer[100];
|
||||
uint32_t start_time;
|
||||
|
||||
DEBUG_PRINT("[ESP8266] Connecting to HTTP server %s:%s\r\n", ip, port);
|
||||
|
||||
// 先断开之前的连接
|
||||
ESP8266_SendCmd("AT+CIPCLOSE");
|
||||
Delay_ms(500);
|
||||
|
||||
// 建立TCP连接
|
||||
sprintf(cmd_buffer, "AT+CIPSTART=\"TCP\",\"%s\",%s", ip, port);
|
||||
Serial_ClearRxBuffer();
|
||||
ESP8266_SendCmd(cmd_buffer);
|
||||
|
||||
// 等待连接成功
|
||||
start_time = Get_Time_ms();
|
||||
|
||||
while(Get_Time_ms() - start_time < 10000) {
|
||||
char* response = Serial_GetRxBuffer();
|
||||
|
||||
if (strstr(response, "CONNECT") != NULL || strstr(response, "ALREADY") != NULL) {
|
||||
DEBUG_OK("[ESP8266] HTTP server connected");
|
||||
Delay_ms(500);
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
|
||||
if (strstr(response, "ERROR") != NULL || strstr(response, "CLOSED") != NULL) {
|
||||
DEBUG_ERROR("[ESP8266] Connection failed");
|
||||
break;
|
||||
}
|
||||
|
||||
Delay_ms(100);
|
||||
}
|
||||
|
||||
DEBUG_ERROR("[ESP8266] Connection timeout");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送加密数据(HTTP POST请求)
|
||||
*/
|
||||
uint8_t ESP8266_SendEncryptedData(const char* encrypted_data)
|
||||
{
|
||||
char http_request[1024];
|
||||
char cmd_buffer[32];
|
||||
uint32_t content_length = strlen(encrypted_data);
|
||||
|
||||
if (ESP8266_CheckConnection() != ESP8266_SUCCESS) {
|
||||
DEBUG_INFO("[ESP8266] HTTP connection not ready, reconnecting...");
|
||||
if (ESP8266_ConnectHTTPServer(HTTP_SERVER_IP, HTTP_SERVER_PORT) != ESP8266_SUCCESS) {
|
||||
DEBUG_ERROR("[ESP8266] Reconnect failed before POST");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
g_server_connected = 1;
|
||||
}
|
||||
|
||||
// 构建HTTP POST请求
|
||||
sprintf(http_request,
|
||||
"POST /api/v1/attendance/checkins HTTP/1.1\r\n"
|
||||
"Host: %s:%s\r\n"
|
||||
"Content-Type: application/octet-stream\r\n"
|
||||
"X-Api-Token: %s\r\n"
|
||||
"X-Encrypted: AES128-CBC\r\n"
|
||||
"Content-Length: %u\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"%s",
|
||||
HTTP_SERVER_IP,
|
||||
HTTP_SERVER_PORT,
|
||||
HTTP_API_TOKEN,
|
||||
content_length,
|
||||
encrypted_data
|
||||
);
|
||||
|
||||
uint32_t request_len = strlen(http_request);
|
||||
|
||||
DEBUG_PRINT("[ESP8266] Sending HTTP POST (%u bytes)\r\n", request_len);
|
||||
|
||||
// 发送数据长度命令
|
||||
sprintf(cmd_buffer, "AT+CIPSEND=%u", request_len);
|
||||
Serial_ClearRxBuffer();
|
||||
ESP8266_SendCmd(cmd_buffer);
|
||||
|
||||
// 等待 ">" 提示符
|
||||
uint32_t start_time = Get_Time_ms();
|
||||
|
||||
while(Get_Time_ms() - start_time < 2000) {
|
||||
if (strstr(Serial_GetRxBuffer(), ">") != NULL) {
|
||||
DEBUG_INFO("[ESP8266] Ready to send");
|
||||
|
||||
// 发送HTTP请求
|
||||
Serial_SendString(http_request);
|
||||
Delay_ms(100);
|
||||
|
||||
// 不等待SEND OK,直接返回成功
|
||||
DEBUG_OK("[ESP8266] Data sent");
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
Delay_ms(10);
|
||||
}
|
||||
|
||||
DEBUG_ERROR("[ESP8266] No '>' prompt");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
uint8_t ESP8266_GetNetworkTime(ESP8266_DateTime* datetime)
|
||||
{
|
||||
char response_buffer[256];
|
||||
char* epoch_ptr;
|
||||
uint32_t epoch_seconds = 0;
|
||||
uint8_t digit_count = 0;
|
||||
|
||||
DEBUG_INFO("[ESP8266] Getting network time...");
|
||||
|
||||
// 优先直接请求业务服务器时间接口
|
||||
// 期望响应: {"epoch":1700000000,"server_time":"2026-02-12 12:34:56"}
|
||||
if (ESP8266_HttpGet("/api/v1/system/time", response_buffer, sizeof(response_buffer)) != ESP8266_SUCCESS) {
|
||||
DEBUG_ERROR("[ESP8266] /api/v1/system/time request failed, try reconnect...");
|
||||
if (ESP8266_ConnectHTTPServer(HTTP_SERVER_IP, HTTP_SERVER_PORT) != ESP8266_SUCCESS) {
|
||||
DEBUG_ERROR("[ESP8266] Reconnect failed when getting time");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
g_server_connected = 1;
|
||||
if (ESP8266_HttpGet("/api/v1/system/time", response_buffer, sizeof(response_buffer)) != ESP8266_SUCCESS) {
|
||||
DEBUG_ERROR("[ESP8266] /api/v1/system/time request failed after reconnect");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
epoch_ptr = strstr(response_buffer, "\"epoch\":");
|
||||
if (epoch_ptr == NULL) {
|
||||
DEBUG_ERROR("[ESP8266] /api/v1/system/time response missing epoch");
|
||||
DEBUG_PRINT("[ESP8266] Response: %s\r\n", response_buffer);
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
epoch_ptr += 8; // 跳过 "epoch":
|
||||
while (*epoch_ptr == ' ' || *epoch_ptr == '\"') epoch_ptr++;
|
||||
while (*epoch_ptr >= '0' && *epoch_ptr <= '9' && digit_count < 10) {
|
||||
epoch_seconds = epoch_seconds * 10U + (uint32_t)(*epoch_ptr - '0');
|
||||
epoch_ptr++;
|
||||
digit_count++;
|
||||
}
|
||||
if (digit_count < 9) {
|
||||
DEBUG_ERROR("[ESP8266] Invalid epoch value in /api/v1/system/time response");
|
||||
DEBUG_PRINT("[ESP8266] Response: %s\r\n", response_buffer);
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
EpochToDateTimeUTC8(epoch_seconds, datetime);
|
||||
DEBUG_PRINT("[ESP8266] Time(from server): %04d-%02d-%02d %02d:%02d:%02d\r\n",
|
||||
datetime->year, datetime->month, datetime->day,
|
||||
datetime->hour, datetime->minute, datetime->second);
|
||||
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t ESP8266_CheckConnection(void)
|
||||
{
|
||||
char* response;
|
||||
|
||||
Serial_ClearRxBuffer();
|
||||
ESP8266_SendCmd("AT+CIPSTATUS");
|
||||
Delay_ms(500);
|
||||
|
||||
response = Serial_GetRxBuffer();
|
||||
|
||||
if (strstr(response, "STATUS:3") != NULL) {
|
||||
DEBUG_INFO("[ESP8266] Connection OK (STATUS:3)");
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
|
||||
if (strstr(response, "STATUS:4") != NULL) {
|
||||
DEBUG_ERROR("[ESP8266] Connection closed (STATUS:4)");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
if (strstr(response, "STATUS:5") != NULL) {
|
||||
DEBUG_ERROR("[ESP8266] WiFi disconnected (STATUS:5)");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
if (strstr(response, "STATUS:2") != NULL) {
|
||||
DEBUG_ERROR("[ESP8266] Not connected to server (STATUS:2)");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
DEBUG_ERROR("[ESP8266] Unknown connection status");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送HTTP GET请求并接收响应
|
||||
*/
|
||||
uint8_t ESP8266_HttpGet(const char* path, char* response_buffer, uint16_t buffer_size)
|
||||
{
|
||||
char http_request[256];
|
||||
char cmd_buffer[32];
|
||||
uint8_t request_sent = 0;
|
||||
|
||||
DEBUG_PRINT("[ESP8266] HTTP GET: %s\r\n", path);
|
||||
|
||||
if (response_buffer != NULL && buffer_size > 0) {
|
||||
response_buffer[0] = '\0';
|
||||
}
|
||||
|
||||
if (ESP8266_CheckConnection() != ESP8266_SUCCESS) {
|
||||
DEBUG_INFO("[ESP8266] HTTP connection not ready, reconnecting...");
|
||||
if (ESP8266_ConnectHTTPServer(HTTP_SERVER_IP, HTTP_SERVER_PORT) != ESP8266_SUCCESS) {
|
||||
DEBUG_ERROR("[ESP8266] Reconnect failed before GET");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
g_server_connected = 1;
|
||||
}
|
||||
|
||||
// 构建HTTP GET请求
|
||||
sprintf(http_request,
|
||||
"GET %s HTTP/1.1\r\n"
|
||||
"Host: %s:%s\r\n"
|
||||
"X-Api-Token: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
path,
|
||||
HTTP_SERVER_IP,
|
||||
HTTP_SERVER_PORT,
|
||||
HTTP_API_TOKEN
|
||||
);
|
||||
|
||||
uint32_t request_len = strlen(http_request);
|
||||
|
||||
// 发送数据长度命令
|
||||
sprintf(cmd_buffer, "AT+CIPSEND=%u", request_len);
|
||||
Serial_ClearRxBuffer();
|
||||
ESP8266_SendCmd(cmd_buffer);
|
||||
|
||||
// 等待 ">" 提示符
|
||||
uint32_t start_time = Get_Time_ms();
|
||||
|
||||
while(Get_Time_ms() - start_time < 2000) {
|
||||
if (strstr(Serial_GetRxBuffer(), ">") != NULL) {
|
||||
DEBUG_INFO("[ESP8266] Ready to send GET request");
|
||||
|
||||
// 发送HTTP GET请求
|
||||
Serial_SendString(http_request);
|
||||
request_sent = 1;
|
||||
break;
|
||||
}
|
||||
Delay_ms(10);
|
||||
}
|
||||
|
||||
if (!request_sent) {
|
||||
DEBUG_ERROR("[ESP8266] No '>' prompt for GET request");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
// 等待接收完整响应(最多8秒)
|
||||
start_time = Get_Time_ms();
|
||||
uint8_t found_header = 0;
|
||||
uint32_t content_length = 0;
|
||||
|
||||
while(Get_Time_ms() - start_time < 8000) {
|
||||
char* response = Serial_GetRxBuffer();
|
||||
|
||||
// 1. 处理+IPD前缀(ESP8266格式:+IPD,长度:数据)
|
||||
char* ipd_marker = strstr(response, "+IPD,");
|
||||
if (ipd_marker != NULL) {
|
||||
// 跳过 "+IPD," 找到实际数据
|
||||
char* data_start = strchr(ipd_marker, ':');
|
||||
if (data_start != NULL) {
|
||||
data_start++; // 跳过 ':'
|
||||
response = data_start; // 从这里开始才是HTTP响应
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 查找HTTP响应体(JSON数据在\r\n\r\n之后)
|
||||
char* body_start = strstr(response, "\r\n\r\n");
|
||||
if (body_start != NULL) {
|
||||
body_start += 4; // 跳过 \r\n\r\n
|
||||
|
||||
// 3. 如果还没提取Content-Length,尝试提取
|
||||
if (!found_header) {
|
||||
char* cl_str = strstr(response, "Content-Length:");
|
||||
if (cl_str != NULL) {
|
||||
cl_str += 15; // 跳过 "Content-Length:"
|
||||
while (*cl_str == ' ') cl_str++;
|
||||
content_length = atoi(cl_str);
|
||||
DEBUG_PRINT("[ESP8266] Content-Length: %u\r\n", content_length);
|
||||
found_header = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 检查是否接收完整(比较实际长度和Content-Length)
|
||||
uint16_t body_len = strlen(body_start);
|
||||
|
||||
// 移除末尾的 "CLOSED" "OK" 等ESP8266附加信息
|
||||
char* closed_marker = strstr(body_start, "\r\nCLOSED");
|
||||
if (closed_marker != NULL) {
|
||||
*closed_marker = '\0';
|
||||
body_len = strlen(body_start);
|
||||
}
|
||||
// 同时处理没有\r\n的情况
|
||||
closed_marker = strstr(body_start, "CLOSED");
|
||||
if (closed_marker != NULL) {
|
||||
*closed_marker = '\0';
|
||||
body_len = strlen(body_start);
|
||||
}
|
||||
// 移除末尾的OK
|
||||
char* ok_marker = strstr(body_start, "\r\nOK");
|
||||
if (ok_marker != NULL) {
|
||||
*ok_marker = '\0';
|
||||
body_len = strlen(body_start);
|
||||
}
|
||||
|
||||
// 5. 如果数据完整或超时,复制到缓冲区
|
||||
if ((found_header && body_len >= content_length) ||
|
||||
(Get_Time_ms() - start_time > 3000 && body_len > 0)) {
|
||||
if (body_len >= buffer_size) {
|
||||
body_len = buffer_size - 1;
|
||||
}
|
||||
|
||||
strncpy(response_buffer, body_start, body_len);
|
||||
response_buffer[body_len] = '\0';
|
||||
|
||||
DEBUG_PRINT("[ESP8266] Received response (%d bytes)\r\n", strlen(response_buffer));
|
||||
DEBUG_PRINT("[ESP8266] Response: %s\r\n", response_buffer);
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
Delay_ms(100);
|
||||
}
|
||||
|
||||
DEBUG_ERROR("[ESP8266] No valid HTTP response or timeout");
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
|
||||
// ========== 兼容旧版本的函数 ==========
|
||||
|
||||
/**
|
||||
* @brief 查询学生在指定时段是否已签到(服务器端查重)
|
||||
* @param student_id 学号
|
||||
* @param slot_start 时段开始时间(格式"HH:MM")
|
||||
* @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];
|
||||
char response_buffer[256];
|
||||
|
||||
// 构建查询路径
|
||||
sprintf(path, "/api/v1/attendance/duplicates?student_id=%s&slot_start=%s&slot_end=%s",
|
||||
student_id, slot_start, slot_end);
|
||||
|
||||
DEBUG_PRINT("[ESP8266] Checking duplicate: %s\r\n", path);
|
||||
|
||||
// 先检查连接状态,如果断开则重连
|
||||
if (ESP8266_CheckConnection() != ESP8266_SUCCESS) {
|
||||
DEBUG_INFO("[ESP8266] Connection lost, reconnecting...");
|
||||
extern const char* HTTP_SERVER_IP;
|
||||
extern const char* HTTP_SERVER_PORT;
|
||||
if (ESP8266_ConnectHTTPServer(HTTP_SERVER_IP, HTTP_SERVER_PORT) != ESP8266_SUCCESS) {
|
||||
DEBUG_ERROR("[ESP8266] Reconnect failed");
|
||||
return 0; // 连接失败,默认允许签到
|
||||
}
|
||||
Delay_ms(200); // 等待连接稳定
|
||||
}
|
||||
|
||||
// 发送HTTP GET请求
|
||||
if (ESP8266_HttpGet(path, response_buffer, sizeof(response_buffer)) == ESP8266_SUCCESS) {
|
||||
// 解析响应,查找 "duplicate":true
|
||||
if (strstr(response_buffer, "\"duplicate\":true") != NULL) {
|
||||
DEBUG_INFO("[ESP8266] Duplicate found on server!");
|
||||
return 1; // 已签到
|
||||
} else {
|
||||
DEBUG_INFO("[ESP8266] No duplicate found");
|
||||
return 0; // 未签到
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_ERROR("[ESP8266] Failed to check duplicate, assuming not duplicate");
|
||||
return 0; // 查询失败时默认为未签到,允许本地签到
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 下载今日已签到学生列表(开机时调用)
|
||||
* @param slot_start 时段开始时间(格式"HH:MM")
|
||||
* @param slot_end 时段结束时间(格式"HH:MM")
|
||||
* @param student_ids 输出:学号数组(逗号分隔的字符串)
|
||||
* @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];
|
||||
char response_buffer[512];
|
||||
|
||||
// 构建查询路径
|
||||
sprintf(path, "/api/v1/attendance/today?slot_start=%s&slot_end=%s", slot_start, slot_end);
|
||||
|
||||
DEBUG_PRINT("[ESP8266] Downloading today's records: %s\r\n", path);
|
||||
|
||||
// 先检查连接状态,如果断开则重连
|
||||
if (ESP8266_CheckConnection() != ESP8266_SUCCESS) {
|
||||
DEBUG_INFO("[ESP8266] Connection lost, reconnecting before download...");
|
||||
extern const char* HTTP_SERVER_IP;
|
||||
extern const char* HTTP_SERVER_PORT;
|
||||
if (ESP8266_ConnectHTTPServer(HTTP_SERVER_IP, HTTP_SERVER_PORT) != ESP8266_SUCCESS) {
|
||||
DEBUG_ERROR("[ESP8266] Reconnect failed");
|
||||
return 0;
|
||||
}
|
||||
Delay_ms(300); // 等待连接稳定
|
||||
}
|
||||
|
||||
// 发送HTTP GET请求
|
||||
if (ESP8266_HttpGet(path, response_buffer, sizeof(response_buffer)) != ESP8266_SUCCESS) {
|
||||
DEBUG_ERROR("[ESP8266] Failed to download today's records");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 解析响应,提取学号列表
|
||||
// 格式: {"date":"2026-02-07","records":["1","2","3"]}
|
||||
char* records_start = strstr(response_buffer, "\"records\":[");
|
||||
if (records_start == NULL) {
|
||||
DEBUG_ERROR("[ESP8266] Invalid response format");
|
||||
return 0;
|
||||
}
|
||||
|
||||
records_start += 11; // 跳过 "records":[
|
||||
char* records_end = strchr(records_start, ']');
|
||||
if (records_end == NULL) {
|
||||
DEBUG_ERROR("[ESP8266] Invalid response format");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 复制学号列表到输出缓冲区
|
||||
uint16_t len = records_end - records_start;
|
||||
if (len >= buffer_size) len = buffer_size - 1;
|
||||
|
||||
strncpy(student_ids, records_start, len);
|
||||
student_ids[len] = '\0';
|
||||
|
||||
// 统计学号数量(空列表返回0)
|
||||
uint8_t count = 0;
|
||||
if (len > 2) { // 至少有一个学号 "x"
|
||||
count = 1;
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
if (student_ids[i] == ',') count++;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_PRINT("[ESP8266] Downloaded %d records: %s\r\n", count, student_ids);
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t ESP8266_ConnectTCPServer(const char* ip, const char* port)
|
||||
{
|
||||
// 重定向到HTTP版本
|
||||
return ESP8266_ConnectHTTPServer(ip, port);
|
||||
}
|
||||
|
||||
uint8_t ESP8266_SendData(char* Data)
|
||||
{
|
||||
char cmd_buffer[32];
|
||||
uint16_t data_len = strlen(Data);
|
||||
|
||||
DEBUG_PRINT("[ESP8266] Sending %d bytes (legacy mode)\r\n", data_len);
|
||||
|
||||
sprintf(cmd_buffer, "AT+CIPSEND=%d", data_len);
|
||||
ESP8266_SendCmd(cmd_buffer);
|
||||
|
||||
uint32_t start_time = Get_Time_ms();
|
||||
Serial_ClearRxBuffer();
|
||||
|
||||
while(Get_Time_ms() - start_time < 1000) {
|
||||
if (strstr(Serial_GetRxBuffer(), ">") != NULL) {
|
||||
Serial_SendString(Data);
|
||||
Delay_ms(50);
|
||||
return ESP8266_SUCCESS;
|
||||
}
|
||||
Delay_ms(10);
|
||||
}
|
||||
|
||||
return ESP8266_FAIL;
|
||||
}
|
||||
Reference in New Issue
Block a user