Hopp til innhald
Nynorsk

Emne

Oppgåvebank

Oppgåve

Kattematar

Ein 3D-printa kattematar med servomotor gir presis fôrdosering og er eit godt døme på korleis vi kan kombinere digitale verktøy og automatikk i teknologi- og produktutvikling.

Kva er ein kattematar?

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.

Funksjonsmåte

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.

Hovudkomponentar

Kattemataren består av følgande hovudkomponentar:

  • Arduino Uno eller Nano – programmerbar mikrokontrollar som styrer servomotoren

  • roterande servomotor (t.d. MG996R)– roterer eit doseringshjul med faste kammer

  • hus og doseringshjul – mekanisk struktur som blir teikna digitalt og 3D-printa

  • trykknapp- eller tidsstyring (valfritt) – manuell mating eller automatisk mating i førehandsprogrammerte faste intervall

Produksjonsprosessar

Ein automatisk kattematar er eit produkt som krev fleire ulike produksjonsprosessar. Her følger ei kort oversikt.

1. 3D-modellering og konstruksjon

  • Utforming av matehjul, kapsling, lokk og luke i eit CAD-program (t.d. Fusion 360)

  • Optimalisering av passform, rørsle og volum

  • Eksport av -filer og utskrift på -printer (bruk av eller )

2. Elektronikk og kopling

  • Kabling av servomotor og Arduino

  • Enkel straumforsyning (batteri eller USB)

  • Eventuell tilkopling av brytar eller sensor

3. Programmering

  • Bruk av Arduino IDE

  • Rotasjon av servo ved aktivering, t.d. 45° per mating

  • Tidsintervall via millis()-funksjon i Arduino (ved bruk av tidsstyring)

  • Bruk av state machine-struktur viss fleire funksjonar er ønskte

4. Testing og justering

  • Kalibrering av rotasjon og mating

  • Justering av hjulkammer for rett porsjonsstorleik

  • Kontroll av passform og klaringar

5. Dokumentasjon og refleksjon

  • Teknisk teikning og 3D-modellar

  • Kodekommentarar og koplingsskjema

  • Evaluering av funksjon, inkludert forbetringsforslag

Døme på produkt

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();

}

Skrive av Alexander U. og Roger Rosmo.
Sist oppdatert 03.06.2025