🍽️ 智能洗碗机控制系统 使用说明书
核心架构: STM32F1 微控制器
功能亮点: 自动洗涤、烘干、UV消毒、WiFi远程控制
视频展示:
📑 1. 系统概述
本智能洗碗机控制系统集成了多种传感器和执行器,致力于提供全自动化的洗碗、烘干和消毒体验。系统配备直观的 OLED显示屏 与 实体按键 操作界面,并支持通过 WiFi 进行远程智能控制。
🛠️ 2. 硬件组成
💻 核心与交互
主控芯片: STM32F1 系列微控制器
显示模块: OLED 显示屏(实时监控系统状态)
人机交互: 4 个功能独立按键(本地操作)
通信模块: WiFi 模块(支持远程指令交互)
📡 传感器模块
🌡️ DS18B20 温度传感器: 实时监测洗涤水温
🌊 水位传感器: 精准监测洗涤舱内水位高度
⚙️ 执行器模块
🔥 加热片: 负责加热洗涤用水
🔄 直流电机: 带动洗涤刷臂进行全方位清洁
💨 散热风扇: 提供强力气流以烘干餐具
🟣 UV紫外线灯: 深度照射,破坏细菌结构进行消毒
🎛️ 控制舵机: 智能控制进水/排水阀门的开闭
🔔 蜂鸣器: 提供关键节点与报警的声音提示
🔄 3. 系统功能与工作流程
🌊 标准洗涤六步曲
进水阶段 ➔ 舵机开启加水阀,自动注水至设定水位。
加热阶段 ➔ 启动加热片,迅速提升水温至目标值。
洗涤阶段 ➔ 直流电机驱动刷臂,进行深层去污。
排水阶段 ➔ 舵机切换至排水位置,快速排净污水。
烘干阶段 ➔ 启动风扇,循环气流吹干餐具表面水分。
消毒阶段 ➔ UV灯开启,进行无死角杀菌。
🎚️ 个性化阈值参数设置
系统支持用户根据实际需求自定义以下核心参数:
水位上限 (mm):设定洗涤所需的水位高度。
排空阈值 (mm):判断排水是否彻底的标准。
目标温度 (℃):洗涤水温设定值。
洗涤时间 (秒):清洁阶段持续时长。
烘干时间 (秒):风干阶段持续时长。
消毒时间 (秒):紫外线杀菌持续时长。
🕹️ 4. 操作指南
🔘 按键功能对照表
按键端口🟢 正常模式功能🟠 阈值设置模式功能KEY1PA0(无特定功能)确认操作 / 切换至下一参数KEY2PA1短按:开始流程*(待机)* / 暂停&恢复*(运行中)⬆️ 向上增加参数值KEY3PA4短按:取消洗碗流程(运行中)*⬇️ 向下减少参数值KEY4PA5短按:切换 OLED 显示页面退出设置模式并保存
📺 屏幕页面说明
📄 页面 0 [传感器数据]:实时温度值 | 当前水位高度 | 水位传感器ADC原始值
📄 页面 1 [洗碗机状态]:当前流程阶段 | 各阶段剩余时间 | 系统报警信息
📄 页面 2 [执行器状态]:加热/风扇/UV灯开关状态 | 舵机当前角度 | 蜂鸣器状态
⚙️ 日常操作流程
▶️ 开始洗碗:确保屏幕显示 IDLE(待机) ➔ 按下 KEY2 启动。
⏸️ 暂停/恢复:运行中按下 KEY2 暂停 ➔ 再次按下 KEY2 恢复。
⏹️ 取消洗碗:运行中按下 KEY3 ➔ 系统关闭所有执行器并返回待机。
🔧 如何修改阈值参数?
进入:同时长按 KEY1 + KEY2 约3秒 ➔ 屏幕显示 SETUP? ➔ 按 KEY1 确认进入。
调整:使用 KEY2 (增加) 和 KEY3 (减少) 修改当前数值。
切换:按 KEY1 切换到下一个需要调整的参数。
保存:调整完毕后,按 KEY4 保存并退出设置模式。
📱 5. WiFi 远程控制
通过手机或上位机连接设备 WiFi 后,可发送以下 JSON 格式指令进行控制:
JSON
// 控制指令
{"cmd": "start"} // 开始洗碗
{"cmd": "pause"} // 暂停/恢复洗碗
{"cmd": "cancel"} // 取消洗碗// 参数设置指令示例
{
"water_high": 25,
"water_empty": 3,
"temp_target": 45,
"wash_time": 30,
"dry_time": 20,
"uv_time": 15
}
⚠️ 6. 报警与故障处理
系统具备自我监测功能。当出现错误时,系统会立即停机并显示错误代码。
按下 KEY3 键可清除当前错误状态并尝试返回待机模式。
🚨 水位超限:检测到舱内水位突破设定的安全上限。
🚨 温度过高:水温超过硬件安全范围,存在干烧或烫伤风险。
🚨 运行超时:某一工作阶段耗时过长(如长时间未注满水或未排空)。
💡 7. 注意事项与保养
📌 安全注意事项
🔌 电源安全:请务必确保接入电源电压符合本系统标称要求。
✋ 禁止触碰:设备运行期间,严禁强行开启舱门或拆卸任何内部组件。
⚙️ 合理设置:参数设置需符合物理常识,避免极端参数导致硬件损坏。
🛠️ 断电排查:如遇严重故障或异味,请先切断电源后再进行检查维修。
🧼 维护保养建议
定期使用软布清洁传感器探头(特别是水温和水位探头),以防污垢影响测量精度。
定期检查各处连接线缆是否有老化、松动或破损。
保持控制主板及外部电路环境干燥,防止水汽侵入导致短路。
定期空载试运行一次,验证电机、加热片等大功率执行器动作是否顺畅。

