一、设计背景和意义
1.1 设计背景
随着社会发展和生活节奏加快,宠物饲养已成为现代家庭的重要组成部分。然而,由于工作繁忙或短期出行,许多宠物主人难以保证宠物的规律饮食和舒适生活环境。传统的喂食方式依赖人工操作,存在投喂时间不固定、喂食量不精确、无法实时监控环境等问题,长期可能导致宠物健康隐患。此外,市场上现有的智能喂食设备功能较为单一,大多仅支持定时投喂或简单远程控制,缺乏对宠物生活环境的综合监测(如温湿度、光照、水质等),且数据交互能力较弱,难以满足用户对智能化、精细化宠物管理的需求。
1.2 设计意义
- 提升宠物饲养的智能化水平:通过多传感器融合和自动化控制,减少人工干预,确保宠物饮食和环境的稳定性;
- 优化用户体验:支持手机 APP 远程监控和手动控制,适应不同使用场景,提高系统的易用性和灵活性;
- 推动智能家居技术应用:本系统的模块化设计和物联网架构可为其他智能设备提供参考;
- 促进嵌入式与物联网技术融合:通过 STM32 与 ESP8266 的协同工作,探索低成本、高效率的物联网终端设备实现方案。
二、实物效果展示
2.1 实物图片

2.2 实物演示视频
【开源】基于 STM32 的智能宠物喂食系统
三、硬件功能简介
3.1 项目功能详解
- 传感器检测:检测温湿度、光照强度、水位传感器等数据。
- 食物称重:HX711 可以实时检测宠物的食物重量。
- 数据显示:0.96OLED 屏幕显示全部的传感器数据以及传感器的阈值等数据。
- 执行机构:步进电机(模拟喂食)、水泵(补充水分)、灯光。
- 定时喂食:采用时钟模块可以设定实时时间,同时可以设定自动喂食的时间。
- 接入云平台:系统通过 ESP8266 WIFI 联网后,接入机智云平台。
- App 远程监控:通过 App 远程监控全部传感器数据;App 远程控制、喂食、水泵、灯光。
- 阈值数据设定:系统通过按键设定阈值,也可以通过手机 App 远程设定。
- 模式切换:可以通过按键或者手机 App 实现自动/手动模式的切换。
- 手动模式:通过手机 App 或小程序控制喂食、补充水分、灯光等。
- 自动模式:根据设定的食物重量阈值,低于阈值,自动开启补充食物;设定水位阈值,低于水位阈值将会自动开启水泵补水;设定光照阈值,低于光照阈值将会自动开启补光灯。
3.2 元器件清单
- 主控 STM32F103C8T6 单片机
- ESP8266 联网 WiFi
- 0.96OLED 显示屏幕
- 光敏电阻光照传感器
- 水位传感器
- DHT11 温湿度传感器
- HX711 称重传感器
- DS1302 时钟模块
- 步进电机、水泵、LED 灯片。
四、主框图与软件流程图
主框图

流程图

五、硬件 PCB 展示


