STM32项目开源:智能宠物喂食系统(代码+原理图+仿真)
·
一、前言
实物项目成品:

仿真项目成品:

哔哩哔哩开源视频链接(复制浏览器打开):
https://www.bilibili.com/video/BV1Wdto6uETq/?vd_source=10da4d04d580bd6b34f7298120737c50
(资料分享见文末)
二、项目功能简介
【开源】STM3单片机智能宠物喂食系统(代码+原理图+仿真)
功能介绍:
(1)核心主控:采用 STM32 单片机作为主控芯片,完成数据处理与外设调度,保障系统稳定高效运行。
(2)OLED显示:配备显示屏,实时展示当前时间、环境温湿度以及食物的实时重量。
(3)精准喂食:利用重量传感器检测出料重量,按照预设参数完成投喂,达到设定重量后自动停止投料。
(4)参数配置:用户可自由设置投喂时间、投喂重量,适配不同宠物的饮食需求。
(5)手动干预:配置实体按键,支持本地手动喂食,便于特殊场景下临时操作。
(6)自动加水:搭载水位检测模块,监测水箱水位,水位低于设定阈值时自动加水。
(7)语音提醒:支持设置定点语音播报,到点发出提示,提醒宠物按时进食。
(8)双模式工作:具备自动、手动两种工作模式,可按需切换运行模式。
(9)手机 APP 控制:通过 WiFi 连接手机 APP,远程查看温湿度、食物重量,设置定时参数、同步手机时间校准设备时钟,支持 APP 远程手动喂食。
收起
三、PCB设计

四、原理图设计

