项目背景
当你忙于工作,中午无法回家;当你回家途中惨遭堵车,无法按时到家;当时需要出差,当你需要约会…...这些情况都会导致家里的宠物处于挨饿或无法按时按量吃饭的状态! 宠物比人类脆弱的多,以上的种种情况都有可能导致宠物患上肠胃疾病!这时候,你就需要一款可以自动或者远程控制的智能猫/狗粮机,来帮你按时按量的投喂家里的小可爱们!并且可以统计小可爱们的吃饭时间,以便在需要的时候向宠物医生反馈,时刻关注家中宠物的健康!
那么先看看我们需要准备些什么吧!~
步骤1 猫粮机结构件制作
木板激光切割
首先,我们需要进行猫粮机整体结构的加工,考虑到拼装的便捷性和材料的成本,我们的猫粮机采用木板和螺丝固定的结构。猫粮机的激光切割图纸,我会放在帖子最后的资料中供大家下载,大家也可以在现有的基础上,给猫粮机增加更好看的外观,给家里的“主子”们做一个独一无二的猫粮机。
PVC水管切割
木板切割完成后,我们需要制作猫粮机输送猫粮的管道,这个就需要我们之前准备的PVC水管(直径和厚度千万不要买错哦~)。我们需要切割一段178mm长的PVC水管,然后在其中一侧用手锯切出一个35mm长,5mm深的槽,以便于安装我们猫粮机的入口。
3D打印
最后,我们还缺一个推动猫粮的绞龙,以及安装在PVC水管两侧防止猫粮从管道中掉落的封盖。3D打印时需要用到的STL文件,我也会在帖子最后的资料中贴上的~绞龙最终打印出来就是下图的效果啦~
步骤2 猫粮机的拼装
完成了所有的结构件制作,接下来我们就要进到最繁琐的拼装环节!在进行拼装之前,提醒一下各位!在猫粮机外壳拼装的时候,需要在螺丝和螺母的基础上,添加一个垫片,增加结构的稳定性,也防止了用力过猛拧断木板的情况(即使这样也不要用力太猛!)!正确的安装方式如下~
因为制作拼装图纸过于繁琐,我给大家一个安装到一半的图,以便大家理解拼装过程。拼装过程中,如果遇到一些不稳定的结构,各位可以使用热熔胶或者502辅助固定。
拼装到还剩最后一块侧面板时,我们将主控以及各种模块放入机器中。
步骤3 烧录程序
这一版的猫粮机,具有可以使用OBLOQ控制猫粮机运行模式的功能。向optic2发送数据1,猫粮机持续出粮;发送数据0,猫粮机停止出粮;发送数据2,猫粮机进入自动模式,外接的触摸传感器每隔一段时间(这段时间可以在程序中自行更改)允许猫咪触摸后出粮。烧录完程序后,将最后一块木板装上,猫粮机就制作完成啦!
#include <PID_v1.h>
#include "U8glib.h"
#include <SoftwareSerial.h>
#include "Obloq.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_DEV_0); // I2C / TWI
static const unsigned char PROGMEM str_kong[] =
{0x02,0x00,0x01,0x00,0x7F,0xFE,0x40,0x02,0x88,0x24,0x10,0x10,0x20,0x08,0x00,0x00,
0x1F,0xF0,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x7F,0xFC,0x00,0x00};/*"空",0*/
static const unsigned char PROGMEM str_bai[] =
{0x01,0x00,0x02,0x00,0x04,0x00,0x3F,0xF8,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,
0x3F,0xF8,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x3F,0xF8,0x20,0x08};/*"白",1*/
static const unsigned char PROGMEM str_zong[] =
{0x10,0x10,0x08,0x20,0x04,0x40,0x00,0x00,0x1F,0xF0,0x10,0x10,0x10,0x10,0x10,0x10,
0x1F,0xF0,0x10,0x10,0x01,0x00,0x08,0x84,0x48,0x92,0x48,0x12,0x87,0xF0,0x00,0x00};/*"总",2*/
static const unsigned char PROGMEM str_liang[] =
{0x00,0x00,0x1F,0xF0,0x10,0x10,0x1F,0xF0,0x10,0x10,0xFF,0xFE,0x00,0x00,0x1F,0xF0,
0x11,0x10,0x1F,0xF0,0x11,0x10,0x1F,0xF0,0x01,0x00,0x1F,0xF0,0x01,0x00,0x7F,0xFC};/*"量",3*/
static const unsigned char PROGMEM str_ci[] =
{0x00,0x80,0x40,0x80,0x20,0x80,0x20,0xFC,0x01,0x04,0x09,0x08,0x0A,0x40,0x14,0x40,
0x10,0x40,0xE0,0xA0,0x20,0xA0,0x21,0x10,0x21,0x10,0x22,0x08,0x24,0x04,0x08,0x02};/*"次",4*/
static const unsigned char PROGMEM str_shu[] =
{0x08,0x20,0x49,0x20,0x2A,0x20,0x08,0x3E,0xFF,0x44,0x2A,0x44,0x49,0x44,0x88,0xA4,
0x10,0x28,0xFE,0x28,0x22,0x10,0x42,0x10,0x64,0x28,0x18,0x28,0x34,0x44,0xC2,0x82};/*"数",5*/
static const unsigned char PROGMEM str_shi[] =
{0x02,0x00,0x01,0x00,0x7F,0xFE,0x40,0x02,0x88,0x84,0x04,0x80,0x04,0x80,0x10,0x80,
0x08,0x80,0x08,0x80,0xFF,0xFE,0x01,0x40,0x02,0x20,0x04,0x10,0x18,0x08,0x60,0x04};/*"实",6*/
static const unsigned char PROGMEM str_m[] =
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00};/*":",0*/
static const unsigned char PROGMEM str_g[] =
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x44,0x44,0x38,0x40,0x3C,0x42,0x42,0x3C};/*"g",0*/
static const unsigned char PROGMEM str_cat[] =
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x01,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xE0,0x00,0x00,0x00,0x00,
0x00,0x00,0x01,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xF8,0x00,0x00,0x00,0x00,
0x00,0x00,0x03,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xEF,0x8F,0x80,0x00,0x00,
0x00,0x00,0x03,0xE7,0xFF,0x00,0x00,0x00,0x00,0x00,0x03,0xE3,0xFF,0x00,0x00,0x00,
0x00,0x00,0x0F,0xE0,0x0F,0x00,0x00,0x00,0x00,0x00,0x0F,0x38,0x1E,0x00,0x00,0x00,
0x00,0x00,0x0F,0xF8,0x1E,0x00,0x00,0x00,0x00,0x00,0x0F,0xF8,0x7C,0x00,0x00,0x00,
0x00,0x00,0x07,0xFB,0xFE,0x00,0x00,0x00,0x00,0x00,0x06,0xC3,0xFE,0x00,0x00,0x00,
0x00,0x00,0x07,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x38,0x00,0x00,0x00,
0x00,0x00,0x03,0x80,0x70,0x00,0x00,0x00,0x00,0x00,0x03,0x80,0x70,0x00,0x00,0x00,
0x00,0x00,0x03,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x38,0x00,0x00,0x00,
0x00,0x00,0x03,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x0F,0x00,0x00,0x00,
0x00,0x00,0x03,0x00,0x07,0x80,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0xC0,0x00,0x00,
0x00,0x00,0x03,0x80,0x01,0xE0,0x00,0x00,0x00,0x00,0x03,0xC0,0x00,0xE0,0x1C,0x00,
0x00,0x00,0x03,0xE0,0x00,0x60,0x3E,0x00,0x00,0x00,0x03,0x60,0x18,0x70,0x73,0x80,
0x00,0x00,0x03,0x70,0x38,0x30,0x79,0x80,0x00,0x00,0x03,0x39,0xB0,0x30,0x79,0xC0,
0x00,0x00,0x03,0xB9,0xF0,0x30,0x19,0xC0,0x00,0x00,0x01,0xBD,0xF0,0x30,0x3D,0x80,
0x00,0x00,0x01,0xBF,0xF0,0x7C,0xF9,0x80,0x00,0x00,0x01,0xFF,0xF0,0xFF,0xF3,0x00,
0x00,0x00,0x01,0xFF,0xE1,0xFF,0x8E,0x00,0x00,0x00,0x01,0xEF,0x7F,0xFF,0xFC,0x00,
0x00,0x00,0x00,0xEE,0x7F,0x0F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/*"C:\Users\lx\Desktop\0604abee9b593c0e2bf6df460d4623d8.bmp",0*/
const byte encoder0pinA = 2;//A pin -> the interrupt pin 0
const byte encoder0pinB = 3;//B pin -> the digital pin 3
int E_left =5; //L298P直流电机驱动板的使能端口连接到数字接口5
int M_left =4; //L298P直流电机驱动板的转向端口连接到数字接口4
int button =7; //控制电机转动按钮7
int i =0;
byte encoder0PinALast;
double duration,abs_duration;//the number of the pulses
boolean Direction;//the rotation direction
boolean result;
double val_output;//用于提供给电机PWM功率值。
double Setpoint;
double Kp=0.6, Ki=5, Kd=0;
PID myPID(&abs_duration, &val_output, &Setpoint, Kp, Ki, Kd, DIRECT);
static unsigned long currentTime = 0;
int buttonState =0;
int times =0;
int iotState =0;
SoftwareSerial softSerial(10,11);
//生成OBLOQ对象,参数:串口指针,wifiSsid, WifiPwd, iotId, iotPwd
//Generate OBLOQ object, parameters: serial pointer, wifiSsid, WifiPwd, iotId, iotPwd
Obloq olq(&softSerial,"DFRobot-guest","dfrobot@2017","xxxxxxxxxx","xxxxxxxxxx");
const String devTopic = "xxxxxxxxxx";
const String devTopic2 = "xxxxxxxxxx";
void setup()
{
Serial.begin(9600);//Initialize the serial port
pinMode(M_left, OUTPUT); //L298P直流电机驱动板的控制端口设置为输出模式
pinMode(E_left, OUTPUT);
Setpoint =80; //设置PID的输出值
myPID.SetMode(AUTOMATIC);//设置PID为自动模式
myPID.SetSampleTime(20);//设置PID采样频率为100ms
EncoderInit();//Initialize the module
pinMode(button, INPUT);
softSerial.begin(9600);
olq.setMsgHandle(msgHandle);//注册消息回调函数 register message callback function
olq.subscribe(devTopic2); //监听设备 Subscribe topic
}
void loop()
{
Serial.print(iotState);
abs_duration=abs(duration);
result=myPID.Compute();//PID转换完成返回值为1
if(result)
{
Serial.print("Pluse: ");
Serial.println(duration);
duration = 0; //计数清零等待下次计数
}
millis();
olq.update();
u8g.firstPage();
do
{
// u8g.setFont(u8g_font_04b_03b_short); // 字体设置
// u8g.drawStr(36,48,times);
u8g.setColorIndex(1);
drawSTART();
drawURL();
u8g.setFont(u8g_font_unifont);
u8g.setPrintPos(48,64);
u8g.print(times);
} while ( u8g.nextPage() );
if(iotState == 1)
{
advance();
}
if(iotState == 0)
{
Stop();
}
if(iotState == 2)
{
if(digitalRead(button) == HIGH)
{
buttonState =1;
}
if(i>=10)
{
Stop();//电机停止
olq.publish("HkHyId8rX",String(times++));
delay(5000);
buttonState =0;
i=0;
}
if(i<10)
{
if(buttonState ==1)
{
i++;
Serial.println(i);
advance();//电机正转
}
}
else
{
Stop();//电机停止
}
}
}
void EncoderInit()
{
Direction = true;//default -> Forward
pinMode(encoder0pinB,INPUT);
attachInterrupt(0, wheelSpeed, CHANGE);
}
void wheelSpeed()
{
int Lstate = digitalRead(encoder0pinA);
if((encoder0PinALast == LOW) && Lstate==HIGH)
{
int val = digitalRead(encoder0pinB);
if(val == LOW && Direction)
{
Direction = false; //Reverse
}
else if(val == HIGH && !Direction)
{
Direction = true; //Forward
}
}
encoder0PinALast = Lstate;
if(!Direction) duration++;
else duration--;
}
void advance()//电机正转
{
digitalWrite(M_left,LOW);
analogWrite(E_left,val_output);
}
void back()//电机反转
{
digitalWrite(M_left,HIGH);
analogWrite(E_left,val_output);
}
void Stop()//电机停止
{
digitalWrite(E_left, LOW);
}
void drawSTART(void)
{
u8g.drawBitmapP(0,16,2,16,str_zong);u8g.drawBitmapP(16,16,2,16,str_liang);u8g.drawBitmapP(32,16,2,16,str_m);u8g.drawBitmapP(64,16,1,16,str_g);
u8g.drawBitmapP(0,32,2,16,str_shi);u8g.drawBitmapP(16,32,2,16,str_liang);u8g.drawBitmapP(32,32,2,16,str_m);u8g.drawBitmapP(64,32,1,16,str_g);
u8g.drawBitmapP(0,48,2,16,str_ci);u8g.drawBitmapP(16,48,2,16,str_shu);u8g.drawBitmapP(32,48,2,16,str_m);
u8g.drawBitmapP(64,16,8,64,str_cat);
}
void drawURL(void)
{
#ifndef MINI_LOGO
u8g.setFont(u8g_font_4x6);
if ( u8g.getHeight() < 59 )
{
u8g.drawStr(53, 9, "www.dfrobot");
u8g.drawStr(77, 18, ".com.cn");
}
else
{
u8g.drawStr(53,8, "www.dfrobot.com.cn");
}
#endif
}
void msgHandle(const String& topic,const String& message)
{
if(devTopic2 == topic)
{
if(message == "1")
iotState = 1;
else if(message == "0")
iotState = 0;
else
iotState = 2;
}
}
总结
这一版的猫粮机还是有很多缺陷的,比如没有留更换电池的接口,结构还不够紧凑等。接下来会不断的改进这款猫粮机的,为大家的猫主子提供更好的投喂机器!
资料
猫粮机激光切割木板资料:链接:https://pan.baidu.com/s/18wUuxueBqd2N9shGOfW4hg 提取码:96jj
3D打印件资料:链接:https://pan.baidu.com/s/1K5I8p9uzllz6FDyLMTp81g 提取码:j1co
DFBJ9p-sPLV2019.03.10
为什么链接打不开呢
Fish2018.12.10
r
Fish2018.12.10
r
DFBJyrxBnnm2018.11.04
哇 好棒
ASH腻2018.10.30
万事俱备 只差猫主子
fpqEu0X2LC7y2018.10.23
大气!
管理员2018.10.22
有意思