feat(BSP): 添加WiFi模块MQTT连接优化和SNTP配置改进

- 添加SNTP配置标志位,确保只在首次连接时配置SNTP,避免重复重启模块
- 实现MQTT断线重连机制,支持持续运行和自动重连
- 增加SNTP时间查询频率控制,避免频繁查询导致性能问题
- 添加连接状态检测和心跳机制,超时自动重连
- 优化LCD时间显示界面,分别显示时分和年月日信息
- 调整LCD页面刷新策略,时间页面30秒刷新一次以节省资源
```
This commit is contained in:
2026-02-23 17:51:03 +08:00
parent 9cadad138e
commit f5077adbe7
2 changed files with 123 additions and 58 deletions

View File

@@ -24,6 +24,13 @@ SNTP_Time_t sntp_time = {0};
static volatile uint8_t mqtt_connected = 0;
static MQTT_State_t mqtt_state = MQTT_STATE_IDLE;
/* SNTP配置标志只配置一次避免重复重启模块 */
static volatile uint8_t sntp_configured = 0;
/* SNTP时间更新控制避免频繁查询 */
static volatile uint32_t sntp_last_query_tick = 0;
static const uint32_t SNTP_QUERY_INTERVAL = 5000; // 5秒查询一次
static void Generate_Random_ClientID(char *out_id, uint16_t max_len) {
@@ -227,6 +234,20 @@ uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
elog_i(TAG, "开始MQTT连接流程");
// 只在第一次配置SNTP避免重复重启模块
if (!sntp_configured) {
elog_i(TAG, "首次配置SNTP服务器...");
if (!WIFI_Enable_SNTP()) {
elog_e(TAG, "SNTP设置失败");
return 0;
}
sntp_configured = 1;
// SNTP配置后会重启模块等待模块恢复
elog_i(TAG, "等待模块重启后恢复...");
osDelay(3000);
}
elog_i(TAG, "断开 MQTT 连接(如果已连接)");
if (!WIFI_CheckAck("AT+MQTTCLEAN\r\n", "OK", 2000)) {
@@ -240,13 +261,6 @@ uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
/* 稍微延时,避免状态机还没稳定 */
osDelay(500);
// 设置SNTP服务器设置成功会自动重启模块
elog_i(TAG,"设置SNTP服务器");
if (!WIFI_Enable_SNTP()) {
elog_e(TAG, "SNTP设置失败");
return 0;
}
elog_i(TAG, "1. 检查模块响应...");
if (!WIFI_CheckAck("AT\r\n", "OK", 1000)) {
elog_e(TAG, "模块无响应");
@@ -449,6 +463,8 @@ uint8_t WIFI_MQTT_Publish_RAW(const char *topic, const uint8_t *payload,
void wifi_task_mqtt(void *argument) {
MQTT_Message_t msg;
uint8_t mqtt_connected = 0;
uint32_t retry_count = 0;
elog_i(TAG, "启动WiFi MQTT任务");
WIFI_RECV_DMA_Init();
@@ -461,30 +477,37 @@ void wifi_task_mqtt(void *argument) {
elog_i(TAG, "生成ClientID: %s", client_id);
elog_i(TAG, "开始MQTT连接...");
if (!WIFI_Connect_MQTT("WIFI_TEST", "88888888", "mqtt.beihong.wang", 1883,
client_id, "STM32_MQTT", "123456")) {
elog_e(TAG, "MQTT连接失败任务退出");
vTaskDelete(NULL); // 删除自身任务
}
elog_i(TAG, "发布设备上线状态");
WIFI_MQTT_Publish_Status("{\"status\":\"online\"}");
// 连接MQTT服务器成功后获取SNTP时间
WIFI_Get_SNTP_Time();
// 获取SNTP时间
elog_i(TAG, "获取SNTP时间...");
WIFI_Get_Current_Time();
elog_i(TAG, "当前SNTP时间为: %04d-%02d-%02d %02d:%02d:%02d",
sntp_time.year, sntp_time.month, sntp_time.day,
sntp_time.hour, sntp_time.minute, sntp_time.second);
elog_i(TAG, "进入MQTT消息处理循环");
// 主循环:一直运行,支持断线重连
for (;;) {
// ========== 尝试MQTT连接 ==========
if (!mqtt_connected) {
elog_i(TAG, "尝试MQTT连接... (重试次数: %lu)", ++retry_count);
if (WIFI_Connect_MQTT("WIFI_TEST", "88888888", "mqtt.beihong.wang", 1883,
client_id, "STM32_MQTT", "123456")) {
elog_i(TAG, "MQTT连接成功");
mqtt_connected = 1;
retry_count = 0;
// 发布设备上线状态
elog_i(TAG, "发布设备上线状态");
WIFI_MQTT_Publish_Status("{\"status\":\"online\"}");
// 获取SNTP时间
elog_i(TAG, "获取SNTP时间...");
WIFI_Get_SNTP_Time();
WIFI_Get_Current_Time();
elog_i(TAG, "当前SNTP时间为: %04d-%02d-%02d %02d:%02d:%02d",
sntp_time.year, sntp_time.month, sntp_time.day,
sntp_time.hour, sntp_time.minute, sntp_time.second);
} else {
elog_w(TAG, "MQTT连接失败5秒后重试...");
osDelay(5000);
continue; // 继续尝试连接
}
}
// ========== MQTT消息处理循环 ==========
if (wifi.rx_flag) {
wifi.rx_flag = 0;
@@ -515,6 +538,20 @@ void wifi_task_mqtt(void *argument) {
}
}
// 检查连接状态:如果长时间没收到数据,认为可能断线
// 这里可以添加心跳检测机制
static uint32_t last_activity = 0;
if (wifi.rx_flag || mqtt_connected) {
last_activity = osKernelGetTickCount();
}
// 5分钟无活动重新连接
if (mqtt_connected && (osKernelGetTickCount() - last_activity > 300000)) {
elog_w(TAG, "检测到长时间无活动重新连接MQTT...");
mqtt_connected = 0;
continue;
}
osDelay(20);
}
}
@@ -605,46 +642,49 @@ uint8_t WIFI_Enable_SNTP(void) {
uint8_t WIFI_Get_SNTP_Time(void) {
char *time_str;
char time_buf[32];
elog_i(TAG, "查询SNTP时间");
uint32_t current_tick = osKernelGetTickCount();
// 避免频繁查询距离上次查询不足5秒则跳过
if (current_tick - sntp_last_query_tick < SNTP_QUERY_INTERVAL) {
return sntp_time.valid; // 返回现有时间状态
}
sntp_last_query_tick = current_tick;
// 发送AT+CIPSNTPTIME命令
if (WIFI_SEND_DMA("AT+CIPSNTPTIME\r\n") != HAL_OK) {
elog_e(TAG, "SNTP时间查询命令发送失败");
elog_w(TAG, "SNTP时间查询命令发送失败");
return 0;
}
// 等待响应
if (!WIFI_WaitEvent("+CIPSNTPTIME:", "ERROR", 5000)) {
elog_e(TAG, "SNTP时间查询失败");
if (!WIFI_WaitEvent("+CIPSNTPTIME:", "ERROR", 3000)) {
// 超时不打印错误日志,避免刷屏
return 0;
}
// 解析时间字符串 "+CIPSNTPTIME:2024-05-08 21:11:38"
time_str = strstr((char *)wifi.rx_buffer, "+CIPSNTPTIME:");
if (!time_str) {
elog_e(TAG, "未找到时间字符串");
return 0;
}
// 提取时间部分 "2024-05-08 21:11:38"
time_str += strlen("+CIPSNTPTIME:");
// 复制到缓冲区以便处理
strncpy(time_buf, time_str, sizeof(time_buf) - 1);
time_buf[sizeof(time_buf) - 1] = '\0';
// 移除末尾的换行符和回车符
char *newline = strchr(time_buf, '\r');
if (newline) *newline = '\0';
newline = strchr(time_buf, '\n');
if (newline) *newline = '\0';
elog_i(TAG, "解析时间字符串: %s", time_buf);
// 解析时间格式 YYYY-MM-DD HH:MM:SS
int year, month, day, hour, minute, second;
if (sscanf(time_buf, "%d-%d-%d %d:%d:%d",
if (sscanf(time_buf, "%d-%d-%d %d:%d:%d",
&year, &month, &day, &hour, &minute, &second) == 6) {
sntp_time.year = (uint16_t)year;
sntp_time.month = (uint8_t)month;
@@ -653,14 +693,17 @@ uint8_t WIFI_Get_SNTP_Time(void) {
sntp_time.minute = (uint8_t)minute;
sntp_time.second = (uint8_t)second;
sntp_time.valid = 1;
elog_i(TAG, "SNTP时间获取成功: %04d-%02d-%02d %02d:%02d:%02d",
sntp_time.year, sntp_time.month, sntp_time.day,
sntp_time.hour, sntp_time.minute, sntp_time.second);
// 只在首次获取成功时打印日志
static uint8_t first_time_sync = 0;
if (!first_time_sync) {
elog_i(TAG, "SNTP时间同步成功: %04d-%02d-%02d %02d:%02d:%02d",
sntp_time.year, sntp_time.month, sntp_time.day,
sntp_time.hour, sntp_time.minute, sntp_time.second);
first_time_sync = 1;
}
return 1;
} else {
elog_e(TAG, "时间字符串解析失败: %s", time_buf);
sntp_time.valid = 0;
return 0;
}
}

View File

@@ -256,14 +256,21 @@ void LCD_Task(void *argument)
WIFI_Get_SNTP_Time();
now = WIFI_Get_Current_Time();
if (now.valid) {
snprintf(display_str, sizeof(display_str), "%02d:%02d:%02d", now.hour,
now.minute, now.second);
// 显示时间(大字体,只显示时:分,不显示秒)
snprintf(display_str, sizeof(display_str), "%02d:%02d", now.hour, now.minute);
ST7735_WriteString(25, 20, display_str, &Font_16x26, text_color, bg_color);
// 显示年月日(小字体,向下移动并居中)
snprintf(display_str, sizeof(display_str), "%04d-%02d-%02d", now.year,
now.month, now.day);
ST7735_WriteString(20, 60, display_str, &Font_7x10, ST7735_CYAN, bg_color);
} else {
snprintf(display_str, sizeof(display_str), "--:--:--");
ST7735_WriteString(25, 20, "--:--", &Font_16x26, text_color, bg_color);
ST7735_WriteString(20, 60, "----/--/--", &Font_7x10, ST7735_CYAN, bg_color);
}
ST7735_WriteString(10, 20, display_str, &Font_16x26, text_color, bg_color);
} else {
ST7735_WriteString(10, 20, "--:--:--", &Font_16x26, text_color, bg_color);
ST7735_WriteString(25, 20, "--:--", &Font_16x26, text_color, bg_color);
ST7735_WriteString(20, 60, "----/--/--", &Font_7x10, ST7735_CYAN, bg_color);
}
break;
@@ -336,7 +343,22 @@ void LCD_Task(void *argument)
break;
}
osDelay(1000);
// 记录当前页面
LCD_Page_t displayed_page = current_page;
// 根据当前页面设置不同的刷新间隔
if (current_page == LCD_PAGE_TIME || current_page == LCD_PAGE_SYSTEM_STATUS) {
// 时间页面和系统状态页面30秒刷新一次但每100ms检查一次是否需要切换页面
for (int i = 0; i < 300; i++) {
if (current_page != displayed_page) {
break; // 检测到页面切换,立即跳出
}
osDelay(100); // 每100ms检查一次总共30秒
}
} else {
// 其他页面1秒刷新一次
osDelay(1000);
}
}
/* USER CODE END LCD_Task */
}