六、软件程序设计
#include "delay.h"
#include "sys.h"
#include "adc.h"
#include "key.h"
#include "oled.h"
#include "usart.h"
#include "control.h"
#include "dht11.h"
#include "hx711.h"
#include "uln2003.h"
#include "timer.h"
#include "ds1302.h"
/**** 数据变量 ****/
uint16_t timeone = 0; //喂食 小时
uint16_t timetwo = 0; //喂食 分钟
uint8_t food_flag = 0; //喂食
uint8_t water_flag = 0; //补水
uint8_t led_flag = 0; //补光
uint8_t auto_mode = 0; //自动模式开关 0 关闭 1 开启
int temp_door=30;
int hum_door=20;
int level_door = 0;
int weight_door = 500;
int light_door = 3000;
/**** 菜单变量 ****/
uint8_t option = 0;
uint8_t keu_num = 0;
uint8_t page_num = 1;
/**** 功能函数声明 ****/
void Key_event_handle(void); //按键功能函数
void get_all_sensor_data(); //传感器数据采集
void Sensor_Data_Handle(); //不同模式下控制效果
void OLED_Show_FirstPage(); //第一屏显示内容
void OLED_Show_SecondPage(); //第二屏显示内容
void OLED_Show_ThirdPage(); //第三屏显示内容
int value;
float weight;
int32_t reset;
u8 bufff[30];
u16 light;
float Weights=38642.0; //100g
int32_t Weights_100=8493860; //100g
int main(void) {
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(); //系统延时函数初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//改变指定管脚的映射,JTAG-DP 禁用 + SW-DP 使能
OLED_Init(); //屏幕初始化
uart3_init(9600); //串口 3 初始化,波特率 9600
uart1_init(115200); //串口 1 初始化,波特率 115200
HX711_Init(); //称重模块初始化
reset = HX711_GetData(); //称重数据归零(去皮)
KEY_Init(); //按键初始化
DHT11_Init(); //DHT11 温湿度初始化
Adc_Init(); //水位、光照强度初始化
Control_Init(); //水泵、补光灯初始化
DS1302_Init(&SysDate); //DS1302 时钟初始化
DS1302_DateSet(&SysDate); //DS1302 时钟设置
LED=0;
Uln2003_Init(); //步进电机初始化
TIME_Config(); //定时器初始化
while(1) {
if(Time_Period[0]>1000){ //Time_Period[1] 在定时器中累加
get_all_sensor_data(); //传感器数据采集
Time_Period[0]=0;
printf("温度:%d℃,湿度:%d%%RH\r\n",temp,hum);
printf("水位:%d%%,光照:%dLux\r\n",Water_Value,Light_Value);
printf("食物重量:%dg\r\n",(int)weight);
}
Key_event_handle(); //按键监测
if(page_num == 1){ //屏幕更新
OLED_Show_FirstPage();
}else if(page_num == 2){
OLED_Show_SecondPage();
}else if(page_num == 3){
OLED_Show_ThirdPage();
}
Sensor_Data_Handle(); //数据处理
}
}
void Sensor_Data_Handle() {
//手动模式开关控制
if(auto_mode==0){
if(water_flag==1){ //水泵开关
water = 1;
}else water = 0;
if(led_flag==1){ //照明开关
LED = 1;
}else LED = 0;
if(food_flag==1){ //喂食开关
Stepper_RotateByLoop(1,1,1);
food_flag=0;
}
//定时喂食
if(SysDate.hour==timeone&&SysDate.min==timetwo&&SysDate.sec==0)food_flag=1;
}else if(auto_mode==1){
//食物重量低于阈值,步进电机转动喂食
if(weight<weight_door)Stepper_RotateByLoop(1,1,1);
//水位低于阈值,打开水泵补水
if(Water_Value<level_door)water=1;
else water=0;
//光照强度低于阈值,打开补光灯
if(Light_Value<light_door)LED=1;
else LED=0;
}
}
void get_all_sensor_data() {
DHT11_Read_Data(&temp,&hum); //获取温湿度数据
value = HX711_GetData(); //获取称重数据
weight=(float)(value-reset)*Weights/(float)(Weights_100-reset);
Get_Water_Value(&Water_Value); //获取水位数据
Get_Light_Value(&Light_Value); //获取光照强度数据
DS1302_DateRead(&SysDate); //读时间
}
void Key_event_handle(void) {
keu_num = KEY_Scan(0); //按键扫描赋值
switch(keu_num) {
case 1: //按键 1 按下
if(page_num==1){
page_num=2;
OLED_Clear();OLED_Show_SecondPage();
}else if(page_num==2){
option++;
if(option==6){
option=0;
page_num=3;
OLED_Clear();OLED_Show_ThirdPage();
}else OLED_Show_SecondPage();
}else if(page_num==3){
option++;
if(option==4){
option=0;
page_num=1;
OLED_Clear();OLED_Show_FirstPage();
}else OLED_Show_ThirdPage();
}
break;
case 2: //按键 2 按下
if(page_num==1){
auto_mode = 1;
}else if(page_num==2){
if(option==1){ level_door++; }
else if(option==2){ weight_door+=10; }
else if(option==3){ light_door+=15; }
else if(option==4){ timeone++; if(timeone==24)timeone=0; }
else if(option==5){ timetwo++; if(timetwo==60)timetwo=0; }
}else if(page_num==3){
if(option==1){ led_flag=1; }
else if(option==2){ water_flag=1; }
else if(option==3){ food_flag=1; }
}
break;
case 3: //按键 3 按下
if(page_num==1){
auto_mode = 0;
}else if(page_num==2){
if(option==1){ if(level_door>0)level_door--; }
else if(option==2){ if(weight_door>0)weight_door-=10; }
else if(option==3){ if(light_door>0)light_door-=15; }
else if(option==4){ timeone--; if(timeone>24)timeone=23; }
else if(option==5){ timetwo--; if(timetwo>60)timetwo=59; }
}else if(page_num==3){
if(option==1){ led_flag=0; }
else if(option==2){ water_flag=0; }
else if(option==3){ food_flag=1; }
}
break;
case 4: //按键 4 按下
break;
}
}
/* 氨 (0) 气 (1) 浓 (2) 度 (3) 温 (4) 水 (5) 位 (6) 自 (7) 动 (8) 喂 (9) 食 (10) 补 (11) 水 (12) 间 (13) 隔 (14) 次 (15) 数 (16) 时 (17) 长 (18) 开 (19) 关 (20) ℃(21) 饲 (22) 料 (23) 设 (24) 置 (25) 最 (26) 低 (27) 高 (28 重 (29) 量 (30) 质 (31) 光 (32) 照 (33) AUTO(34) 定 (35) 有 (36) 害 (37) 气 (38) 体 (39) 湿 (40) 阈 (41) 值 (42) 自 (43) 手 (44) 动 (45) */
void OLED_Show_FirstPage(){
OLED_ShowChar(8*8,0,SysDate.year/10+'0',8,1);
OLED_ShowChar(8*9,0,SysDate.year%10+'0',8,1);
OLED_ShowChar(8*10,0,'/',8,1);
OLED_ShowChar(8*11,0,SysDate.mon/10+'0',8,1);
OLED_ShowChar(8*12,0,SysDate.mon%10+'0',8,1);
OLED_ShowChar(8*13,0,'/',8,1);
OLED_ShowChar(8*14,0,SysDate.day/10+'0',8,1);
OLED_ShowChar(8*15,0,SysDate.day%10+'0',8,1);
OLED_ShowChar(8*8,8,SysDate.hour/10+'0',8,1);
OLED_ShowChar(8*9,8,SysDate.hour%10+'0',8,1);
OLED_ShowChar(8*10,8,':',8,1);
OLED_ShowChar(8*11,8,SysDate.min/10+'0',8,1);
OLED_ShowChar(8*12,8,SysDate.min%10+'0',8,1);
OLED_ShowChar(8*13,8,':',8,1);
OLED_ShowChar(8*14,8,SysDate.sec/10+'0',8,1);
OLED_ShowChar(8*15,8,SysDate.sec%10+'0',8,1);
OLED_ShowChar(8*0,0,'T',16,1);
OLED_ShowChar(8*1,0,':',16,1);
OLED_ShowNum(16*1,0,temp,2,16,1);
OLED_ShowChinese(16*2,0,21,16,1);
OLED_ShowChar(8*0,16,'H',16,1);
OLED_ShowChar(8*1,16,':',16,1);
OLED_ShowNum(16*1,16,hum,2,16,1);
OLED_ShowChar(16*2,16,'%',16,1);
OLED_ShowChinese(16*3+8,16,32,16,1); //光照
OLED_ShowChinese(16*4+8,16,33,16,1);
OLED_ShowChar(16*5+8,16,':',16,1);
OLED_ShowNum(16*6,16,Light_Value,4,16,1);
OLED_ShowChinese(16*0,32,5,16,1); //水位
OLED_ShowChinese(16*1,32,6,16,1);
OLED_ShowChar(16*2,32,':',16,1);
OLED_ShowNum(16*2+8,32,Water_Value,2,16,1);
OLED_ShowChinese(16*0,48,10,16,1); //食重
OLED_ShowChinese(16*1,48,29,16,1);
OLED_ShowChar(16*2,48,':',16,1);
OLED_ShowNum(16*2+8,48,weight,4,16,1);
OLED_ShowChar(16*4+8,48,'g',16,1);
if(auto_mode==1){
OLED_ShowChinese(16*6,48,43,16,1);
}else{
OLED_ShowChinese(16*6,48,44,16,1);
}
OLED_ShowChinese(16*7,48,45,16,1);
OLED_Refresh();
}
void OLED_Show_SecondPage(){
OLED_ShowChinese(16*0,0,5,16,1); //水位阈值
OLED_ShowChinese(16*1,0,6,16,1);
OLED_ShowChinese(16*2,0,41,16,1);
OLED_ShowChinese(16*3,0,42,16,1);
OLED_ShowChar(16*4,0,':',16,1);
if(option==1)OLED_ShowNum(16*4+8,0,level_door,3,16,0);
else OLED_ShowNum(16*4+8,0,level_door,3,16,1);
OLED_ShowChinese(16*0,16,10,16,1); //食重阈值
OLED_ShowChinese(16*1,16,29,16,1);
OLED_ShowChinese(16*2,16,41,16,1);
OLED_ShowChinese(16*3,16,42,16,1);
OLED_ShowChar(16*4,16,':',16,1);
if(option==2)OLED_ShowNum(16*4+8,16,weight_door,4,16,0);
else OLED_ShowNum(16*4+8,16,weight_door,4,16,1);
OLED_ShowChinese(16*0,32,32,16,1); //光照阈值
OLED_ShowChinese(16*1,32,33,16,1);
OLED_ShowChinese(16*2,32,41,16,1);
OLED_ShowChinese(16*3,32,42,16,1);
OLED_ShowChar(16*4,32,':',16,1);
if(option==3)OLED_ShowNum(16*4+8,32,light_door,4,16,0);
else OLED_ShowNum(16*4+8,32,light_door,4,16,1);
OLED_ShowChinese(16*0,48,35,16,1); //定时喂食
OLED_ShowChinese(16*1,48,17,16,1);
OLED_ShowChinese(16*2,48,9,16,1);
OLED_ShowChinese(16*3,48,10,16,1);
OLED_ShowChar(16*4,48,':',16,1);
if(option==4)OLED_ShowNum(16*4+8,48,timeone,2,16,0);
else OLED_ShowNum(16*4+8,48,timeone,2,16,1);
OLED_ShowChar(16*5+8,48,':',16,1);
if(option==5)OLED_ShowNum(16*6,48,timetwo,2,16,0);
else OLED_ShowNum(16*6,48,timetwo,2,16,1);
OLED_Refresh();
}
void OLED_Show_ThirdPage(){
//补光开关
if(option==1){
OLED_ShowChinese(16*0,0,11,16,0);
OLED_ShowChinese(16*1,0,32,16,0);
}else{
OLED_ShowChinese(16*0,0,11,16,1);
OLED_ShowChinese(16*1,0,32,16,1);
}
OLED_ShowChar(16*2,0,':',16,1);
if(led_flag==1)OLED_ShowChinese(16*2+8,0,19,16,1);
else OLED_ShowChinese(16*2+8,0,20,16,1);
//补水开关
if(option==2){
OLED_ShowChinese(16*0,16,11,16,0);
OLED_ShowChinese(16*1,16,5,16,0);
}else{
OLED_ShowChinese(16*0,16,11,16,1);
OLED_ShowChinese(16*1,16,5,16,1);
}
OLED_ShowChar(16*2,16,':',16,1);
if(water_flag==1)OLED_ShowChinese(16*2+8,16,19,16,1);
else OLED_ShowChinese(16*2+8,16,20,16,1);
//喂食
if(option==3){
OLED_ShowChinese(16*0,32,9,16,0);
OLED_ShowChinese(16*1,32,10,16,0);
}else{
OLED_ShowChinese(16*0,32,9,16,1);
OLED_ShowChinese(16*1,32,10,16,1);
}
OLED_Refresh();
}