原理图:

PCB:PCB:@TOC

在这里插入代码片
/*
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 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 "main.h"
#include "adc.h"
#include "dma.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "math.h"
#include "OLED.h"
#include "Key.h"
#include "threshold_setting.h"
// ===== 基于STM32的智能洗碗机系统 =====
#include "Serial.h"
#include "DS18B20.h"
#include "Water.h"
#include <stdbool.h>
#include <string.h>
#include "Delay.h"
// ===== 智能洗碗机阈值默认值 =====
#define WATER_LEVEL_THRESHOLD_DEFAULT 25 // 水位上限阈值(mm),加水到此水位
#define WATER_EMPTY_THRESHOLD_DEFAULT 3 // 排空阈值(mm),低于此值认为排空
#define TEMP_THRESHOLD_DEFAULT 25 // 加热目标温度(℃)
#define WASH_TIME_DEFAULT 10 // 洗碗时间(秒)
#define DRY_TIME_DEFAULT 5 // 烘干时间(秒)
#define UV_TIME_DEFAULT 5 // 消毒时间(秒)
// ===== 舵机角度对应PWM脉宽(TIM4, 1MHz, 20ms周期) =====
// SG90: 0°=500us, 90°=1500us, 180°=2500us
#define SERVO_0_DEG 500 // 舵机 0°(排水位置)
#define SERVO_90_DEG 1500 // 舵机 90°(默认/关闭位置)
#define SERVO_180_DEG 2500 // 舵机 180°(加水位置)
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
// ===== 串口JSON数据缓冲 =====
char json_buffer[512];
// ===== ADC采集缓冲 =====
uint32_t ADC_Value[1]; // ADC DMA缓冲区:[0]=PB1水位传感器(CH9)
// ===== 传感器数据 =====
float temperature = 0.0f; // DS18B20温度(℃)
float water_level = 0.0f; // 水位(mm)
uint32_t water_adc = 0; // 水位传感器ADC原始值
// ===== 洗碗机状态 =====
WashState_t wash_state = WASH_STATE_IDLE; // 当前洗碗状态
WashState_t pause_resume_state = WASH_STATE_IDLE; // 暂停前的状态(用于恢复)
AlarmType_t active_alarm = ALARM_NONE; // 当前报警类型
// ===== 执行器状态 =====
uint8_t heater_state = 0; // 加热片: 0关 1开 (PA6)
uint8_t fan_state = 0; // 风扇: 0关 1开 (PA7)
uint8_t uv_lamp_state = 0; // UV消毒灯: 0关 1开 (PB0)
uint8_t motor_state = 0; // 直流电机: 0关 1开
uint16_t motor_speed = 0; // 电机PWM速度 0~999
uint16_t servo_angle = 90; // 舵机当前角度
uint8_t buzzer_state = 0; // 蜂鸣器: 0关 1开 (PB7)
// ===== 流程计时 =====
static uint32_t step_start_tick = 0; // 当前步骤开始时间
static uint32_t buzzer_toggle_tick = 0; // 蜂鸣器间歇时间
static uint8_t drain_to_reverse = 0; // 1: 本次排水结束后进入反转, 0: 进入烘干
// ===== OLED显示页面 =====
uint8_t display_page = 0;
#define DISPLAY_PAGE_COUNT 3 // 0:传感器数据 1:洗碗状态 2:执行器状态
// ===== 阈值项索引枚举 =====
typedef enum
{
IDX_WATER_HIGH = 0, // 水位上限阈值(mm)
IDX_WATER_EMPTY, // 排空阈值(mm)
IDX_TEMP_TARGET, // 加热目标温度(℃)
IDX_WASH_TIME, // 洗碗时间(秒)
IDX_DRY_TIME, // 烘干时间(秒)
IDX_UV_TIME, // 消毒时间(秒)
IDX_COUNT // 阈值项数量
} ThresholdIndex;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
// ================== 执行器控制函数 ==================
/**
* @brief 控制加热片 (PA6, 高电平开启)
*/
void Control_Heater(uint8_t state)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
heater_state = state;
}
/**
* @brief 控制风扇 (PA7, 高电平开启)
*/
void Control_Fan(uint8_t state)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
fan_state = state;
}
/**
* @brief 控制UV消毒灯 (PB0, 高电平开启)
*/
void Control_UVLamp(uint8_t state)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
uv_lamp_state = state;
}
/**
* @brief 控制蜂鸣器 (PB7, 低电平触发有源蜂鸣器)
*/
void Control_Buzzer(uint8_t state)
{
if (state)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
buzzer_state = 1;
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
buzzer_state = 0;
}
}
/**
* @brief 控制舵机角度 (PB6, TIM4_CH1)
* @param angle 角度(0~180)
* @note SG90: 0°=500us, 90°=1500us, 180°=2500us
* TIM4: 72MHz/72=1MHz, Period=20000 → 20ms周期
*/
void Control_Servo(uint16_t angle)
{
if (angle > 180)
angle = 180;
// 线性映射: 0°→500, 180°→2500
uint16_t pulse = SERVO_0_DEG + (uint16_t)((float)angle / 180.0f * (SERVO_180_DEG - SERVO_0_DEG));
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, pulse);
servo_angle = angle;
}
/**
* @brief 控制直流电机 (DRV8833: PA8=PWMA TIM1_CH1, PA11=PWMB TIM1_CH4)
* @param direction 0=停止, 1=正转, 2=反转
* @param speed 速度值 0~999 (0=停止)
* @note 正转: PWMA=speed, PWMB=0
* 反转: PWMA=0, PWMB=speed
* 停止: PWMA=0, PWMB=0
*/
void Control_Motor(uint8_t direction, uint16_t speed)
{
if (speed > 999)
speed = 999;
if (direction == 1 && speed > 0) // 正转
{
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, speed); // PWMA
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, 0); // PWMB
motor_state = 1;
}
else if (direction == 2 && speed > 0) // 反转
{
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0); // PWMA
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, speed); // PWMB
motor_state = 1;
}
else // 停止
{
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, 0);
motor_state = 0;
}
motor_speed = speed;
}
/**
* @brief 关闭所有执行器(紧急停止/复位用)
*/
void AllActuators_Off(void)
{
Control_Heater(0);
Control_Fan(0);
Control_UVLamp(0);
Control_Motor(0, 0);
Control_Servo(90); // 回到默认位置
Control_Buzzer(0);
}
// ================== 传感器读取函数 ==================
/**
* @brief 更新所有传感器数据
*/
static void UpdateSensorData(void)
{
// 读取DS18B20温度
temperature = Ds18b20_Get_Temp();
// 读取水位传感器(ADC DMA方式)
water_adc = ADC_Value[0];
water_level = Water_GetLevel(water_adc);
}
// ================== 洗碗流程状态机 ==================
/**
* @brief 获取当前步骤已运行的秒数
*/
static uint32_t GetStepElapsedSec(void)
{
return (HAL_GetTick() - step_start_tick) / 1000;
}
/**
* @brief 进入新的洗碗步骤,记录开始时间
*/
static void EnterStep(WashState_t new_state)
{
wash_state = new_state;
step_start_tick = HAL_GetTick();
}
/**
* @brief 获取洗碗状态的中文描述
*/
static const char *GetWashStateStr(WashState_t state)
{
switch (state)
{
case WASH_STATE_IDLE:
return "IDLE";
case WASH_STATE_FILLING:
return "FILLING";
case WASH_STATE_WATER_CHECK:
return "WCHK";
case WASH_STATE_HEATING:
return "HEATING";
case WASH_STATE_WASHING_FWD:
return "WASH_FWD";
case WASH_STATE_WASHING_REV:
return "WASH_REV";
case WASH_STATE_DRAINING:
return "DRAINING";
case WASH_STATE_DRAIN_CHECK:
return "DCHK";
case WASH_STATE_DRYING:
return "DRYING";
case WASH_STATE_STERILIZING:
return "UV";
case WASH_STATE_COMPLETE:
return "DONE";
case WASH_STATE_PAUSED:
return "PAUSED";
case WASH_STATE_ERROR:
return "ERROR";
default:
return "UNKN";
}
}
/**
* @brief 洗碗流程状态机
*
* 流程: 舵机180°(加水) → 检测水位达标 → 加热到温度 → 电机正转洗碗
* → 舵机0°(排水) → 检测排空 → 电机反转洗碗
* → 舵机0°(排水) → 检测排空 → 风扇烘干 → UV消毒 → 完成
*/
void WashProcess_StateMachine(void)
{
int water_high = ThresholdSetting_GetCurrentValue(IDX_WATER_HIGH);
int water_empty = ThresholdSetting_GetCurrentValue(IDX_WATER_EMPTY);
int temp_target = ThresholdSetting_GetCurrentValue(IDX_TEMP_TARGET);
int wash_time_sec = ThresholdSetting_GetCurrentValue(IDX_WASH_TIME);
int dry_time_sec = ThresholdSetting_GetCurrentValue(IDX_DRY_TIME);
int uv_time_sec = ThresholdSetting_GetCurrentValue(IDX_UV_TIME);
switch (wash_state)
{
case WASH_STATE_IDLE:
// 待机状态,等待开始指令
break;
case WASH_STATE_FILLING:
// 舵机转到180°模拟打开进水阀
Control_Servo(180);
// 持续检测水位
if (water_level >= (float)water_high)
{
// 水位达标,关闭进水(舵机回90°)
Control_Servo(90);
EnterStep(WASH_STATE_WATER_CHECK);
}
// 超时保护:120秒加水超时
if (GetStepElapsedSec() > 120)
{
Control_Servo(90);
active_alarm = ALARM_TIMEOUT;
EnterStep(WASH_STATE_ERROR);
}
break;
case WASH_STATE_WATER_CHECK:
// 水位确认(短暂停留确认水位稳定)
if (GetStepElapsedSec() >= 2)
{
if (water_level >= (float)water_high)
{
// 水位确认OK,开始加热
EnterStep(WASH_STATE_HEATING);
}
else
{
// 水位不够,重新加水
EnterStep(WASH_STATE_FILLING);
}
}
break;
case WASH_STATE_HEATING:
// 开启加热片
Control_Heater(1);
// 温度达标
if (temperature >= (float)temp_target)
{
Control_Heater(0);
drain_to_reverse = 1;
EnterStep(WASH_STATE_WASHING_FWD);
}
// 超时保护:180秒加热超时
if (GetStepElapsedSec() > 180)
{
Control_Heater(0);
active_alarm = ALARM_TIMEOUT;
EnterStep(WASH_STATE_ERROR);
}
break;
case WASH_STATE_WASHING_FWD:
// 启动直流电机正转模拟洗碗
Control_Motor(1, 700);
// 正转洗碗时间到后,先排水再进入反转
if (GetStepElapsedSec() >= (uint32_t)wash_time_sec)
{
Control_Motor(0, 0);
drain_to_reverse = 1;
EnterStep(WASH_STATE_DRAINING);
}
break;
case WASH_STATE_WASHING_REV:
// 启动直流电机反转模拟洗碗
Control_Motor(2, 700);
// 反转洗碗时间到后,排水并进入后续烘干
if (GetStepElapsedSec() >= (uint32_t)wash_time_sec)
{
Control_Motor(0, 0);
drain_to_reverse = 0;
EnterStep(WASH_STATE_DRAINING);
}
break;
case WASH_STATE_DRAINING:
// 舵机转到0°模拟打开排水阀
Control_Servo(0);
// 检测水位排空
if (water_level <= (float)water_empty)
{
Control_Servo(90); // 回到默认位
EnterStep(WASH_STATE_DRAIN_CHECK);
}
// 超时保护:120秒排水超时
if (GetStepElapsedSec() > 120)
{
Control_Servo(90);
active_alarm = ALARM_TIMEOUT;
EnterStep(WASH_STATE_ERROR);
}
break;
case WASH_STATE_DRAIN_CHECK:
// 排空确认
if (GetStepElapsedSec() >= 2)
{
if (water_level <= (float)water_empty)
{
if (drain_to_reverse)
{
drain_to_reverse = 0;
EnterStep(WASH_STATE_WASHING_REV);
}
else
{
EnterStep(WASH_STATE_DRYING);
}
}
else
{
// 还没排空,继续排水
EnterStep(WASH_STATE_DRAINING);
}
}
break;
case WASH_STATE_DRYING:
// 开启风扇烘干
Control_Fan(1);
if (GetStepElapsedSec() >= (uint32_t)dry_time_sec)
{
Control_Fan(0);
EnterStep(WASH_STATE_STERILIZING);
}
break;
case WASH_STATE_STERILIZING:
// 开启UV消毒灯
Control_UVLamp(1);
if (GetStepElapsedSec() >= (uint32_t)uv_time_sec)
{
Control_UVLamp(0);
EnterStep(WASH_STATE_COMPLETE);
}
break;
case WASH_STATE_COMPLETE:
// 洗碗完成,蜂鸣器提示
AllActuators_Off();
// 完成提示音3秒
if (GetStepElapsedSec() < 3)
{
uint32_t now = HAL_GetTick();
if (now - buzzer_toggle_tick >= 300)
{
buzzer_toggle_tick = now;
Control_Buzzer(buzzer_state ? 0 : 1);
}
}
else
{
Control_Buzzer(0);
wash_state = WASH_STATE_IDLE;
}
break;
case WASH_STATE_PAUSED:
// 暂停状态:所有执行器保持当前状态或关闭
Control_Motor(0, 0);
Control_Heater(0);
break;
case WASH_STATE_ERROR:
// 错误状态:关闭所有执行器,报警
AllActuators_Off();
// 报警蜂鸣器间歇响
{
uint32_t now = HAL_GetTick();
if (now - buzzer_toggle_tick >= 500)
{
buzzer_toggle_tick = now;
Control_Buzzer(buzzer_state ? 0 : 1);
}
}
break;
default:
wash_state = WASH_STATE_IDLE;
break;
}
}
/**
* @brief 开始洗碗流程
*/
void WashProcess_Start(void)
{
if (wash_state == WASH_STATE_IDLE || wash_state == WASH_STATE_COMPLETE)
{
active_alarm = ALARM_NONE;
drain_to_reverse = 0;
Control_Buzzer(1);
HAL_Delay(100);
Control_Buzzer(0);
EnterStep(WASH_STATE_FILLING);
}
}
/**
* @brief 暂停/恢复洗碗流程
*/
void WashProcess_PauseResume(void)
{
if (wash_state == WASH_STATE_PAUSED)
{
// 恢复到暂停前的状态
EnterStep(pause_resume_state);
Control_Buzzer(1);
HAL_Delay(50);
Control_Buzzer(0);
}
else if (wash_state != WASH_STATE_IDLE && wash_state != WASH_STATE_COMPLETE && wash_state != WASH_STATE_ERROR)
{
// 暂停当前流程
pause_resume_state = wash_state;
wash_state = WASH_STATE_PAUSED;
Control_Buzzer(1);
HAL_Delay(50);
Control_Buzzer(0);
HAL_Delay(50);
Control_Buzzer(1);
HAL_Delay(50);
Control_Buzzer(0);
}
}
/**
* @brief 取消洗碗流程
*/
void WashProcess_Cancel(void)
{
if (wash_state != WASH_STATE_IDLE)
{
AllActuators_Off();
active_alarm = ALARM_NONE;
drain_to_reverse = 0;
wash_state = WASH_STATE_IDLE;
Control_Buzzer(1);
HAL_Delay(200);
Control_Buzzer(0);
}
}
// ================== OLED显示页面 ==================
/**
* @brief OLED显示 - 传感器数据页面
*/
static void DisplayPage_Sensor(void)
{
OLED_Printf(0, 0, OLED_8X16, "=Dish Washer=");
OLED_Printf(0, 16, OLED_8X16, "Temp:%.1fC", temperature);
OLED_Printf(0, 32, OLED_8X16, "Water:%.1fmm", water_level);
OLED_Printf(0, 48, OLED_8X16, "ADC:%d", (int)water_adc);
}
/**
* @brief OLED显示 - 洗碗状态页面
*/
static void DisplayPage_WashStatus(void)
{
OLED_Printf(0, 0, OLED_8X16, "State:%s", GetWashStateStr(wash_state));
// 显示当前步骤倒计时或进度
switch (wash_state)
{
case WASH_STATE_FILLING:
OLED_Printf(0, 16, OLED_8X16, "Filling..%ds", (int)GetStepElapsedSec());
OLED_Printf(0, 32, OLED_8X16, "Tgt:%dmm rea:%.1fmm ", ThresholdSetting_GetCurrentValue(IDX_WATER_HIGH), water_level);
break;
case WASH_STATE_HEATING:
OLED_Printf(0, 16, OLED_8X16, "Heat:%.1f/%dC", temperature, ThresholdSetting_GetCurrentValue(IDX_TEMP_TARGET));
OLED_Printf(0, 32, OLED_8X16, "Time:%ds", (int)GetStepElapsedSec());
break;
case WASH_STATE_WASHING_FWD:
{
int remain = ThresholdSetting_GetCurrentValue(IDX_WASH_TIME) - (int)GetStepElapsedSec();
if (remain < 0)
remain = 0;
OLED_Printf(0, 16, OLED_8X16, "Wash FWD:%ds", remain);
OLED_Printf(0, 32, OLED_8X16, "Motor:FWD");
break;
}
case WASH_STATE_WASHING_REV:
{
int remain = ThresholdSetting_GetCurrentValue(IDX_WASH_TIME) - (int)GetStepElapsedSec();
if (remain < 0)
remain = 0;
OLED_Printf(0, 16, OLED_8X16, "Wash REV:%ds", remain);
OLED_Printf(0, 32, OLED_8X16, "Motor:REV");
break;
}
case WASH_STATE_DRAINING:
OLED_Printf(0, 16, OLED_8X16, "Drain..%ds", (int)GetStepElapsedSec());
OLED_Printf(0, 32, OLED_8X16, "Tgt:%dmm Wat:%.1fmm ", ThresholdSetting_GetCurrentValue(IDX_WATER_EMPTY), water_level);
break;
case WASH_STATE_DRYING:
{
int remain = ThresholdSetting_GetCurrentValue(IDX_DRY_TIME) - (int)GetStepElapsedSec();
if (remain < 0)
remain = 0;
OLED_Printf(0, 16, OLED_8X16, "Dry:%ds left", remain);
OLED_Printf(0, 32, OLED_8X16, "Fan:ON");
break;
}
case WASH_STATE_STERILIZING:
{
int remain = ThresholdSetting_GetCurrentValue(IDX_UV_TIME) - (int)GetStepElapsedSec();
if (remain < 0)
remain = 0;
OLED_Printf(0, 16, OLED_8X16, "UV:%ds left", remain);
OLED_Printf(0, 32, OLED_8X16, "UVLamp:ON");
break;
}
case WASH_STATE_PAUSED:
OLED_Printf(0, 16, OLED_8X16, "Prev:%s", GetWashStateStr(pause_resume_state));
OLED_Printf(0, 32, OLED_8X16, "KEY2:Resume");
break;
case WASH_STATE_ERROR:
OLED_Printf(0, 16, OLED_8X16, "Error!");
OLED_Printf(0, 32, OLED_8X16, "KEY3:Cancel");
break;
case WASH_STATE_COMPLETE:
OLED_Printf(0, 16, OLED_8X16, "Complete!");
OLED_Printf(0, 32, OLED_8X16, "");
break;
default:
OLED_Printf(0, 16, OLED_8X16, "Ready");
OLED_Printf(0, 32, OLED_8X16, "KEY2:Start");
break;
}
// 报警信息
switch (active_alarm)
{
case ALARM_NONE:
OLED_Printf(0, 48, OLED_8X16, "Alarm:None");
break;
case ALARM_WATER_OVERFLOW:
OLED_Printf(0, 48, OLED_8X16, "ALM:WaterHigh!");
break;
case ALARM_TEMP_OVER:
OLED_Printf(0, 48, OLED_8X16, "ALM:TempHigh!");
break;
case ALARM_TIMEOUT:
OLED_Printf(0, 48, OLED_8X16, "ALM:Timeout!");
break;
}
}
/**
* @brief OLED显示 - 执行器状态页面
*/
static void DisplayPage_Actuator(void)
{
OLED_Printf(0, 0, OLED_8X16, "=Actuator=");
OLED_Printf(0, 16, OLED_8X16, "Heat:%s Fan:%s",
heater_state ? "ON" : "OFF",
fan_state ? "ON" : "OFF");
OLED_Printf(0, 32, OLED_8X16, "UV:%s Mtr:%s",
uv_lamp_state ? "ON" : "OFF",
motor_state ? "ON" : "OFF");
OLED_Printf(0, 48, OLED_8X16, "Servo:%d Buz:%s",
(int)servo_angle,
buzzer_state ? "ON" : "OFF");
}
// ================== OLED显示更新 ==================
/**
* @brief 更新OLED显示
*/
static void Display_Update(void)
{
OLED_Clear();
switch (display_page)
{
case 0:
DisplayPage_Sensor();
break;
case 1:
DisplayPage_WashStatus();
break;
case 2:
DisplayPage_Actuator();
break;
default:
DisplayPage_Sensor();
break;
}
OLED_Update();
}
// ================== 远程控制接口 ==================
/**
* @brief 解析WiFi模块(串口2)下发的JSON指令
*
* 洗碗控制:
* {"cmd":"start"} - 开始洗碗
* {"cmd":"pause"} - 暂停/恢复
* {"cmd":"cancel"} - 取消洗碗
*
* 阈值设置:
* {"water_high":25,"water_empty":3,"temp_target":45,"wash_time":30,"dry_time":20,"uv_time":15}
*/
static void RemoteCommand_Parse(const char *data, uint16_t len)
{
const char *json_start = strchr(data, '{');
if (json_start == NULL)
return;
int ival;
// ===== 洗碗控制指令 =====
const char *p = strstr(json_start, "\"cmd\"");
if (p)
{
if (strstr(p, "\"start\""))
{
WashProcess_Start();
return;
}
else if (strstr(p, "\"pause\""))
{
WashProcess_PauseResume();
return;
}
else if (strstr(p, "\"cancel\""))
{
WashProcess_Cancel();
return;
}
}
// ===== 阈值设置指令 =====
p = strstr(json_start, "\"water_high\"");
if (p)
{
p = strchr(p, ':');
if (p && sscanf(p + 1, "%d", &ival) == 1)
ThresholdSetting_SetCurrentValue(IDX_WATER_HIGH, ival);
}
p = strstr(json_start, "\"water_empty\"");
if (p)
{
p = strchr(p, ':');
if (p && sscanf(p + 1, "%d", &ival) == 1)
ThresholdSetting_SetCurrentValue(IDX_WATER_EMPTY, ival);
}
p = strstr(json_start, "\"temp_target\"");
if (p)
{
p = strchr(p, ':');
if (p && sscanf(p + 1, "%d", &ival) == 1)
ThresholdSetting_SetCurrentValue(IDX_TEMP_TARGET, ival);
}
p = strstr(json_start, "\"wash_time\"");
if (p)
{
p = strchr(p, ':');
if (p && sscanf(p + 1, "%d", &ival) == 1)
ThresholdSetting_SetCurrentValue(IDX_WASH_TIME, ival);
}
p = strstr(json_start, "\"dry_time\"");
if (p)
{
p = strchr(p, ':');
if (p && sscanf(p + 1, "%d", &ival) == 1)
ThresholdSetting_SetCurrentValue(IDX_DRY_TIME, ival);
}
p = strstr(json_start, "\"uv_time\"");
if (p)
{
p = strchr(p, ':');
if (p && sscanf(p + 1, "%d", &ival) == 1)
ThresholdSetting_SetCurrentValue(IDX_UV_TIME, ival);
}
}
// ================== 阈值配置 ==================
/**
* @brief 定义系统中可配置的阈值项
*/
ThresholdItemConfig threshold_config[] = {
{"水位上限(mm)", WATER_LEVEL_THRESHOLD_DEFAULT, 5, 40},
{"排空阈值(mm)", WATER_EMPTY_THRESHOLD_DEFAULT, 0, 10},
{"目标温度(C)", TEMP_THRESHOLD_DEFAULT, 20, 80},
{"洗碗时间(s)", WASH_TIME_DEFAULT, 5, 120},
{"烘干时间(s)", DRY_TIME_DEFAULT, 5, 120},
{"消毒时间(s)", UV_TIME_DEFAULT, 5, 60},
};
uint8_t threshold_count = sizeof(threshold_config) / sizeof(ThresholdItemConfig);
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
// 用户自定义初始化代码
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
// 用户自定义初始化代码
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
// 用户自定义系统初始化代码
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_I2C1_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
OLED_Init();
// 初始化DS18B20温度传感器(PB5)
DQ_GPIO_Init();
// 初始化水位传感器模块
Water_Init();
// 启动ADC DMA连续采集(1通道:PB1水位传感器 CH9)
HAL_ADC_Start_DMA(&hadc1, ADC_Value, 1);
// 启动定时器1 PWM输出(电机驱动 PA8=CH1 PWMA, PA11=CH4 PWMB)
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
// 启动定时器4 PWM输出(舵机 PB6=CH1)
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
// 启动定时器3中断(用于周期性数据上传到WiFi)
HAL_TIM_Base_Start_IT(&htim3);
OLED_Clear();
OLED_Printf(0, 0, OLED_8X16, "Smart Dish");
OLED_Printf(0, 16, OLED_8X16, "Washer Init..");
OLED_Update();
// 初始化所有执行器关闭
AllActuators_Off();
Serial_Init();
// 初始化阈值设置模块(自动从Flash加载保存的阈值,若无有效数据则用默认值)
ThresholdSetting_Init(threshold_config, threshold_count);
HAL_Delay(500);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// ===== 处理WiFi远程控制指令(UART2) =====
if (Uart2.RxFlag == 1)
{
Uart2.RxFlag = 0;
RemoteCommand_Parse((const char *)Uart2.RxBuf, Uart2.RxLen);
Serial_BUF_Clear((char *)Uart2.RxBuf, Uart2.RxLen);
}
// ===== 更新传感器数据 =====
UpdateSensorData();
// ===== 处理阈值设置按键 =====
ThresholdSetting_HandleKey(KEY_1, KEY_2, KEY_3, KEY_4);
if (ThresholdSetting_GetMode() != THRESHOLD_SETTING_NORMAL)
{
// --- 阈值设置模式 ---
int adjust_value = ThresholdSetting_NeedAdjust();
if (adjust_value != 0)
{
uint8_t current_index = ThresholdSetting_GetCurrentIndex();
int current_value = ThresholdSetting_GetCurrentValue(current_index);
current_value += adjust_value;
ThresholdSetting_SetCurrentValue(current_index, current_value);
}
ThresholdSetting_Display();
}
else
{
// --- 正常工作模式 ---
// KEY2短按:开始/暂停洗碗
if (Key_Check(KEY_2, KEY_SINGLE))
{
if (wash_state == WASH_STATE_IDLE || wash_state == WASH_STATE_COMPLETE)
{
WashProcess_Start();
}
else
{
WashProcess_PauseResume();
}
}
// KEY3短按:取消洗碗(运行中),或切换显示页面(待机时)
if (Key_Check(KEY_3, KEY_SINGLE))
{
if (wash_state != WASH_STATE_IDLE && wash_state != WASH_STATE_COMPLETE)
{
WashProcess_Cancel();
}
}
// KEY4短按:切换OLED显示页面
if (Key_Check(KEY_4, KEY_SINGLE))
{
display_page = (display_page + 1) % DISPLAY_PAGE_COUNT;
}
// 执行洗碗流程状态机
WashProcess_StateMachine();
// 更新OLED显示
Display_Update();
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/**
* @brief 定时器中断回调 - 周期性上传传感器数据(JSON格式通过WiFi串口2)
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
int len = snprintf(json_buffer, sizeof(json_buffer),
"{"
"\"temperature\":%.1f,"
"\"water_level\":%.1f"
"}",
temperature, water_level);
Serial_Printf_huart2("%s\r\n", json_buffer);
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */