## 主要修改
### 1. MQTT setMode命令处理修复 - 添加Set_System_Mode函数extern声明,解决编译错误 - 将setMode处理逻辑移到control主题,符合小程序规范 - 完善Set_System_Mode函数,添加音频播放功能 ### 2. 按键4手动补水功能 - 实现key4_single_click_handler函数,添加手动补水逻辑 - 与按键2喂食逻辑保持一致:相同的模式检查和错误处理 - 提供完整的本地手动控制功能 ### 3. 系统优化 - 统一本地和远程控制逻辑 - 完善错误处理和用户反馈 - 优化代码结构和日志记录 ## 影响 - 系统现在支持完整的双重控制方式(本地按键+远程MQTT) - 所有按键功能完善:模式切换、手动喂食、页面切换、手动补水 - 编译无错误,代码结构清晰,便于维护
This commit is contained in:
@@ -6,15 +6,22 @@
|
||||
3. [API 参考](#api-参考)
|
||||
4. [使用示例](#使用示例)
|
||||
5. [注意事项](#注意事项)
|
||||
6. [版本历史](#版本历史)
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本驱动用于 STM32F103 与 DX-WF-24 WiFi 模块的串口通信,基于 HAL 库实现,支持:
|
||||
|
||||
- **DMA 发送** - 非阻塞式数据发送
|
||||
- **DMA + 空闲中断接收** - 自动帧识别接收
|
||||
- **同步应答检测** - AT 指令交互
|
||||
- **MQTT 客户端** - 完整的MQTT协议支持,包括认证和双主题订阅
|
||||
- **SNTP 时间同步** - 网络时间同步功能,支持中国时区
|
||||
- **音频反馈** - 集成MP3模块,提供系统状态语音提示
|
||||
- **断线重连** - 自动检测连接状态并重连
|
||||
- **消息解析** - 自动解析MQTT订阅消息
|
||||
|
||||
---
|
||||
|
||||
@@ -57,21 +64,25 @@ HAL_StatusTypeDef WIFI_SEND_DMA(const char *data);
|
||||
|
||||
---
|
||||
|
||||
### 3. 数据接收
|
||||
### 3. 数据接收处理
|
||||
|
||||
#### `WIFI_Get_Received_Data()`
|
||||
#### `WIFI_UART_IDLE_Callback()`
|
||||
```c
|
||||
int WIFI_Get_Received_Data(uint8_t *buffer, uint16_t len);
|
||||
void WIFI_UART_IDLE_Callback(UART_HandleTypeDef *huart);
|
||||
```
|
||||
- **功能**:获取接收到的数据
|
||||
- **功能**:UART空闲中断回调函数,需要在USART1_IRQHandler中调用
|
||||
- **参数**:`huart` - UART句柄指针
|
||||
|
||||
#### `WIFI_WaitEvent()`
|
||||
```c
|
||||
uint8_t WIFI_WaitEvent(const char *ok_str, const char *fail_str, uint32_t timeout_ms);
|
||||
```
|
||||
- **功能**:等待特定事件或响应
|
||||
- **参数**:
|
||||
- `buffer` - 输出缓冲区
|
||||
- `len` - 缓冲区大小
|
||||
- **返回值**:实际复制的数据长度,0 表示无数据
|
||||
|
||||
---
|
||||
|
||||
### 4. 应答检测(新增)
|
||||
- `ok_str` - 期望的成功响应字符串
|
||||
- `fail_str` - 期望的失败响应字符串
|
||||
- `timeout_ms` - 超时时间(毫秒)
|
||||
- **返回值**:1表示成功,0表示失败或超时
|
||||
|
||||
#### `WIFI_CheckAck()`
|
||||
```c
|
||||
@@ -86,25 +97,35 @@ uint8_t WIFI_CheckAck(const char *cmd, const char *expect, uint32_t timeout_ms);
|
||||
- `1` - 收到期望应答
|
||||
- `0` - 超时或收到 ERROR
|
||||
|
||||
### 5. MQTT连接(支持认证)
|
||||
---
|
||||
|
||||
### 4. MQTT连接与发布
|
||||
|
||||
#### `Generate_Random_ClientID()`
|
||||
```c
|
||||
static void Generate_Random_ClientID(char *out_id, uint16_t max_len);
|
||||
```
|
||||
- **功能**:生成唯一的MQTT客户端ID,基于系统运行时间和STM32 UID
|
||||
- **参数**:
|
||||
- `out_id` - 输出缓冲区
|
||||
- `max_len` - 缓冲区最大长度
|
||||
|
||||
#### `WIFI_Connect_MQTT()`
|
||||
```c
|
||||
uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
|
||||
const char *mqtt_broker, uint16_t mqtt_port,
|
||||
const char *client_id, const char *mqtt_user,
|
||||
const char *mqtt_pass, const char *sub_topic);
|
||||
const char *mqtt_host, uint16_t mqtt_port,
|
||||
const char *client_id, const char *user,
|
||||
const char *pass);
|
||||
```
|
||||
- **功能**:完整MQTT连接流程(WiFi+MQTT)
|
||||
- **功能**:完整MQTT连接流程(WiFi+MQTT+SNTP)
|
||||
- **参数**:
|
||||
- `wifi_ssid` - WiFi名称
|
||||
- `wifi_pass` - WiFi密码
|
||||
- `mqtt_broker` - MQTT服务器地址
|
||||
- `mqtt_host` - MQTT服务器地址
|
||||
- `mqtt_port` - MQTT端口(通常为1883)
|
||||
- `client_id` - MQTT客户端ID(需唯一)
|
||||
- `mqtt_user` - MQTT用户名(传NULL表示无需认证)
|
||||
- `mqtt_pass` - MQTT密码(传NULL表示无需认证)
|
||||
- `sub_topic` - 订阅的主题
|
||||
- `user` - MQTT用户名
|
||||
- `pass` - MQTT密码
|
||||
- **返回值**:
|
||||
- `1` - 连接成功
|
||||
- `0` - 连接失败
|
||||
@@ -112,11 +133,115 @@ uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
|
||||
1. 清理MQTT环境
|
||||
2. AT指令测试
|
||||
3. 设置STA模式
|
||||
4. 连接WiFi(带重试)
|
||||
5. 配置MQTT参数(包括认证信息)
|
||||
6. 连接MQTT服务器(带重试)
|
||||
7. 订阅主题
|
||||
8. 发布上线消息
|
||||
4. 连接WiFi(带重试和音频反馈)
|
||||
5. 配置SNTP时间同步(首次连接时)
|
||||
6. 配置MQTT参数(客户端ID、用户名、密码)
|
||||
7. 连接MQTT服务器(带重试)
|
||||
8. 订阅双主题:`petfeeder/control`和`petfeeder/config`
|
||||
9. 发布设备上线状态
|
||||
|
||||
#### `WIFI_MQTT_Publish_RAW()`
|
||||
```c
|
||||
uint8_t WIFI_MQTT_Publish_RAW(const char *topic, const uint8_t *payload,
|
||||
uint16_t length, uint8_t qos, uint8_t retain);
|
||||
```
|
||||
- **功能**:发布原始数据到MQTT主题
|
||||
- **参数**:
|
||||
- `topic` - 目标主题
|
||||
- `payload` - 数据负载
|
||||
- `length` - 数据长度
|
||||
- `qos` - 服务质量等级(0-2)
|
||||
- `retain` - 保留标志
|
||||
- **返回值**:1表示成功,0表示失败
|
||||
|
||||
#### `WIFI_MQTT_Publish_Sensor()`
|
||||
```c
|
||||
uint8_t WIFI_MQTT_Publish_Sensor(const char *json);
|
||||
```
|
||||
- **功能**:发布传感器数据到`petfeeder/sensor`主题
|
||||
- **参数**:`json` - JSON格式的传感器数据
|
||||
- **返回值**:1表示成功,0表示失败
|
||||
|
||||
#### `WIFI_MQTT_Publish_Status()`
|
||||
```c
|
||||
uint8_t WIFI_MQTT_Publish_Status(const char *json);
|
||||
```
|
||||
- **功能**:发布设备状态到`petfeeder/status`主题(retain=1)
|
||||
- **参数**:`json` - JSON格式的设备状态
|
||||
- **返回值**:1表示成功,0表示失败
|
||||
|
||||
#### `WIFI_Parse_MQTT_Message()`
|
||||
```c
|
||||
uint8_t WIFI_Parse_MQTT_Message(char *input, MQTT_Message_t *msg);
|
||||
```
|
||||
- **功能**:解析MQTT订阅消息(+MQTTSUBRECV格式)
|
||||
- **参数**:
|
||||
- `input` - 原始输入字符串
|
||||
- `msg` - 输出消息结构体
|
||||
- **返回值**:1表示解析成功,0表示失败
|
||||
|
||||
---
|
||||
|
||||
### 5. SNTP时间同步
|
||||
|
||||
#### `WIFI_Enable_SNTP()`
|
||||
```c
|
||||
uint8_t WIFI_Enable_SNTP(void);
|
||||
```
|
||||
- **功能**:启用SNTP功能并配置NTP服务器(阿里云NTP)
|
||||
- **返回值**:1表示成功,0表示失败
|
||||
- **注意**:配置后模块会重启,需要重新连接WiFi
|
||||
|
||||
#### `WIFI_Get_SNTP_Time()`
|
||||
```c
|
||||
uint8_t WIFI_Get_SNTP_Time(void);
|
||||
```
|
||||
- **功能**:获取当前SNTP网络时间
|
||||
- **返回值**:1表示成功获取有效时间,0表示失败
|
||||
- **注意**:避免频繁查询(默认5秒间隔)
|
||||
|
||||
#### `WIFI_Get_Current_Time()`
|
||||
```c
|
||||
SNTP_Time_t WIFI_Get_Current_Time(void);
|
||||
```
|
||||
- **功能**:获取存储的当前时间(返回结构体副本)
|
||||
- **返回值**:SNTP时间结构体
|
||||
|
||||
#### `WIFI_Is_Time_Valid()`
|
||||
```c
|
||||
uint8_t WIFI_Is_Time_Valid(void);
|
||||
```
|
||||
- **功能**:检查SNTP时间是否有效
|
||||
- **返回值**:1表示有效,0表示无效
|
||||
|
||||
---
|
||||
|
||||
### 6. 状态检查
|
||||
|
||||
#### `WIFI_Is_MQTT_Connected()`
|
||||
```c
|
||||
uint8_t WIFI_Is_MQTT_Connected(void);
|
||||
```
|
||||
- **功能**:检查MQTT是否已连接
|
||||
- **返回值**:1表示已连接,0表示未连接
|
||||
|
||||
---
|
||||
|
||||
### 7. 主任务函数
|
||||
|
||||
#### `wifi_task_mqtt()`
|
||||
```c
|
||||
void wifi_task_mqtt(void *argument);
|
||||
```
|
||||
- **功能**:WiFi和MQTT主任务函数,包含完整的连接、消息处理和断线重连逻辑
|
||||
- **参数**:`argument` - FreeRTOS任务参数
|
||||
- **工作流程**:
|
||||
1. 初始化DMA接收
|
||||
2. 生成唯一ClientID
|
||||
3. 连接MQTT(支持重试)
|
||||
4. 发布设备上线状态
|
||||
5. 获取SNTP时间
|
||||
6. 进入主循环:处理MQTT消息、后台时间同步、连接状态监测
|
||||
|
||||
---
|
||||
|
||||
@@ -139,110 +264,81 @@ void System_Init(void)
|
||||
}
|
||||
```
|
||||
|
||||
### 示例2:发送数据
|
||||
### 示例2:在FreeRTOS任务中使用
|
||||
|
||||
```c
|
||||
// 发送 AT 指令测试
|
||||
void WiFi_Test(void)
|
||||
// FreeRTOS任务定义
|
||||
osThreadId_t wifi_mqttHandle;
|
||||
const osThreadAttr_t wifi_mqtt_attributes = {
|
||||
.name = "wifi_mqtt",
|
||||
.stack_size = 3000 * 4,
|
||||
.priority = (osPriority_t)osPriorityHigh,
|
||||
};
|
||||
|
||||
// 创建任务
|
||||
wifi_mqttHandle = osThreadNew(wifi_task_mqtt, NULL, &wifi_mqtt_attributes);
|
||||
```
|
||||
|
||||
### 示例3:手动发布传感器数据
|
||||
|
||||
```c
|
||||
void Publish_Sensor_Data(float temp, float humi, float weight)
|
||||
{
|
||||
HAL_StatusTypeDef status = WIFI_SEND_DMA("AT\r\n");
|
||||
char json[128];
|
||||
snprintf(json, sizeof(json),
|
||||
"{\"temperature\":%.1f,\"humidity\":%.1f,\"weight\":%.1f}",
|
||||
temp, humi, weight);
|
||||
|
||||
if (status == HAL_OK) {
|
||||
elog_i("WIFI", "Command sent");
|
||||
} else if (status == HAL_BUSY) {
|
||||
elog_w("WIFI", "WiFi busy, try later");
|
||||
if (WIFI_MQTT_Publish_Sensor(json)) {
|
||||
elog_i("SENSOR", "数据发布成功");
|
||||
} else {
|
||||
elog_e("SENSOR", "数据发布失败");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例3:检测模块就绪
|
||||
### 示例4:解析MQTT消息
|
||||
|
||||
```c
|
||||
// 初始化时检测 WiFi 模块
|
||||
uint8_t WiFi_Init_Check(void)
|
||||
void Process_MQTT_Message(void)
|
||||
{
|
||||
// 发送 AT,期望收到 OK,超时 1 秒
|
||||
if (WIFI_CheckAck("AT\r\n", "OK", 1000)) {
|
||||
elog_i("WIFI", "Module ready");
|
||||
return 1;
|
||||
} else {
|
||||
elog_e("WIFI", "Module not responding");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例4:完整的 MQTT 连接(支持认证)
|
||||
|
||||
```c
|
||||
// 连接到 MQTT 服务器(无需认证)
|
||||
void Connect_MQTT_Public(void)
|
||||
{
|
||||
uint8_t success = WIFI_Connect_MQTT(
|
||||
"MyWiFi", // WiFi名称
|
||||
"12345678", // WiFi密码
|
||||
"broker.emqx.io", // MQTT服务器(公共测试服务器)
|
||||
1883, // MQTT端口
|
||||
"PetFeeder-001", // 客户端ID(需唯一)
|
||||
NULL, // MQTT用户名(NULL表示无需认证)
|
||||
NULL, // MQTT密码(NULL表示无需认证)
|
||||
"pet/control" // 订阅主题
|
||||
);
|
||||
|
||||
if (success) {
|
||||
elog_i("MQTT", "Connected to public MQTT server");
|
||||
} else {
|
||||
elog_e("MQTT", "Connection failed");
|
||||
}
|
||||
}
|
||||
|
||||
// 连接到 MQTT 服务器(需要用户名密码认证)
|
||||
void Connect_MQTT_Private(void)
|
||||
{
|
||||
uint8_t success = WIFI_Connect_MQTT(
|
||||
"MyWiFi", // WiFi名称
|
||||
"12345678", // WiFi密码
|
||||
"mqtt.myserver.com", // 私有MQTT服务器
|
||||
1883, // MQTT端口
|
||||
"PetFeeder-001", // 客户端ID
|
||||
"myusername", // MQTT用户名
|
||||
"mypassword", // MQTT密码
|
||||
"pet/control" // 订阅主题
|
||||
);
|
||||
|
||||
if (success) {
|
||||
elog_i("MQTT", "Connected to private MQTT server");
|
||||
} else {
|
||||
elog_e("MQTT", "Connection failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
### 示例5:异步接收处理
|
||||
|
||||
```c
|
||||
// 在任务循环中处理接收数据
|
||||
void WiFi_Task(void)
|
||||
{
|
||||
uint8_t buffer[512];
|
||||
int len;
|
||||
|
||||
while (1) {
|
||||
// 检查是否有新数据
|
||||
len = WIFI_Get_Received_Data(buffer, sizeof(buffer));
|
||||
if (wifi.rx_flag) {
|
||||
wifi.rx_flag = 0;
|
||||
|
||||
if (len > 0) {
|
||||
// 处理接收到的数据
|
||||
elog_i("WIFI", "Received: %s", buffer);
|
||||
MQTT_Message_t msg;
|
||||
if (WIFI_Parse_MQTT_Message((char *)wifi.rx_buffer, &msg)) {
|
||||
elog_i("MQTT", "收到主题: %s", msg.topic);
|
||||
elog_i("MQTT", "内容: %s", msg.payload);
|
||||
|
||||
// 根据内容做不同处理...
|
||||
if (strstr((char*)buffer, "+IPD")) {
|
||||
// 收到网络数据
|
||||
Process_Network_Data(buffer);
|
||||
if (strcmp(msg.topic, "petfeeder/control") == 0) {
|
||||
// 处理控制指令
|
||||
if (strstr(msg.payload, "feed")) {
|
||||
elog_i("MQTT", "执行喂食动作");
|
||||
// 调用喂食函数
|
||||
}
|
||||
} else if (strcmp(msg.topic, "petfeeder/config") == 0) {
|
||||
// 处理配置更新
|
||||
elog_i("MQTT", "更新配置参数");
|
||||
}
|
||||
}
|
||||
|
||||
osDelay(10); // 10ms 轮询
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例5:获取网络时间
|
||||
|
||||
```c
|
||||
void Check_Network_Time(void)
|
||||
{
|
||||
if (WIFI_Is_Time_Valid()) {
|
||||
SNTP_Time_t current_time = WIFI_Get_Current_Time();
|
||||
elog_i("TIME", "当前网络时间: %04d-%02d-%02d %02d:%02d:%02d",
|
||||
current_time.year, current_time.month, current_time.day,
|
||||
current_time.hour, current_time.minute, current_time.second);
|
||||
} else {
|
||||
elog_w("TIME", "网络时间未同步");
|
||||
// 尝试获取时间
|
||||
WIFI_Get_SNTP_Time();
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -280,17 +376,31 @@ void USART1_IRQHandler(void)
|
||||
#define WIFI_RX_BUF_SIZE 512
|
||||
```
|
||||
|
||||
### 4. MQTT 用户名密码认证
|
||||
### 4. SNTP时间同步
|
||||
|
||||
- 公共MQTT服务器(如 broker.emqx.io)通常不需要认证,传 `NULL` 即可
|
||||
- 私有MQTT服务器需要用户名和密码,传入对应字符串
|
||||
- 确保 MQTT 服务器已开启用户名密码认证功能
|
||||
- SNTP配置后模块会重启,需要等待模块恢复(约15秒)
|
||||
- 避免频繁查询SNTP时间(默认5秒间隔)
|
||||
- 首次连接时会自动配置SNTP,后续连接会跳过此步骤
|
||||
|
||||
### 5. 超时处理
|
||||
### 5. 音频反馈
|
||||
|
||||
`WIFI_CheckAck()` 是阻塞函数,不适合在中断或高优先级任务中调用。
|
||||
- WiFi连接成功/失败时会有语音提示
|
||||
- 需要正确初始化MP3模块并加载音频文件
|
||||
- 音频文件索引定义在`mp3_play_index.h`中
|
||||
|
||||
### 6. 多线程安全
|
||||
### 6. MQTT主题
|
||||
|
||||
- 固定订阅两个主题:`petfeeder/control`(控制指令)和`petfeeder/config`(配置更新)
|
||||
- 固定发布到三个主题:`petfeeder/status`(设备状态)、`petfeeder/sensor`(传感器数据)
|
||||
- 主题名称在代码中硬编码,如需修改需要修改源代码
|
||||
|
||||
### 7. 断线重连
|
||||
|
||||
- 自动检测MQTT连接状态
|
||||
- 断线后自动重连(5秒重试间隔)
|
||||
- 长时间无活动(5分钟)也会触发重连
|
||||
|
||||
### 8. 多线程安全
|
||||
|
||||
当前实现未加互斥锁,如果在多任务环境中同时调用发送/接收,需要额外保护。
|
||||
|
||||
@@ -304,6 +414,9 @@ void USART1_IRQHandler(void)
|
||||
| 数据截断 | 缓冲区太小 | 增大 `WIFI_RX_BUF_SIZE` |
|
||||
| 发送失败 | DMA 忙 | 检查返回值,稍后重试 |
|
||||
| 检测超时 | 波特率不匹配 | 确认模块波特率(默认 115200)|
|
||||
| SNTP配置失败 | 模块重启未完成 | 增加等待时间,检查WiFi连接 |
|
||||
| MQTT连接失败 | 服务器地址错误 | 检查MQTT服务器地址和端口 |
|
||||
| 音频无输出 | MP3模块未初始化 | 检查MP3模块初始化代码 |
|
||||
|
||||
---
|
||||
|
||||
@@ -314,9 +427,10 @@ void USART1_IRQHandler(void)
|
||||
| 1.0 | 2026-02-09 | 初始版本,基础 DMA 收发 |
|
||||
| 1.1 | 2026-02-09 | 新增 `WIFI_CheckAck()` 同步应答检测 |
|
||||
| 1.2 | 2026-02-09 | 新增 `WIFI_Connect_MQTT()` 支持用户名密码认证 |
|
||||
| 2.0 | 2026-02-25 | 重大更新:<br>- 集成SNTP时间同步功能<br>- 增加MP3音频反馈<br>- 改进MQTT连接流程<br>- 增加断线重连机制<br>- 增加消息解析功能<br>- 更新API接口 |
|
||||
|
||||
---
|
||||
|
||||
## 联系方式
|
||||
|
||||
如有问题,请参考 DX-WF-24 模块 AT 指令手册。
|
||||
如有问题,请参考 DX-WF-24 模块 AT 指令手册。
|
||||
@@ -1,13 +1,25 @@
|
||||
#include "dx_wf_24.h"
|
||||
#include "bsp_rtc.h" // RTC管理模块
|
||||
#include "cmsis_os.h"
|
||||
#include "cmsis_os2.h"
|
||||
#include "elog.h"
|
||||
#include "mp3_driver.h" // 添加MP3模块头文件
|
||||
#include "bsp_rtc.h" // RTC管理模块
|
||||
#include "mp3_driver.h" // 添加MP3模块头文件
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ================= 外部函数声明 ================= */
|
||||
// 喂食控制函数声明(定义在freertos.c中)
|
||||
extern uint8_t Request_Feed(uint8_t cmd, uint16_t angle, uint8_t amount);
|
||||
#define FEED_CMD_REMOTE 3 // 远程喂食命令,与freertos.c中的FEED_CMD_REMOTE对应
|
||||
|
||||
// 加水控制函数声明(定义在freertos.c中)
|
||||
extern uint8_t Request_Water(uint8_t cmd);
|
||||
#define WATER_CMD_REMOTE 3 // 远程加水命令,与freertos.c中的WATER_CMD_REMOTE对应
|
||||
|
||||
// 系统模式设置函数声明(定义在freertos.c中)
|
||||
extern void Set_System_Mode(uint8_t mode);
|
||||
|
||||
/* ================= 配置 ================= */
|
||||
|
||||
#define WIFI_TX_BUF_SIZE 512
|
||||
@@ -32,9 +44,7 @@ static volatile uint8_t sntp_configured = 0;
|
||||
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) {
|
||||
static void Generate_Random_ClientID(char *out_id, uint16_t max_len) {
|
||||
// 获取系统运行时间
|
||||
uint32_t tick = HAL_GetTick();
|
||||
|
||||
@@ -58,6 +68,12 @@ static const uint32_t SNTP_QUERY_INTERVAL = 5000; // 5秒查询一次
|
||||
sum);
|
||||
}
|
||||
|
||||
/* 清理WiFi接收缓冲区 */
|
||||
void WIFI_Clear_Rx_Buffer(void) {
|
||||
wifi.rx_flag = 0;
|
||||
wifi.rx_len = 0;
|
||||
memset(wifi.rx_buffer, 0, WIFI_RX_BUF_SIZE);
|
||||
}
|
||||
|
||||
/* ================= DMA RX 初始化 ================= */
|
||||
|
||||
@@ -132,6 +148,9 @@ uint8_t WIFI_WaitEvent(const char *ok_str, const char *fail_str,
|
||||
uint32_t timeout_ms) {
|
||||
uint32_t start = HAL_GetTick();
|
||||
|
||||
// 清理旧数据,避免干扰
|
||||
WIFI_Clear_Rx_Buffer();
|
||||
|
||||
while (HAL_GetTick() - start < timeout_ms) {
|
||||
if (wifi.rx_flag) {
|
||||
wifi.rx_flag = 0;
|
||||
@@ -140,7 +159,6 @@ uint8_t WIFI_WaitEvent(const char *ok_str, const char *fail_str,
|
||||
|
||||
elog_i(TAG, "接收到WiFi模块数据: %s", buf);
|
||||
|
||||
|
||||
/* ===== 优先处理断线事件 ===== */
|
||||
if (strstr(buf, "+MQTTDISCONNECTED")) {
|
||||
mqtt_connected = 0;
|
||||
@@ -210,19 +228,19 @@ uint8_t WIFI_Connect_WiFi(const char *ssid, const char *password,
|
||||
elog_i(TAG, "等待WiFi基础连接响应...");
|
||||
if (!WIFI_WaitEvent("OK", "ERROR", 3000)) {
|
||||
elog_e(TAG, "WiFi基础连接失败");
|
||||
MP3_Play(WIFI_CONNECT_FAIL); // WiFi连接失败,播放提示音
|
||||
MP3_Play(WIFI_CONNECT_FAIL); // WiFi连接失败,播放提示音
|
||||
return 0;
|
||||
}
|
||||
|
||||
elog_i(TAG, "等待WiFi详细连接结果...");
|
||||
if (!WIFI_WaitEvent("+CWJAP:1", "+CWJAP:0", timeout_ms)) {
|
||||
elog_e(TAG, "WiFi详细连接失败");
|
||||
MP3_Play(WIFI_CONNECT_FAIL); // WiFi连接失败,播放提示音
|
||||
MP3_Play(WIFI_CONNECT_FAIL); // WiFi连接失败,播放提示音
|
||||
return 0;
|
||||
}
|
||||
|
||||
elog_i(TAG, "WiFi连接成功");
|
||||
MP3_Play(WIFI_CONNECT_OK); // WiFi连接成功,播放提示音
|
||||
MP3_Play(WIFI_CONNECT_OK); // WiFi连接成功,播放提示音
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -269,8 +287,6 @@ uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
mqtt_state = MQTT_STATE_WIFI_CONNECTED;
|
||||
elog_i(TAG, "WiFi连接完成,状态: %d", mqtt_state);
|
||||
|
||||
@@ -491,140 +507,194 @@ uint8_t WIFI_MQTT_Publish_RAW(const char *topic, const uint8_t *payload,
|
||||
elog_i(TAG, "MQTT发布成功到主题: %s", topic);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================= MQTT接收任务 ================= */
|
||||
|
||||
void wifi_task_mqtt(void *argument) {
|
||||
MQTT_Message_t msg;
|
||||
uint8_t mqtt_connected = 0;
|
||||
uint32_t retry_count = 0;
|
||||
MQTT_Message_t msg;
|
||||
uint8_t mqtt_connected = 0;
|
||||
uint32_t retry_count = 0;
|
||||
|
||||
elog_i(TAG, "启动WiFi MQTT任务");
|
||||
WIFI_RECV_DMA_Init();
|
||||
osDelay(3000);
|
||||
elog_i(TAG, "启动WiFi MQTT任务");
|
||||
WIFI_RECV_DMA_Init();
|
||||
osDelay(3000);
|
||||
|
||||
char client_id[64] = {0};
|
||||
char client_id[64] = {0};
|
||||
|
||||
// 生成唯一ClientID
|
||||
Generate_Random_ClientID(client_id, sizeof(client_id));
|
||||
// 生成唯一ClientID
|
||||
Generate_Random_ClientID(client_id, sizeof(client_id));
|
||||
|
||||
elog_i(TAG, "生成ClientID: %s", client_id);
|
||||
elog_i(TAG, "生成ClientID: %s", client_id);
|
||||
|
||||
// 主循环:一直运行,支持断线重连
|
||||
for (;;) {
|
||||
// ========== 尝试MQTT连接 ==========
|
||||
if (!mqtt_connected) {
|
||||
elog_i(TAG, "尝试MQTT连接... (重试次数: %lu)", ++retry_count);
|
||||
// 主循环:一直运行,支持断线重连
|
||||
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;
|
||||
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\"}");
|
||||
// 发布设备上线状态
|
||||
elog_i(TAG, "发布设备上线状态");
|
||||
WIFI_MQTT_Publish_Status("{\"status\":\"online\"}");
|
||||
|
||||
// 获取SNTP时间(需要等待模块同步NTP服务器)
|
||||
elog_i(TAG, "等待NTP服务器同步...");
|
||||
osDelay(10000); // 等待10秒让模块同步NTP
|
||||
// 获取SNTP时间(需要等待模块同步NTP服务器)
|
||||
elog_i(TAG, "等待NTP服务器同步...");
|
||||
osDelay(10000); // 等待10秒让模块同步NTP
|
||||
|
||||
elog_i(TAG, "获取SNTP时间...");
|
||||
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);
|
||||
continue; // 继续尝试连接
|
||||
}
|
||||
elog_i(TAG, "获取SNTP时间...");
|
||||
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();
|
||||
}
|
||||
|
||||
// ========== MQTT消息处理循环 ==========
|
||||
if (wifi.rx_flag) {
|
||||
wifi.rx_flag = 0;
|
||||
|
||||
elog_i(TAG, "收到WiFi数据: %s", wifi.rx_buffer);
|
||||
|
||||
if (WIFI_Parse_MQTT_Message((char *)wifi.rx_buffer, &msg)) {
|
||||
elog_i(TAG, "解析MQTT消息成功");
|
||||
elog_i(TAG, "收到主题: %s", msg.topic);
|
||||
elog_i(TAG, "内容: %s", msg.payload);
|
||||
|
||||
/* ===== control主题 ===== */
|
||||
if (strcmp(msg.topic, "petfeeder/control") == 0) {
|
||||
elog_i(TAG, "处理control主题消息");
|
||||
if (strstr(msg.payload, "feed")) {
|
||||
elog_i(TAG, "执行喂食动作");
|
||||
// 执行喂食函数
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== config主题 ===== */
|
||||
else if (strcmp(msg.topic, "petfeeder/config") == 0) {
|
||||
elog_i(TAG, "处理config主题消息");
|
||||
elog_i(TAG, "更新配置参数");
|
||||
// 更新参数逻辑
|
||||
}
|
||||
} else {
|
||||
elog_w(TAG, "MQTT消息解析失败");
|
||||
}
|
||||
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时间尚未同步,将在后台继续尝试");
|
||||
}
|
||||
|
||||
// ========== 后台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;
|
||||
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);
|
||||
} else {
|
||||
elog_w(TAG, "MQTT连接失败,5秒后重试...");
|
||||
osDelay(5000);
|
||||
continue; // 继续尝试连接
|
||||
}
|
||||
}
|
||||
|
||||
// ========== MQTT消息处理循环 ==========
|
||||
if (wifi.rx_flag) {
|
||||
wifi.rx_flag = 0;
|
||||
|
||||
elog_i(TAG, "收到WiFi数据: %s", wifi.rx_buffer);
|
||||
|
||||
if (WIFI_Parse_MQTT_Message((char *)wifi.rx_buffer, &msg)) {
|
||||
elog_i(TAG, "解析MQTT消息成功");
|
||||
elog_i(TAG, "收到主题: %s", msg.topic);
|
||||
elog_i(TAG, "内容: %s", msg.payload);
|
||||
|
||||
/* ===== control主题 ===== */
|
||||
if (strcmp(msg.topic, "petfeeder/control") == 0) {
|
||||
elog_i(TAG, "处理control主题消息");
|
||||
if (strstr(msg.payload, "feed")) {
|
||||
elog_i(TAG, "执行喂食动作");
|
||||
// 调用远程喂食函数
|
||||
if (Request_Feed(FEED_CMD_REMOTE, 90, 1)) {
|
||||
elog_i(TAG, "远程喂食请求已提交");
|
||||
} else {
|
||||
elog_w(TAG, "喂食进行中,无法接受新命令");
|
||||
}
|
||||
}
|
||||
if (strstr(msg.payload, "addWater")) {
|
||||
elog_i(TAG, "执行添加水动作");
|
||||
// 调用远程加水函数
|
||||
if (Request_Water(WATER_CMD_REMOTE)) {
|
||||
elog_i(TAG, "远程加水请求已提交");
|
||||
} else {
|
||||
elog_w(TAG, "加水进行中,无法接受新命令");
|
||||
}
|
||||
}
|
||||
// 解析setMode命令(小程序规定在control主题中处理)
|
||||
if (strstr(msg.payload, "setMode")) {
|
||||
elog_i(TAG, "处理setMode主题消息");
|
||||
|
||||
// 简单的JSON解析:查找"mode"字段
|
||||
char *mode_start = strstr(msg.payload, "\"mode\"");
|
||||
if (mode_start) {
|
||||
// 查找冒号后的值
|
||||
char *colon = strstr(mode_start, ":");
|
||||
if (colon) {
|
||||
// 查找引号内的值
|
||||
char *quote1 = strstr(colon, "\"");
|
||||
if (quote1) {
|
||||
char *quote2 = strstr(quote1 + 1, "\"");
|
||||
if (quote2) {
|
||||
// 提取模式字符串
|
||||
char mode_str[16] = {0};
|
||||
int len = quote2 - (quote1 + 1);
|
||||
if (len > 0 && len < sizeof(mode_str)) {
|
||||
strncpy(mode_str, quote1 + 1, len);
|
||||
mode_str[len] = '\0';
|
||||
|
||||
elog_i(TAG, "解析到模式: %s", mode_str);
|
||||
|
||||
// 根据模式设置系统状态
|
||||
if (strcmp(mode_str, "auto") == 0) {
|
||||
Set_System_Mode(1); // 自动模式
|
||||
elog_i(TAG, "系统模式设置为: 自动模式");
|
||||
} else if (strcmp(mode_str, "manual") == 0) {
|
||||
Set_System_Mode(0); // 手动模式
|
||||
elog_i(TAG, "系统模式设置为: 手动模式");
|
||||
} else {
|
||||
elog_w(TAG, "未知模式: %s", mode_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== config主题 ===== */
|
||||
else if (strcmp(msg.topic, "petfeeder/config") == 0) {
|
||||
elog_i(TAG, "处理config主题消息");
|
||||
elog_i(TAG, "更新配置参数");
|
||||
// 其他配置参数更新逻辑
|
||||
}
|
||||
} else {
|
||||
elog_w(TAG, "MQTT消息解析失败");
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 后台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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t WIFI_Parse_MQTT_Message(char *input, MQTT_Message_t *msg) {
|
||||
@@ -695,110 +765,106 @@ uint8_t WIFI_MQTT_Publish_Status(const char *json) {
|
||||
/* ================= SNTP 时间相关函数 ================= */
|
||||
|
||||
uint8_t WIFI_Enable_SNTP(void) {
|
||||
elog_i(TAG, "使能SNTP服务器,设置中国时区");
|
||||
elog_i(TAG, "使能SNTP服务器,设置中国时区");
|
||||
|
||||
// 发送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;
|
||||
}
|
||||
// 发送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服务器配置成功,模块将自动重启以应用设置");
|
||||
/* 注意:配置SNTP后模块会重启,这里只等待基本重启时间
|
||||
WiFi重连和模块恢复由调用者处理 */
|
||||
elog_i(TAG, "SNTP服务器配置成功,模块将自动重启以应用设置");
|
||||
/* 注意:配置SNTP后模块会重启,这里只等待基本重启时间
|
||||
WiFi重连和模块恢复由调用者处理 */
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t WIFI_Get_SNTP_Time(void) {
|
||||
char *time_str;
|
||||
char time_buf[32];
|
||||
uint32_t current_tick = osKernelGetTickCount();
|
||||
char *time_str;
|
||||
char time_buf[32];
|
||||
uint32_t current_tick = osKernelGetTickCount();
|
||||
|
||||
// 避免频繁查询:距离上次查询不足5秒则跳过
|
||||
if (current_tick - sntp_last_query_tick < SNTP_QUERY_INTERVAL) {
|
||||
return sntp_time.valid; // 返回现有时间状态
|
||||
// 避免频繁查询:距离上次查询不足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) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 等待响应
|
||||
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) {
|
||||
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';
|
||||
|
||||
// 解析时间格式 YYYY-MM-DD HH:MM:SS
|
||||
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_last_query_tick = current_tick;
|
||||
sntp_time.year = (uint16_t)year;
|
||||
sntp_time.month = (uint8_t)month;
|
||||
sntp_time.day = (uint8_t)day;
|
||||
sntp_time.hour = (uint8_t)hour;
|
||||
sntp_time.minute = (uint8_t)minute;
|
||||
sntp_time.second = (uint8_t)second;
|
||||
sntp_time.valid = 1;
|
||||
|
||||
// 发送AT+CIPSNTPTIME命令
|
||||
if (WIFI_SEND_DMA("AT+CIPSNTPTIME\r\n") != HAL_OK) {
|
||||
return 0;
|
||||
// 只在首次获取成功时打印日志
|
||||
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;
|
||||
}
|
||||
|
||||
// 等待响应
|
||||
if (!WIFI_WaitEvent("+CIPSNTPTIME:", "ERROR", 3000)) {
|
||||
return 0;
|
||||
}
|
||||
// 自动更新RTC时间
|
||||
BSP_RTC_UpdateFromSNTP(sntp_time.year, sntp_time.month, sntp_time.day,
|
||||
sntp_time.hour, sntp_time.minute, sntp_time.second);
|
||||
|
||||
// 解析时间字符串 "+CIPSNTPTIME:2024-05-08 21:11:38"
|
||||
time_str = strstr((char *)wifi.rx_buffer, "+CIPSNTPTIME:");
|
||||
if (!time_str) {
|
||||
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';
|
||||
|
||||
// 解析时间格式 YYYY-MM-DD HH:MM:SS
|
||||
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;
|
||||
sntp_time.hour = (uint8_t)hour;
|
||||
sntp_time.minute = (uint8_t)minute;
|
||||
sntp_time.second = (uint8_t)second;
|
||||
sntp_time.valid = 1;
|
||||
|
||||
// 只在首次获取成功时打印日志
|
||||
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;
|
||||
}
|
||||
|
||||
// 自动更新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;
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前SNTP时间(返回结构体副本)
|
||||
SNTP_Time_t WIFI_Get_Current_Time(void) {
|
||||
return sntp_time;
|
||||
}
|
||||
SNTP_Time_t WIFI_Get_Current_Time(void) { return sntp_time; }
|
||||
|
||||
// 检查SNTP时间是否有效
|
||||
uint8_t WIFI_Is_Time_Valid(void) {
|
||||
return sntp_time.valid;
|
||||
}
|
||||
uint8_t WIFI_Is_Time_Valid(void) { return sntp_time.valid; }
|
||||
|
||||
// 检查MQTT是否已连接
|
||||
uint8_t WIFI_Is_MQTT_Connected(void) {
|
||||
return mqtt_connected;
|
||||
}
|
||||
uint8_t WIFI_Is_MQTT_Connected(void) { return mqtt_connected; }
|
||||
|
||||
Reference in New Issue
Block a user