#define BLYNK_TEMPLATE_ID "TMPL6rpOJ82Ec"
#define BLYNK_TEMPLATE_NAME "Smart Fish Feeder"
#define BLYNK_AUTH_TOKEN "N4OWrLDdfax2eAjIDRK1i5wEcamR1CfH"
#define BLYNK_PRINT Serial
#include <ESP32Servo.h>
#include "RTClib.h"
#include <DHT.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
char auth[] = BLYNK_AUTH_TOKEN;
char ssid[] = "Wokwi-GUEST";
char pass[] = "";
RTC_DS1307 rtc;
BlynkTimer timer;
Servo servoku;
#define DHTPIN 5
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
const int BUTTON_PIN = 4;
const int RED_LIGHT_PIN = 14;
const int YELLOW_LED_PIN = 32;
const int BLUE_LED_PIN = 18;
const int TOSCA_LED_PIN = 33;
const int TRIG_PIN = 4;
const int ECHO_PIN = 15;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
int feedCount = 0;
int manualFeedButtonState = 0;
unsigned long previousMillisLCD = 0;
unsigned long previousNotificationMillis = 0;
unsigned long previousAutoFeedMillis = 0;
const long intervalLCD = 3000; // 3 Seconds
const long notificationInterval = 10000; // 10 seconds
// 28,800,000 milliseconds (every 8 hours)
//const long autoFeedInterval = 28800000;
// 30,000 milliseconds (every 30 seconds)
const long autoFeedInterval = 30000;
const long chartUpdateInterval = 60000L; // 1 minute
bool displayTime = true;
BLYNK_WRITE(V0) {
int status = param.asInt();
if (status == 1) {
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("Feeding");
lcd.setCursor(3, 1);
lcd.print("In Progress");
servoku.write(180);
delay(500);
Blynk.virtualWrite(V0, 0);
feedCount++;
delay(500);
lcd.clear();
servoku.write(0);
} else {
servoku.write(0);
Blynk.virtualWrite(V0, 0);
}
}
void temperature() {
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
Blynk.virtualWrite(V3, h);
Blynk.virtualWrite(V2, t);
Serial.print(" Temperature : ");
Serial.print(t);
Serial.print(" Humidity : ");
Serial.println(h);
unsigned long currentMillis = millis();
if (t < 20) {
if (currentMillis - previousNotificationMillis >= notificationInterval) {
Blynk.logEvent("temperature_low", "Warning: Fish tank temperature is too low!");
previousNotificationMillis = currentMillis;
}
digitalWrite(YELLOW_LED_PIN, HIGH);
digitalWrite(RED_LIGHT_PIN, LOW);
} else if (t > 28) {
if (currentMillis - previousNotificationMillis >= notificationInterval) {
Blynk.logEvent("temperature_high", "Warning: Fish tank temperature is too high!");
previousNotificationMillis = currentMillis;
}
digitalWrite(RED_LIGHT_PIN, HIGH);
digitalWrite(YELLOW_LED_PIN, LOW);
} else {
digitalWrite(RED_LIGHT_PIN, LOW);
digitalWrite(YELLOW_LED_PIN, LOW);
}
lcd.setCursor(0, 0);
lcd.print("Temp: ");
lcd.print(t);
lcd.print(" C");
}
void checkWaterLevel() {
long duration;
int distance;
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
duration = pulseIn(ECHO_PIN, HIGH);
distance = duration * 0.034 / 2;
int waterLevel = 70 - distance;
Blynk.virtualWrite(V1, waterLevel);
Serial.print("Water Level : ");
Serial.print(waterLevel);
if (waterLevel > 65) {
digitalWrite(BLUE_LED_PIN, HIGH);
digitalWrite(TOSCA_LED_PIN, LOW);
if (millis() - previousNotificationMillis >= notificationInterval) {
Blynk.logEvent("water_level_high", "Warning: Water level too high!");
previousNotificationMillis = millis();
}
} else if (waterLevel < 50) {
digitalWrite(TOSCA_LED_PIN, HIGH);
digitalWrite(BLUE_LED_PIN, LOW);
if (millis() - previousNotificationMillis >= notificationInterval) {
Blynk.logEvent("water_level_low", "Warning: Water level too low!");
previousNotificationMillis = millis();
}
} else {
digitalWrite(BLUE_LED_PIN, LOW);
digitalWrite(TOSCA_LED_PIN, LOW);
}
lcd.setCursor(0, 1);
lcd.print("Water Level: ");
lcd.print(waterLevel);
lcd.print(" cm");
}
void displayTimeAndDate() {
DateTime now = rtc.now();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(daysOfTheWeek[now.dayOfTheWeek()]);
lcd.setCursor(8, 0);
printWithLeadingZero(lcd, now.hour());
lcd.print(':');
printWithLeadingZero(lcd, now.minute());
lcd.print(':');
printWithLeadingZero(lcd, now.second());
lcd.setCursor(0, 1);
printWithLeadingZero(lcd, now.day());
lcd.print('/');
printWithLeadingZero(lcd, now.month());
lcd.print('/');
lcd.print(now.year(), DEC);
}
void displayWaterLevelandTemp() {
temperature();
checkWaterLevel();
}
void updateLCD() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillisLCD >= intervalLCD) {
previousMillisLCD = currentMillis;
if (displayTime) {
displayTimeAndDate();
} else {
displayWaterLevelandTemp();
}
displayTime = !displayTime;
}
}
void printWithLeadingZero(LiquidCrystal_I2C& lcd, int value) {
if (value < 10) {
lcd.print('0');
}
lcd.print(value);
}
void autoFeed() {
unsigned long currentMillis = millis();
if (currentMillis - previousAutoFeedMillis >= autoFeedInterval) {
feedFish();
previousAutoFeedMillis = currentMillis;
}
}
void feedFish() {
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Auto Feeding");
lcd.setCursor(4, 1);
lcd.print("In Progress");
servoku.write(180);
delay(500);
feedCount++;
delay(500);
lcd.clear();
servoku.write(0);
Blynk.logEvent("feeding_time", "Yay! Fish feeding completed successfully. Your fish have been fed on time!");
sendFeedDataToChart();
}
void feedCountFunction() {
DateTime now = rtc.now();
if (now.hour() == 0 && now.minute() == 0 && now.second() == 0) {
feedCount = 0;
}
if (now.second() == 0) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Today Feeds");
lcd.setCursor(0, 1);
lcd.print("Count: " + String(feedCount) + " Times");
delay(4000);
lcd.clear();
}
Blynk.virtualWrite(V0, feedCount);
}
void sendFeedDataToChart() {
long timestamp = millis() / 1000;
Blynk.virtualWrite(V10, timestamp, feedCount);
Serial.print("Sent to Blynk Chart - Timestamp: ");
Serial.print(timestamp);
Serial.print(" - Feed Count: ");
Serial.println(feedCount);
}
void lcdTurnOn() {
lcd.init();
lcd.backlight();
lcd.setCursor(5, 0);
lcd.print("System");
lcd.setCursor(5, 1);
lcd.print("Online");
delay(3000);
lcd.clear();
}
void setup() {
pinMode(V4, INPUT);
Serial.begin(115200);
Serial.println("Activating Automatic Feeding System!");
Blynk.begin(auth, ssid, pass);
dht.begin();
lcdTurnOn();
servoku.attach(2);
servoku.write(0);
pinMode(BUTTON_PIN, INPUT);
pinMode(RED_LIGHT_PIN, OUTPUT);
pinMode(YELLOW_LED_PIN, OUTPUT);
pinMode(BLUE_LED_PIN, OUTPUT);
pinMode(TOSCA_LED_PIN, OUTPUT);
digitalWrite(RED_LIGHT_PIN, LOW);
digitalWrite(YELLOW_LED_PIN, LOW);
digitalWrite(BLUE_LED_PIN, LOW);
digitalWrite(TOSCA_LED_PIN, LOW);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
if (!rtc.begin()) {
Serial.println("RTC not found!");
Serial.flush();
abort();
}
timer.setInterval(2000L, updateLCD);
timer.setInterval(chartUpdateInterval, sendFeedDataToChart);
}
BLYNK_WRITE(V4) {
int manualFeedStatus = param.asInt();
if (manualFeedStatus == 1) {
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("Manual Feed");
lcd.setCursor(3, 1);
lcd.print("In Progress");
servoku.write(180);
delay(500);
Blynk.virtualWrite(V4, 0);
feedCount++;
delay(500);
lcd.clear();
servoku.write(0);
}
}
void loop() {
Blynk.run();
timer.run();
feedCountFunction();
handleManualFeedButton();
autoFeed();
}
void handleManualFeedButton() {
int buttonState = digitalRead(BUTTON_PIN);
if (buttonState == HIGH && manualFeedButtonState == 0) {
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("Manual Feed");
lcd.setCursor(3, 1);
lcd.print("In Progress");
servoku.write(180);
delay(500);
Blynk.virtualWrite(V0, 0);
feedCount++;
delay(500);
lcd.clear();
servoku.write(0);
manualFeedButtonState = 1;
} else if (buttonState == LOW && manualFeedButtonState == 1) {
manualFeedButtonState=0;
}
}