Ein automatisert kattematar er ei lita maskin som sikrar regelmessig og kontrollert fôring sjølv om katteeigaren ikkje er heime, noko som betyr mindre stress for både eigar og katt.
Når du lagar ein slik kattematar, lærer du samtidig om mekanisk design, elektronikk, programmering og 3D-printing. Du bruker additiv produksjon (3D-printing) og enkle, lett tilgjengelege komponentar for å utvikle eit komplett produkt.
Matingsmekanismen blir aktivert via knapp eller tidsstyring, servomotoren roterer doseringshjulet slik at neste kammer med mat kjem over utløpet, og maten fell ned i skåla.
Mengda fôr kan du programmere ved å oppgi kor mange gradar servomotoren skal rotere for kvar mating. Alternativt kan du måle volumet til ein porsjon med fôr og deretter designe kammera på fôringshjulet slik at dei rommar rett volum på fôr.
Ein automatisk kattematar er eit produkt som krev fleire ulike produksjonsprosessar. Her følger ei kort oversikt.
Vi har laga eit døme på ein kattematar og har lagt ved teikningar og filer for utskrift på ein 3D-printer. Vi har òg lagt ved døme på kode som kan brukast til å styre kattemataren. Du kan ta utgangspunkt i dømematerialet vårt og tilpasse det om du vil.
Nokre delar er spesialdesigna til ein spesiell komponent, slik som servomotoren og mikrobrytarane. Viss du nyttar ein servomotor eller mikrobrytar med andre mål, må du justere designet på dømet eller lage eit heilt nytt design.
Dømeproduktet har tre trykknappar. Éin av knappane aktiverer mating. Dei to andre kan til dømes brukast til å auke og minske fôrmengda med ein viss prosentdel for kvart trykk. Kanskje du i tillegg vil kople til eit display for å få informasjon om fôringa?
Kode – ekstern spenningsforsyning med timer
#include <Servo.h>
#define second 1000
#define minute 60000
#define hour 3600000
Servo servo;
const int servoPin = 9;
const int buttonDispensePin = 7;
const int buttonMinusPin = 6;
const int buttonPlusPin = 8;
const int minMoveTime = 130;
const int minServing = 300;
const int ledPin = 13;
int servingTime = 1400; // 1000 – 9g, 2000 – 18g, 3000 – 27g, 3500 – 31g, 4000 - 36g
int servingDelta = 150;
int jerkPeriod = 700; // 1000/1000 – 9g, 1000/500 – 7g, 1400/700 – 12g
unsigned long lastFeeding=0;
void setup ()
{
Serial.begin(57600);
Serial.println();
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
pinMode(buttonDispensePin, INPUT_PULLUP);
pinMode(buttonMinusPin, INPUT_PULLUP);
pinMode(buttonPlusPin, INPUT_PULLUP);
buzz();
}
void loop ()
{
int dispenseVal = digitalRead(buttonDispensePin);
if(dispenseVal == LOW)
dispense(servingTime);
int plusVal = digitalRead(buttonPlusPin);
if(plusVal == LOW){
delay(1000);
int plusVal = digitalRead(buttonPlusPin);
if(plusVal == LOW){
servingTime+=servingDelta;
buzz();
}
}
int minusVal = digitalRead(buttonMinusPin);
if(minusVal == LOW){
delay(1000);
int minusVal = digitalRead(buttonMinusPin);
if(minusVal == LOW){
servingTime-=servingDelta;
servingTime = servingTime<minServing?minServing:servingTime;
buzz();
}
}
if(millis() - lastFeeding>12*hour)//
dispense(servingTime);
delay(10);
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void dispense(int dispenseTime)
{
lastFeeding = millis();
servo.attach(servoPin);
for(;dispenseTime>0;dispenseTime-=jerkPeriod){
servo.write(100);
delay(dispenseTime>=jerkPeriod ? jerkPeriod : dispenseTime%1000);
jerk();
}
stop();
}
void jerk()
{
servo.write(0);
delay(minMoveTime);
servo.write(180);
delay(minMoveTime);
}
void stop()
{
servo.write(90);
delay(100);
servo.detach();
}
void buzz()
{
servo.attach(servoPin);
servo.write(0);
delay(50);
jerk();
jerk();
stop();
}
Kode – batteri med knapp
#include <Servo.h>
#include <LowPower.h>
#include <EventManager.h>
#include <ThreeWire.h>
#include <RtcDS1302.h>
#define EI_NOTINT0
#define EI_NOTINT1
#define EI_ARDUINO_INTERRUPTED_PIN
#include <EnableInterrupt.h>
#define minute 60
#define hour 3600
#define timerDelta 10000
#define servoPin 9
#define servoPowerPin 10
#define buttonDispensePin 7
#define buttonMinusPin 6
#define buttonPlusPin 8
#define buttonPin 5
#define ledPin 13
#define limit10m 2
#define limit24h 6
#define minMoveTime 130
#define minServing 300
EventManager eventManager;
Servo servo;
ThreeWire myWire(A1,A2,A0); // DAT, CLK, RST
RtcDS1302<ThreeWire> Rtc(myWire);
int servingTime = 800; // 1000 – 9g, 2000 – 18g, 3000 – 27g, 3500 – 31g, 4000 - 36g
int jerkPeriod = 800; // 1000/1000 – 9g, 1000/500 – 7g, 1400/700 – 12g
int servingDelta = 150;
int times10m = 0;
int times24h = 0;
uint32_t lastFeeding=0;
void setup()
{
pinMode(buttonDispensePin, INPUT_PULLUP);
pinMode(buttonMinusPin, INPUT_PULLUP);
pinMode(buttonPlusPin, INPUT_PULLUP);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(servoPowerPin, OUTPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
servoDetach();
enableInterrupt(buttonPin, interruptHandler, FALLING);
eventManager.addListener( buttonPin, buttonListener );
Serial.begin(9600);
setupTime();
buzz();
}
void setupTime()
{
Rtc.Begin();
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
Serial.print("Compiled: ");
printDateTime(compiled);
Serial.println();
Serial.print("RTC: ");
RtcDateTime now = Rtc.GetDateTime();
printDateTime(now);
Serial.println();
if (Rtc.GetIsWriteProtected())
{
Serial.println("RTC was write protected, enabling writing now");
Rtc.SetIsWriteProtected(false);
}
if (!Rtc.GetIsRunning())
{
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
if (now < compiled)
{
Serial.println("RTC is older than compile time! (Updating DateTime)");
Rtc.SetDateTime(compiled);
}
else if (now > compiled)
{
Serial.println("RTC is newer than compile time. (this is expected)");
}
else if (now == compiled)
{
Serial.println("RTC is the same as compile time! (not expected but all is fine)");
}
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt)
{
char datestring[20];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second() );
Serial.print(datestring);
}
void interruptHandler()
{
eventManager.queueEvent(arduinoInterruptedPin, 0);
}
void buttonListener(int, int)
{
RtcDateTime nowT = Rtc.GetDateTime();
// Serial.print("Event: ");
// printDateTime(nowT);
// Serial.println();
uint32_t now = nowT.TotalSeconds();
if(now-lastFeeding < 5)
return; // Simple debouncer
if(now-lastFeeding > 10*minute)
times10m = 0;
if(now-lastFeeding > 24*hour)
times24h = 0;
bool allowed = times10m<limit10m && times24h<limit24h;
// Serial.print(times10m);
// Serial.print(" ");
// Serial.print(times24h);
// Serial.print(" ");
// Serial.print(now);
// Serial.print(" ");
// Serial.println(allowed);
if(!allowed) return;
lastFeeding = now;
dispense(servingTime);
times10m++;
times24h++;
}
void loop() {
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_ON);
eventManager.processAllEvents();
}
void servoAttach()
{
servo.attach(servoPin);
digitalWrite(servoPowerPin, HIGH);
delay(200);
}
void servoDetach()
{
pinMode(servoPin, INPUT);
digitalWrite(servoPowerPin, LOW);
}
void dispense(int dispenseTime)
{
servoAttach();
for(;dispenseTime>0;dispenseTime-=jerkPeriod){
servo.write(100);
delay(dispenseTime>=jerkPeriod ? jerkPeriod : dispenseTime%1000);
jerk();
}
stop();
}
void jerk()
{
servo.write(0);
delay(minMoveTime);
servo.write(180);
delay(minMoveTime);
}
void stop()
{
servo.write(90);
delay(100);
servoDetach();
}
void buzz()
{
servoAttach();
servo.write(0);
delay(50);
jerk();
jerk();
stop();
}