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

| 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.

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_TOKENline 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_ANGLEandOPEN_ANGLEto suit your build. - Compile error about Servo.h or ESP32Servo.h: install the ESP32Servo library.