Files
SmartPetFeeder_STM32/Core/Src/freertos.c
wangbeihong f5077adbe7 ```
feat(BSP): 添加WiFi模块MQTT连接优化和SNTP配置改进

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

536 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "device_ctrl.h"
#include "dx_wf_24.h"
#include "elog.h"
#include "mp3_driver.h"
#include "mp3_play_index.h"
#include "multi_button.h"
#include "spi_st7735s.h"
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define TAG "Main"
/******************** 按键功能定义 ********************/
#define KEY1_MODE_SWITCH // 按键1模式切换自动/手动)
#define KEY2_MANUAL_FEED // 按键2手动喂食仅手动模式有效
#define KEY3_DISPLAY_NEXT // 按键3切换显示界面
#define KEY4_TIME_SET // 按键4长按设置时间
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
// LCD页面枚举定义
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;
// 传感器数据结构体
typedef struct {
float temperature; // 温度值
float humidity; // 湿度值
float food_weight; // 食物重量
uint8_t water_level; // 水位状态 (0=无水, 1=有水)
uint8_t system_mode; // 系统模式 (0=手动, 1=自动)
} Sensor_Data_t;
// 全局变量
static LCD_Page_t current_page = LCD_PAGE_TIME; // 当前显示页面
static Sensor_Data_t sensor_data = {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,
};
/* 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,
};
/* 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,
};
/* Definitions for button */
osThreadId_t buttonHandle;
const osThreadAttr_t button_attributes = {
.name = "button",
.stack_size = 512 * 4,
.priority = (osPriority_t) osPriorityRealtime2,
};
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
void LCD_NextPage(void);
void LCD_PrevPage(void);
void LCD_SetPage(LCD_Page_t page);
LCD_Page_t LCD_GetCurrentPage(void);
void LCD_UpdateSensorData(float temp, float humi, float weight, uint8_t water,
uint8_t mode);
Sensor_Data_t *LCD_GetSensorData(void);
void user_button_init(void);
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
extern void wifi_task_mqtt(void *argument);
void LCD_Task(void *argument);
void button_task(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
ST7735_Init(); // 初始化ST7735显示屏
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of wifi_mqtt */
wifi_mqttHandle = osThreadNew(wifi_task_mqtt, NULL, &wifi_mqtt_attributes);
/* creation of LCD_SHOW_Task */
LCD_SHOW_TaskHandle = osThreadNew(LCD_Task, NULL, &LCD_SHOW_Task_attributes);
/* creation of button */
buttonHandle = osThreadNew(button_task, NULL, &button_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
// 1. 打开运行灯
Device_Control(DEVICE_LED_RUN, 1);
// 2. 初始化日志和屏幕(在 RTOS 任务中)
easylogger_init();
// 初始化MP3模块
HAL_StatusTypeDef ret = MP3_Init();
if (ret != HAL_OK) {
elog_e("MP3", "MP3模块初始化失败");
return;
}
elog_i("MP3", "模块初始化完成");
MP3_Play(SYS_POWER_ON); // 播放系统启动音
/* Infinite loop */
for (;;) {
Device_Control(DEVICE_LED_RUN, 1); // 打开运行灯
osDelay(1000);
Device_Control(DEVICE_LED_RUN, 0); // 关闭运行灯
osDelay(1000);
}
/* USER CODE END StartDefaultTask */
}
/* USER CODE BEGIN Header_LCD_Task */
/**
* @brief Function implementing the LCD_SHOW_Task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_LCD_Task */
void LCD_Task(void *argument)
{
/* USER CODE BEGIN LCD_Task */
char display_str[32];
SNTP_Time_t now;
uint16_t text_color = ST7735_WHITE;
uint16_t bg_color = ST7735_BLACK;
// 显示区域参数 - 适配160x80横屏
// 宽度160px高度80px使用较小字体确保内容完整显示
// 初始化传感器数据 - 全部设置为NC状态
sensor_data.temperature = 0.0f; // NC - 未检测到温度
sensor_data.humidity = 0.0f; // NC - 未检测到湿度
sensor_data.food_weight = 0.0f; // NC - 未检测到重量
sensor_data.water_level = 0; // NC - 未检测到水
sensor_data.system_mode = 1; // 自动模式
/* Infinite loop */
for (;;) {
// 根据当前页面显示不同内容
switch (current_page) {
case LCD_PAGE_TIME:
// 时间显示页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "Time", &Font_7x10, text_color, bg_color);
if (WIFI_Is_Time_Valid()) {
WIFI_Get_SNTP_Time();
now = WIFI_Get_Current_Time();
if (now.valid) {
// 显示时间(大字体,只显示时:分,不显示秒)
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 {
ST7735_WriteString(25, 20, "--:--", &Font_16x26, text_color, bg_color);
ST7735_WriteString(20, 60, "----/--/--", &Font_7x10, ST7735_CYAN, bg_color);
}
} else {
ST7735_WriteString(25, 20, "--:--", &Font_16x26, text_color, bg_color);
ST7735_WriteString(20, 60, "----/--/--", &Font_7x10, ST7735_CYAN, bg_color);
}
break;
case LCD_PAGE_TEMP_HUMI:
// 温湿度页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "Temp & Humi", &Font_7x10, text_color, bg_color);
// 显示温度
snprintf(display_str, sizeof(display_str), "T: %.1f C", sensor_data.temperature);
ST7735_WriteString(5, 20, display_str, &Font_11x18, text_color, bg_color);
// 显示湿度
snprintf(display_str, sizeof(display_str), "H: %.1f %%", sensor_data.humidity);
ST7735_WriteString(5, 45, display_str, &Font_11x18, text_color, bg_color);
break;
case LCD_PAGE_FOOD_WEIGHT:
// 食物重量页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "Food Weight", &Font_7x10, text_color, bg_color);
// 显示食物重量
snprintf(display_str, sizeof(display_str), "%.1f g", sensor_data.food_weight);
ST7735_WriteString(25, 20, display_str, &Font_16x26, text_color, bg_color);
// 显示状态
if (sensor_data.food_weight < 50.0f) {
ST7735_WriteString(20, 55, "Low", &Font_11x18, ST7735_RED, bg_color);
} else if (sensor_data.food_weight < 100.0f) {
ST7735_WriteString(15, 55, "Medium", &Font_11x18, ST7735_YELLOW, bg_color);
} else {
ST7735_WriteString(20, 55, "Good", &Font_11x18, ST7735_GREEN, bg_color);
}
break;
case LCD_PAGE_WATER_LEVEL:
// 水位页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "Water Level", &Font_7x10, text_color, bg_color);
// 根据水位传感器状态显示
if (sensor_data.water_level == 0) {
ST7735_WriteString(20, 20, "NO WATER", &Font_16x26, ST7735_RED, bg_color);
ST7735_WriteString(35, 55, "Add water", &Font_11x18, ST7735_YELLOW, bg_color);
} else {
ST7735_WriteString(50, 25, "OK", &Font_16x26, ST7735_GREEN, bg_color);
ST7735_WriteString(35, 55, "Water OK", &Font_11x18, text_color, bg_color);
}
break;
case LCD_PAGE_SYSTEM_STATUS:
// 系统状态页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "System Status", &Font_7x10, text_color, bg_color);
// 显示WiFi状态
if (WIFI_Is_Time_Valid()) {
ST7735_WriteString(5, 20, "WiFi: OK", &Font_11x18, ST7735_GREEN, bg_color);
} else {
ST7735_WriteString(5, 20, "WiFi: OFF", &Font_11x18, ST7735_RED, bg_color);
}
// 显示系统模式
if (sensor_data.system_mode) {
ST7735_WriteString(5, 45, "Mode: AUTO", &Font_11x18, text_color, bg_color);
} else {
ST7735_WriteString(5, 45, "Mode: MANUAL", &Font_11x18, text_color, bg_color);
}
break;
}
// 记录当前页面
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 */
}
/* USER CODE BEGIN Header_button_task */
/**
* @brief Function implementing the button thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_button_task */
void button_task(void *argument)
{
/* USER CODE BEGIN button_task */
user_button_init();
/* Infinite loop */
for(;;)
{
button_ticks();
osDelay(5);
}
/* USER CODE END button_task */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
// 按键库实现部分//
/* USER CODE BEGIN KEY Prototypes */
static Button KEY1; // 按键1
static Button KEY2; // 按键2
static Button KEY3; // 按键3
static Button KEY4; // 按键4
uint8_t read_button_gpio(uint8_t button_id) {
switch (button_id) {
case 1:
return HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin);
break;
case 2:
return HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin);
break;
case 3:
return HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin);
break;
case 4:
return HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin);
break;
default:
return 0;
}
}
void key1_single_click_handler(Button *btn) { elog_i("KEY", "按键1单击"); }
void key2_single_click_handler(Button *btn) { elog_i("KEY", "按键2单击"); }
void key3_single_click_handler(Button *btn) {
elog_i("KEY", "按键3单击");
LCD_NextPage();
}
void key4_single_click_handler(Button *btn) { elog_i("KEY", "按键4单击"); }
void user_button_init(void) {
// 初始化按键 (active_level: 0=低电平有效, 1=高电平有效)
button_init(&KEY1, read_button_gpio, 0, 1);
button_init(&KEY2, read_button_gpio, 0, 2);
button_init(&KEY3, read_button_gpio, 0, 3);
button_init(&KEY4, read_button_gpio, 0, 4);
elog_i("BUTTON", "按键初始化完成");
// 设置按键回调函数
button_attach(&KEY1, BTN_SINGLE_CLICK, key1_single_click_handler);
button_attach(&KEY2, BTN_SINGLE_CLICK, key2_single_click_handler);
button_attach(&KEY3, BTN_SINGLE_CLICK, key3_single_click_handler);
button_attach(&KEY4, BTN_SINGLE_CLICK, key4_single_click_handler);
elog_i("BUTTON", "按键回调函数设置完成");
// 启动按键任务
button_start(&KEY1);
button_start(&KEY2);
button_start(&KEY3);
button_start(&KEY4);
elog_i("BUTTON", "按键任务已启动");
}
/* USER CODE END KEY Prototypes */
/* USER CODE BEGIN LCD_Page_Functions */
/**
* @brief 切换到下一个显示页面
* @retval None
*/
void LCD_NextPage(void) {
current_page = (current_page + 1) % 5; // 循环切换5个页面
elog_i("LCD", "切换到页面 %d", current_page + 1);
// 播放页面切换提示音
MP3_Play(PARAM_SAVE_OK); // 使用参数保存成功的音效作为页面切换提示
}
/**
* @brief 切换到上一个显示页面
* @retval None
*/
void LCD_PrevPage(void) {
if (current_page == 0) {
current_page = 4;
} else {
current_page--;
}
elog_i("LCD", "切换到页面 %d", current_page + 1);
// 播放页面切换提示音
MP3_Play(PARAM_SAVE_OK);
}
/**
* @brief 设置当前显示页面
* @param page: 目标页面索引 (0-4)
* @retval None
*/
void LCD_SetPage(LCD_Page_t page) {
if (page < 5) {
current_page = page;
elog_i("LCD", "设置页面为 %d", page + 1);
MP3_Play(PARAM_SAVE_OK);
}
}
/**
* @brief 获取当前页面索引
* @retval 当前页面索引
*/
LCD_Page_t LCD_GetCurrentPage(void) { return current_page; }
/**
* @brief 更新传感器数据
* @param temp: 温度值
* @param humi: 湿度值
* @param weight: 食物重量
* @param water: 水位状态 (0=无水, 1=有水)
* @param mode: 系统模式
* @retval None
*/
void LCD_UpdateSensorData(float temp, float humi, float weight, uint8_t water,
uint8_t mode) {
sensor_data.temperature = temp;
sensor_data.humidity = humi;
sensor_data.food_weight = weight;
sensor_data.water_level = water;
sensor_data.system_mode = mode;
elog_i("LCD", "传感器数据更新: T=%.1fC H=%.1f%% W=%.1fg Water=%s Mode=%s",
temp, humi, weight, water ? "DETECTED" : "NONE",
mode ? "AUTO" : "MANUAL");
}
/**
* @brief 获取传感器数据指针
* @retval 传感器数据结构体指针
*/
Sensor_Data_t *LCD_GetSensorData(void) { return &sensor_data; }
/* USER CODE END LCD_Page_Functions */
/* USER CODE END Application */