Automatic Pet Feeder

April 16, 2025
Description
Summary
I designed this Automatic Pet Feeder to be a simple and reliable solution for dispensing pet food, especially large chunks that many existing feeders struggle with. Most automatic feeders on the market are designed for small cat food pellets, but I wanted something that could handle larger food pieces while remaining easy to use and maintain.
This feeder operates with a single button press, making it straightforward and efficient. The design features a modular structure that allows it to be easily removed from its wall-mounted docking for cleaning, ensuring hygiene and long-term durability. By combining 3D printing and Arduino, I created a customizable, user-friendly solution for pet owners who need a manual yet controlled feeding system without complex programming or smart app dependencies.
________________________________________________________________________________________________________________
Functions
- Dispenses a portion of food with a single button press.
- Easily adjust the amount of food exactly to what your pet likes.
- Ensures a consistent portion size each time.
- Easy to clean and maintain.
________________________________________________________________________________________________________________
Hardware and Software UsedÂ
Hardware:
3D Printed Parts
# | Part Name | Quantity |
---|---|---|
1 | Cylinder Lid | 1 |
2 | Docking Station | 1 |
3 | Electronics Box | 1 |
4 | Electronics Box Lid | 1 |
5 | Mixing Spur Gear | 1 |
6 | Mixing Tank | 1 |
7 | Mixing Tank Floor | 1 |
8 | Mixing Tank Funnel | 1 |
9 | Spur Gear | 1 |
10 | Spur Gear Cover | 1 |
Electronic Components
# | Component | PN / Note | Supplier | Picture |
---|---|---|---|---|
1 | Arduino Micro | - | Arduino | |
2 | Motor Driver | TB6612FNG | SparkFun | |
3 | Motor | JGY370, 12V, 100RPM | AliExpress | |
4 | Push Button | LBJQKJ LB16A-P11/E | AliExpress | |
5 | Half Breadboard | - | AliExpress | |
6 | DC Barrel Jack | - | AliExpress | |
7 | 12V Power Supply | 2 amp | AliExpress |
Fasteners
# | Fasteners | Quantity |
---|---|---|
1 | Phillips Thread-Forming Screw M3x10 | 7 |
2 | ISO 7380 M3x8mm (socket head screw) | 4 |
3 | DIN 934 M3 (hex nut) | 1 |
4 | DIN 913 M3x6mm (set screw) | 1 |
Other
Polycarbonate (or other food-safe material) tube:
- Outer diameter: 120 mm.
- Wall thickness: around 3 mm.
- Height: I used a 300 mm height.Â
Choose the height according to the amount of food you want to store.
*Make sure that the tube is made of food-safe material.
Software:
- PrusaSlicer
- Arduino IDE
- SolidWorks (for designing the 3d parts)
____________________________________________________________________________________________________________
Wiring Diagram
The wiring is pretty straightforward. Just follow the wiring diagram.
Motor:
(+) → A01 on SparkFun Motor Driver
(-) → A02 on SparkFun Motor Driver
Push Button:
LED → D2 Pin on ArduinoÂ
C (Common) → D3 Pin on Arduino
NO (Normally Open) → VCC on SparkFun Motor Driver
NC (Normally Close) → GND on SparkFun Motor Driver/Arduino
A → NC on Push Button
DC Barrel Jack:
(+) → VIN on Arduino and  VM on SparkFun Motor Driver
(-) → GND on Arduino and GND on SparkFun Motor Driver
Arduino:
D4 Pin → STBY on SparkFun Motor Driver
D5 Pin → PWMA on SparkFun Motor Driver
D6 Pin → AI1 on SparkFun Motor Driver
D7 Pin → AI2 on SparkFun Motor Driver
5V → VCC on SparkFun Motor Driver
Arduino Micro Board:
SparkFun Motor Driver Board:
________________________________________________________________________________________________________________
Code
The Arduino code controls the DC motor that rotates the feeder’s dispensing mechanism.Â
The button press triggers the motor to turn for a set number of cycles, moving the food through the dispensing system.
Button Pressing Options
The button has 2 pressing options:
- Short press (less than 1 second) → start the feeding process.
- Long press (more than 1 second) → stop the motor and reset the Arduino.
Use the Long Press option to reset the Arduino if something gets stuck during the feeding process and you want to immediately stop the motor.
Key Code Functionality:
- The feeding amount is controlled by the
feedingCycles
variable. - When the button is pressed, the motor completes a set number of on-off cycles to ensure a consistent portion is dispensed.
(The LED light on the button will turn on) - The default value of
feedingCycles
is 5, which dispenses approximately 200 grams of food. - You can adjust the feeding amount by changing the
feedingCycles
value in the code. This variable is in the first line of the code. - After dispensing, the motor stops, waiting for the next button to be pressed.Â
(The LED light on the button will turn off)
How to Adjust the Portion Size:
- To dispense more food, increase the value of
feedingCycles
. - To dispense less food, decrease the value of
feedingCycles
.
Upload the code to the Arduino Micro:
- Open the Arduino IDE.
- Start a new sketch, and delete everything.
- Copy and paste the following code into the sketch.
- Make sure to select the correct Arduino Micro board.
- Upload the sketch.
- You will probably see the following message in the Arduino IDE output during the upload. Don’t worry — this is completely normal.
- Make sure to see the Done uploading message at the lower right corner of the Arduino IDE when it completes the upload.
This is the code to copy into a new sketch:
const int feedingCycles = 5;
#include <avr/wdt.h>
// %%%%%%%%%% CONSTANT PARAMETERS %%%%%%%%%%
#define CW 1
#define CCW -1
#define STP 0
#define CW_Time 4000
#define CCW_Time 500
#define feedingPWM 250
// %%%%%%%%%% PINS DEFENITION %%%%%%%%%%
const int buttonLedPin = 2; // push button integrated blue led
const int buttonPin = 3; // push button
const int stbyPin = 4; // motor driver standby
const int pwmPin = 5; // motor driver PWM
const int AIn1pin = 6; // motor driver INA1: CW input
const int AIn2pin = 7; // motor driver INA2: CCW input
// %%%%%%%%%% INITIALIZE VARIABLES %%%%%%%%%%
int buttonState = HIGH;
int lastButtonState = LOW;
int cyclesCounter = 1;
unsigned long buttonPressTime = 0; // Variable to store the time when the button was pressed
unsigned long cycleStartTime = 0; // Variable to store the time when new cycle has started
bool longPress = false; // Flag to track if the long-press action has been executed
bool shortPress = false; // Flag to track if the short-press action has been executed
bool feedingInProcess = false; // Flag for feeding process
bool cycleInProcess = false; // Flag for cycle initiate actions
void setup() {
Serial.begin(9600);
delay(1000);
pinMode(buttonLedPin, OUTPUT);
pinMode(buttonPin, INPUT);
pinMode(stbyPin, OUTPUT);
pinMode(pwmPin, OUTPUT);
pinMode(AIn1pin, OUTPUT);
pinMode(AIn2pin, OUTPUT);
digitalWrite(buttonLedPin, LOW);
digitalWrite(stbyPin, LOW);
digitalWrite(pwmPin, LOW);
digitalWrite(AIn1pin, LOW);
digitalWrite(AIn2pin, LOW);
}
void loop() {
buttonState = digitalRead(buttonPin);
// Check for a button press
if (buttonState == HIGH && lastButtonState == LOW) {
buttonPressTime = millis(); // Record the time when the button was pressed
longPress = false; // Reset the long press action flag
shortPress = false; // Reset the short press action flag
Serial.println("Button pressed: ");
}
// Long press
if (buttonState == HIGH && (millis() - buttonPressTime >= 1000) && !longPress) {
Serial.println("Long press");
longPress = true;
setMotor(STP, 0);
delay(50);
Serial.println("Arduino about to reset");
for (int i = 0; i < 8; i++) {
digitalWrite(buttonLedPin, HIGH);
Serial.print("-");
delay(150);
digitalWrite(buttonLedPin, LOW);
Serial.print("-");
delay(150);
}
wdt_enable(WDTO_15MS); // Enable watchdog timer (15ms timeout)
while (1); // Force reset
}
// Short press
if (buttonState == LOW && lastButtonState == HIGH && !shortPress && !longPress && !feedingInProcess) {
Serial.println("Short press --> Feeding start");
shortPress = true;
feedingInProcess = true;
delay(10);
}
if(buttonState == LOW && feedingInProcess) {
feeding(feedingCycles);
}
lastButtonState = buttonState;
}
// Motor function
void setMotor(int direction, int pwmVal) {
analogWrite(pwmPin, pwmVal);
if (direction == 1) {
digitalWrite(stbyPin, HIGH);
digitalWrite(AIn1pin, HIGH);
digitalWrite(AIn2pin, LOW);
//Serial.println("Motor Go CW");
} else if (direction == -1) {
digitalWrite(stbyPin, HIGH);
digitalWrite(AIn1pin, LOW);
digitalWrite(AIn2pin, HIGH);
Serial.println("Motor Go CCW");
} else {
digitalWrite(stbyPin, LOW);
digitalWrite(AIn1pin, LOW);
digitalWrite(AIn2pin, LOW);
Serial.println("Motor Stop");
}
}
// Feeding function
void feeding (int cyclesNumber) {
if (buttonState == LOW && feedingInProcess && cyclesCounter <= cyclesNumber) {
if (!cycleInProcess) {
cycleStartTime = millis(); // store the time when new cycle start
Serial.print("Cycle number: ");
Serial.println(cyclesCounter);
Serial.println("Motor Go CW");
digitalWrite(buttonLedPin, HIGH);
}
// Motor Go CW as long as the time difference is between the CW_Time range
if (millis() - cycleStartTime < CW_Time) {
cycleInProcess = true;
setMotor(CW, feedingPWM);
}
// Cycle last actions
else {
setMotor(STP, 0);
delay(100);
setMotor(CCW, feedingPWM);
delay(CCW_Time);
setMotor(STP, 0);
cycleInProcess = false;
cyclesCounter++;
delay(50);
}
}
else {
// Feeding finish
feedingInProcess = false;
cyclesCounter = 1;
Serial.println("Feeding finished");
Serial.println("-----------------------------------------");
Serial.println();
digitalWrite(buttonLedPin, LOW);
delay(100);
}
}
________________________________________________________________________________________________________________
Printing Instructions
- Material: PLA (make sure that you use a food-safe material).
- Layer height: 0.2 mm.
- All the printable parts files are already correctly oriented for printing.
# | Part Name | Image | Infill [%] | Walls | Support |
---|---|---|---|---|---|
1 | Cylinder Lid | 15 | 2 | No | |
2 | Docking Station | 20 | 4 | Yes | |
3 | Electronics Box | 15 | 2 | No | |
4 | Electronics Box Lid | 15 | 2 | No | |
5 | Mixing Spur Gear | 50 | 4 | No | |
6 | Mixing Tank | 15 | 2 | Yes | |
7 | Mixing Tank Floor | 20 | 4 | No | |
8 | Mixing Tank Funnel | 15 | 2 | No | |
9 | Spur Gear | 100 | 4 | No | |
10 | Spur Gear Cover | 15 | 2 | No |
________________________________________________________________________________________________________________
Assembly Instructions
It is recommended to review the attached STEP files for the top and sub-assemblies.Â
This will help you understand how each component fits together.
Docking Station Assembly
- Connect the motor the the docking station part using 4 ISO 7380 M3x8mm (socket head screw).
- Insert 1 DIN 934 M3 (hex nut) into the spur gear.
- Connect the spur gear to the motor shaft using 1 DIN 913 M3x6mm (set screw).
Pay attention to tighten the screw on the flat part of the shaft. - Insert the spur gear lid on top of the spur gear to cover it.
- Insert the push button into the docking station and lock it with its nut from the other side.
- Connect the electronic box assembly to the docking station using 3 Phillips Thread-Forming Screw M3x10.Â
It is recommended to wire everything on the breadboard before inserting the breadboard and the components inside the electronics box. - Insert the motor and the push button wires inside the electronics box via the side slot. Wire the wires to the breadboard.
- Arrange all the wires inside and outside the electronics box to have a nice and clean look.
- Close the electronics box lid.
Mixing Tank Assembly
- Insert the mixing tank funnel into the mixing tank. You can use a bit of super glue to get a better connection between the parts.
- Insert the mixing spur gear into the center hole in the mixing tank floor. Try to rotate the mixing spur gear with your hand and see that it rotates freely.
- Connect the mixing tank floor (with the mixing spur gear on it) to the mixing tank using 4 Phillips Thread-Forming Screw M3x10. Make sure to align the groove in the mixing tank floor to the groove in the mixing tank.
- Insert the polycarbonate tube into the mixing tank.Â
Top Assembly
- Insert the mixing tank assembly into the docking station assembly. Make sure to align them so the groove in the mixing tank will be positioned where the spur gear is.Â
The spur gear and the mixing spur gear should properly engage with each other. - Push the button and check that the motor rotates the mixing spur gear properly.
- Attach the docking station assembly to the wall using the 2 mounting holes.
________________________________________________________________________________________________________________
Video
________________________________________________________________________________________________________________
If you have any further questions, please feel free to send me a message.