五、程序设计
代码如下:
//头文件
#include "main.h"//所有头文件都放在里面方便调用管理
#include "stm32f10x.h"
//函数声明
void Key_Init(void);//按键函数声明
void display(void);
void music_play(unsigned char dat);
u8 b_1s,bee_num;
u16 sys_time,sys_time2;//系统计数
u8 mod=0;//显示模式
u16 kaojin_f,shuiwei_f;
short menu1,set_f; //一级目录编号 设置数据位标记
u8 run_b;//运行标志,短信发送标志
u32 begin_code[21] = {90, 1,//重量 set_code[0] 输出口
02,13,00,//喂食时间1 set_code [2]时[3]分[4]秒
0,0,0,//喂食时间2 set_code [5]时[6]分[7]秒
0,0,0,//喂食时间3 set_code [8]时[9]分[10]秒
0,0,0,0,//备用数组 set_code[4]~[15]
0,0,0,0,66};//备用数组 set_code[19]验证位
u32 set_code[21];//设置参数
u8 run_mod,Motor_on_b,Motor_off_b;//手自动标志位 电机开关动作标志位
char motor_pos;
char str[32];//显示缓存
//=============================================================================
//文件名称:KEY
//功能概要:按键服务
//参数说明:无
//函数返回:VOID
//=============================================================================
void key(void)
{
// int i;
//////////////////////////////////////////开启按键按下
if(PAin(0)==0)
{
DelayMs(5);
if(PAin(0)==0)//
{
while(PAin(0)==0);
switch(mod)
{
case 0://待机
mod = 1 ;//切换到菜单设置
OLED_CLS();//清屏
menu1 = 1;
break;
case 1://设置菜单
mod = menu1 + 1 ;//切换到对应的设置界面
OLED_CLS();//清屏
break;
case 2://时间调整
set_f++;
if(set_f>6) set_f = 0;
DS1302_SetTime(year,mon,day,hour,min,sec);//按一下就保存一次时间
break;
case 3://参数设置
set_f++;
if(set_f>2) set_f = 0;
break;
case 4://喂食时间设置
set_f++;
if(set_f>9) set_f = 0;
break;
}
}
}
//////////////////////////////////////////++
if(PAin(1)==0)
{
DelayMs(5);
if(PAin(1)==0)//
{
while(PAin(1)==0);
switch(mod)
{
case 0://待机
if(run_mod) run_mod = 0;
else run_mod = 1;
//printf("hello");
break;
case 1://设置菜单
menu1--;
if(menu1<1) menu1 = 3;
break;
case 2://时间调整
switch(set_f)
{
case 1://设置年
if(year < 99) year++;
else year = 0;
break;
case 2://设置月
if(mon < 12) mon++;
else mon = 1;
break;
case 3://设置日
if(day < 31) day++;
else day = 1;
break;
case 4://设置时
if(hour < 24) hour++;
else hour = 0;
break;
case 5://设置分
if(min < 60) min++;
else min = 0;
break;
case 6://设置秒
if(sec < 60) sec++;
else sec = 0;
break;
}
break;
case 3://参数设置
if((set_f-1)<0) break;
switch(set_f-1)
{
case 0://设置宠物重量
if(set_code[set_f-1]<365)
set_code[set_f-1] = set_code[set_f-1] + 1;
break;
case 1:
if(set_code[set_f-1]<=1) set_code[set_f-1] = 2 ;
else set_code[set_f-1] = 1;
break;
default:
break;
}
break;
case 4://宠物喂食时间设置
if((set_f == 1)|(set_f == 4)|(set_f == 7))
{
set_code[set_f+1]++;
if(set_code[set_f+1]>23) set_code[set_f+1] = 0;
}
else
{
set_code[set_f+1]++;
if(set_code[set_f+1]>59) set_code[set_f+1] = 0;
}
break;
default:
break;
}
}
}
//////////////////////////////////////////--
if(PAin(2)==0)
{
DelayMs(5);
if(PAin(2)==0)//
{
while(PAin(2)==0);
switch(mod)
{
case 0://待机
// music_play(0);
break;
case 1://设置菜单
menu1++;
if(menu1>3) menu1 = 1;
break;
case 2://时间调整
switch(set_f)
{
case 1://设置年
if(year > 0) year--;
else year = 99;
break;
case 2://设置月
if(mon > 1) mon--;
else mon = 12;
break;
case 3://设置日
if(day > 1) day--;
else day = 31;
break;
case 4://设置时
if(hour > 0) hour--;
else hour = 23;
break;
case 5://设置分
if(min > 0) min--;
else min = 59;
break;
case 6://设置秒
if(sec > 0) sec--;
else sec = 59;
break;
}
break;
case 3://参数设置
if((set_f-1)<0) break;
switch(set_f-1)
{
case 0://设置宠物重量
if(set_code[set_f-1]>1)
set_code[set_f-1] = set_code[set_f-1] - 1;
break;
case 1:
if(set_code[set_f-1]<=1) set_code[set_f-1] = 2 ;
else set_code[set_f-1] = 1;
break;
default:
break;
}
break;
case 4:
if((set_f == 1)|(set_f == 4)|(set_f == 7))
{
if(set_code[set_f+1]==0) set_code[set_f+1] = 23;
else set_code[set_f+1]--;
}
else
{
if(set_code[set_f+1]==0) set_code[set_f+1] = 59;
else set_code[set_f+1]--;
}
break;
}
}
}
//////////////////////////////////////////返回
if(PAin(3)==0)
{
DelayMs(5);
if(PAin(3)==0)//
{
while(PAin(3)==0);
switch(mod)
{
case 0://待机
music_play(1);
if(run_b == 0)
{
if(run_b == 0) OLED_ShowCC(40,6,"手动中");//显示状态
// if(set_code[1]==1)
{
Motor(3,500,0);//步进电机转动 打开喂食仓门
DelayMs(1000);
Motor(3,500,1);//步进电机转动 打开喂食仓门
}
// else
// {
// jdq_io = 0;
// DelayMs(4000);
// jdq_io = 1;
// }
if(run_b == 0) OLED_ShowCC(40,6,"待机中");//显示状态
}
break;
case 1://设置菜单
mod = 0 ;//切换到菜单设置
OLED_CLS();//清屏
break;
case 2://时间调整
mod = 1 ;//切换到菜单设置
DS1302_SetTime(year,mon,day,hour,min,sec);//返回就保存时间
OLED_CLS();//清屏
break;
case 3://参数设置
Writeflash(0,set_code,0,20);//保存设置
mod = 1 ;//切换到菜单设置
OLED_CLS();//清屏
break;
case 4://参数设置
Writeflash(0,set_code,0,20);//保存设置
mod = 1 ;//切换到菜单设置
OLED_CLS();//清屏
break;
}
}
}
}
void display(void)
{
char d_code ,i;
switch(mod)
{
case 0://待机界面
OLED_ShowCC(0,0,"智能宠物喂食系统");
sprintf(str,"%02d:%02d:%02d RH:%02d%%",hour,min,sec,DHT11_RH);//显示当前时间
OLED_ShowCC(0,2,(unsigned char *)str);
sprintf(str,"重量:%04dgT:%02d℃",Weight_Shiwu,DHT11_temp);//显示当前时间
OLED_ShowCC(0,4,(unsigned char *)str);
// else OLED_ShowCC(0,4,"启用自动喂食模式");
OLED_ShowStr(0,6,"SET ",2);
OLED_ShowStr(96,6,"Feed",2);
if(run_b == 0) OLED_ShowCC(40,6,"待机中");//显示按键备注和模式
if(run_b == 1) OLED_ShowCC(40,6,"喂食中");
break;
///////////////////////////////////////////////////////////////////////////
case 1://菜单设置界面
OLED_ShowCC(0,0,"====系统设置====");
if(menu1 == 1) d_code = '<';//菜单选中时显示《《没选中的换不显示
else d_code = ' ';
sprintf(str,"系统时间设置 %c%c ",d_code,d_code);
OLED_ShowCC(0,2,(unsigned char *)str);
if(menu1 == 2) d_code = '<';
else d_code = ' ';
sprintf(str,"系统参数设置 %c%c ",d_code,d_code);
OLED_ShowCC(0,4,(unsigned char *)str);
if(menu1 == 3) d_code = '<';
else d_code = ' ';
sprintf(str,"喂食时间设置 %c%c ",d_code,d_code);
OLED_ShowCC(0,6,(unsigned char *)str);
// OLED_ShowStr(0,6,"OK + - Return",2);
break;
//////////////////////////////////////////////////////////////
case 2://系统时间设置界面
OLED_ShowCC(0,0,"==系统时间设置==");
sprintf(str," 20%02d-%02d-%02d ",year,mon,day);//显示设置年月日
if(set_f==1) i=4; else {
if((set_f>=2)&(set_f<=3)) i=2; else i = 0;}
OLED_ShowStr_S(0,2,(unsigned char *)str,8-i+(set_f-1)*3,i,2);//显示时分秒
sprintf(str," %02d:%02d:%02d ",hour,min,sec);
if((set_f>=4)&(set_f<=6)) i=2; else i = 0;
OLED_ShowStr_S(0,4,(unsigned char *)str,5+(set_f-4)*3,i,2);
OLED_ShowStr(0,6,"OK + - Return",2);
break;
/////////////////////////////////////////////////////////////
case 3://系统参数设置界面
OLED_ShowCC(0,0,"==系统参数设置==");
OLED_ShowCC(0,2,"喂食重量:");
sprintf(str,"%04d g",set_code[0]*10);//显示角度
if(set_f==1) i=4; else i = 0;
OLED_ShowStr_S(80,2,(unsigned char *)str,1,i,2);
OLED_ShowStr(0,6,"OK + - Return",2);
break;
/////////////////////////////////////////////////////////////
case 4://系统参数设置界面
OLED_ShowCC(0,0,"==喂食时间设置==");
sprintf(str,"Time1: %02d:%02d:%02d ",set_code[2],set_code[3],set_code[4]);//显示设置时间
if((set_f>=1)&(set_f<=3)) i=2; else i = 0;
OLED_ShowStr_S(0,2,(unsigned char *)str,8+(set_f-1)*3,i,2);
sprintf(str,"Time2: %02d:%02d:%02d ",set_code[5],set_code[6],set_code[7]);//显示设置时间
if((set_f>=4)&(set_f<=6)) i=2; else i = 0;
OLED_ShowStr_S(0,4,(unsigned char *)str,8+(set_f-4)*3,i,2);
sprintf(str,"Time3: %02d:%02d:%02d ",set_code[8],set_code[9],set_code[10]);//显示设置时间
if((set_f>=7)&(set_f<=9)) i=2; else i = 0;
OLED_ShowStr_S(0,6,(unsigned char *)str,8+(set_f-7)*3,i,2);
break;
}
}
void music_play1()
{
unsigned char music_code[6] = {0X7E,0X03,0X06,0X1E,0XEF};
int i;
for(i=0;i<6;i++)
{
UART1SendByte(music_code[i]);
// printf("hello");
}
}
void music_play(unsigned char dat)
{
unsigned char music_code[6] = {0X7E,0X04,0X03,0X00,0X01,0XEF};
int i;
music_code[4] = dat;
for(i=0;i<6;i++)
{
UART1SendByte(music_code[i]);
// printf("hello");
}
}
//这是定时识别
void run(void)
{
if(run_b==0)
{
//判断时间到了吗
if(((hour==set_code[2])&(min==set_code[3])&(sec==set_code[4]))|((hour==set_code[5])&(min==set_code[6])&(sec==set_code[7]))|((hour==set_code[8])&(min==set_code[9])&(sec==set_code[10])))//到达设定的时间
{
run_b = 1;//喂食状态标志 1的话现在是喂食中
bee_num = 10;//蜂鸣器叫的次数/2
music_play(1);
if(set_code[1] == 1)
{
Motor(3,1000,0);//步进电机转动 打开喂食仓门
motor_pos = 1; //标记电机现在转动的方向
}
// Get_Maopi();//为了避免喂食时有其他杂物在称重平台上 这边重新去毛皮
}
}
if(run_b)//现在是喂食中
{
if(Weight_Shiwu>=set_code[0]) //称重检测到重量满足要求
{
run_b = 0;//设置位待机模式
if(motor_pos == 1)
{
Motor(3,1000,1);//步进电机转动 打开喂食仓门
motor_pos = 0; //标记电机现在的转动方向
}
}
}
/////////////////////////////////////////////这边是靠近喂水
if(kaojin_f>1)//宠物靠近
{
if(shuiwei_f<1)//水还没满
{
jdq_io = 0;
}
else
{
jdq_io = 1;
}
}
else
{
jdq_io = 0;
}
}
unsigned char table[]="temp 25 shui 25 wegh 0000 001 ";
unsigned char ta[]="0123456789- ";
unsigned char testdata,bluemod;
void esp8266_play(void);
//=============================================================================
//文件名称:main
//功能概要:主函数
//参数说明:无
//函数返回:int
//=============================================================================
// RCC_ClocksTypeDef get_rcc_clock;//定义一个结构体检查系统时钟
int main(void)
{
DelayInit();//系统延时函数初始化
DelayMs(1000);
uart_init(9600);//串口以初始化 波特率设置为9600
ESP8266_Init(); //WIFI模块初始化
Key_Init();//按键引脚初始化
motor_io_Init();//步进电机控制脚初始化
DHT11_Init();
TIM2_Int_Init(10000,3600);//输出话定时器 这边用到定时器2作为0.5秒钟中断发生源
TIM_Cmd(TIM2, ENABLE);//开启定时器2
DS1302_init(0x23,0x02,0x18,0x14,0x30,0x30);//时钟模块初始化
//I2C_Configuration();//开启IIC功能
music_play1();
OLED_Init();//OLED屏幕初始化
OLED_CLS();//清屏
Init_HX711pin();
Get_Maopi();
mod = 0;//开机显示开机界面
bluemod=0;
Readflash(set_code,0,20); //读取内部保存数据
DelayMs(10);
if(set_code[19] != 66)//对比内部数据是不是以及被重新覆盖 是的话载入初始化数据
{
OLED_ShowStr(0,0,"===NO FLASH===",2);
Writeflash(0,begin_code,0,20);//写入初始化数据
DelayMs(500);
Readflash(set_code,0,20);//读取写写入后的数据
sprintf(str,"Init System [%2d]",set_code[19]);//显示验证数据
OLED_ShowStr(0,4,(unsigned char *)str,2);
DelayMs(1500);
OLED_CLS();//清屏
}
while (1)
{
if(mod == 0)//待机模式下
{
Get_Weight();
DS1302_Readtime(); //获取当前日期/时间
}
DHT11_Read_Data();
display();//显示程序
testdata=0;
esp8266_play();
key();//按键程序
if(run_mod == 0) run();//自由在自动模式下才检测时间
}
}
//=============================================================================
//文件名称:Key_Init
//功能概要:GPIO初始化
//参数说明:无
//函数返回:无
//=============================================================================
void Key_Init(void) //按键引脚定义
{
//将按键接口设置为上拉电阻输入因为我们使用低电平信号触发
GPIO_InitTypeDef gpio_initstruct;
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//按键引脚定义
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA , ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
gpio_initstruct.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_0;
gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_initstruct);
//红外传感器引脚定位
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA , ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
gpio_initstruct.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12;
gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_initstruct);
//LED继电器引脚定义
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA , ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
gpio_initstruct.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_initstruct.GPIO_Pin = GPIO_Pin_8;
gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_initstruct);
jdq_io = 0;
//蜂鸣器引脚定义
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB , ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
gpio_initstruct.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_initstruct.GPIO_Pin = GPIO_Pin_15;
gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio_initstruct);
beep_io = 0;
}
void esp8266_play(void)
{
table[5]=ta[DHT11_temp/10];
table[6]=ta[DHT11_temp%10];
table[16]=ta[DHT11_RH/10];
table[17]=ta[DHT11_RH%10];
table[26]=ta[Weight_Shiwu/1000];
table[27]=ta[Weight_Shiwu/100%10];
table[28]=ta[Weight_Shiwu/10%10];
table[29]=ta[Weight_Shiwu%10];
////////////////////////////////////////////////
table[31]=ta[testdata/100];//存放bai位数据
table[32]=ta[testdata%100/10]; //存放shi位数据
table[33]=ta[testdata%10];
////////////////////////////////////////////////
if(++bluemod>3)
{
ESP8266_SendData(table,34);
bluemod=0;
}
if(BlueCmd("TE",20))
{
testdata=testdata+1;
}
if(BlueCmd("KA",20))
{
switch(mod)
{
case 0://待机
mod = 1 ;//切换到菜单设置
OLED_CLS();//清屏
menu1 = 1;
break;
case 1://设置菜单
mod = menu1 + 1 ;//切换到对应的设置界面
OLED_CLS();//清屏
break;
case 2://时间调整
set_f++;
if(set_f>6) set_f = 0;
DS1302_SetTime(year,mon,day,hour,min,sec);//按一下就保存一次时间
break;
case 3://参数设置
set_f++;
if(set_f>2) set_f = 0;
break;
case 4://喂食时间设置
set_f++;
if(set_f>9) set_f = 0;
break;
}
}
if(BlueCmd("KB",20))
{
switch(mod)
{
case 0://待机
if(run_mod) run_mod = 0;
else run_mod = 1;
//printf("hello");
break;
case 1://设置菜单
menu1--;
if(menu1<1) menu1 = 3;
break;
case 2://时间调整
switch(set_f)
{
case 1://设置年
if(year < 99) year++;
else year = 0;
break;
case 2://设置月
if(mon < 12) mon++;
else mon = 1;
break;
case 3://设置日
if(day < 31) day++;
else day = 1;
break;
case 4://设置时
if(hour < 24) hour++;
else hour = 0;
break;
case 5://设置分
if(min < 60) min++;
else min = 0;
break;
case 6://设置秒
if(sec < 60) sec++;
else sec = 0;
break;
}
break;
case 3://参数设置
if((set_f-1)<0) break;
switch(set_f-1)
{
case 0://设置宠物重量
if(set_code[set_f-1]<365)
set_code[set_f-1] = set_code[set_f-1] + 1;
break;
case 1:
if(set_code[set_f-1]<=1) set_code[set_f-1] = 2 ;
else set_code[set_f-1] = 1;
break;
default:
break;
}
break;
case 4://宠物喂食时间设置
if((set_f == 1)|(set_f == 4)|(set_f == 7))
{
set_code[set_f+1]++;
if(set_code[set_f+1]>23) set_code[set_f+1] = 0;
}
else
{
set_code[set_f+1]++;
if(set_code[set_f+1]>59) set_code[set_f+1] = 0;
}
break;
default:
break;
}
}
if(BlueCmd("KC",20))
{
switch(mod)
{
case 0://待机
// music_play(0);
break;
case 1://设置菜单
menu1++;
if(menu1>3) menu1 = 1;
break;
case 2://时间调整
switch(set_f)
{
case 1://设置年
if(year > 0) year--;
else year = 99;
break;
case 2://设置月
if(mon > 1) mon--;
else mon = 12;
break;
case 3://设置日
if(day > 1) day--;
else day = 31;
break;
case 4://设置时
if(hour > 0) hour--;
else hour = 23;
break;
case 5://设置分
if(min > 0) min--;
else min = 59;
break;
case 6://设置秒
if(sec > 0) sec--;
else sec = 59;
break;
}
break;
case 3://参数设置
if((set_f-1)<0) break;
switch(set_f-1)
{
case 0://设置宠物重量
if(set_code[set_f-1]>1)
set_code[set_f-1] = set_code[set_f-1] - 1;
break;
case 1:
if(set_code[set_f-1]<=1) set_code[set_f-1] = 2 ;
else set_code[set_f-1] = 1;
break;
default:
break;
}
break;
case 4:
if((set_f == 1)|(set_f == 4)|(set_f == 7))
{
if(set_code[set_f+1]==0) set_code[set_f+1] = 23;
else set_code[set_f+1]--;
}
else
{
if(set_code[set_f+1]==0) set_code[set_f+1] = 59;
else set_code[set_f+1]--;
}
break;
}
}
if(BlueCmd("KD",20))
{
switch(mod)
{
case 0://待机
music_play(1);
if(run_b == 0)
{
if(run_b == 0) OLED_ShowCC(40,6,"手动中");//显示状态
// if(set_code[1]==1)
{
Motor(3,500,0);//步进电机转动 打开喂食仓门
DelayMs(1000);
Motor(3,500,1);//步进电机转动 打开喂食仓门
}
// else
// {
// jdq_io = 0;
// DelayMs(4000);
// jdq_io = 1;
// }
if(run_b == 0) OLED_ShowCC(40,6,"待机中");//显示状态
}
break;
case 1://设置菜单
mod = 0 ;//切换到菜单设置
OLED_CLS();//清屏
break;
case 2://时间调整
mod = 1 ;//切换到菜单设置
DS1302_SetTime(year,mon,day,hour,min,sec);//返回就保存时间
OLED_CLS();//清屏
break;
case 3://参数设置
Writeflash(0,set_code,0,20);//保存设置
mod = 1 ;//切换到菜单设置
OLED_CLS();//清屏
break;
case 4://参数设置
Writeflash(0,set_code,0,20);//保存设置
mod = 1 ;//切换到菜单设置
OLED_CLS();//清屏
break;
}
}
}
六、包含内容

七、资料分享
资料获取:请查看主页介绍搜索或观看开源视频获取
更多推荐



所有评论(0)