基于STM32的智能小型洗碗机控制系统设计

🍽️ 智能洗碗机控制系统 使用说明书

核心架构: 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 */
相关推荐
cmpxr_2 小时前
【算法】ECC验签名
单片机·算法
送外卖的CV工程师2 小时前
STM32 CubeMX Makefile 工程编译 入门指南
stm32·单片机·嵌入式硬件·学习·makefile·stm32cubemx
项目題供诗2 小时前
STM32-新建工程(二)
stm32·单片机·嵌入式硬件
ghie90902 小时前
STM32 待机模式与唤醒实验(标准库函数版)
stm32·单片机·嵌入式硬件
somi72 小时前
ARM-驱动-06-中断底半部 + ioctl + 原子操作与锁
arm开发·单片机·嵌入式硬件
进击的小头3 小时前
第9篇:嵌入式芯片指令集架构(ISA)详解:ARM_RISC-V等主流ISA全对比
arm开发·单片机·架构·risc-v
篮子里的玫瑰3 小时前
一个隐藏的坑:MicroLib与串口打印的关系
驱动开发·stm32·嵌入式硬件
qq_401700413 小时前
大彩串口屏DC80480M070使用以及软件配置
嵌入式硬件
LNN202215 小时前
STM32H7 + 迪文屏 DGUS 开发实战:从零构建工业级时间设置界面
stm32·单片机·嵌入式硬件