Project Overview
This project creates a smart feeding reminder for your aquarium. When you approach the fish tank, the system checks if it’s time to feed and plays a voice reminder if needed. It also tracks the last feeding time and prevents overfeeding.
Difficulty: Beginner
Estimated time: 1-2 hours
Estimated cost: $20-30
How It Works
A PIR sensor detects when someone approaches the fish tank. The ESP32 checks the current time against a feeding schedule. If it’s within the feeding window and the last feeding was more than 8 hours ago, it plays a voice reminder. A button can be pressed to mark feeding as done.
Materials Needed
- ESP32 or Arduino (1)
- HC-SR501 PIR sensor (1)
- DFPlayer Mini MP3 module (1)
- MicroSD card (with feeding reminder audio)
- Small speaker (3W)
- Push button (to mark feeding done)
- LEDs (optional, for status)
- Resistors (220Ω)
- Jumper wires
- Power supply (5V 1A)
Circuit Diagram
Connection Table
| Component |
Pin |
ESP32 Pin |
PIR Sensor |
OUT
| GPIO 4
| DFPlayer Mini |
TX
| GPIO 16
| DFPlayer Mini |
RX
| GPIO 17
| Push Button |
One side
| GPIO 5
| Push Button |
Other side
| GND
| Green LED |
Anode
| GPIO 2
| Red LED |
Anode
| GPIO 15
|
表
Arduino Code
// Fish Tank Feeding Reminder
#include <WiFi.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <time.h>
SoftwareSerial mySoftwareSerial(16, 17);
DFRobotDFPlayerMini myDFPlayer;
const int pirPin = 4;
const int buttonPin = 5;
const int greenLedPin = 2;
const int redLedPin = 15;
// RTC via NTP
const char* ntpServer = "pool.ntp.org";
const long gmtOffset = 0;
const int daylightOffset = 0;
// Feeding schedule (hour, minute)
struct FeedingTime {
int hour;
int minute;
};
FeedingTime feedTimes[] = {
{8, 0}, // 8:00 AM
{18, 0} // 6:00 PM
};
const int numFeedTimes = 2;
// State variables
unsigned long lastFeedTime = 0;
bool fedToday[2] = {false, false};
unsigned long lastReminderTime = 0;
const unsigned long reminderCooldown = 3600000; // 1 hour
void setup() {
Serial.begin(115200);
pinMode(pirPin, INPUT);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(greenLedPin, OUTPUT);
pinMode(redLedPin, OUTPUT);
digitalWrite(greenLedPin, LOW);
digitalWrite(redLedPin, LOW);
// Connect to Wi-Fi for time
WiFi.begin("YourSSID", "YourPassword");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
configTime(gmtOffset, daylightOffset, ntpServer);
// Initialize DFPlayer
mySoftwareSerial.begin(9600);
if (!myDFPlayer.begin(mySoftwareSerial)) {
Serial.println("DFPlayer error");
}
myDFPlayer.volume(20);
Serial.println("Fish Tank Feeder Ready");
delay(30000); // PIR warm-up
}
bool isTimeToFeed(int currentHour, int currentMinute, int feedHour, int feedMinute) {
// Check if within 30-minute window after feeding time
int currentMinutes = currentHour * 60 + currentMinute;
int feedMinutes = feedHour * 60 + feedMinute;
return (currentMinutes >= feedMinutes && currentMinutes < feedMinutes + 30);
}
void markFed(int feedIndex) {
fedToday[feedIndex] = true;
lastFeedTime = millis();
digitalWrite(greenLedPin, HIGH);
myDFPlayer.play(3); // "Thank you!" message
delay(2000);
digitalWrite(greenLedPin, LOW);
}
void playReminder(int feedIndex) {
if (feedIndex == 0) {
myDFPlayer.play(1); // "Time to feed the fish"
} else {
myDFPlayer.play(2); // "Evening feeding time"
}
digitalWrite(redLedPin, HIGH);
delay(2000);
digitalWrite(redLedPin, LOW);
}
void resetDaily() {
// Reset feeding flags at midnight
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
if (timeinfo.tm_hour == 0 && timeinfo.tm_min == 0) {
for (int i = 0; i < numFeedTimes; i++) {
fedToday[i] = false;
}
Serial.println("Daily flags reset");
}
}
}
void loop() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) return;
resetDaily();
bool motion = digitalRead(pirPin) == HIGH;
bool buttonPressed = digitalRead(buttonPin) == LOW;
if (buttonPressed) {
delay(50);
if (digitalRead(buttonPin) == LOW) {
// Manual feed button - find current feeding time
for (int i = 0; i < numFeedTimes; i++) {
if (isTimeToFeed(timeinfo.tm_hour, timeinfo.tm_min,
feedTimes[i].hour, feedTimes[i].minute)) {
markFed(i);
break;
}
}
while (digitalRead(buttonPin) == LOW) delay(10);
}
}
if (motion && (millis() - lastReminderTime > reminderCooldown)) {
// Check each feeding time
for (int i = 0; i < numFeedTimes; i++) {
if (isTimeToFeed(timeinfo.tm_hour, timeinfo.tm_min,
feedTimes[i].hour, feedTimes[i].minute) && !fedToday[i]) {
lastReminderTime = millis();
playReminder(i);
break;
}
}
}
delay(100);
}
Voice Prompt Files
Create MP3 files on SD card (folder "01"):
- 001.mp3: "It's time to feed the fish. Please give them some food."
- 002.mp3: "Evening feeding time. Don't forget to feed your fish."
- 003.mp3: "Thank you for feeding the fish. They appreciate it!"
Installation Steps
- Mount PIR sensor: Place near fish tank, angled to detect approach.
- Position speaker: Place where reminder can be heard clearly.
- Mount button: Place near fish tank for easy access after feeding.
- Assemble electronics: Enclose ESP32 and DFPlayer in small box.
- Set feeding times: Adjust
feedTimes array in code to match your schedule.
- Test: Approach tank at feeding time, verify reminder plays.
Project Extensions
- Automatic feeder: Add servo to dispense food automatically.
- Wi-Fi dashboard: View feeding history on web page.
- Mobile notifications: Send push notification when feeding is missed.
- Water temperature: Add DS18B20 to monitor tank temperature.
- LED lighting: Add LED strip to simulate sunrise/sunset.
Troubleshooting
- No reminder: Check Wi-Fi connection (for time). Verify feeding times in code.
- No audio: Check DFPlayer wiring and SD card files.
- False triggers: Adjust PIR sensitivity. Ensure not facing windows.
- Time incorrect: Ensure ESP32 has Wi-Fi to sync time. Add backup RTC module for reliability.
Conclusion
This fish tank feeding reminder ensures your aquatic pets are fed on schedule, even when you're busy. With voice reminders, you'll never forget feeding time again.