feat: 添加RTC管理模块并集成SNTP时间同步功能

实现RTC时间管理功能,支持通过SNTP自动同步网络时间并持久化存储
提供时间来源追踪和掉电保持功能,优化LCD显示的时间更新机制
This commit is contained in:
2026-02-24 01:06:41 +08:00
parent 43a5042c9e
commit addfb3faad
9 changed files with 798 additions and 50 deletions

View File

@@ -3,6 +3,7 @@
#include "cmsis_os2.h"
#include "elog.h"
#include "mp3_driver.h" // 添加MP3模块头文件
#include "bsp_rtc.h" // RTC管理模块
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -234,19 +235,8 @@ 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);
}
/* 注意SNTP配置需要WiFi连接后才能生效
因此在WIFI_Connect_WiFi成功后再配置 */
elog_i(TAG, "断开 MQTT 连接(如果已连接)");
@@ -284,6 +274,49 @@ uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
mqtt_state = MQTT_STATE_WIFI_CONNECTED;
elog_i(TAG, "WiFi连接完成状态: %d", mqtt_state);
/* WiFi连接成功后配置SNTP仅首次配置需要模块重启 */
if (!sntp_configured) {
elog_i(TAG, "WiFi已连接现在配置SNTP服务器...");
if (!WIFI_Enable_SNTP()) {
elog_e(TAG, "SNTP设置失败");
// SNTP失败不阻断MQTT连接继续执行
} else {
sntp_configured = 1;
/* SNTP配置后模块会重启等待模块恢复 */
elog_i(TAG, "等待模块重启...");
osDelay(15000);
/* 模块重启后需要重新检查响应并连接WiFi */
elog_i(TAG, "检查模块恢复状态...");
uint8_t module_ready = 0;
uint32_t wait_start = HAL_GetTick();
while (HAL_GetTick() - wait_start < 30000) {
if (WIFI_CheckAck("AT\r\n", "OK", 1000)) {
module_ready = 1;
break;
}
osDelay(1000);
}
if (!module_ready) {
elog_e(TAG, "模块重启后未恢复响应");
return 0;
}
/* 检查WiFi是否已连接避免重复连接和重复播报 */
if (!WIFI_CheckAck("AT+CWJAP?\r\n", "OK", 2000) ||
!strstr((char *)wifi.rx_buffer, "+CWJAP:")) {
/* WiFi未连接重新连接 */
if (!WIFI_Connect_WiFi(wifi_ssid, wifi_pass, 15000)) {
elog_e(TAG, "SNTP配置后WiFi重连失败");
return 0;
}
} else {
elog_i(TAG, "WiFi已连接跳过重新连接");
}
}
}
elog_i(TAG, "4. 设置MQTT客户端ID: %s", client_id);
snprintf(cmd, sizeof(cmd), "AT+MQTTLONGCLIENTID=%s\r\n", client_id);
if (!WIFI_CheckAck(cmd, "OK", 2000)) {
@@ -493,13 +526,27 @@ void wifi_task_mqtt(void *argument) {
elog_i(TAG, "发布设备上线状态");
WIFI_MQTT_Publish_Status("{\"status\":\"online\"}");
// 获取SNTP时间
// 获取SNTP时间需要等待模块同步NTP服务器
elog_i(TAG, "等待NTP服务器同步...");
osDelay(10000); // 等待10秒让模块同步NTP
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);
uint8_t sntp_ok = 0;
// 首次获取失败后等待3秒再重试最多重试5次
for (int retry = 0; retry < 5 && !sntp_ok; retry++) {
if (retry > 0) {
osDelay(3000); // 等待3秒再重试
}
sntp_ok = WIFI_Get_SNTP_Time();
}
if (sntp_time.valid) {
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, "SNTP时间尚未同步将在后台继续尝试");
}
} else {
elog_w(TAG, "MQTT连接失败5秒后重试...");
osDelay(5000);
@@ -538,6 +585,30 @@ void wifi_task_mqtt(void *argument) {
}
}
// ========== 后台SNTP时间同步 ==========
static uint32_t sntp_sync_timer = 0;
static uint8_t sntp_reconfig_count = 0;
const uint32_t SNTP_SYNC_INTERVAL = 30000; // 30秒尝试同步一次
if (!sntp_time.valid && (osKernelGetTickCount() - sntp_sync_timer > SNTP_SYNC_INTERVAL)) {
sntp_sync_timer = osKernelGetTickCount();
// 如果连续10分钟20次都没同步成功尝试重新配置SNTP
if (++sntp_reconfig_count > 20) {
elog_w(TAG, "SNTP长时间未同步尝试重新配置...");
sntp_configured = 0; // 清除配置标志,下次重新配置
sntp_reconfig_count = 0;
}
// 尝试同步时间(静默尝试,不打印日志)
WIFI_Get_SNTP_Time();
if (sntp_time.valid) {
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 uint32_t last_activity = 0;
@@ -625,17 +696,17 @@ uint8_t WIFI_MQTT_Publish_Status(const char *json) {
uint8_t WIFI_Enable_SNTP(void) {
elog_i(TAG, "使能SNTP服务器设置中国时区");
// 发送AT+CIPSNTPCFG=1,8,cn.ntp.org.cn命令
if (!WIFI_CheckAck("AT+CIPSNTPCFG=1,8,cn.ntp.org.cn\r\n", "OK", 3000)) {
// 发送AT+CIPSNTPCFG=1,8,ntp.aliyun.com命令阿里云NTP更稳定
if (!WIFI_CheckAck("AT+CIPSNTPCFG=1,8,ntp.aliyun.com\r\n", "OK", 3000)) {
elog_e(TAG, "SNTP配置失败");
return 0;
}
elog_i(TAG, "SNTP服务器配置成功模块将自动重启以应用设置");
osDelay(15000); // 等待模块重启并连接到SNTP服务器
/* 注意配置SNTP后模块会重启这里只等待基本重启时间
WiFi重连和模块恢复由调用者处理 */
return 1;
}
@@ -653,13 +724,11 @@ uint8_t WIFI_Get_SNTP_Time(void) {
// 发送AT+CIPSNTPTIME命令
if (WIFI_SEND_DMA("AT+CIPSNTPTIME\r\n") != HAL_OK) {
elog_w(TAG, "SNTP时间查询命令发送失败");
return 0;
}
// 等待响应
if (!WIFI_WaitEvent("+CIPSNTPTIME:", "ERROR", 3000)) {
// 超时不打印错误日志,避免刷屏
return 0;
}
@@ -686,6 +755,12 @@ uint8_t WIFI_Get_SNTP_Time(void) {
int year, month, day, hour, minute, second;
if (sscanf(time_buf, "%d-%d-%d %d:%d:%d",
&year, &month, &day, &hour, &minute, &second) == 6) {
// 检查是否为默认时间2021-01-01如果是则说明未同步成功
if (year == 2021 && month == 1 && day == 1) {
// 静默返回,不打印日志
return 0;
}
sntp_time.year = (uint16_t)year;
sntp_time.month = (uint8_t)month;
sntp_time.day = (uint8_t)day;
@@ -702,6 +777,11 @@ uint8_t WIFI_Get_SNTP_Time(void) {
sntp_time.hour, sntp_time.minute, sntp_time.second);
first_time_sync = 1;
}
// 自动更新RTC时间
BSP_RTC_UpdateFromSNTP(sntp_time.year, sntp_time.month, sntp_time.day,
sntp_time.hour, sntp_time.minute, sntp_time.second);
return 1;
} else {
return 0;
@@ -718,3 +798,7 @@ uint8_t WIFI_Is_Time_Valid(void) {
return sntp_time.valid;
}
// 检查MQTT是否已连接
uint8_t WIFI_Is_MQTT_Connected(void) {
return mqtt_connected;
}

View File

@@ -75,10 +75,12 @@ void wifi_task_mqtt(void *argument);
uint8_t WIFI_MQTT_Publish_Sensor(const char *json);
uint8_t WIFI_MQTT_Publish_Status(const char *json);
/* ================= SNTP函数声明 ================= */
uint8_t WIFI_Enable_SNTP(void);
uint8_t WIFI_Get_SNTP_Time(void);
SNTP_Time_t WIFI_Get_Current_Time(void);
uint8_t WIFI_Is_Time_Valid(void);
uint8_t WIFI_Is_MQTT_Connected(void);
#endif /* __DX_WF_24_H__ */