1任务:
设计制作一个基于单片机的宠物喂食器,能够按计划或手动喂食宠物,确保宠物的合理饮食和健康成长。
2要求:
(1)定时喂食功能: 设定每天的喂食时间表,宠物喂食器按照设定的时间自动喂食,保证宠物的饮食规律。
(2)定量喂食功能: 定量控制宠物的食物分配,根据不同大小和品种的宠物,设定合适的食物分配量,确保宠物摄取恰当的食物。
(3)手动喂食功能: 设计一个按钮或触摸屏,允许主人手动触发喂食过程,可以根据需要进行额外的喂食。
(4)食物存储容器: 设计一个可拆卸的食物容器,装载和储存宠物的食物,容易清洗和补充。
(5)系统稳定性和硬件设计: 确保系统的稳定性和可靠性,包括电路设计、驱动器、传感器等方面的考虑。
3说明:
(1)硬件和软件实现: 描述所选用的单片机和相关的传感器、执行器等硬件组件的使用,同时介绍所需的软件开发环境和编程语言。
(2)系统集成与测试: 将硬件和软件进行集成,并进行功能测试、性能评估等,确保宠物喂食器满足设计要求。
(3)节能与安全性: 分析和评估系统的能耗情况,并确保喂食器的安全性,如防止卡住、食物溢出等问题。
(4)结果与分析: 展示实验结果,对比分析系统的性能和喂食效果,并给出改进的建议。
4 硬件搭建:
5软件编程(一接入onenet平台部分,二功能实现部分)
(1)
#include
#include
#include
//设置用于连接温湿度传感器的引脚
#define DHTPIN 19 //根据硬件连接确定引脚编号(注意,这里我连接的是D7,也就是GPIO13)
#define led 2
#define LED_BUILTIN 2
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
float h,t;
// 引入 wifi 模块,并实例化,不同的芯片这里的依赖可能不同
#include
static WiFiClient espClient;
// 引入阿里云 IoT SDK
#include "AliyunIoTSDK.h"
// 设置产品和设备的信息,从阿里云设备信息里查看
#define PRODUCT_KEY "a1eMrKgKacD"
#define DEVICE_NAME "esp32"
#define DEVICE_SECRET "284e56bcb8052e38ba4e1f7cbb2e92a7"//(这三个东西在阿里云里面)
#define REGION_ID "cn-shanghai"
// 设置 wifi 信息
#define WIFI_SSID "www"//(这里面连你自己家的WiFi,注意要是2.4G频段的,5G的不行)
#define WIFI_PASSWD "12345678"//(你自己家的WiFi密码)
void setup()
{
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
// 初始化 wifi
wifiInit(WIFI_SSID, WIFI_PASSWD);
//初始化 iot,需传入 wifi 的 client,和设备产品信息
AliyunIoTSDK::begin(espClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET, REGION_ID);
// 绑定一个设备属性回调,当远程修改此属性,会触发 powerCallback
// PowerSwitch 是在设备产品中定义的物联网模型的 id
// AliyunIoTSDK::bindData("温湿度",CurrentTemperature);
// // 发送一个数据到云平台,LightLuminance 是在设备产品中定义的物联网模型的 id
AliyunIoTSDK::bindData("LED", LED);
pinMode(DHTPIN,INPUT);
dht.begin();
}
void loop()
{
AliyunIoTSDK::loop();
AliyunIoTSDK::send("temperature",t);
AliyunIoTSDK::send("Humidity",h);
//发送LED状态到云平台(高电平:1;低电平:0)
AliyunIoTSDK::send("LEDPIN", digitalRead(LED_BUILTIN));
h = dht.readHumidity();
t = dht.readTemperature();
Serial.print("Temperature:");
Serial.println(t);
delay(1000);
Serial.print("Humidity:");
Serial.print(h);
Serial.println("%");
delay(1000);
}
// 初始化 wifi 连接
void wifiInit(const char *ssid, const char *passphrase)
{
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passphrase);
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.println("WiFi not Connect");
}
Serial.println("Connected to AP");
digitalWrite(led, LOW);
}
//回调函数
void LED(JsonVariant L)//固定格式,修改参数l
{
int LED = L["LED"];//参数l
if (LED == 1)
{
digitalWrite(LED_BUILTIN, LOW);
}
else
{
digitalWrite(LED_BUILTIN, HIGH);
}
}
(2)
#include
#include
#include
#include
#include
#include
#include
#define USE_MULTCORE 1//使用多核心
#include
#include
unsigned long s=0;//将存储上次的时间
unsigned long a=0;//将存储上次的时间
unsigned long b=0;//将存储上次的时间
unsigned long c=0;//将存储上次的时间
unsigned long d=0;//将存储上次的时间
unsigned long e=0;//将存储上次的时间
unsigned long f=0;//将存储上次的时间
unsigned long g=0;//将存储上次的时间
#define water 4 //水深管脚0
#define buzzer 5 //蜂鸣器
#define relay 18 //创建继电器
#define TrigPin 16//号引脚触发端
#define EchoPin 17//号引脚接收端
#define Lan 13//蓝色
#define Hong 12//红色
#define Lv 14//绿色
char xiaoshi;//小时
char fengzhong;//分钟
char miao;//秒
int x=1;//工作模式,1自动,2手动
float z=0;//喂养打开时间,由宠物重量决定
int y=0;//喂养的食物重量
int w=1;//喂养开关1关闭,2打开
int t=1;//1未喂养2已喂养
float distance_cm;//存储距离单位值cm
//DS1302初始化
LiquidCrystal_I2C lcd(0x27,16,2); // 将LCD地址设置为0x27,用于16个字符和2行显示
namespace {
const int kCePin = 27; //RST
const int kIoPin = 26; //DAT
const int kSclkPin = 25; //CLK
// Create a DS1302 object.
DS1302 rtc(kCePin, kIoPin, kSclkPin);
//星期几
String dayAsString(const Time::Day day) {
switch (day) {
case Time::kSunday: return "1";
case Time::kMonday: return "2";
case Time::kTuesday: return "3";
case Time::kWednesday: return "4";
case Time::kThursday: return "5";
case Time::kFriday: return "6";
case Time::kSaturday: return "7";
}
return "(unknown day)";
}
//输出时间
void printTime() {
Time t = rtc.time(); // 获取当前时间和日期
const String day = dayAsString(t.day);
// Format the time and date and insert into the temporary buffer. //格式化时间和日期并插入临时缓冲区。
char buf[50]; //定义一个字符型数组用来存放日期数据(用于串口打印)
char DateBuf[12]; //定义一个字符型数组用来存放日期数据(由于1602每行只能打印16个字符,所以日期和时间分两个数组存)
char TimeBuf[10];//定义一个字符型数组用来存放时间数据(由于1602每行只能打印16个字符,所以日期和时间分两个数组存)
snprintf(buf, sizeof(buf), "%s %04d-%02d-%02d %02d:%02d:%02d",day.c_str(),t.yr, t.mon, t.date,t.hr, t.min, t.sec);
//把日期和时间数据存放到数组中
snprintf(DateBuf,sizeof(DateBuf),"%04d-%02d-%02d",t.yr, t.mon, t.date); //把日期数据存放到数组中
snprintf(TimeBuf,sizeof(TimeBuf),"%02d:%02d:%02d",t.hr, t.min, t.sec); //把时间数据存放到数组中
Serial.println(buf); //串口打印输出日期与时间
lcd.setCursor(0,0); //设置1602的光标到第0行第0个字符处
lcd.print(DateBuf); //在LCD光标所在处打印DateBuf数组里的数据
lcd.setCursor(12,0); //设置1602的光标到第0行第0个字符处
lcd.print("W:");
lcd.print(day.c_str());
lcd.setCursor(0,1); //设置1602的光标到第1行第3个字符处
lcd.print(TimeBuf); //在LCD光标所在处打印TimeBuf数组里的数据
xiaoshi=t.hr;
fengzhong=t.min;
miao=t.sec;
}
}
int pos=90;// 舵机角度
Servo myservo1;//创建喂养舵机
float i,data;//定义变量i是水深传感器数值,data是深度
HX711 HX711_CH0(15, 2, 300); //初始化SCK,DT,GapValue
long chongwu1=0;//定义一个变量用于存放宠物g
Adafruit_SSD1306 oled(128,64,&Wire,4);//oled定义初始化
static const unsigned char PROGMEM shui[]={
0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x08,0x01,0x08,0x7D,0x90,0x05,0xA0,0x09,0x40,
0x09,0x40,0x11,0x20,0x11,0x10,0x21,0x08,0x41,0x06,0x81,0x00,0x05,0x00,0x02,0x00,/*"水",0*/
};//水
static const unsigned char PROGMEM shen[]={
0x00,0x00,0x27,0xFC,0x14,0x04,0x14,0xA4,0x81,0x10,0x42,0x08,0x40,0x40,0x10,0x40,
0x17,0xFC,0x20,0x40,0xE0,0xE0,0x21,0x50,0x22,0x48,0x2C,0x46,0x20,0x40,0x00,0x40,/*"深",1*/
};//深
static const unsigned char PROGMEM chong[]={
0x02,0x00,0x01,0x00,0x7F,0xFE,0x40,0x02,0x84,0x24,0x04,0x10,0x04,0x10,0xFF,0xFE,
0x04,0x80,0x08,0x88,0x08,0x90,0x10,0xA0,0x10,0xC2,0x20,0x82,0x41,0x82,0x86,0x7E,/*"宠",0*/
};//宠
static const unsigned char PROGMEM wu[]={
0x10,0x80,0x10,0x80,0x50,0x80,0x50,0xFC,0x7D,0x54,0x52,0x54,0x90,0x54,0x10,0x94,
0x1C,0x94,0xF1,0x24,0x52,0x24,0x10,0x44,0x10,0x44,0x10,0x84,0x11,0x28,0x10,0x10,/*"物",0*/
};//物
static const unsigned char PROGMEM zi[]={
0x01,0x00,0x02,0x00,0x04,0x00,0x1F,0xF0,0x10,0x10,0x10,0x10,0x10,0x10,0x1F,0xF0,
0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,/*"自",0*/
};//自
static const unsigned char PROGMEM shou[]={
0x00,0x10,0x00,0xF8,0x3F,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,0x01,0x00,0x01,0x00,
0x01,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x05,0x00,0x02,0x00,/*"手",0*/
};//手
static const unsigned char PROGMEM dong[]={
0x00,0x40,0x00,0x40,0x7C,0x40,0x00,0x40,0x01,0xFC,0x00,0x44,0xFE,0x44,0x20,0x44,
0x20,0x44,0x20,0x84,0x48,0x84,0x44,0x84,0xFD,0x04,0x45,0x04,0x02,0x28,0x04,0x10,/*"动",0*/
};//动
static const unsigned char PROGMEM zhuang[]={
0x08,0x40,0x08,0x48,0x08,0x44,0x48,0x44,0x28,0x40,0x2F,0xFE,0x08,0x40,0x08,0x40,
0x18,0x40,0x28,0xA0,0xC8,0xA0,0x08,0x90,0x09,0x10,0x09,0x08,0x0A,0x04,0x0C,0x02,/*"状",0*/
};//状
static const unsigned char PROGMEM tai[]={
0x01,0x00,0x01,0x00,0x7F,0xFC,0x01,0x00,0x02,0x80,0x04,0x40,0x0A,0x20,0x31,0x18,
0xC0,0x06,0x01,0x00,0x08,0x88,0x48,0x84,0x48,0x12,0x48,0x12,0x87,0xF0,0x00,0x00,/*"态",0*/
};//态
static const unsigned char PROGMEM wei[]={
0x00,0x00,0x03,0xF8,0x7A,0x48,0x4A,0x48,0x4B,0xF8,0x4A,0x48,0x4A,0x48,0x4B,0xF8,
0x48,0x00,0x4F,0xFE,0x7A,0x40,0x4A,0x44,0x02,0x28,0x02,0x90,0x03,0x08,0x02,0x06,/*"喂",0*/
};//喂
static const unsigned char PROGMEM shi[]={
0x01,0x00,0x02,0x80,0x04,0x40,0x0A,0x20,0x31,0x18,0xDF,0xF6,0x10,0x10,0x1F,0xF0,
0x10,0x10,0x1F,0xF0,0x10,0x08,0x11,0x90,0x10,0x60,0x12,0x10,0x14,0x08,0x18,0x04,/*"食",0*/
};//时
static const unsigned char PROGMEM wei1[]={
0x01,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,0x01,0x00,0x01,0x00,0x01,0x00,0xFF,0xFE,
0x03,0x80,0x05,0x40,0x09,0x20,0x11,0x10,0x21,0x08,0xC1,0x06,0x01,0x00,0x01,0x00,/*"未",0*/
};//未
static const unsigned char PROGMEM yi[]={
0x00,0x00,0x3F,0xF0,0x00,0x10,0x00,0x10,0x00,0x10,0x20,0x10,0x20,0x10,0x3F,0xF0,
0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x04,0x20,0x04,0x20,0x04,0x1F,0xFC,0x00,0x00,/*"已",0*/
};//已
// void xTaskOne(void *xTask1)//任务函数一
// {
// while (1)
// {
// }
// }
void xTaskTwo(void *xTask2)//任务函数二
{
while (1)
{
delay(10);//多任务需要
y=(int)(chongwu1*0.07*100+0.5)/100.0;
if(x==1)
{
if( (xiaoshi==8)&& (fengzhong==0) &&(miao==0))//闹钟8:00
{
for(int i=0;i<=3;i++)//3s蜂鸣器
{
digitalWrite(buzzer,HIGH);
delay(1000);
}
digitalWrite(buzzer,LOW);
if(chongwu1>0)
{
for(pos=90;pos>=0;pos--)//打开喂养
{
myservo1.write(pos);
delay(15);
}
myservo1.write(0);
z=chongwu1*0.07*100;
delay(z);//宠物越重喂养时间越长
for(pos=0;pos<=90;pos++)//关闭喂养while
{
myservo1.write(pos);
delay(15);
}
t=2;
}
}
}
if(x==2)
{
if(w==2)
{
for(pos=90;pos>=0;pos--)//打开喂养
{
myservo1.write(pos);
delay(15);
}
}
if(w==1)
{
for(pos=0;pos<=90;pos++)//关闭喂养while
{
myservo1.write(pos);
delay(15);
}
}
}
}
}
// if( (oledhour==12)&& (oledminute==0) &&(oledsecond==0))//闹钟12:00
// {
// unsigned long currentMillis = millis(); // 存储当前时间
// if (currentMillis - s>= 1000) { // 检查是否经过了 1000ms
// s = currentMillis;
// for(int i=0;i<=3;i++)//3s蜂鸣器
// {
// digitalWrite(buzzer,HIGH);
// delay(1000);
// }
// do
// {
// myservo1.write(90);//驱动舵机打开仓库阀门90度
// }while(shiwu1-chongwu1*0.004<0);//喂养0.4%都体重食物
// myservo1.write(0);//驱动舵机关闭仓库阀门
// for(int i=0;i<=3;i++)
// {
// myservo2.write(90);//喂食舵机打开3s
// delay(1000);
// }
// myservo2.write(0);
// while(data<4)
// {
// digitalWrite(relay,LOW);//继电器打开水泵
// }
// }
// }
// }
// if( (oledhour==17)&& (oledminute==0) &&(oledsecond==0))//闹钟17:00
// {
// unsigned long currentMillis = millis(); // 存储当前时间
// if (currentMillis - s>= 1000) { // 检查是否经过了 1000ms
// s = currentMillis;
// for(int i=0;i<=3;i++)//3s蜂鸣器
// {
// digitalWrite(buzzer,HIGH);
// delay(1000);
// }
// do
// {
// myservo1.write(90);//驱动舵机打开仓库阀门90度
// }while(shiwu1-chongwu1*0.004<0);//喂养0.4%都体重食物
// myservo1.write(0);//驱动舵机关闭仓库阀门
// for(int i=0;i<=3;i++)
// {
// myservo2.write(90);//喂食舵机打开3s
// delay(1000);
// }
// myservo2.write(0);
// while(data<4)
// {
// digitalWrite(relay,LOW);//继电器打开水泵
// }
// }
// }
// }
void setup()
{
Serial.begin(115200);
delay(10);//多任务需要
#if !USE_MULTCORE
// xTaskCreate(
// xTaskOne, /* 任务函数. */
// "TaskOne", /* 名称 */
// 4096, /* 堆栈大小. */
// NULL, /* 参数输入传递给任务的*/
// 1, /* 任务的优先级*/
// NULL); /* 任务所在核心 */
xTaskCreate(
xTaskTwo, /* Task function. */
"TaskTwo", /* String with name of task. */
4096, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
2, /* Priority of the task.(configMAX_PRIORITIES - 1 being the highest, and 0 being the lowest.) */
NULL);
#else
//最后一个参数至关重要,决定这个任务创建在哪个核上.PRO_CPU 为 0, APP_CPU 为 1,或者 tskNO_AFFINITY 允许任务在两者上运行.
// xTaskCreatePinnedToCore(xTaskOne, "TaskOne", 4096, NULL, 1, NULL, 0);//TaskOne在 0核心
xTaskCreatePinnedToCore(xTaskTwo, "TaskTwo", 4096, NULL, 2, NULL, 1);//TaskOne在 1核心
#endif
lcd.init(); //初始化LCD
lcd.backlight(); //打开LCD背光
rtc.writeProtect(false);
rtc.halt(false);
// Make a new time object to set the date and time.
// Wednesday, March 2, 2023 at 16:49:46.
Time t(2023, 3, 2, 7, 59, 45, Time::kWednesday);//起初时间设定
// Set the time and date on the chip.
rtc.time(t);
pinMode(buzzer,OUTPUT);//蜂鸣器管脚
digitalWrite(buzzer,LOW);//蜂鸣器管脚高电平
pinMode(TrigPin,OUTPUT);
pinMode(EchoPin,INPUT);
myservo1.attach(23,500,2500);//初始化喂养舵机D23
pinMode(relay,OUTPUT);
digitalWrite(relay,LOW);//继电器
Wire.begin(/*SDA*/21,/*SCL*/22);
oled.begin(SSD1306_SWITCHCAPVCC,0x3c);//oled初始化
pinMode(water,INPUT);//A0输入模式
HX711_CH0.begin();//读取支架的重量
delay(3000);
HX711_CH0.begin();//再次读取支架的重量
pinMode(Lan,OUTPUT);//蓝
digitalWrite(Lan,LOW);//
pinMode(Hong,OUTPUT);//红
digitalWrite(Hong,LOW);//
pinMode(Lv,OUTPUT);//绿
digitalWrite(Lv,LOW);//
}
void loop() {
delay(10);//多任务需要
chongwu();//hx711宠物
shuishen();//水深函数
unsigned long currentMillis = millis(); // 存储当前时间
if (currentMillis - a >= 1000) { // 检查是否经过了 1000ms
a = currentMillis; // 保存上次闪烁 LED 的时间
printTime();// LCD1602每秒钟循环并打印一次时间。
HCSR04(); //每秒打印一次距离
oled.clearDisplay();//清除缓存
oled.setTextSize(2);//字体大小
oled.setTextColor(1);//字体颜色
oled.setCursor(32,16);//位置
oled.print(":");//内容
oled.print(data);//内容
oled.print("cm");
oled.setCursor(32,32);//位置
oled.print(":");//内容
oled.print(chongwu1);//宠物
oled.print("g");
oled.setCursor(32,0);//位置
oled.print(":");//内容
oled.setCursor(48,48);//位置
oled.print(":");//内容
oled.print(y);//显示喂食物重量
oled.print("g");//内容
oled.drawBitmap(0,16,shui,16,16,1);//显示水
oled.drawBitmap(16,16,shen,16,16,1);//显示深
oled.drawBitmap(0,32,chong,16,16,1);//显示宠
oled.drawBitmap(16,32,wu,16,16,1);//显示物
oled.drawBitmap(0,0,zhuang,16,16,1);//显示状
oled.drawBitmap(16,0,tai,16,16,1);//显示态
oled.drawBitmap(0,48,wei,16,16,1);//显示喂
oled.drawBitmap(16,48,shi,16,16,1);//显示食
oled.drawBitmap(32,48,wu,16,16,1);//显示物
if(x==1)
{
digitalWrite(Lan,HIGH);//蓝色
oled.drawBitmap(48,0,zi,16,16,1);//显示自
oled.drawBitmap(64,0,dong,16,16,1);//显示动
}
if(x==2)
{
digitalWrite(Hong,HIGH);//红色
oled.drawBitmap(48,0,shou,16,16,1);//显示手
oled.drawBitmap(64,0,dong,16,16,1);//显示动
}
if(t==1)
{
oled.drawBitmap(80,0,wei1,16,16,1);//显示未
oled.drawBitmap(96,0,wei,16,16,1);//显示喂
oled.drawBitmap(112,0,shi,16,16,1);//显示养
}
if(t==2)
{
oled.drawBitmap(80,0,yi,16,16,1);//显示已
oled.drawBitmap(96,0,wei,16,16,1);//显示喂
oled.drawBitmap(112,0,shi,16,16,1);//显示养
}
oled.display(); //将显存显示到屏幕
if(data<3)//恒定水深4cm
{
digitalWrite(relay,HIGH);//继电器
}
else
{
digitalWrite(relay,LOW);//继电器
}
lcd.setCursor(9,1); //设置1602的光标到第1行第11个字符处
lcd.print("S:");
lcd.print(distance_cm); //在LCD光标所在处打印食物仓库剩余数据量
}
}
void shuishen()//水深
{
unsigned long currentMillis = millis(); // 存储当前时间
if (currentMillis - b>= 1000) { // 检查是否经过了 1000ms
b = currentMillis;
i=(long)analogRead(water);//A0值
data=(i/2261)*4;//转换
Serial.print(data);//水深
Serial.println("cm");//
}
}
void chongwu()//宠物
{
unsigned long currentMillis = millis(); // 存储当前时间
if (currentMillis - c>= 1000) { // 检查是否经过了 1000ms
c= currentMillis;
chongwu1 = HX711_CH0.Get_Weight(); //采样当前传感器重量,该重量已经自动去皮,去皮值根据初始化程序中采样的值计算。
Serial.print(chongwu1); //串口输出当前重量
Serial.println(" g"); //单位为g
}
}
void HCSR04()
{
digitalWrite(TrigPin,LOW);//设置初状态
delayMicroseconds(2);//发送前准备
digitalWrite(TrigPin,HIGH);//发送超声波信号
delayMicroseconds(10);//发送持续时间
digitalWrite(TrigPin,LOW);//结束发送超声信号
distance_cm=float(pulseIn(EchoPin,HIGH)*17)/1000;//开始读取并换算成cm
Serial.print(distance_cm);
Serial.println("cm");
}
6效果图
(1)
(2)手机端
(3)pc端