En automatisert kattemater er en liten maskin som sikrer regelmessig og kontrollert fôring selv om katteeieren ikke er hjemme, noe som betyr mindre stress for både eier og katt.
Når du lager en slik kattemater, lærer du samtidig om mekanisk design, elektronikk, programmering og 3D-printing. Du bruker additiv produksjon (3D-printing) og enkle, lett tilgjengelige komponenter for å utvikle et komplett produkt.
Matingsmekanismen aktiveres via knapp eller tidsstyring, servomotoren roterer doseringshjulet slik at neste kammer med mat kommer over utløpet, og maten faller ned i skåla.
Mengden fôr kan du programmere ved å angi antall grader servomotoren skal rotere for hver mating. Alternativt kan du måle volumet til en porsjon med fôr og deretter designe kamrene på fôringshjulet slik at de rommer riktig volum på fôr.
En automatisk kattemater er et produkt som krever flere ulike produksjonsprosesser. Her følger en kort oversikt.
Vi har laga et eksempel på en kattemater og har lagt ved tegninger og filer for utskrift på en 3D-printer. Vi har også lagt ved eksempler på kode som kan brukes til å styre kattemateren. Du kan ta utgangspunkt i eksempelmaterialet vårt og tilpasse det om du vil.
Enkelte deler er spesialdesigna til en spesiell komponent, slik som servomotoren og mikrobryterne. Hvis du benytter en servomotor eller mikrobryter med andre mål, må du justere designet på eksempelet eller lage et helt nytt design.
Eksempelproduktet har tre trykknapper. Én av knappene aktiverer mating. De to andre kan for eksempel brukes til å øke eller minske fôrmengden med en viss prosentandel for hvert trykk. Kanskje du også vil kople til et 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();
}