Build a remote pet feeder

Use a servo to open a food flap from the Blynk app, a button or a timer, and see in the app when your pet was last fed.
  • Level:Advanced
  • Duration: 1 h
  • Ages 13+
  • Platform: ESP32

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Build a remote pet feeder – EEMENTOR STEM™ lesson

Going out for the day? A pet feeder can drop food at set times, and you can add a snack from your phone. In this lesson a servo opens and closes a flap under a small food container. You feed from the Blynk app, with a button on the breadboard, or automatically every few hours, and the app shows when the last meal was served.

What you'll learn

  • How to control a servo on the ESP32 with the ESP32Servo library
  • How to react to a push button widget
  • How to run an action every few hours
  • How to show the date and time of an event in the app

What you need

  • ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
  • Servo motor (ESP32 IoT Starter Kit)
  • Button module (45-in-1 Sensor Kit)
  • A small plastic container or paper cup, card and tape for the flap
  • Breadboard and jumper wires (ESP32 IoT Starter Kit)
  • A free Blynk account and the Blynk IoT app on a phone
  • A 2.4 GHz Wi-Fi network

Build the feeder

Make a hole about 2 cm wide in the bottom of a small container or paper cup. Tape a piece of card to the servo arm so that it covers the hole from below. Fix the servo and the cup so that turning the arm 90° uncovers the hole. Test it with dry food or rice before trusting it with your pet's dinner, and always keep an eye on your pet's food too.

Wiring

Breadboard wiring diagram: Build a remote pet feeder
Breadboard view. Check each connection against the wiring table below.
From To
Servo brown / black wire GND
Servo red wire 5V
Servo orange / yellow wire ESP32 GPIO 13
Button module S ESP32 GPIO 27
Button module + 3V3
Button module − GND

Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.

Set up Blynk

In the Blynk console, create a new template called Pet feeder (hardware ESP32, connection type Wi-Fi) and add these datastreams. Lesson 1 shows each step in detail.

Virtual pin Name Data type Range Phone widget
V0 Feed now Integer 0 – 1 Button (push mode)
V1 Portions Integer 1 – 5 Slider
V2 Feed every Integer 0 – 12, units h Slider
V3 Last meal String n/a Value Display

Save the template, create a new device from it and copy its three BLYNK_… lines. In the phone app, open the device, tap the wrench icon and add a Button in push mode (V0), two Sliders (V1, V2) and a Value Display (V3). Link each widget to its datastream.

Example Blynk web dashboard and phone app for: Build a remote pet feeder
Example dashboard with sample values. Your layout may look different.

The sketch

The standard Servo library doesn't work on the ESP32. Install ESP32Servo (by Kevin Harrington) in Tools → Manage Libraries.

Replace the three BLYNK_… lines at the top with the ones from your device's Device info page, and add your Wi-Fi name and password (2.4 GHz only). The BLYNK_… lines must stay above #include <BlynkSimpleEsp32.h>.

Some versions of this module switch their output to HIGH when they trigger, others to LOW. The sketch reads the output when it starts (keep things still for a moment) and treats any change from that resting state as a trigger, so it works with both.

#define BLYNK_TEMPLATE_ID "YOUR_TEMPLATE_ID"
#define BLYNK_TEMPLATE_NAME "Pet feeder"
#define BLYNK_AUTH_TOKEN "YOUR_AUTH_TOKEN"
#define BLYNK_PRINT Serial          // show connection messages in the Serial Monitor

#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <ESP32Servo.h>
#include <time.h>

char ssid[] = "YOUR_WIFI_NAME";
char pass[] = "YOUR_WIFI_PASSWORD";
const char* TIME_ZONE = "EET-2EEST,M3.5.0/3,M10.5.0/4";  // Estonia; change for your country

const int SERVO_PIN = 13;
const int BUTTON_PIN = 27;
const int CLOSED_ANGLE = 0;
const int OPEN_ANGLE = 90;

Servo flap;
BlynkTimer timer;

int portions = 1;
int everyHours = 0;          // 0 = no automatic feeding
unsigned long lastMeal = 0;
int buttonRest;
bool wasPressed = false;

String timeNow() {
  struct tm t;
  if (!getLocalTime(&t, 10)) return "time not set yet";
  char text[20];
  strftime(text, sizeof(text), "%d.%m %H:%M:%S", &t);
  return String(text);
}

void feed(String reason) {
  Serial.println("Feeding: " + reason);
  for (int i = 0; i < portions; i++) {   // one short opening per portion
    flap.write(OPEN_ANGLE);
    delay(400);
    flap.write(CLOSED_ANGLE);
    delay(400);
  }
  lastMeal = millis();
  Blynk.virtualWrite(V3, timeNow() + " (" + reason + ")");
}

BLYNK_WRITE(V0) {
  if (param.asInt() == 1) feed("app");   // push buttons send 1, then 0
}
BLYNK_WRITE(V1) { portions = param.asInt(); }
BLYNK_WRITE(V2) {
  everyHours = param.asInt();
  lastMeal = millis();                   // count from now
}

BLYNK_CONNECTED() { Blynk.syncVirtual(V1, V2); }

void checkButton() {
  bool pressed = digitalRead(BUTTON_PIN) != buttonRest;
  if (pressed && !wasPressed) feed("button");
  wasPressed = pressed;
}

void checkSchedule() {
  if (everyHours > 0 && millis() - lastMeal >= everyHours * 3600000UL) feed("timer");
}

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT);
  delay(200);
  buttonRest = digitalRead(BUTTON_PIN);

  flap.setPeriodHertz(50);               // standard servo signal
  flap.attach(SERVO_PIN, 500, 2400);
  flap.write(CLOSED_ANGLE);

  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  configTzTime(TIME_ZONE, "pool.ntp.org");
  timer.setInterval(30L, checkButton);
  timer.setInterval(60000L, checkSchedule);   // check once a minute
}

void loop() {
  Blynk.run();
  timer.run();
}

How it works

A push button widget sends 1 when you press it and 0 when you let go, so the sketch only feeds on the 1. Each portion is one short opening of the flap. That takes 0.8 s, and the sketch waits with delay() during it; a few seconds is fine for Blynk, but never use long delays in an IoT sketch. Once a minute, checkSchedule() checks whether enough hours have passed since the last meal. everyHours * 3600000UL turns hours into milliseconds; the UL makes it an unsigned long, because the number is too big for a normal int.

Try this next

  • Add a notification event when a meal is served, so you know it worked.
  • Show how many meals were served today and reset the count at midnight.
  • Add the IR obstacle avoidance sensor below the flap to check that food really came out.

Troubleshooting

  • The device shows "Offline" in the app: check the Serial Monitor at 115200 baud. "Invalid auth token" means the BLYNK_AUTH_TOKEN line is wrong; if it never connects, check the Wi-Fi name and password, and that your network is 2.4 GHz.
  • The servo jitters or the ESP32 resets: servos draw bursts of current. Use a good USB port, or power the servo from a separate 5 V supply with its GND connected to the ESP32's GND.
  • The flap doesn't close fully: change CLOSED_ANGLE and OPEN_ANGLE to suit your build.
  • Compile error about Servo.h or ESP32Servo.h: install the ESP32Servo library.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

ESP32 IoT Starter Kit | Arduino IDE Compatible | Free Tutorials $57.00

Shop the kit