#include <LiquidCrystal_I2C.h>
// Define LCD settings
#define LCD_ADDRESS 0x27
#define LCD_COLUMNS 16
#define LCD_ROWS 2
// Define pin connections
#define TRIG_PIN 2
#define ECHO_PIN 3
#define RELAY_PIN 13
#define AUTO_MANUAL_SWITCH_PIN 7
#define MANUAL_SWITCH_PIN 6
// Define thresholds (in cm) for a standard tank
#define TANK_HEIGHT 200 // Standard tank height in cm
#define PUMP_ON_LEVEL 160 // 80% distance from the ultrasonic sensor (20% below the tank)
#define PUMP_OFF_LEVEL 20 // 90% of the tank height
// Define delays
#define PUMP_DELAY 2000
// Initialize the LCD
LiquidCrystal_I2C lcd(LCD_ADDRESS, LCD_COLUMNS, LCD_ROWS);
// Variables for non-blocking operations
unsigned long previousPumpMillis = 0;
bool pumpState = false;
bool isAutoMode = true;
int lastWaterLevel = -1;
bool lastPumpState = false;
bool lastIsAutoMode = true;
void setup() {
// Configure pin modes
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(RELAY_PIN, OUTPUT);
pinMode(AUTO_MANUAL_SWITCH_PIN, INPUT_PULLUP);
pinMode(MANUAL_SWITCH_PIN, INPUT_PULLUP);
// Initialize relay as OFF
digitalWrite(RELAY_PIN, LOW);
// Initialize the LCD
lcd.begin(LCD_COLUMNS, LCD_ROWS);
lcd.backlight();
// Display initial message
lcd.setCursor(0, 0);
lcd.print("GOD IS MY LIGHT");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
delay(2000);
lcd.clear();
}
void loop() {
// Read the mode
isAutoMode = digitalRead(AUTO_MANUAL_SWITCH_PIN) == HIGH;
// Get water level
int distance = getDistance();
int waterLevel = map(distance, 0, TANK_HEIGHT, 100, 0); // Convert distance to percentage
waterLevel = constrain(waterLevel, 0, 100);
// Update pump state based on the mode
if (isAutoMode) {
handleAutoMode(distance, millis());
} else {
handleManualMode();
}
// Update the display only when there's a change
if (shouldUpdateDisplay(waterLevel)) {
updateDisplay(waterLevel);
}
}
// Function to handle automatic mode
void handleAutoMode(int distance, unsigned long currentMillis) {
if (distance > PUMP_ON_LEVEL && !pumpState && currentMillis - previousPumpMillis >= PUMP_DELAY) {
pumpState = true;
previousPumpMillis = currentMillis;
digitalWrite(RELAY_PIN, HIGH);
} else if (distance < PUMP_OFF_LEVEL && pumpState && currentMillis - previousPumpMillis >= PUMP_DELAY) {
pumpState = false;
previousPumpMillis = currentMillis;
digitalWrite(RELAY_PIN, LOW);
}
}
// Function to handle manual mode
void handleManualMode() {
if (digitalRead(MANUAL_SWITCH_PIN) == HIGH) {
pumpState = true;
digitalWrite(RELAY_PIN, HIGH);
} else {
pumpState = false;
digitalWrite(RELAY_PIN, LOW);
}
}
// Function to update the LCD display
void updateDisplay(int waterLevel) {
lcd.setCursor(0, 0);
// Display mode
if (isAutoMode) {
lcd.print("Mode: Auto ");
} else {
lcd.print("Mode: Manual ");
}
// Display pump status
lcd.setCursor(0, 1);
if (pumpState) {
lcd.print("Pump: ON ");
} else {
lcd.print("Pump: OFF ");
}
// Display water level
lcd.setCursor(10, 1);
lcd.print("Lvl:");
if (waterLevel < 10) lcd.print(" "); // Ensure alignment for single digits
lcd.print(waterLevel);
lcd.print("% ");
// Save the last states
lastWaterLevel = waterLevel;
lastPumpState = pumpState;
lastIsAutoMode = isAutoMode;
}
// Function to determine if the display needs to be updated
bool shouldUpdateDisplay(int waterLevel) {
return waterLevel != lastWaterLevel || pumpState != lastPumpState || isAutoMode != lastIsAutoMode;
}
// Function to get the distance from the ultrasonic sensor
int getDistance() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // Timeout after 30ms
if (duration == 0) return TANK_HEIGHT + 1; // Return max distance if no reflection
int distance = (duration * 0.0343) / 2;
return distance > 0 ? distance : TANK_HEIGHT + 1; // Return max distance if invalid
}