目录
[PCB图 编辑编辑](#PCB图 编辑编辑)
[六、实验效果 编辑](#六、实验效果 编辑)
一、前言
项目成品图片:

哔哩哔哩视频链接:
(资料分享见文末)
二、项目简介
1.功能详解
基于STM32的智能家用垃圾桶(升级版)
功能如下:
- 环境采集:超声波传感器检测垃圾桶剩余容量,将垃圾桶剩余容量映射为百分比显示;光电红外传感器检测是否有人靠近,火焰传感器检测是否发生火灾。
- 显示功能:OLED屏幕显示采集到的信息数据
- 模式切换:可通过按键切换自动模式和手动模式
- 自动模式:自动模式下如果垃圾桶未满则指示灯1点亮,如果垃圾桶已满则指示灯2点亮并且蓝牙APP接收信息"垃圾桶已满";如果有人且垃圾桶未满则打开垃圾桶桶盖,如果垃圾桶已满则不打开桶盖且蜂鸣器报警提醒;通过火焰传感器检测是否发生火灾,如果发生火灾则蜂鸣器报警且两个指示灯同时亮起;垃圾桶桶盖关闭且消毒模式打开时开启消毒灯进行消毒,压力传感器检测到压力时,开始计时,超过阈值蜂鸣器报警
- 手动模式:手动模式下可通过按键控制垃圾桶桶盖和消毒灯的开关
- 阈值调节:可以通过按键调节垃圾桶的距离阈值以及控制消毒模式是否打开、计时阈值调节
- 蓝牙APP:通过蓝牙APP可接收环境信息数据和控制指令下发
- 语音控制:可通过语音控制模式切换,打开垃圾桶、消毒灯
2.主要器件
- STM32F103C8T6最小系统板
- OLED显示屏(4针IIC协议)
- 光电红外传感器
- SNR8016语音模块
- FSR402压力传感器
- 火焰传感器
- HC-SR04超声波传感器
- BT04-A蓝牙模块
- UV紫外线消毒灯
- 有源蜂鸣器
- 垃圾桶模型
- 继电器
- 舵机
三、原理图设计

四、PCB硬件设计
PCB图 

五、程序设计
cpp
#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "usart.h"
#include "delay.h"
#include "oled.h"
#include "key.h"
#include "Modules.h"
#include "TIM2.h"
#include "adcx.h"
#include "flash.h"
#include "usart2.h"
#include "usart3.h"
#include "ultrasonic.h"
#include "IR.h"
#include "HW.h"
#include "servo.h"
#include "timer.h"
/****************异方辰电子工作室******************
STM32
* 项目 : STM32智能家用垃圾桶
* 版本 : V2.0
* 日期 : 2025.11.07
* MCU : STM32F103C8T6
* 接口 : 见代码
* IP账号 : 异方辰电子/辰哥单片机设计(同BILIBILI|抖音|快手|小红书|CSDN|公众号|视频号等)
* 作者 : 辰哥
* 工作室 : 异方辰电子工作室
* 授权IP : 辰哥单片机设计、异方辰电子、YFC电子、异方辰系列
* 官方网站 : www.yfcdz.cn
**********************BEGIN***********************/
#define KEY_Long1 11
#define KEY_1 1
#define KEY_2 2
#define KEY_3 3
#define KEY_4 4
#define FLASH_START_ADDR 0x0801f000 //写入的起始地址
SensorModules sensorData; //声明传感器数据结构体变量
SensorThresholdValue Sensorthreshold; //声明传感器阈值结构体变量
DriveModules driveData; //声明驱动器状态结构体变量
uint8_t mode = 1; //系统模式 1自动 2手动 3设置
u8 dakai;//串口3使用的传递变量
u8 Flag_dakai;//串口3接收标志位
uint8_t is_secondary_menu = 0; // 0一级菜单,1二级菜单
uint8_t secondary_pos = 1; // 二级菜单光标位置(1-3对应时/分/秒)
uint8_t secondary_type = 0; // 二级菜单类型:0=RTC时间,1=定时开启,2=定时关闭
//uint8_t send_data[] = "A7:00001";//语音播放曲目1
//uint8_t send_data1[] = "A7:00002";//语音播放曲目2
extern u8 Res;
float Angle; //定义角度变量
//static uint8_t last_pressure_state = 0;
//系统静态变量
static uint8_t count_a = 1; //自动模式按键数
static uint8_t count_m = 1; //手动模式按键数
static uint8_t count_s = 1; //设置模式按键数
//static uint8_t last_mode = 0; // 记录上一次的模式
//static uint8_t last_count_s = 0; // 记录设置模式内上一次的页面
/**
* @brief 显示菜单内容
* @param 无
* @retval 无
*/
enum
{
AUTO_MODE = 1,
MANUAL_MODE,
SETTINGS_MODE
}MODE_PAGES;
int main(void)
{
SystemInit();//配置系统时钟为72M
delay_init(72); // 系统时钟72MHz
ADCx_Init();
LED_Init();
LED1_Init();
LED1_On();
LED2_Init();
BEEP_Init();
BEEP_OFF;
USART1_Config();//串口1初始化PC
USART2_Init(); //串口3初始化蓝牙
USART3_Config();//串口2初始化//语音模块
Key_Init();
OLED_Init();
OLED_Clear();//清屏
Ultrasonic_Init();//超声波初始化
IR_Init();
HW_Init();
Servo_Init();
FLASH_ReadThreshold();
// 添加的状态管理变量
static uint8_t last_mode = 0; // 记录上一次模式
static uint32_t last_sensor_time = 0; // 传感器扫描时间控制
static uint32_t last_display_time = 0; // 显示刷新时间控制
// 新增:保存当前自动模式的页码,避免重复定义
static uint8_t auto_page = 1;
TIM2_Init(72-1,1000-1); // 2ms定时中断
printf("Start \n");
while (1)
{
// ==================== 优先处理按键和通信 ====================
USART2_ProcessCmd(); // 先处理串口命令
USART3_ProcessCmd();
// ==================== 获取当前系统时间 ====================
uint32_t current_time = delay_get_tick(); // 使用系统滴答计数
// ==================== 优化传感器扫描频率 ====================
if(current_time - last_sensor_time > 100) // 每200ms扫描一次传感器 (100 * 2ms = 200ms)
{
SensorScan(); //获取传感器数据
// printf("距离:%d\r\n",sensorData.distance);
// printf("有无压力:%s\r\n",sensorData.pressure ? "是" : "否");
// printf("是否有人:%s\r\n",sensorData.value ? "是" : "否");
// printf("是否着火:%s\r\n",sensorData.fire ? "是" : "否");
// printf("---------------------------\r\n");
last_sensor_time = current_time;
}
// ==================== 立即处理按键 ====================
uint8_t current_key_num = KeyNum; // 保存当前按键值
// 模式切换按键立即处理
if(current_key_num != 0)
{
switch(mode)
{
case AUTO_MODE:
// 修复:将auto_page赋值移到这里,且只在按键触发时更新
auto_page = SetAuto();
if(auto_page == 1)
{
OLED_Clear();
OLED_autoPage1(); //显示自动模式第一页固定内容
SensorDataDisplay1(); //显示第一页传感器数据
}
else if(auto_page == 2)
{
OLED_Clear();
OLED_autoPage2(); //显示自动模式第二页固定内容
SensorDataDisplay2(); //显示第二页传感器数据
}
AutoControl();
if(current_key_num == KEY_1)
{
mode = MANUAL_MODE;
count_m = 1;
// 切换到手动模式时关闭灯和蜂鸣器
driveData.LED_Flag = 1;
driveData.LED1_Flag = 1;
driveData.LED2_Flag = 0;
driveData.BEEP_Flag = 0;
KeyNum = 0; // 立即清除按键
}
else if(current_key_num == KEY_Long1)
{
mode = SETTINGS_MODE;
count_s = 1;
KeyNum = 0; // 立即清除按键
}
break;
case MANUAL_MODE:
if(current_key_num == KEY_1)
{
mode = AUTO_MODE;
KeyNum = 0; // 立即清除按键
}
break;
case SETTINGS_MODE:
// 设置模式内部按键在各自模式中处理
break;
}
}
// ==================== 模式切换优化 ====================
if(last_mode != mode)
{
OLED_Clear();
last_mode = mode;
// 立即绘制新模式的固定内容
switch(mode)
{
case AUTO_MODE:
// 修复:模式切回自动时,默认显示第一页
auto_page = 1;
OLED_autoPage1();
SensorDataDisplay1();
break;
case MANUAL_MODE:
OLED_manualPage1();
break;
case SETTINGS_MODE:
OLED_settingsPage1();
break;
}
OLED_Refresh(); // 立即刷新显示
}
// ==================== 模式处理 ====================
switch(mode)
{
case AUTO_MODE:
// 修复:移除了原有的 SensorDataDisplay1() 无条件调用
// 改为根据当前auto_page显示对应数据
if(auto_page == 1)
{
SensorDataDisplay1(); //显示第一页传感器数据
}
else if(auto_page == 2)
{
SensorDataDisplay2(); //显示第二页传感器数据
}
AutoControl();
Control_Manager();
break;
// 手动模式、设置模式代码不变,此处省略...
case MANUAL_MODE:
{
// 原有手动模式代码(无修改)
static uint8_t manual_page_initialized = 0;
static uint8_t last_manual_count = 0;
static uint8_t last_LED_Flag = 1;
static uint8_t last_BEEP_Flag = 0;
static uint8_t force_refresh = 0; // 强制刷新标志
if(last_mode != mode)
{
manual_page_initialized = 0;
last_manual_count = 0;
last_LED_Flag = driveData.LED_Flag;
last_BEEP_Flag = driveData.BEEP_Flag;
force_refresh = 1;
count_m = 1;
driveData.LED_Flag = 0;
driveData.BEEP_Flag = 0;
}
uint8_t current_manual_count = SetManual();
uint8_t need_refresh = 0;
if(driveData.LED_Flag != last_LED_Flag || driveData.BEEP_Flag != last_BEEP_Flag)
{
need_refresh = 1;
last_LED_Flag = driveData.LED_Flag;
last_BEEP_Flag = driveData.BEEP_Flag;
}
if(!manual_page_initialized || current_manual_count != last_manual_count || need_refresh || force_refresh)
{
OLED_manualPage1();
OLED_manualOption(current_manual_count);
ManualSettingsDisplay1();
manual_page_initialized = 1;
last_manual_count = current_manual_count;
force_refresh = 0;
OLED_Refresh();
}
if(current_key_num != 0)
{
ManualControl(current_manual_count);
OLED_manualPage1();
OLED_manualOption(current_manual_count);
ManualSettingsDisplay1();
OLED_Refresh();
KeyNum = 0;
}
OLED_manualPage1();
OLED_manualOption(current_manual_count);
ManualSettingsDisplay1();
Control_Manager();
break;
}
case SETTINGS_MODE:
{
static uint8_t is_threshold_page_inited = 0;
uint8_t curr_count_s = SetSelection();
if(current_key_num != 0)
{
if (is_secondary_menu == 1)
{
if (current_key_num == KEY_2 || current_key_num == KEY_3 || current_key_num == KEY_4)
{
OLED_Refresh();
KeyNum = 0;
}
else if (current_key_num == KEY_1)
{
is_secondary_menu = 0;
secondary_pos = 1;
OLED_Clear();
OLED_settingsPage1();
SettingsThresholdDisplay1();
OLED_settingsOption(curr_count_s);
OLED_Refresh();
KeyNum = 0;
}
}
else
{
if (current_key_num == KEY_3 || current_key_num == KEY_4)
{
ThresholdSettings(curr_count_s);
SettingsThresholdDisplay1();
OLED_Refresh();
KeyNum = 0;
}
else if (current_key_num == KEY_1)
{
mode = AUTO_MODE;
is_threshold_page_inited = 0;
FLASH_W(FLASH_START_ADDR,
Sensorthreshold.distance_threshold,
Sensorthreshold.led2_enabled,
Sensorthreshold.time);
mode = AUTO_MODE;
is_threshold_page_inited = 0;
KeyNum = 0;
}
}
}
if (is_secondary_menu == 1)
{
}
else
{
if (curr_count_s >= 1 && curr_count_s <= 4)
{
if (is_threshold_page_inited == 0)
{
OLED_settingsPage1();
is_threshold_page_inited = 1;
}
}
OLED_settingsOption(curr_count_s);
SettingsThresholdDisplay1();
}
break;
}
}
// ==================== 限制显示刷新频率 ====================
if(current_time - last_display_time > 25)
{
OLED_Refresh();
last_display_time = current_time;
}
}
}
六、实验效果 
七、包含内容
