// Template ID, Device Name and Auth Token are provided by the Blynk.Cloud
// See the Device Info tab, or Template settings
#define BLYNK_TEMPLATE_ID "TMPLewjGkpy4"
#define BLYNK_DEVICE_NAME "Aquaphonics System"
#define BLYNK_AUTH_TOKEN "iCTbn2Bu3UxsCyqRdx2TPFFIOhKGrQQR"
//#define BLYNK_PRINT Serial
// Libraries
#include <WiFi.h> ; //used to access the ESP 32 Wi-Fi functionality
#include <WiFiClient.h> //“WiFiClient.h” library was put for WiFi shield
#include <BlynkSimpleEsp32.h> // to use Blynk app with our ESP32 board
#include <DS1307RTC.h> // REal time clock
#include <TimeLib.h> //“TimeLib.h” provides timekeeping functionality on the Arduino.
#include <Wire.h> //“Wire.h” library allows user to communicate with I2C device.
#include <Servo.h> //“Servo.h” library was used to access servo motor.
// Blynk Connection Setup.
char auth[] = BLYNK_AUTH_TOKEN;
// Your Wifi Credentials.
// Set password to "" for open networks.
char ssid[] = "YourNetworkName"; // Fill in your WIFI SSID
char pass[] = "YourPassword"; // Fill in SSID Password
// ===================== Feeding Schedule ======================
int HourSchedule1 = 8; // Set the hour of feeding schedule (24 Hour System).
int HourSchedule2 = 19; // Set the hour of 2nd feeding schedule comment line if not used.
int MinuteSchedule = 44; // Set minute of feeding schedule (24 hour system).
// Variable and Pin Defined
// ======================== Ph Sensor ======================
const int PhPin = 34;
const int Acidic = 5;
const int Neutral = 2;
const int AnodeLed = 4;
int phval = 0;
float PhValue;
int buffer_arr[10], temp;
unsigned long int avgval;
const float MAX = 4095.00;
float calibration_value = 21.34;
// ======================== Water Level ====================
int WaterLevel, PumpOverride;
const int WaterPin = 39;
const int WaterPump = 19;
// ======================== Plant Watering =====================
int SprinklerOverride, moistureValue;
const int SoilPin = 36;
const int Sprinkler = 18;
// ======================== Food Dispenser ======================
int Hour, Minute, FoodLevel, FoodDispIndOverride, FoodDispStatus;
const int Open = 180; //servo rotates 18 degree to relase food
const int Close = 0; //servo closes the container
const int FoodPin = 35;
const int FoodDisp = 23;
const int FoodInd = 15;
const int tempfood = 32;
bool feedFlag;
Servo food;
const long foodOpenIntervalRTC = 60000;
const long foodOpenIntervalManual = 2000;
const long foodCloseInterval = 2000;
unsigned long previousTime = 0;
// =================== BLYNK SETUP ===================
//defining the virtual pin of widget LED
WidgetLED FoodLevelB(V4);
WidgetLED AcidicB(V5);
WidgetLED NeutralB(V6);
WidgetLED PumpB(V7);
WidgetLED SprinklerB(V8);
WidgetLED FoodDispB(V9);
// Widget Color Defines
#define BLYNK_BLUE "#04C0F8"
#define BLYNK_YELLOW "#ED9D00"
#define BLYNK_RED "#D3435C"
BlynkTimer timer; //
BLYNK_CONNECTED() //to run certain routine when hardware connects
{ //-to Blynk Cloud or private server.
Blynk.syncAll(); //restores all the Widget’s values based on
} //-the last saved values on the server
// Blynk Food Dispenser Button
BLYNK_WRITE(V0) //Read the data from blynk to manually cotrol the foodDispenser
{
int btnState = param.asInt();
if (btnState == HIGH)
{
FoodDispIndOverride = 1;
FoodDispB.on(); //close the cointainer after 2 secs
timer.setTimeout(2000L, FoodDispOFF);
}
else
FoodDispIndOverride = 0;
food.write(Close);
}
void FoodDispOFF()
{
FoodDispIndOverride = 0;
food.write(Close);
FoodDispB.off();
}
// Blynk Water Pump Button
BLYNK_WRITE(V10)
{ //Read the data from blynk to manually control the water pump
int btnState1 = param.asInt();
if (btnState1 == HIGH)
{
PumpB.on();
PumpOverride = 1;
}
else
PumpOverride = 0;
digitalWrite(WaterPump, LOW);
}
// Blynk Sprinkler Button
BLYNK_WRITE(V11)
{//Read the data from blynk to manually control the sprinkler
int btnState2 = param.asInt();
if (btnState2 == HIGH)
{
SprinklerOverride = 1;
SprinklerB.on();
}
else
SprinklerOverride = 0;
SprinklerB.off();
digitalWrite(Sprinkler, LOW);
}
// Sprinkler Function
void SprinklerMode() // ================ SPRINKLER @ SOIL MOISTURE =====================
{
moistureValue = (100 - ((analogRead(SoilPin) / MAX) * 100)); //read and calculated value
if (SprinklerOverride == 1) //-from sensor(slider in replace)
{
digitalWrite(Sprinkler, HIGH);
}
else if (SprinklerOverride == 0)
{
if (moistureValue < 40) //Sprinkler turns on if the moisture value below 40%
{
digitalWrite(Sprinkler, HIGH);
}
else if (moistureValue > 40) //Sprinkler turns if the moisture value below 40%
{
digitalWrite(Sprinkler, LOW);
}
}
else
{
digitalWrite(Sprinkler, LOW);
}
}
// Food Dispenser Function
void FoodDispenserMode() // ================== FOOD DISPENSER =============================
{
FoodLevel = digitalRead(FoodPin); //read the the input(FoodPin ), HIGH OR LOW
Hour = hour();
Minute = minute();
if (FoodLevel == LOW) //LED will turn on if FoodLevel is low(no food)
{
digitalWrite(FoodInd, HIGH);
}
else if (FoodLevel == HIGH) //LED will not light up if FoodLevel is high
{
digitalWrite(FoodInd, LOW);
}
if (FoodDispIndOverride == 1) //Servo rotates(food released) when there is manual control
{
food.write(Open);
}
else if (FoodDispIndOverride == 0)
{ //Food released at 0844 and 1944 everyday
if (Hour == HourSchedule1 || Hour == HourSchedule2)
{
if (Minute == MinuteSchedule)
{
if (feedFlag == false)
{
previousTime = millis();
food.write(Open);
feedFlag = true;
}
}
}
// if (digitalRead(tempfood) == HIGH)
// {
// if (feedFlag == false)
// {
// previousTime = millis();
// food.write(Open);
// feedFlag = true;
// }
// }
if (feedFlag == true)
{ //Serco rotates to 0 degree and the food container closed
if (millis() - previousTime >= foodOpenIntervalRTC)
{
food.write(Close);
feedFlag = false;
}
}
}
else
{
food.write(Close);
digitalWrite(FoodInd, LOW);
}
}
// Water Level Function
void WaterLevelMode() // =============================== WATER PUMP @ WATER LEVEL =============================
{
WaterLevel = (100 - ((analogRead(WaterPin) / MAX) * 100)); //Read and calculate the value read from sensor
if (PumpOverride == 1) //Water pumped out if user control manually
{
digitalWrite(WaterPump, HIGH);
}
else if (PumpOverride == 0)
{
if (WaterLevel < 40) //Water pumped out if water level below 40% of sensor
{
digitalWrite(WaterPump, HIGH);
}
else if (WaterLevel > 60) //No Water pumped out if water level above 60% of sensor
{
digitalWrite(WaterPump, LOW);
}
}
else
{
digitalWrite(WaterPump, LOW);
}
}
// Water PH Level Function
void WaterQualityMode() // =============================== WATER QUALITY @ PH SENSOR =============================
{
for (int i = 0; i < 10; i++)
{
buffer_arr[i] = analogRead(PhPin); //Read and store the value of pH in an array
}
//Sorting the array from small to big
for (int i = 0; i < 9; i++)
{
for (int j = i + 1; j < 10; j++)
{
if (buffer_arr[i] > buffer_arr[j])
{
temp = buffer_arr[i];
buffer_arr[i] = buffer_arr[j];
buffer_arr[j] = temp;
}
}
}
avgval = 0;
for (int i = 2; i < 8; i++)
{ //Calculation to get the pH value
avgval += buffer_arr[i];
float volt = (float)avgval * 5.0 / MAX / 6;
float ph_act = -5.70 * volt + calibration_value;
PhValue = ph_act;
}
if (PhValue < 6.5)
{
digitalWrite(Acidic, LOW);
digitalWrite(Neutral, HIGH); //rgb LED turns RED
}
else if (PhValue > 6.6)
{
digitalWrite(Acidic, HIGH);
digitalWrite(Neutral, LOW); //RGB LED turns BLUE
}
else
{
digitalWrite(Acidic, HIGH);
digitalWrite(Neutral, HIGH); //RGB LED turns oFF
}
}
// Blynk Sensor Gauge Function
void Status() //Write data to blynk
{
Blynk.virtualWrite(V1, PhValue);
Blynk.virtualWrite(V2, WaterLevel);
Blynk.virtualWrite(V3, moistureValue);
}
// Blynk LED Widget Indicators Function
void ledStatus()
{
if (SprinklerOverride == 1)
{
SprinklerB.on();
}
else if (SprinklerOverride == 0)
{
if (moistureValue > 60)
{ //Gauge widget will become blue when moisture level is above 60%
Blynk.setProperty(V3, "color", BLYNK_BLUE);
SprinklerB.off();
}
else if ((moistureValue < 60) && (moistureValue > 40))
{ //Gauge widget will become yellowwhen moisture level is between 40% and 60%
Blynk.setProperty(V3, "color", BLYNK_YELLOW);
SprinklerB.off();
}
else if (moistureValue < 40)
{ //Gauge widget will become red whne mositure levek is below 40%
Blynk.setProperty(V3, "color", BLYNK_RED);
SprinklerB.on();
}
}
if (FoodLevel == LOW)
{
FoodLevelB.on();
Blynk.logEvent("food_dispenser"); //notification will be sent on blynk to notify user
}
else if (FoodLevel == HIGH)
{
FoodLevelB.off();
}
if (FoodDispIndOverride == 1)
{
FoodDispB.on();
}
else if (FoodDispIndOverride == 0)
{
if (feedFlag == true)
{
FoodDispB.on();
}
else if (feedFlag == false)
{
FoodDispB.off();
}
}
if (PumpOverride == 0)
{
if (WaterLevel < 30)
{ //Gauge widget turns red when water level is below 30%
Blynk.setProperty(V2, "color", BLYNK_RED);
PumpB.on();
}
else if ((WaterLevel < 60) && (30 < WaterLevel))
{ //Gauge widget turns yellow when water level is between 30% and 60%
Blynk.setProperty(V2, "color", BLYNK_YELLOW);
PumpB.off();
}
else if (WaterLevel > 60)
{ //Gauge widget turns blue when water level is aobve 60%
Blynk.setProperty(V2, "color", BLYNK_BLUE);
PumpB.off();
}
}
if (PhValue < 6.5)
{ //Gauge widget turns red when pH level is below 6.5
Blynk.setProperty(V1, "color", BLYNK_RED);
NeutralB.off();
AcidicB.on();
}
else if (PhValue > 6.6)
{ //Gauge widget turns blue when pH level is above 6.6
Blynk.setProperty(V1, "color", BLYNK_BLUE);
AcidicB.off();
NeutralB.on();
}
else
{
//Turn offa ll the widget LEDs
PumpB.off();
AcidicB.off();
NeutralB.off();
FoodDispB.off();
FoodLevelB.off();
SprinklerB.off();
}
}
// Function for Serial Data Monitoring
void SerialData()
{
Serial.print("Moisture Level: ");
Serial.println(moistureValue);
Serial.print("Water Level: ");
Serial.println(WaterLevel);
Serial.print("Ph Level: ");
Serial.println(PhValue);
Serial.print("Food Level: ");
Serial.println(FoodLevel);
Serial.print("Time: ");
Serial.print(Hour);
Serial.print(" : ");
Serial.println(Minute);
Serial.println("\n");
}
// ================================ SETUP AND LOOP ================================
void setup()
{
Serial.begin(115200);
Serial.print("Connecting to WiFi"); //Displaying internet connection status
WiFi.begin("Wokwi-GUEST", "", 6); // Uncomment line if simulating using WOKWI
//Blynk.connectWiFi(ssid, pass); // Uncomment line if using WIFI (SSID and Password needs to be defined)
while (WiFi.status() != WL_CONNECTED)
{
delay(100);
Serial.print(".");
}
Serial.println(" Connected!");
Blynk.config(auth);
while (Blynk.connect() == false)
{
Serial.println("Connection Failed");
}
//Determne if thepins are I/ODCQ2
pinMode(PhPin, INPUT);
pinMode(SoilPin, INPUT);
pinMode(FoodPin, INPUT);
pinMode(tempfood, INPUT);
pinMode(WaterPin, INPUT);
pinMode(Acidic, OUTPUT);
pinMode(Neutral, OUTPUT);
pinMode(AnodeLed, OUTPUT);
pinMode(FoodInd, OUTPUT);
pinMode(WaterPump,OUTPUT);
pinMode(Sprinkler,OUTPUT);
food.attach(FoodDisp);
food.write(Close); //no any degree of rotation initially
digitalWrite(AnodeLed, HIGH);
setSyncProvider(RTC.get); //Get the real time
timer.setInterval(1000L, Status); // Run Blynk Status Function
timer.setInterval(500L, ledStatus); // Run Blynk ledStatus Function
}
void loop()
{
// SerialData(); //Uncomment if you want to monitor the outputs of sensors
//calls the data to execute their function
SprinklerMode();
FoodDispenserMode();
WaterLevelMode();
WaterQualityMode();
Blynk.run(); //Run the blynk
timer.run(); //Run the timer
}