#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHTesp.h>
#define PIN_TRIG 5
#define PIN_ECHO 18
#define ON_LED 0
#define ALERT_LED 17
#define TANK_HEIGHT_CM 300
#define BUZZER_ALARM 23
#define BUZZER_DURATION_MS 5000
#define LCD_BUTTON 13 // display mode button
#define RST_BUTTON 14
#define DHT_22 33
LiquidCrystal_I2C lcd(0x27, 16, 2);
DHTesp dhtSensor;
unsigned long buzzerStartTime = 0;
bool buzzerActive = false;
bool warningOrNormalDetected = false;
unsigned long previousMillis = 0;
const long interval = 500;
bool ledState = LOW;
int displayMode = 0; // 0 = water lvl, 1 = temperature, 2 = humidity
unsigned long lastButtonPressTime = 0; // to debounce the button
const long debounceDelay = 50; // debounce delay
void displayStartup() {
lcd.clear();
// dynamic dots (...............)
for (int i = 0; i < 16; i++) {
lcd.setCursor(i, 0);
lcd.print(".");
delay(100); // speed of the dots appearing
}
delay(200); // Pause before showing the title
lcd.clear();
String line1 = "WATER TANK";
int len1 = line1.length();
int pos1 = (16 - len1) / 2;
for (int i = 0; i < len1; i++) {
lcd.setCursor(pos1 + i, 0);
lcd.print(line1[i]);
delay(100);
}
String line2 = "MONITORING";
int len2 = line2.length();
int pos2 = (16 - len2) / 2;
for (int i = 0; i < len2; i++) {
lcd.setCursor(pos2 + i, 1);
lcd.print(line2[i]);
delay(100);
}
delay(1200);
}
void setup() {
Serial.begin(115200);
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
pinMode(ON_LED, OUTPUT);
pinMode(ALERT_LED, OUTPUT);
pinMode(BUZZER_ALARM, OUTPUT);
pinMode(LCD_BUTTON, INPUT_PULLUP);
pinMode(RST_BUTTON, INPUT_PULLUP);
digitalWrite(ON_LED, HIGH);
digitalWrite(ALERT_LED, LOW);
digitalWrite(BUZZER_ALARM, LOW);
dhtSensor.setup(DHT_22, DHTesp::DHT22);
lcd.init();
lcd.backlight();
displayStartup();
displayMsg("SystemInitialize", "Please wait...");
}
void displayMsg(String message1, String message2) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(message1);
lcd.setCursor(0, 1);
lcd.print(message2);
delay(3000);
}
void displayResetMsg() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("System Resetting");
lcd.setCursor(0, 1);
lcd.print("Please wait...");
}
int measureWaterLvl() {
digitalWrite(PIN_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);
int duration = pulseIn(PIN_ECHO, HIGH);
return duration * 0.0343 / 2;
}
int calcWaterLevel(int distanceCM) {
return distanceCM;
}
void handleWarningLevel() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
ledState = !ledState;
digitalWrite(ALERT_LED, ledState);
}
digitalWrite(BUZZER_ALARM, LOW);
noTone(BUZZER_ALARM);
buzzerActive = false;
warningOrNormalDetected = true;
}
void handleCriticalLevel() {
digitalWrite(ALERT_LED, HIGH);
if (!buzzerActive && warningOrNormalDetected) {
digitalWrite(BUZZER_ALARM, HIGH);
tone(BUZZER_ALARM, 1000);
buzzerStartTime = millis();
buzzerActive = true;
warningOrNormalDetected = false;
}
}
void handleNormalLevel() {
digitalWrite(ALERT_LED, LOW);
digitalWrite(BUZZER_ALARM, LOW);
noTone(BUZZER_ALARM);
buzzerActive = false;
warningOrNormalDetected = true;
}
void checkBuzzer() {
if (buzzerActive && (millis() - buzzerStartTime >= BUZZER_DURATION_MS)) {
noTone(BUZZER_ALARM);
buzzerActive = false;
}
}
String waterLvlStatus(int waterLevel) {
if (waterLevel <= 50) {
return "CRITICAL";
} else if (waterLevel <= 150) {
return "WARNING";
} else {
return "NORMAL";
}
}
String tempStatus(float temperature) {
if (temperature < 5.0 || temperature > 45.0) {
return "WARNING";
} else {
return "NORMAL";
}
}
String humStatus(float humidity) {
if (humidity < 20.0 or humidity > 80.0) {
return "WARNING";
} else {
return "NORMAL";
}
}
void displayLCD(int waterLevel, float temperature, float humidity) {
lcd.clear();
lcd.setCursor(0, 0);
switch (displayMode) {
case 0: // display Water Level
lcd.print("WaterLvl: ");
lcd.print(waterLevel);
lcd.print("cm");
lcd.setCursor(0, 1);
lcd.print("Status: ");
lcd.print(waterLvlStatus(waterLevel));
break;
case 1: // display Temperature
lcd.print("Temp: ");
lcd.print(temperature, 1);
lcd.print("degC");
lcd.setCursor(0, 1);
lcd.print("Status: ");
lcd.print(tempStatus(temperature));
break;
case 2: // display Humidity
lcd.print("Hum: ");
lcd.print(humidity, 1);
lcd.print(" %RH");
lcd.setCursor(0, 1);
lcd.print("Status: ");
lcd.print(humStatus(humidity));
break;
}
}
void loop() {
//cCheck if the reset button is pressed
int valReset = digitalRead(RST_BUTTON);
if (valReset == LOW) { // reset button is pressed
Serial.println("Reset button pressed! Restarting...");
displayResetMsg(); // display reset message on the LCD
ESP.restart(); // perform software reset
}
// check if button is pressed
if (digitalRead(LCD_BUTTON) == LOW) {
unsigned long currentMillis = millis();
if (currentMillis - lastButtonPressTime > debounceDelay) {
// button press detected, change display mode
displayMode = (displayMode + 1) % 3; // Cycle through 0, 1, 2
lastButtonPressTime = currentMillis;
//delay(100)
}
}
int distanceCM = measureWaterLvl();
int waterLevel = calcWaterLevel(distanceCM);
TempAndHumidity data = dhtSensor.getTempAndHumidity();
float temperature = data.temperature;
float humidity = data.humidity;
displayLCD(waterLevel, temperature, humidity);
if (waterLevel <= 50) {
handleCriticalLevel();
} else if (waterLevel <= 150) {
handleWarningLevel();
} else {
handleNormalLevel();
}
checkBuzzer();
delay(300); // main loop delay
// parameters are updated every 0.3 second, allowing the measurement to be updated in real-time
}