diff --git a/Core/Bsp/BSP_WF_24/BSP_WF_24使用说明.md b/Core/Bsp/BSP_WF_24/BSP_WF_24使用说明.md
index 2cc51be..300100d 100644
--- a/Core/Bsp/BSP_WF_24/BSP_WF_24使用说明.md
+++ b/Core/Bsp/BSP_WF_24/BSP_WF_24使用说明.md
@@ -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 | 重大更新:
- 集成SNTP时间同步功能
- 增加MP3音频反馈
- 改进MQTT连接流程
- 增加断线重连机制
- 增加消息解析功能
- 更新API接口 |
---
## 联系方式
-如有问题,请参考 DX-WF-24 模块 AT 指令手册。
+如有问题,请参考 DX-WF-24 模块 AT 指令手册。
\ No newline at end of file
diff --git a/Core/Bsp/BSP_WF_24/dx_wf_24.c b/Core/Bsp/BSP_WF_24/dx_wf_24.c
index 018dbb0..7f01dcc 100644
--- a/Core/Bsp/BSP_WF_24/dx_wf_24.c
+++ b/Core/Bsp/BSP_WF_24/dx_wf_24.c
@@ -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
#include
#include
+/* ================= 外部函数声明 ================= */
+// 喂食控制函数声明(定义在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; }
diff --git a/Core/Src/freertos.c b/Core/Src/freertos.c
index ffc38d5..b1f460e 100644
--- a/Core/Src/freertos.c
+++ b/Core/Src/freertos.c
@@ -19,10 +19,9 @@
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
-#include "cmsis_os.h"
-#include "cmsis_os2.h"
-#include "main.h"
#include "task.h"
+#include "main.h"
+#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
@@ -110,48 +109,67 @@ static volatile Feed_Cmd_t feed_command = FEED_CMD_NONE; // 喂食命令标志
static volatile uint16_t feed_angle = 90; // 喂食角度(默认90度)
static volatile uint8_t feed_amount = 1; // 喂食份数
+// 加水控制命令枚举
+typedef enum {
+ WATER_CMD_NONE = 0, // 无命令
+ WATER_CMD_MANUAL, // 手动加水
+ WATER_CMD_AUTO, // 自动加水
+ WATER_CMD_REMOTE // 远程加水
+} Water_Cmd_t;
+
+// 加水控制相关全局变量
+static volatile Water_Cmd_t water_command = WATER_CMD_NONE; // 加水命令标志
+static volatile uint8_t watering_in_progress = 0; // 加水进行中标志
+
/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
- .name = "defaultTask",
- .stack_size = 256 * 4,
- .priority = (osPriority_t)osPriorityHigh1,
+ .name = "defaultTask",
+ .stack_size = 256 * 4,
+ .priority = (osPriority_t) osPriorityHigh1,
};
/* Definitions for wifi_mqtt */
osThreadId_t wifi_mqttHandle;
const osThreadAttr_t wifi_mqtt_attributes = {
- .name = "wifi_mqtt",
- .stack_size = 3000 * 4,
- .priority = (osPriority_t)osPriorityHigh,
+ .name = "wifi_mqtt",
+ .stack_size = 3000 * 4,
+ .priority = (osPriority_t) osPriorityHigh,
};
/* Definitions for LCD_SHOW_Task */
osThreadId_t LCD_SHOW_TaskHandle;
const osThreadAttr_t LCD_SHOW_Task_attributes = {
- .name = "LCD_SHOW_Task",
- .stack_size = 1024 * 4,
- .priority = (osPriority_t)osPriorityHigh,
+ .name = "LCD_SHOW_Task",
+ .stack_size = 1024 * 4,
+ .priority = (osPriority_t) osPriorityHigh,
};
/* Definitions for button */
osThreadId_t buttonHandle;
const osThreadAttr_t button_attributes = {
- .name = "button",
- .stack_size = 512 * 4,
- .priority = (osPriority_t)osPriorityRealtime2,
+ .name = "button",
+ .stack_size = 512 * 4,
+ .priority = (osPriority_t) osPriorityRealtime2,
};
/* Definitions for sensor */
osThreadId_t sensorHandle;
const osThreadAttr_t sensor_attributes = {
- .name = "sensor",
- .stack_size = 1024 * 4,
- .priority = (osPriority_t)osPriorityNormal,
+ .name = "sensor",
+ .stack_size = 1024 * 4,
+ .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for step_motor */
osThreadId_t step_motorHandle;
const osThreadAttr_t step_motor_attributes = {
- .name = "step_motor",
- .stack_size = 512 * 4,
- .priority = (osPriority_t)osPriorityNormal,
+ .name = "step_motor",
+ .stack_size = 512 * 4,
+ .priority = (osPriority_t) osPriorityNormal,
+};
+/* Definitions for water_control */
+osThreadId_t water_controlHandle;
+const osThreadAttr_t water_control_attributes = {
+ .name = "water_control",
+ .stack_size = 512 * 4,
+ .priority = (osPriority_t) osPriorityNormal,
};
/* Private function prototypes -----------------------------------------------*/
@@ -165,6 +183,16 @@ void LCD_UpdateSensorData(float temp, float humi, float weight, uint8_t water,
Sensor_Data_t *LCD_GetSensorData(void);
void user_button_init(void);
void RTC_TimeUpdateCallback(void); // RTC时间更新回调函数
+uint8_t Build_Sensor_JSON(const Sensor_Data_t *data, char *buffer, uint16_t buffer_size);
+
+
+static void Execute_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) ;
+void Clear_Feed_Command(void);
+uint8_t Request_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount);
+
+uint8_t Request_Water(Water_Cmd_t cmd);
+void Clear_Water_Command(void);
+
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
@@ -173,14 +201,15 @@ void LCD_Task(void *argument);
void button_task(void *argument);
void sensorTask(void *argument);
void step_motor_task(void *argument);
+void water_controlTask(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/**
- * @brief FreeRTOS initialization
- * @param None
- * @retval None
- */
+ * @brief FreeRTOS initialization
+ * @param None
+ * @retval None
+ */
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
ST7735_Init(); // 初始化ST7735显示屏
@@ -205,8 +234,7 @@ void MX_FREERTOS_Init(void) {
/* Create the thread(s) */
/* creation of defaultTask */
- defaultTaskHandle =
- osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
+ defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of wifi_mqtt */
wifi_mqttHandle = osThreadNew(wifi_task_mqtt, NULL, &wifi_mqtt_attributes);
@@ -223,6 +251,9 @@ void MX_FREERTOS_Init(void) {
/* creation of step_motor */
step_motorHandle = osThreadNew(step_motor_task, NULL, &step_motor_attributes);
+ /* creation of water_control */
+ water_controlHandle = osThreadNew(water_controlTask, NULL, &water_control_attributes);
+
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
@@ -230,6 +261,7 @@ void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
+
}
/* USER CODE BEGIN Header_StartDefaultTask */
@@ -239,7 +271,8 @@ void MX_FREERTOS_Init(void) {
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
-void StartDefaultTask(void *argument) {
+void StartDefaultTask(void *argument)
+{
/* USER CODE BEGIN StartDefaultTask */
// 1. 打开运行灯
Device_Control(DEVICE_LED_RUN, 1);
@@ -281,7 +314,8 @@ void StartDefaultTask(void *argument) {
* @retval None
*/
/* USER CODE END Header_LCD_Task */
-void LCD_Task(void *argument) {
+void LCD_Task(void *argument)
+{
/* USER CODE BEGIN LCD_Task */
char display_str[32];
@@ -503,7 +537,8 @@ void LCD_Task(void *argument) {
* @retval None
*/
/* USER CODE END Header_button_task */
-void button_task(void *argument) {
+void button_task(void *argument)
+{
/* USER CODE BEGIN button_task */
user_button_init();
@@ -522,7 +557,8 @@ void button_task(void *argument) {
* @retval None
*/
/* USER CODE END Header_sensorTask */
-void sensorTask(void *argument) {
+void sensorTask(void *argument)
+{
/* USER CODE BEGIN sensorTask */
elog_i(TAG, "启动传感器任务");
@@ -582,108 +618,35 @@ void sensorTask(void *argument) {
sensor_data.food_weight= HX711_GetWeight(10); // 采样10次取平均
elog_d(TAG, "当前重量:%.2f g", sensor_data.food_weight);
+ // ========== 发布传感器数据到MQTT ==========
+ char json_buffer[128];
+ if (Build_Sensor_JSON(&sensor_data, json_buffer, sizeof(json_buffer))) {
+ if (WIFI_MQTT_Publish_Sensor(json_buffer)) {
+ elog_i(TAG, "传感器数据发布成功: %s", json_buffer);
+ } else {
+ elog_e(TAG, "传感器数据发布失败");
+ }
+ } else {
+ elog_e(TAG, "构建JSON数据失败");
+ }
+ // ========== 发布结束 ===============
// 每2秒读取一次
osDelay(2000);
}
/* USER CODE END sensorTask */
}
-/**
- * @brief 请求喂食操作
- * @param cmd: 喂食命令类型
- * @param angle: 转动角度
- * @param amount: 喂食份数
- * @retval 1=请求成功, 0=请求失败(正在喂食中)
- */
-uint8_t Request_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) {
- if (feeding_in_progress) {
- elog_w("FEED", "喂食进行中,无法接受新命令");
- return 0;
- }
-
- feed_command = cmd;
- feed_angle = angle;
- feed_amount = amount;
-
- elog_i("FEED", "喂食请求已提交: cmd=%d, angle=%d, amount=%d", cmd, angle,
- amount);
- return 1;
-}
-
-/**
- * @brief 获取当前喂食命令
- * @retval 当前喂食命令
- */
-Feed_Cmd_t Get_Feed_Command(void) { return feed_command; }
-
-/**
- * @brief 清除喂食命令
- */
-void Clear_Feed_Command(void) { feed_command = FEED_CMD_NONE; }
-
-/**
- * @brief 执行喂食操作
- * @param cmd: 喂食命令类型
- * @param angle: 转动角度
- * @param amount: 喂食份数
- */
-static void Execute_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) {
- if (feeding_in_progress) {
- return; // 防止重复执行
- }
-
- feeding_in_progress = 1;
-
- // 根据命令类型播放不同音频
- switch (cmd) {
- case FEED_CMD_MANUAL:
- MP3_Play(FEED_MANUAL_TRIGGER);
- elog_i("FEED", "执行手动喂食: 角度%d度, %d份", angle, amount);
- break;
- case FEED_CMD_AUTO:
- MP3_Play(FEED_AUTO_START);
- elog_i("FEED", "执行自动喂食: 角度%d度, %d份", angle, amount);
- break;
- case FEED_CMD_REMOTE:
- MP3_Play(REMOTE_CMD_RECEIVED);
- elog_i("FEED", "执行远程喂食: 角度%d度, %d份", angle, amount);
- break;
- case FEED_CMD_TEST:
- elog_i("FEED", "执行测试喂食: 角度%d度", angle);
- break;
- default:
- break;
- }
-
- // 执行实际的喂食动作
- for (uint8_t i = 0; i < amount; i++) {
- Stepper_Motor_RotateAngle(angle, STEPPER_DIR_CW, 2, STEPPER_MODE_FULL_STEP);
- if (i < amount - 1) {
- osDelay(1000); // 多份之间间隔1秒
- }
- }
-
- // 等待食物落下
- osDelay(3000);
-
- // 停止电机
- Stepper_Motor_Stop();
-
- // 播放完成音效
- MP3_Play(FEED_COMPLETE);
- elog_i("FEED", "喂食完成");
-
- feeding_in_progress = 0;
-}
-
/* USER CODE BEGIN Header_step_motor_task */
+
+
/**
* @brief Function implementing the step_motor thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_step_motor_task */
-void step_motor_task(void *argument) {
+void step_motor_task(void *argument)
+{
/* USER CODE BEGIN step_motor_task */
// 初始化步进电机
Stepper_Motor_Init();
@@ -708,6 +671,91 @@ void step_motor_task(void *argument) {
/* USER CODE END step_motor_task */
}
+/* USER CODE BEGIN Header_water_controlTask */
+/**
+* @brief Function implementing the water_control thread.
+* @param argument: Not used
+* @retval None
+*/
+/* USER CODE END Header_water_controlTask */
+void water_controlTask(void *argument)
+{
+ /* USER CODE BEGIN water_controlTask */
+ elog_i("WATER", "启动加水控制任务");
+
+ /* Infinite loop */
+ for(;;)
+ {
+ // 检查是否有加水命令需要执行
+ if (water_command != WATER_CMD_NONE && !watering_in_progress) {
+ Water_Cmd_t current_cmd = water_command;
+
+ // 清除命令标志
+ Clear_Water_Command();
+
+ // 执行加水
+ watering_in_progress = 1;
+
+ // 根据命令类型播放不同音频
+ switch (current_cmd) {
+ case WATER_CMD_MANUAL:
+ MP3_Play(WATER_REFILL_DONE);
+ elog_i("WATER", "执行手动加水");
+ break;
+ case WATER_CMD_AUTO:
+ MP3_Play(WATER_REFILL_DONE);
+ elog_i("WATER", "执行自动加水");
+ break;
+ case WATER_CMD_REMOTE:
+ MP3_Play(REMOTE_CMD_RECEIVED);
+ elog_i("WATER", "执行远程加水");
+ break;
+ default:
+ break;
+ }
+
+ // 打开水泵继电器(假设DEVICE_RELAY控制水泵)
+ Device_Control(DEVICE_RELAY, 1);
+ elog_i("WATER", "水泵启动,等待水位传感器检测到有水");
+
+ // 加水超时保护(最大30秒)
+ uint32_t start_time = osKernelGetTickCount();
+ const uint32_t MAX_WATERING_TIME = 30000; // 30秒超时
+
+ // 循环检查水位传感器,直到检测到有水或超时
+ while (1) {
+ // 检查是否检测到有水
+ if (sensor_data.water_level == 1) {
+ elog_i("WATER", "水位传感器检测到有水,停止加水");
+ break;
+ }
+
+ // 检查是否超时
+ if (osKernelGetTickCount() - start_time > MAX_WATERING_TIME) {
+ elog_w("WATER", "加水超时(30秒),强制停止");
+ break;
+ }
+
+ // 短暂延时,避免占用过多CPU
+ osDelay(100);
+ }
+
+ // 关闭水泵继电器
+ Device_Control(DEVICE_RELAY, 0);
+ elog_i("WATER", "水泵停止");
+
+ // 播放完成音效
+ MP3_Play(WATER_REFILL_DONE);
+ elog_i("WATER", "加水完成");
+
+ watering_in_progress = 0;
+ }
+
+ osDelay(100); // 短暂延时,保持任务响应性
+ }
+ /* USER CODE END water_controlTask */
+}
+
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
@@ -795,7 +843,26 @@ void key3_single_click_handler(Button *btn) {
LCD_NextPage();
}
-void key4_single_click_handler(Button *btn) { elog_i("KEY", "按键4单击"); }
+/**
+ * @brief 按键4处理函数:手动补水(仅手动模式有效)
+ * @param btn: 按键句柄
+ */
+void key4_single_click_handler(Button *btn) {
+ // 检查是否为手动模式
+ if (!system_mode) {
+ // 手动模式下才允许手动补水
+ if (Request_Water(WATER_CMD_MANUAL)) {
+ elog_i("KEY", "按键4单击 - 手动补水请求已提交");
+ } else {
+ elog_w("KEY", "补水进行中,请稍后再试");
+ MP3_Play(SYS_ERROR_ALARM);
+ }
+ } else {
+ // 自动模式下按键无效
+ elog_w("KEY", "当前为自动模式,按键4无效");
+ MP3_Play(SYS_ERROR_ALARM);
+ }
+}
void M3_long_press_start_handler(Button *btn) {
elog_i("KEY", "M3水位传感器检测到有水(低电平)");
@@ -856,6 +923,10 @@ void Set_System_Mode(uint8_t mode) {
system_mode = mode;
sensor_data.system_mode = mode;
lcd_force_refresh = 1; // 强制刷新显示
+
+ // 播放模式切换音效(与按键1逻辑保持一致)
+ MP3_Play(mode ? MODE_AUTO : MODE_MANUAL);
+
elog_i("SYSTEM", "系统模式设置为: %s", mode ? "自动" : "手动");
}
}
@@ -950,6 +1021,153 @@ Sensor_Data_t *LCD_GetSensorData(void) { return &sensor_data; }
*/
void RTC_TimeUpdateCallback(void) { lcd_force_refresh = 1; }
+
+
+
+/**
+ * @brief 构建传感器数据JSON字符串(符合小程序格式要求)
+ * @param data 传感器数据结构体指针
+ * @param buffer 输出缓冲区
+ * @param buffer_size 缓冲区大小
+ * @return 1表示成功,0表示失败(参数错误或缓冲区不足)
+ */
+uint8_t Build_Sensor_JSON(const Sensor_Data_t *data, char *buffer, uint16_t buffer_size) {
+ // 参数检查
+ if (!data || !buffer || buffer_size < 128) {
+ return 0;
+ }
+
+ // 将float类型的湿度和重量转换为整数(小程序要求整数)
+ int humidity_int = (int)data->humidity;
+ int food_weight_int = (int)data->food_weight;
+
+ // 构建符合小程序要求的JSON字符串
+ // 注意字段名:foodWeight(camelCase)而不是food_weight(snake_case)
+ // waterLevel(camelCase)而不是water_level(snake_case)
+ snprintf(buffer, buffer_size,
+ "{\"temperature\":%.1f,\"humidity\":%d,\"foodWeight\":%d,\"waterLevel\":%d}",
+ data->temperature, // 温度(保留1位小数)
+ humidity_int, // 湿度(整数)
+ food_weight_int, // 食物重量(整数)
+ data->water_level); // 水位状态(0或1)
+
+ return 1;
+}
+
+
+
+/**
+ * @brief 请求喂食操作
+ * @param cmd: 喂食命令类型
+ * @param angle: 转动角度
+ * @param amount: 喂食份数
+ * @retval 1=请求成功, 0=请求失败(正在喂食中)
+ */
+uint8_t Request_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) {
+ if (feeding_in_progress) {
+ elog_w("FEED", "喂食进行中,无法接受新命令");
+ return 0;
+ }
+
+ feed_command = cmd;
+ feed_angle = angle;
+ feed_amount = amount;
+
+ elog_i("FEED", "喂食请求已提交: cmd=%d, angle=%d, amount=%d", cmd, angle,
+ amount);
+ return 1;
+}
+
+/**
+ * @brief 获取当前喂食命令
+ * @retval 当前喂食命令
+ */
+Feed_Cmd_t Get_Feed_Command(void) { return feed_command; }
+
+/**
+ * @brief 清除喂食命令
+ */
+void Clear_Feed_Command(void) { feed_command = FEED_CMD_NONE; }
+
+/**
+ * @brief 请求加水操作
+ * @param cmd: 加水命令类型
+ * @retval 1=请求成功, 0=请求失败(正在加水中)
+ */
+uint8_t Request_Water(Water_Cmd_t cmd) {
+ if (watering_in_progress) {
+ elog_w("WATER", "加水进行中,无法接受新命令");
+ return 0;
+ }
+
+ water_command = cmd;
+
+ elog_i("WATER", "加水请求已提交: cmd=%d", cmd);
+ return 1;
+}
+
+/**
+ * @brief 清除加水命令
+ */
+void Clear_Water_Command(void) { water_command = WATER_CMD_NONE; }
+
+/**
+ * @brief 执行喂食操作
+ * @param cmd: 喂食命令类型
+ * @param angle: 转动角度
+ * @param amount: 喂食份数
+ */
+static void Execute_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) {
+ if (feeding_in_progress) {
+ return; // 防止重复执行
+ }
+
+ feeding_in_progress = 1;
+
+ // 根据命令类型播放不同音频
+ switch (cmd) {
+ case FEED_CMD_MANUAL:
+ MP3_Play(FEED_MANUAL_TRIGGER);
+ elog_i("FEED", "执行手动喂食: 角度%d度, %d份", angle, amount);
+ break;
+ case FEED_CMD_AUTO:
+ MP3_Play(FEED_AUTO_START);
+ elog_i("FEED", "执行自动喂食: 角度%d度, %d份", angle, amount);
+ break;
+ case FEED_CMD_REMOTE:
+ MP3_Play(REMOTE_CMD_RECEIVED);
+ elog_i("FEED", "执行远程喂食: 角度%d度, %d份", angle, amount);
+ break;
+ case FEED_CMD_TEST:
+ elog_i("FEED", "执行测试喂食: 角度%d度", angle);
+ break;
+ default:
+ break;
+ }
+
+ // 执行实际的喂食动作
+ for (uint8_t i = 0; i < amount; i++) {
+ Stepper_Motor_RotateAngle(angle, STEPPER_DIR_CW, 2, STEPPER_MODE_FULL_STEP);
+ if (i < amount - 1) {
+ osDelay(1000); // 多份之间间隔1秒
+ }
+ }
+
+ // 等待食物落下
+ osDelay(3000);
+
+ // 停止电机
+ Stepper_Motor_Stop();
+
+ // 播放完成音效
+ MP3_Play(FEED_COMPLETE);
+ elog_i("FEED", "喂食完成");
+
+ feeding_in_progress = 0;
+}
+
+
/* USER CODE END LCD_Page_Functions */
-/* USER CODE END Application */
\ No newline at end of file
+/* USER CODE END Application */
+
diff --git a/README.md b/README.md
index 17aa84a..6241444 100644
--- a/README.md
+++ b/README.md
@@ -75,4 +75,174 @@
- `SYS_ERROR_ALARM` (20): 系统检测到异常,请检查硬件模块
### 使用说明:
-系统通过调用相应的音频索引值来播放对应的提示音,为用户提供直观的听觉反馈,增强用户体验。
\ No newline at end of file
+系统通过调用相应的音频索引值来播放对应的提示音,为用户提供直观的听觉反馈,增强用户体验。
+
+## FreeRTOS任务架构与系统功能
+
+### 系统概述
+本智能宠物喂食器系统基于FreeRTOS实时操作系统,采用多任务架构实现模块化功能。系统包含6个主要任务,分别负责不同的功能模块。
+
+### 任务列表与优先级
+| 任务名称 | 堆栈大小 | 优先级 | 功能描述 |
+|----------|----------|--------|----------|
+| defaultTask | 256*4字节 | High1 | 系统默认任务,负责初始化核心组件(LED、日志、RTC、MP3) |
+| wifi_mqtt | 3000*4字节 | High | WiFi和MQTT通信任务,负责网络连接和数据传输 |
+| LCD_SHOW_Task | 1024*4字节 | High | LCD显示任务,负责显示系统状态和信息 |
+| button | 512*4字节 | Realtime2 | 按钮处理任务,负责检测和处理按键输入 |
+| sensor | 1024*4字节 | Normal | 传感器数据采集任务,负责读取温湿度和重量数据 |
+| step_motor | 512*4字节 | Normal | 步进电机控制任务,负责执行喂食操作 |
+
+### 主要数据结构
+
+#### LCD页面枚举 (LCD_Page_t)
+```c
+typedef enum {
+ LCD_PAGE_TIME = 0, // 时间显示页面
+ LCD_PAGE_TEMP_HUMI, // 温湿度页面
+ LCD_PAGE_FOOD_WEIGHT, // 食物重量页面
+ LCD_PAGE_WATER_LEVEL, // 水位页面
+ LCD_PAGE_SYSTEM_STATUS // 系统状态页面
+} LCD_Page_t;
+```
+
+#### 传感器数据结构体 (Sensor_Data_t)
+```c
+typedef struct {
+ float temperature; // 温度值
+ float humidity; // 湿度值
+ float food_weight; // 食物重量
+ uint8_t water_level; // 水位状态 (0=无水, 1=有水)
+ uint8_t system_mode; // 系统模式 (0=手动, 1=自动)
+} Sensor_Data_t;
+```
+
+#### 喂食控制命令枚举 (Feed_Cmd_t)
+```c
+typedef enum {
+ FEED_CMD_NONE = 0, // 无命令
+ FEED_CMD_MANUAL, // 手动喂食
+ FEED_CMD_AUTO, // 自动喂食
+ FEED_CMD_REMOTE, // 远程喂食
+ FEED_CMD_TEST // 测试喂食
+} Feed_Cmd_t;
+```
+
+### 全局变量
+- `current_page`: 当前LCD显示页面
+- `sensor_data`: 传感器数据
+- `lcd_force_refresh`: 强制刷新标志(RTC更新时设置)
+- `system_mode`: 系统模式(1=自动模式,0=手动模式)
+- `feeding_in_progress`: 喂食进行中标志
+- `feed_command`: 喂食命令标志
+- `feed_angle`: 喂食角度(默认90度)
+- `feed_amount`: 喂食份数
+
+### 各任务详细功能
+
+#### 1. defaultTask (默认任务)
+- 初始化运行LED
+- 初始化日志系统 (easylogger)
+- 初始化RTC管理模块
+- 初始化MP3模块并播放系统启动音
+- 主循环:闪烁运行LED(1秒开,1秒关)
+
+#### 2. LCD_SHOW_Task (LCD显示任务)
+- 显示5个不同页面:
+ 1. **时间页面**: 显示当前时间和日期
+ 2. **温湿度页面**: 显示温度和湿度值
+ 3. **食物重量页面**: 显示食物重量和状态(LOW/MEDIUM/GOOD)
+ 4. **水位页面**: 显示水位状态(NO WATER/WATER OK)
+ 5. **系统状态页面**: 显示MQTT连接状态、系统模式、喂食状态
+- 刷新策略:
+ - 时间页面和系统状态页面:每30秒刷新
+ - 其他页面:每2秒刷新
+- 支持RTC时间更新回调,时间更新时立即刷新显示
+
+#### 3. sensorTask (传感器任务)
+- HX711称重传感器校准(零点校准)
+- AHT30温湿度传感器初始化
+- 循环读取温湿度数据(每2秒一次)
+- 循环读取食物重量数据(每2秒一次)
+- 更新全局传感器数据结构
+
+#### 4. button_task (按钮任务)
+- 初始化6个按钮/传感器:
+ 1. KEY1: 模式切换(自动/手动)
+ 2. KEY2: 手动喂食(仅手动模式有效)
+ 3. KEY3: 切换显示界面
+ 4. KEY4: 长按设置时间
+ 5. M3_IO: 水位传感器
+ 6. HC_SR505: 人体存在传感器
+- 主循环:每5ms检测一次按钮状态
+
+#### 5. step_motor_task (步进电机任务)
+- 初始化步进电机
+- 检查喂食命令队列
+- 执行喂食操作:
+ - 根据命令类型播放相应音频
+ - 控制步进电机旋转指定角度和份数
+ - 等待食物落下(3秒)
+ - 停止电机并播放完成音效
+
+#### 6. wifi_task_mqtt (WiFi和MQTT任务)
+- 在外部文件中实现
+- 负责WiFi连接和MQTT通信
+- 处理远程控制指令
+
+### 按键功能说明
+| 按键 | 功能 | 说明 |
+|------|------|------|
+| KEY1 | 模式切换 | 单击切换自动/手动模式,播放相应音效 |
+| KEY2 | 手动喂食 | 仅在手动模式下有效,触发90度单份喂食 |
+| KEY3 | 显示切换 | 单击切换到下一个LCD页面 |
+| KEY4 | 时间设置 | 长按进入时间设置模式(待实现) |
+| M3_IO | 水位检测 | 检测水位状态,更新传感器数据 |
+| HC_SR505 | 人体存在 | 检测人体接近(待实现更多功能) |
+
+### 喂食控制流程
+1. **请求阶段**: 通过`Request_Feed()`函数提交喂食请求
+2. **命令传递**: 设置全局变量`feed_command`, `feed_angle`, `feed_amount`
+3. **执行阶段**: `step_motor_task`检测到命令后调用`Execute_Feed()`
+4. **动作执行**:
+ - 播放相应音频提示
+ - 步进电机旋转指定角度和份数
+ - 等待食物落下
+ - 播放完成音效
+5. **状态恢复**: 清除喂食进行中标志
+
+### LCD页面刷新策略
+- **时间页面**和**系统状态页面**: 每30秒刷新,但每100ms检查一次页面切换
+- **其他页面**: 每2秒刷新
+- **强制刷新**: RTC时间更新时立即刷新显示
+
+### 系统模式
+- **自动模式 (system_mode=1)**: 系统根据预设规则自动执行喂食
+- **手动模式 (system_mode=0)**: 用户通过按键或远程控制手动触发喂食
+
+### 音频反馈系统
+系统通过MP3模块提供丰富的音频反馈,包括:
+- 系统启动音
+- 模式切换提示
+- 喂食开始和完成提示
+- 错误报警音
+- 远程控制接收提示
+
+### 数据流
+```
+传感器采集 → sensor_data更新 → LCD显示更新 → 用户查看
+按键输入 → 模式/喂食控制 → 状态更新 → LCD/音频反馈
+远程指令 → MQTT接收 → 喂食控制 → 执行反馈
+```
+
+### 扩展接口
+- `Request_Feed()`: 外部请求喂食接口
+- `Get_System_Mode()`: 获取当前系统模式
+- `Set_System_Mode()`: 设置系统模式
+- `LCD_UpdateSensorData()`: 更新传感器数据
+- `LCD_GetSensorData()`: 获取传感器数据指针
+
+### 注意事项
+1. HX711称重传感器首次使用时需要执行校准流程
+2. 喂食过程中不接受新的喂食请求
+3. 手动喂食仅在手动模式下有效
+4. RTC时间通过网络同步,网络断开时使用本地RTC时间
\ No newline at end of file
diff --git a/hahha.ioc b/hahha.ioc
index 51d1cc4..ea15647 100644
--- a/hahha.ioc
+++ b/hahha.ioc
@@ -35,7 +35,7 @@ Dma.USART1_TX.0.Priority=DMA_PRIORITY_VERY_HIGH
Dma.USART1_TX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
FREERTOS.FootprintOK=true
FREERTOS.IPParameters=Tasks01,FootprintOK,configTOTAL_HEAP_SIZE
-FREERTOS.Tasks01=defaultTask,41,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;wifi_mqtt,40,3000,wifi_task_mqtt,As external,NULL,Dynamic,NULL,NULL;LCD_SHOW_Task,40,1024,LCD_Task,Default,NULL,Dynamic,NULL,NULL;button,50,512,button_task,Default,NULL,Dynamic,NULL,NULL;sensor,24,1024,sensorTask,Default,NULL,Dynamic,NULL,NULL;step_motor,24,512,step_motor_task,Default,NULL,Dynamic,NULL,NULL
+FREERTOS.Tasks01=defaultTask,41,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;wifi_mqtt,40,3000,wifi_task_mqtt,As external,NULL,Dynamic,NULL,NULL;LCD_SHOW_Task,40,1024,LCD_Task,Default,NULL,Dynamic,NULL,NULL;button,50,512,button_task,Default,NULL,Dynamic,NULL,NULL;sensor,24,1024,sensorTask,Default,NULL,Dynamic,NULL,NULL;step_motor,24,512,step_motor_task,Default,NULL,Dynamic,NULL,NULL;water_control,24,512,water_controlTask,Default,NULL,Dynamic,NULL,NULL
FREERTOS.configTOTAL_HEAP_SIZE=30000
File.Version=6
GPIO.groupedBy=Group By Peripherals
diff --git a/wex_small/utils/mqtt.config.js b/wex_small/utils/mqtt.config.js
index eec78c0..ade966f 100644
--- a/wex_small/utils/mqtt.config.js
+++ b/wex_small/utils/mqtt.config.js
@@ -13,7 +13,7 @@ const MQTT_CONFIG = {
username: 'STM32_MQTT',
// 密码(用于认证)
- password: 'STM32_MQTT123456',
+ password: '123456',
// 客户端ID(唯一标识)
clientId: `SmartPetFeeder_${Math.random().toString(16).substr(2, 8)}`,