//=========================================Blynk setup =======================================================================
#define BLYNK_TEMPLATE_ID "TMPL6yEZFxTY8"
#define BLYNK_TEMPLATE_NAME "test1"
#define BLYNK_AUTH_TOKEN "2fxSsboW8-1-G2fbsjlmgG7dyU7U3mkk"
#define BLYNK_PRINT Serial
char ssid[] = "Wokwi-GUEST";
char pass[] = "";
char auth[] = BLYNK_AUTH_TOKEN;
//===========================================Libraries =========================================================================
#include <WiFi.h>
#include <BlynkSimpleEsp32.h>
#include <WiFiClient.h>
#include "DHT.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//==================================== Hardwre setup =======================================================================
#define pHSensor 35
#define TPHSensor 32
#define DHTPIN 26 //Connect Out pin to D2 in NODE MCU
#define DHTTYPE DHT22
#define pin_tds_pumpA 27
#define pin_tds_pumpB 14
#define pin_ph_plus 33
#define pin_ph_minus 12
#define pin_watering 25
#define TdsSensorPin 34
#define VREF 3.3 // analog reference voltage(Volt) of the ADC
#define samples 10 // Number of sample point
#define SCREEN_WIDTH 128 // OLED width, in pixels
#define SCREEN_HEIGHT 64 // OLED height, in pixels
#define motorPin_tds_pumpA 27
#define motorPin_tds_pumpB 14
//================================== Others setup ==========================================================================
float temperature = 25;
unsigned long overFlow = 4294967295 ;
float adc_resolution = 4095.0; //ESP 32 ADC Resolution
//=================================Initializing variables =================================================================
//pump
bool watering_flag = false ;
///=============================== Sensor Variables ========================================
float averageVoltage = 0;
//tds
int analogBuffertds[samples]; // store the analog value in the array, read from ADC (tds)
int Indextds = 0;
float tdsValue = 0;
int value_overFlow_td=0 ;
int tds_lampDuration_overflow = 0;
unsigned long tds_lampStartTime_Green = 0;
unsigned long tds_lampStartTime_Red = 0;
unsigned long tds_waitStartTime = 0;
long tds_pumpDuration = 3000; // 3 seconds in milliseconds
long tds_pumpOffTime = 100; // 1 minute in milliseconds
bool tds_pump_state = 0 ;
int pwm_tds_pumpA = 200;
int pwm_tds_pumpB = 200;
unsigned long tds_currentMillis = 0;
unsigned long tds_pumpStartTime=0 ;
unsigned long tds_overFlowCompensation = 0;
//PH
float voltage = 0.0 ;
int analogBufferph[samples]; // store the analog value in the array, read from ADC (ph)
int Indexph = 0;
int value_overFlow_p=0 ;
int p_lampDuration_overflow = 0;
unsigned long p_lampStartTime_Green = 0;
unsigned long p_lampStartTime_Red = 0;
unsigned long p_waitStartTime = 0;
long p_lampDuration = 3000; // 3 seconds in milliseconds
long p_waitDuration = 10000; // 1 minute in milliseconds
bool p_pump_state = 0 ;
/// DHT
float h = 0.0;
float t = 0.0;
float temp_low =0 ;
float temp_high =0 ;
/// =========================== Pumps variables ==========================================
/// watering pump
unsigned long pumpDuration_overflow = 0;
int pump_time =0 ;
unsigned long start_pump = 0;
unsigned long waitStartTime = 0;
unsigned long pumpDuration = 0;
unsigned long waitDuration = 0;
bool flag= 0 ;
/// Calibration pumps
bool ph_plus = 0, ph_minus = 0, tds_pumpON = 0, tds_pumpOFF = 0 ;
// ==========================================Expert System Knowledge base =======================================
int plants_PH[5][3] = {
{0, 5.5, 6},
{1, 5.0, 5.5},
{2, 6, 6.5},
{3, 5.2, 6.4},
{4, 5.5, 6}
};
int plants_tds[5][3] = {
{0, 550, 1100},
{1, 850, 1000},
{2, 1000, 2000},
{3, 1000, 2000},
{4, 500, 800}
};
int value = 0 ; // plants Slider
int waiting_time = 0 ;
int watering_duration = 0 ;
int waiting_time_tds= 0 ;
int watering_duration_tds = 0 ;
//====================================== initializing hardware =======================================================
// create an OLED display object connected to I2C
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
WiFiClient espClient;
BlynkTimer timer;
DHT dht(DHTPIN, DHTTYPE);
//======================================= Feunction definition =====================================================
/// initialize on connection
BLYNK_CONNECTED() {
Blynk.syncVirtual(V0);
Blynk.syncVirtual(V1);
Blynk.syncVirtual(V2);
Blynk.syncVirtual(V3);
Blynk.syncVirtual(V5);
}
/// median filtering algorithm
int getMedianNum(int bArray[], int iFilterLen) {
int bTab[iFilterLen];
for (byte i = 0; i < iFilterLen; i++)
bTab[i] = bArray[i];
int i, j, bTemp;
for (j = 0; j < iFilterLen - 1; j++) {
for (i = 0; i < iFilterLen - j - 1; i++) {
if (bTab[i] > bTab[i + 1]) {
bTemp = bTab[i];
bTab[i] = bTab[i + 1];
bTab[i + 1] = bTemp;
}
}
}
if ((iFilterLen & 1) > 0) {
bTemp = bTab[(iFilterLen - 1) / 2];
}
else {
bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
}
return bTemp;
}
/// Initialize the wifi connection
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
BLYNK_WRITE(V0)
{
tds_pumpOffTime = param.asInt() * 1000 ; // Get value as integer
Serial.print("Waiting Time TDS: ");
Serial.println(tds_pumpOffTime);
}
BLYNK_WRITE(V1)
{
watering_duration_tds = param.asInt(); // Get value as integer
Serial.print("Watering TDS: ");
Serial.println(watering_duration_tds);
}
BLYNK_WRITE(V2)
{
waiting_time = param.asInt(); // Get value as integer
Serial.print("Waitting Time: ");
Serial.println(waiting_time);
}
BLYNK_WRITE(V3)
{
pump_time = param.asInt(); // Get value as integer
Serial.print("Pump Time: ");
Serial.println(pump_time);
}
void TDS_sensor()
{
analogBuffertds[Indextds] = analogRead(TdsSensorPin); //read the analog value and store into the buffer
Indextds++;
if (Indextds == samples) {
Indextds = 0;
}
// read the analog value more stable by the median filtering algorithm, and convert to voltage value
averageVoltage = getMedianNum(analogBuffertds, samples) * (float)VREF / adc_resolution;
//temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
float compensationCoefficient = 1.0 + 0.02 * (temperature - 25.0);
//temperature compensation
float compensationVoltage = averageVoltage / compensationCoefficient;
//convert voltage value to tds value
tdsValue = (133.42 * compensationVoltage * compensationVoltage * compensationVoltage - 255.86 * compensationVoltage * compensationVoltage + 857.39 * compensationVoltage) * 0.5;
Serial.print("TDS Value:");
Serial.print(tdsValue, 0);
Serial.println("ppm");
Blynk.virtualWrite(V3, tdsValue);
tds_currentMillis = millis();
if (tds_currentMillis < tds_pumpStartTime) { // Check for overflow condition
// Overflow occurred, compensate and reset the start time
tds_overFlowCompensation = overFlow - tds_pumpStartTime;
tds_pumpStartTime = tds_currentMillis;
Serial.print("Overflow compensated:");
Serial.println(tds_overFlowCompensation);
}
if (!tds_pump_state) { // If pump is currently off
if ((tds_currentMillis - tds_pumpStartTime + tds_overFlowCompensation) >= tds_pumpOffTime) { // Check if pump off time has elapsed
if (tdsValue < (plants_tds[value][1]))
{
tds_pumpON=1;// Turn on the pump
tds_pumpOFF=0;
}
tds_pump_state = true;// Update pump state
tds_pumpStartTime = tds_currentMillis; // Reset start time for pump operation
Serial.println("Onnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn");
tds_overFlowCompensation=0;
}
} else { // If pump is currently on
if ((tds_currentMillis - tds_pumpStartTime +tds_overFlowCompensation)>= tds_pumpDuration) { // Check if pump on time has elapsed
tds_pumpOFF=1 ; // Turn off the pump
tds_pumpON = 0;
tds_pump_state = false; // Update pump state
tds_pumpStartTime = tds_currentMillis; // Reset start time for pump off period
Serial.println("Offffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
tds_overFlowCompensation=0;
}
}
// if (tds_pump_state == 0 && watering_flag) {
// Serial.print("5000==");
// Serial.println(tds_currentMillis - tds_lampStartTime_Green);
// if (tdsValue < (plants_tds[value][1])) {
// tds_minus = 0 ;
// if ((tds_currentMillis - tds_lampStartTime_Green) < tds_lampDuration_overflow)
// {
// // 3 seconds
// ledcWrite(0, pwm_tds_plus);
// tds_plus = 1 ;
// Serial.println("HIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII");
// }
// else
// {
// // Turn off the lights
// ledcWrite(0, 0);
// tds_plus = 0 ;
// tds_pump_state = 1;
// tds_waitStartTime = tds_currentMillis;
// tds_lampDuration_overflow = tds_lampDuration ;
// value_overFlow_td =0 ;
// }
// }
// else if (tdsValue > (plants_tds[value][2])) {
// tds_plus = 0 ;
// if (tds_currentMillis - tds_lampStartTime_Red < tds_lampDuration_overflow)
// {
// tds_minus = 1 ;
// ledcWrite(0, pwm_tds_minus);
// }
// else {
// // Turn off the lights
// ledcWrite(0, 0);
// tds_minus = 0 ;
// tds_pump_state = 1;
// tds_waitStartTime = tds_currentMillis;
// tds_lampDuration_overflow = tds_lampDuration ;
// value_overFlow_td =0 ;
// }
// }
// else {
// tds_minus = 0 ;
// tds_plus = 0 ;
// }
// }
// if ((tds_currentMillis - tds_waitStartTime >= tds_waitDuration) && tds_pump_state == 1) {
// Serial.print("oooooooooooooooooooooooooooooooooo");
// tds_lampStartTime_Green = tds_currentMillis;
// tds_lampStartTime_Red = tds_currentMillis;
// tds_pump_state = 0;
// }
}
void DHT_sensor()
{
Serial.println("rawan1");
h = dht.readHumidity();
t = dht.readTemperature(); // or dht.readTemperature(true) for Fahrenheit
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
if(t < temp_low || t> temp_high)
{
Blynk.logEvent("test1");
}
//Blynk.virtualWrite(V0, t);
//Blynk.virtualWrite(V1, h);
Serial.print("Temperature : ");
Serial.print(t);
Serial.print(" Humidity : ");
Serial.println(h);
}
float ph (float voltage) {
return 7 + ((2.50 - voltage) / 0.18);
}
void PH_sensor() {
p_waitDuration = waiting_time * 1000 ;
p_lampDuration = watering_duration * 1000 ;
analogBufferph[Indexph] = analogRead(pHSensor); //read the analog value and store into the buffer
Indexph++;
if (Indexph == samples) {
Indexph = 0;
}
delay(10);
voltage = 3.3 * getMedianNum(analogBufferph, samples) / 4095.0; //adc
Serial.print("pH= ");
Serial.println(ph(voltage));
//Blynk.virtualWrite(V2, ph(voltage));
unsigned long p_currentMillis = millis();
Serial.print("m=");
Serial.println(p_currentMillis);
Serial.print("pump_state=");
Serial.println(p_pump_state);
// Check pH conditions
if (p_currentMillis + p_lampDuration >= overFlow)
{
value_overFlow_p = (p_currentMillis + p_lampDuration) - overFlow;
}
if (value_overFlow_p >0)
{
p_lampDuration_overflow = value_overFlow_p ;
}
else
p_lampDuration_overflow = p_lampDuration;
if (p_pump_state == 0) {
Serial.print("5000==");
Serial.println(p_currentMillis - p_lampStartTime_Green);
if (ph(voltage) < (plants_PH[value][1])) {
ph_minus = 0 ;
if (p_currentMillis - p_lampStartTime_Green < p_lampDuration_overflow)
{
ph_plus = 1 ;
Serial.println("HIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII");
// 3 seconds
}
else
{
// Turn off the lights
ph_plus = 0 ;
p_pump_state = 1;
p_waitStartTime = p_currentMillis;
p_lampDuration_overflow = p_lampDuration ;
value_overFlow_p =0 ;
}
}
else if (ph(voltage) > (plants_PH[value][2])) {
ph_plus = 0 ;
if (p_currentMillis - p_lampStartTime_Red < p_lampDuration_overflow)
{
ph_minus = 1 ;
}
else {
// Turn off the lights
ph_minus = 0 ;
p_pump_state = 1;
p_waitStartTime = p_currentMillis;
p_lampDuration_overflow = p_lampDuration ;
value_overFlow_p =0 ;
}
}
else {
ph_minus = 0 ;
ph_plus = 0 ;
}
}
if ((p_currentMillis - p_waitStartTime >= p_waitDuration) && p_pump_state == 1) {
Serial.print("oooooooooooooooooooooooooooooooooo");
p_lampStartTime_Green = p_currentMillis;
p_lampStartTime_Red = p_currentMillis;
p_pump_state = 0;
}
}
//get value from slider
BLYNK_WRITE(V4)
{
value = param.asInt(); // Get value as integer
Serial.print("rawannnnnnnnn V44444444444444");
Serial.println(value);
}
void Actuation()
{
if (ph_plus)
{
digitalWrite(pin_ph_plus, HIGH); // move the actuation to a seperat part at the end of the code and use marker to store the decesions such as in PLC
digitalWrite(pin_ph_minus, LOW);
}
if (ph_minus)
{
digitalWrite(pin_ph_minus, HIGH);
digitalWrite(pin_ph_plus, LOW);
}
if (ph_plus == 0)
{
digitalWrite(pin_ph_plus, LOW);
}
if (ph_minus == 0)
{
digitalWrite(pin_ph_minus, LOW);
}
if (tds_pumpON)
{
digitalWrite(pin_tds_pumpA, HIGH); // move the actuation to a seperat part at the end of the code and use marker to store the decesions such as in PLC
digitalWrite(pin_tds_pumpB, HIGH);
ledcWrite(0, pwm_tds_pumpA);
ledcWrite(0, pwm_tds_pumpB);
}
if (tds_pumpOFF)
{
digitalWrite(pin_tds_pumpA, LOW);
digitalWrite(pin_tds_pumpB, LOW);
ledcWrite(0,0);
ledcWrite(0,0);
}
}
void watering()
{
waitDuration = waiting_time*30*60*1 ; // modified to speedup the code should be *1000 instead of *10
pumpDuration = pump_time*60*10 ; // modified to speedup the code should be *1000 instead of *10
unsigned long currentMillis = millis();
Blynk.run();
if (overFlow- currentMillis <= pumpDuration)
pumpDuration_overflow = pumpDuration -(overFlow - currentMillis ) ; // all the calculations should be done without allowing the overflow to appeare (currentMilis + pumpDuration might lead to overflow)
else
pumpDuration_overflow = pumpDuration ;
if (flag==0)
{
if (currentMillis-start_pump <pumpDuration_overflow)
{
digitalWrite(pin_watering,HIGH);
watering_flag = true ;
if((pumpDuration_overflow + start_pump - currentMillis) % 100 ==0)
{
Serial.print("Pump On for:");
Serial.println(pumpDuration_overflow + start_pump - currentMillis );
}
}
else
{
digitalWrite(pin_watering,LOW);
flag = 1;
waitStartTime = currentMillis;
Serial.println("Pump off");
watering_flag = false;
// pumpDuration_overflow = pumpDuration;
// value_overFlow=0 ;
}
}
if ((currentMillis - waitStartTime >= waitDuration) && flag == 1) {
Serial.println("Pump started");
start_pump= currentMillis;
flag = 0;
}
}
void display()
{
oled.clearDisplay();
oled.setTextSize(1); // set text size
oled.setTextColor(WHITE); // set text color
oled.setCursor(0, 10); // set position to display
oled.println("tds = ");
oled.setCursor(40, 10);
oled.println(tdsValue); // set tds value
oled.setCursor(0, 25); // set position to display
oled.println("temp = ");
oled.setCursor(40, 25);
oled.println(t); // set temp value
oled.setCursor(0, 40); // set position to display
oled.println("hum = "); // set text
oled.setCursor(40, 40);
oled.println(h); // set hum value
oled.setCursor(0, 50); // set position to display
oled.println("ph = "); // set text
oled.setCursor(40, 50);
oled.println(ph(voltage)); // set ph value
oled.display();
}
void setup()
{
//pinMode(pin_ph_plus, OUTPUT);
//pinMode(pin_ph_minus, OUTPUT);
pinMode(pin_tds_pumpA, OUTPUT);
pinMode(pin_tds_pumpB, OUTPUT);
setup_wifi();
p_lampStartTime_Green = 0;
p_lampStartTime_Red = 0;
p_waitStartTime = 0;
tds_lampStartTime_Green = 0;
tds_lampStartTime_Red = 0;
tds_waitStartTime = 0;
Serial.begin(115200);
Serial.println("helllo") ;
WiFi.begin(ssid, pass);
Blynk.config(auth);
Blynk.begin(auth, ssid, pass, "blynk.cloud", 80);
dht.begin();
timer.setInterval(0L, PH_sensor);
timer.setInterval(0L, TDS_sensor);
timer.setInterval(0L, DHT_sensor);
timer.setInterval(0L, Actuation);
timer.setInterval(0L, watering);
timer.setInterval(0L,display);
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("failed to start SSD1306 OLED"));
while (1);
}
ledcSetup(0, 5000, 8); // Configure LEDC channel 0
ledcAttachPin(motorPin_tds_pumpA, 0);
ledcAttachPin(motorPin_tds_pumpB, 0);
}
void loop()
{
Blynk.run();
timer.run();
}