feat: 添加HX711称重传感器支持并清理旧文档

- 新增HX711称重传感器驱动(bsp_hx711.c/h)
- 添加HX711相关文档和README
- 更新MP3驱动和FreeRTOS配置
- 修改CMakeLists.txt构建配置
- 删除旧的DX-WF24 WiFi模块开发文档和示例代码
- 整理Development_Docs目录结构
This commit is contained in:
2026-02-25 14:06:47 +08:00
parent 97d6d10731
commit d184d96052
1369 changed files with 383 additions and 406067 deletions

View File

@@ -19,24 +19,26 @@
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "cmsis_os2.h"
#include "main.h"
#include "task.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bsp_aht30.h"
#include "bsp_hx711.h"
#include "bsp_rtc.h"
#include "device_ctrl.h"
#include "dx_wf_24.h"
#include "elog.h"
#include "i2c.h"
#include "mp3_driver.h"
#include "mp3_play_index.h"
#include "multi_button.h"
#include "spi_st7735s.h"
#include "stdio.h"
#include "bsp_aht30.h"
#include "bsp_rtc.h"
#include "i2c.h"
#include "stepper_motor.h" // 添加步进电机驱动头文件
#include "stepper_motor.h" // 添加步进电机驱动头文件
/* USER CODE END Includes */
@@ -53,9 +55,10 @@
#define KEY2_MANUAL_FEED // 按键2手动喂食仅手动模式有效
#define KEY3_DISPLAY_NEXT // 按键3切换显示界面
#define KEY4_TIME_SET // 按键4长按设置时间
/***********************人体存在和水位检测作为两个单独的按键GPIO输入******************* */
#define KEY_WATER_LEVEL // 水位检测
#define KEY_BODY_EXIST // 人体存在
/***********************人体存在和水位检测作为两个单独的按键GPIO输入*******************
*/
#define KEY_WATER_LEVEL // 水位检测
#define KEY_BODY_EXIST // 人体存在
/* USER CODE END PD */
@@ -87,67 +90,68 @@ typedef struct {
// 喂食控制命令枚举
typedef enum {
FEED_CMD_NONE = 0, // 无命令
FEED_CMD_MANUAL, // 手动喂食
FEED_CMD_AUTO, // 自动喂食
FEED_CMD_REMOTE, // 远程喂食
FEED_CMD_TEST // 测试喂食
FEED_CMD_NONE = 0, // 无命令
FEED_CMD_MANUAL, // 手动喂食
FEED_CMD_AUTO, // 自动喂食
FEED_CMD_REMOTE, // 远程喂食
FEED_CMD_TEST // 测试喂食
} Feed_Cmd_t;
// 全局变量
static LCD_Page_t current_page = LCD_PAGE_TIME; // 当前显示页面
static Sensor_Data_t sensor_data = {0}; // 传感器数据
static volatile uint8_t lcd_force_refresh = 0; // 强制刷新标志RTC更新时设置
static volatile uint8_t system_mode = 1; // 系统模式1=自动模式0=手动模式(全局变量)
static volatile uint8_t lcd_force_refresh = 0; // 强制刷新标志RTC更新时设置
static volatile uint8_t system_mode =
1; // 系统模式1=自动模式0=手动模式(全局变量)
static volatile uint8_t feeding_in_progress = 0; // 喂食进行中标志
// 喂食控制相关全局变量
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; // 喂食份数
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; // 喂食份数
/* 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,
};
/* Private function prototypes -----------------------------------------------*/
@@ -160,7 +164,7 @@ 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);
void RTC_TimeUpdateCallback(void); // RTC时间更新回调函数
void RTC_TimeUpdateCallback(void); // RTC时间更新回调函数
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
@@ -173,16 +177,14 @@ void step_motor_task(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显示屏
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
@@ -203,7 +205,8 @@ 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);
@@ -227,7 +230,6 @@ void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
@@ -237,8 +239,7 @@ 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);
@@ -256,6 +257,9 @@ void StartDefaultTask(void *argument)
return;
}
// 设置为低音量-测试用
MP3_SetVolume(20);
elog_i("MP3", "模块初始化完成");
MP3_Play(SYS_POWER_ON); // 播放系统启动音
@@ -277,8 +281,7 @@ 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];
@@ -306,71 +309,87 @@ void LCD_Task(void *argument)
case LCD_PAGE_TIME:
// 时间显示页面
ST7735_FillScreen(bg_color);
// 绘制标题栏
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_BLUE);
ST7735_WriteString(5, 2, "Time", &Font_7x10, ST7735_WHITE, ST7735_BLUE);
// 使用RTC时间独立于SNTP即使网络断开也能显示
if (BSP_RTC_GetTime(&rtc_time) == 0) {
// 显示时间(大字体,只显示时:分,不显示秒)
snprintf(display_str, sizeof(display_str), "%02d:%02d", rtc_time.hour, rtc_time.minute);
ST7735_WriteString(40, 30, display_str, &Font_16x26, ST7735_WHITE, bg_color);
snprintf(display_str, sizeof(display_str), "%02d:%02d", rtc_time.hour,
rtc_time.minute);
ST7735_WriteString(40, 30, display_str, &Font_16x26, ST7735_WHITE,
bg_color);
// 显示年月日(小字体,向下移动并居中)
snprintf(display_str, sizeof(display_str), "%04d-%02d-%02d",
rtc_time.year, rtc_time.month, rtc_time.day);
ST7735_WriteString(30, 65, display_str, &Font_7x10, ST7735_CYAN, bg_color);
ST7735_WriteString(30, 65, display_str, &Font_7x10, ST7735_CYAN,
bg_color);
} else {
ST7735_WriteString(40, 30, "--:--", &Font_16x26, ST7735_WHITE, bg_color);
ST7735_WriteString(30, 65, "----/--/--", &Font_7x10, ST7735_CYAN, bg_color);
ST7735_WriteString(40, 30, "--:--", &Font_16x26, ST7735_WHITE,
bg_color);
ST7735_WriteString(30, 65, "----/--/--", &Font_7x10, ST7735_CYAN,
bg_color);
}
break;
case LCD_PAGE_TEMP_HUMI:
// 温湿度页面
ST7735_FillScreen(bg_color);
// 绘制标题栏
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_GREEN);
ST7735_WriteString(5, 2, "Temp & Humi", &Font_7x10, ST7735_WHITE, ST7735_GREEN);
ST7735_WriteString(5, 2, "Temp & Humi", &Font_7x10, ST7735_WHITE,
ST7735_GREEN);
// 绘制分隔线
ST7735_DrawLine(0, 15, ST7735_WIDTH, 15, ST7735_WHITE);
// 显示温度(橙色 - 国际标准温度色)
snprintf(display_str, sizeof(display_str), "Temp:");
ST7735_WriteString(5, 25, display_str, &Font_7x10, ST7735_WHITE, bg_color);
snprintf(display_str, sizeof(display_str), "%.3fC", sensor_data.temperature);
ST7735_WriteString(40, 25, display_str, &Font_16x26, ST7735_ORANGE, bg_color);
ST7735_WriteString(5, 25, display_str, &Font_7x10, ST7735_WHITE,
bg_color);
snprintf(display_str, sizeof(display_str), "%.3fC",
sensor_data.temperature);
ST7735_WriteString(40, 25, display_str, &Font_16x26, ST7735_ORANGE,
bg_color);
// 显示湿度(青色 - 国际标准湿度色)
snprintf(display_str, sizeof(display_str), "Humi:");
ST7735_WriteString(5, 55, display_str, &Font_7x10, ST7735_WHITE, bg_color);
snprintf(display_str, sizeof(display_str), "%.3f%%", sensor_data.humidity);
ST7735_WriteString(40, 55, display_str, &Font_16x26, ST7735_CYAN, bg_color);
ST7735_WriteString(5, 55, display_str, &Font_7x10, ST7735_WHITE,
bg_color);
snprintf(display_str, sizeof(display_str), "%.3f%%",
sensor_data.humidity);
ST7735_WriteString(40, 55, display_str, &Font_16x26, ST7735_CYAN,
bg_color);
break;
case LCD_PAGE_FOOD_WEIGHT:
// 食物重量页面
ST7735_FillScreen(bg_color);
// 绘制标题栏
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_YELLOW);
ST7735_WriteString(5, 2, "Food Weight", &Font_7x10, ST7735_BLACK, ST7735_YELLOW);
ST7735_WriteString(5, 2, "Food Weight", &Font_7x10, ST7735_BLACK,
ST7735_YELLOW);
// 绘制分隔线
ST7735_DrawLine(0, 15, ST7735_WIDTH, 15, ST7735_WHITE);
// 显示食物重量
snprintf(display_str, sizeof(display_str), "%.2f g", sensor_data.food_weight);
ST7735_WriteString(15, 25, display_str, &Font_16x26, ST7735_WHITE, bg_color);
snprintf(display_str, sizeof(display_str), "%.2f g",
sensor_data.food_weight);
ST7735_WriteString(15, 25, display_str, &Font_16x26, ST7735_WHITE,
bg_color);
// 显示状态
if (sensor_data.food_weight < 50.0f) {
ST7735_WriteString(20, 65, "LOW", &Font_11x18, ST7735_RED, bg_color);
} else if (sensor_data.food_weight < 100.0f) {
ST7735_WriteString(15, 65, "MEDIUM", &Font_11x18, ST7735_YELLOW, bg_color);
ST7735_WriteString(15, 65, "MEDIUM", &Font_11x18, ST7735_YELLOW,
bg_color);
} else {
ST7735_WriteString(20, 65, "GOOD", &Font_11x18, ST7735_GREEN, bg_color);
}
@@ -379,11 +398,12 @@ void LCD_Task(void *argument)
case LCD_PAGE_WATER_LEVEL:
// 水位页面
ST7735_FillScreen(bg_color);
// 绘制标题栏
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_CYAN);
ST7735_WriteString(5, 2, "Water Level", &Font_7x10, ST7735_BLACK, ST7735_CYAN);
ST7735_WriteString(5, 2, "Water Level", &Font_7x10, ST7735_BLACK,
ST7735_CYAN);
// 绘制分隔线
ST7735_DrawLine(0, 15, ST7735_WIDTH, 15, ST7735_WHITE);
@@ -391,30 +411,36 @@ void LCD_Task(void *argument)
if (sensor_data.water_level == 0) {
// 绘制警告背景
ST7735_FillRectangle(0, 20, ST7735_WIDTH, 30, ST7735_RED);
ST7735_WriteString(20, 20, "NO WATER", &Font_16x26, ST7735_WHITE, ST7735_RED);
ST7735_WriteString(10, 60, "Add water", &Font_16x26, ST7735_YELLOW, bg_color);
ST7735_WriteString(20, 20, "NO WATER", &Font_16x26, ST7735_WHITE,
ST7735_RED);
ST7735_WriteString(10, 60, "Add water", &Font_16x26, ST7735_YELLOW,
bg_color);
} else {
// 绘制正常背景
ST7735_FillRectangle(0, 20, ST7735_WIDTH, 30, ST7735_GREEN);
ST7735_WriteString(5, 20, "WATER OK", &Font_16x26, ST7735_WHITE, ST7735_GREEN);
ST7735_WriteString(10, 60, "Not add", &Font_16x26, ST7735_WHITE, bg_color);
ST7735_WriteString(5, 20, "WATER OK", &Font_16x26, ST7735_WHITE,
ST7735_GREEN);
ST7735_WriteString(10, 60, "Not add", &Font_16x26, ST7735_WHITE,
bg_color);
}
break;
case LCD_PAGE_SYSTEM_STATUS:
// 系统状态页面
ST7735_FillScreen(bg_color);
// 绘制标题栏
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_MAGENTA);
ST7735_WriteString(5, 2, "System", &Font_7x10, ST7735_WHITE, ST7735_MAGENTA);
ST7735_WriteString(5, 2, "System", &Font_7x10, ST7735_WHITE,
ST7735_MAGENTA);
// 绘制分隔线
ST7735_DrawLine(0, 15, ST7735_WIDTH, 15, ST7735_WHITE);
// 显示MQTT连接状态
snprintf(display_str, sizeof(display_str), "MQTT:");
ST7735_WriteString(5, 25, display_str, &Font_7x10, ST7735_WHITE, bg_color);
ST7735_WriteString(5, 25, display_str, &Font_7x10, ST7735_WHITE,
bg_color);
if (WIFI_Is_MQTT_Connected()) {
ST7735_WriteString(40, 25, "OK", &Font_7x10, ST7735_GREEN, bg_color);
} else {
@@ -423,16 +449,19 @@ void LCD_Task(void *argument)
// 显示系统模式
snprintf(display_str, sizeof(display_str), "Mode:");
ST7735_WriteString(5, 45, display_str, &Font_7x10, ST7735_WHITE, bg_color);
ST7735_WriteString(5, 45, display_str, &Font_7x10, ST7735_WHITE,
bg_color);
if (sensor_data.system_mode) {
ST7735_WriteString(40, 45, "AUTO", &Font_7x10, ST7735_GREEN, bg_color);
} else {
ST7735_WriteString(40, 45, "MANUAL", &Font_7x10, ST7735_YELLOW, bg_color);
ST7735_WriteString(40, 45, "MANUAL", &Font_7x10, ST7735_YELLOW,
bg_color);
}
// 显示喂食状态
snprintf(display_str, sizeof(display_str), "Feed:");
ST7735_WriteString(5, 65, display_str, &Font_7x10, ST7735_WHITE, bg_color);
ST7735_WriteString(5, 65, display_str, &Font_7x10, ST7735_WHITE,
bg_color);
if (feeding_in_progress) {
ST7735_WriteString(40, 65, "Busy", &Font_7x10, ST7735_ORANGE, bg_color);
} else {
@@ -445,18 +474,19 @@ void LCD_Task(void *argument)
LCD_Page_t displayed_page = current_page;
// 根据当前页面设置不同的刷新间隔
if (current_page == LCD_PAGE_TIME || current_page == LCD_PAGE_SYSTEM_STATUS) {
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; // 检测到页面切换,立即跳出
break; // 检测到页面切换,立即跳出
}
if (lcd_force_refresh) {
// RTC时间更新立即刷新LCD
lcd_force_refresh = 0;
break;
}
osDelay(100); // 每100ms检查一次总共30秒
osDelay(100); // 每100ms检查一次总共30秒
}
} else {
// 其他页面1秒刷新一次
@@ -468,19 +498,17 @@ void LCD_Task(void *argument)
/* USER CODE BEGIN Header_button_task */
/**
* @brief Function implementing the button thread.
* @param argument: Not used
* @retval None
*/
* @brief Function implementing the button thread.
* @param argument: Not used
* @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();
/* Infinite loop */
for(;;)
{
for (;;) {
button_ticks();
osDelay(5);
}
@@ -489,17 +517,29 @@ void button_task(void *argument)
/* USER CODE BEGIN Header_sensorTask */
/**
* @brief Function implementing the sensor thread.
* @param argument: Not used
* @retval None
*/
* @brief Function implementing the sensor thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_sensorTask */
void sensorTask(void *argument)
{
void sensorTask(void *argument) {
/* USER CODE BEGIN sensorTask */
elog_i(TAG, "启动传感器任务");
// #################### 校准流程(首次使用必须执行!)####################
HAL_Delay(1000); // 上电稳定
elog_i(TAG, "开始校准零点...");
HX711_CalibrateZero(); // 空载时调用(传感器上不放任何东西)
elog_i(TAG, "零点校准完成零点AD值%d", hx711_zero_offset);
HAL_Delay(1000);
// elog_i(TAG, "请在传感器上放置已知重量的物体3秒后开始标定...");
// HAL_Delay(3000);
// HX711_CalibrateScale(350.0f); // 放置100g的物体根据实际重量修改
elog_i(TAG, "量程标定完成!标定系数:%.6f", hx711_scale);
// #################### 循环读取重量 ####################
// 扫描I2C设备调试
elog_i(TAG, "扫描I2C设备");
AHT30_ScanI2C(&hi2c1);
// 如果硬件I2C扫描失败尝试软件I2C
@@ -525,8 +565,7 @@ void sensorTask(void *argument)
}
/* Infinite loop */
for(;;)
{
for (;;) {
// 读取温湿度数据
AHT30_Data_t aht30_data;
if (AHT30_ReadData(&aht30_data)) {
@@ -540,6 +579,9 @@ void sensorTask(void *argument)
elog_w(TAG, "AHT30数据读取失败");
}
sensor_data.food_weight= HX711_GetWeight(10); // 采样10次取平均
elog_d(TAG, "当前重量:%.2f g", sensor_data.food_weight);
// 每2秒读取一次
osDelay(2000);
}
@@ -558,12 +600,13 @@ uint8_t Request_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) {
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);
elog_i("FEED", "喂食请求已提交: cmd=%d, angle=%d, amount=%d", cmd, angle,
amount);
return 1;
}
@@ -571,16 +614,12 @@ uint8_t Request_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) {
* @brief 获取当前喂食命令
* @retval 当前喂食命令
*/
Feed_Cmd_t Get_Feed_Command(void) {
return feed_command;
}
Feed_Cmd_t Get_Feed_Command(void) { return feed_command; }
/**
* @brief 清除喂食命令
*/
void Clear_Feed_Command(void) {
feed_command = FEED_CMD_NONE;
}
void Clear_Feed_Command(void) { feed_command = FEED_CMD_NONE; }
/**
* @brief 执行喂食操作
@@ -592,30 +631,30 @@ 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;
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);
@@ -623,49 +662,47 @@ static void Execute_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) {
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
*/
* @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();
/* Infinite loop */
for(;;)
{
for (;;) {
// 检查是否有喂食命令需要执行
if (feed_command != FEED_CMD_NONE && !feeding_in_progress) {
Feed_Cmd_t current_cmd = feed_command;
uint16_t current_angle = feed_angle;
uint8_t current_amount = feed_amount;
// 清除命令标志
Clear_Feed_Command();
// 执行喂食
Execute_Feed(current_cmd, current_angle, current_amount);
}
osDelay(100); // 短暂延时,保持任务响应性
}
/* USER CODE END step_motor_task */
@@ -678,12 +715,12 @@ void step_motor_task(void *argument)
/* USER CODE BEGIN KEY Prototypes */
static Button KEY1; // 按键1
static Button KEY2; // 按键2
static Button KEY3; // 按键3
static Button KEY4; // 按键4
static Button M3_IO; // M3 IO
static Button HC_SR505; //HC-SR505
static Button KEY1; // 按键1
static Button KEY2; // 按键2
static Button KEY3; // 按键3
static Button KEY4; // 按键4
static Button M3_IO; // M3 IO
static Button HC_SR505; // HC-SR505
uint8_t read_button_gpio(uint8_t button_id) {
switch (button_id) {
@@ -717,16 +754,17 @@ uint8_t read_button_gpio(uint8_t button_id) {
void key1_single_click_handler(Button *btn) {
// 切换系统模式
system_mode = !system_mode; // 0和1之间切换
// 更新传感器数据中的模式状态
sensor_data.system_mode = system_mode;
// 记录日志
elog_i("KEY", "按键1单击 - 模式切换: %s", system_mode ? "自动模式" : "手动模式");
elog_i("KEY", "按键1单击 - 模式切换: %s",
system_mode ? "自动模式" : "手动模式");
// 播放模式切换音效
MP3_Play(system_mode ? MODE_AUTO : MODE_MANUAL);
// 强制刷新LCD显示以更新模式状态
lcd_force_refresh = 1;
}
@@ -752,27 +790,24 @@ void key2_single_click_handler(Button *btn) {
}
}
void key3_single_click_handler(Button *btn) {
elog_i("KEY", "按键3单击");
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 M3_long_press_start_handler(Button *btn) {
elog_i("KEY", "M3水位传感器检测到有水低电平");
void M3_long_press_start_handler(Button *btn) {
elog_i("KEY", "M3水位传感器检测到有水低电平");
sensor_data.water_level = 1;
}
void M3_press_up_handler(Button *btn) {
elog_i("KEY", "M3水位传感器检测到无水高电平");
void M3_press_up_handler(Button *btn) {
elog_i("KEY", "M3水位传感器检测到无水高电平");
sensor_data.water_level = 0;
}
void HC_SR055_long_click_handler(Button *btn) { elog_i("KEY", "HC-SR505触发"); }
void user_button_init(void) {
// 初始化按键 (active_level: 0=低电平有效, 1=高电平有效)
button_init(&KEY1, read_button_gpio, 0, 1);
@@ -790,7 +825,8 @@ void user_button_init(void) {
button_attach(&KEY4, BTN_SINGLE_CLICK, key4_single_click_handler);
button_attach(&M3_IO, BTN_LONG_PRESS_START, M3_long_press_start_handler);
button_attach(&M3_IO, BTN_PRESS_UP, M3_press_up_handler);
button_attach(&HC_SR505, BTN_LONG_PRESS_START, HC_SR055_long_click_handler); // 长按触发
button_attach(&HC_SR505, BTN_LONG_PRESS_START,
HC_SR055_long_click_handler); // 长按触发
elog_i("BUTTON", "按键回调函数设置完成");
@@ -801,7 +837,7 @@ void user_button_init(void) {
button_start(&KEY4);
button_start(&M3_IO);
button_start(&HC_SR505);
elog_i("BUTTON", "按键任务已启动");
}
@@ -809,9 +845,7 @@ void user_button_init(void) {
* @brief 获取当前系统模式
* @retval 系统模式 (0=手动, 1=自动)
*/
uint8_t Get_System_Mode(void) {
return system_mode;
}
uint8_t Get_System_Mode(void) { return system_mode; }
/**
* @brief 设置系统模式
@@ -830,9 +864,7 @@ void Set_System_Mode(uint8_t mode) {
* @brief 检查是否正在喂食
* @retval 1=正在喂食, 0=空闲
*/
uint8_t Is_Feeding_In_Progress(void) {
return feeding_in_progress;
}
uint8_t Is_Feeding_In_Progress(void) { return feeding_in_progress; }
/* USER CODE END KEY Prototypes */
@@ -916,9 +948,7 @@ Sensor_Data_t *LCD_GetSensorData(void) { return &sensor_data; }
* @brief RTC时间更新回调函数
* @note 当RTC通过SNTP更新时间时调用立即刷新LCD显示
*/
void RTC_TimeUpdateCallback(void) {
lcd_force_refresh = 1;
}
void RTC_TimeUpdateCallback(void) { lcd_force_refresh = 1; }
/* USER CODE END LCD_Page_Functions */