#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <Preferences.h>
// Define pins
#define TRIGGER_PIN 9 // Trigger pin for RCWL-1655
#define ECHO_PIN 10 // Echo pin for RCWL-1655
#define RELAY_PIN 14 // Relay pin for pump control
// WiFi credentials
const char* ssid = "YourSSID";
const char* password = "YourPassword";
// Calibration variables
int maxWaterLevel = 0; // Maximum level in cm (set during calibration)
int minWaterLevel = 0; // Minimum level in cm (set during calibration)
// Variables
long pumpStartTime = 0;
long pumpActiveTime = 0;
int currentWaterLevel = 0; // Current water level in %
bool pumpState = false; // Pump initially off
Preferences preferences; // Preferences object for saving data
// Web page HTML content
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<!doctype html>
<html>
<head>
<title>Water Tank Monitor</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; margin: 20px; }
h1 { color: #333; }
p { font-size: 18px; }
button { padding: 10px 20px; font-size: 16px; cursor: pointer; margin: 10px; }
</style>
</head>
<body>
<h1>Water Tank Monitor</h1>
<p>Current Water Level: <span id="waterLevel">--</span>%</p>
<p>Total Water Usage: <span id="waterUsage">--</span> liters</p>
<button onclick="calibrate()">Recalibrate</button>
<button onclick="togglePump()">Toggle Pump</button>
<script>
async function updateData() {
const response = await fetch('/data');
const data = await response.json();
document.getElementById('waterLevel').innerText = data.waterLevel;
document.getElementById('waterUsage').innerText = data.waterUsage.toFixed(2);
}
async function calibrate() {
await fetch('/calibrate');
updateData();
}
async function togglePump() {
await fetch('/toggle');
updateData();
}
setInterval(updateData, 1000); // Update data every second
updateData(); // Initial data load
</script>
</body>
</html>
)rawliteral";
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
// Calculate water usage (arbitrary multiplier for liters based on pump time)
float calculateWaterUsage() {
return (pumpActiveTime / 1000.0) * 0.5; // Example: 0.5 liters per second
}
// Read water level from RCWL-1655
int readRawDistance() {
digitalWrite(TRIGGER_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIGGER_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIGGER_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH);
return duration / 58; // Convert pulse width to distance in cm
}
// Convert raw distance to percentage based on calibration
int calculateWaterLevelPercentage(int rawDistance) {
if (rawDistance <= maxWaterLevel) return 100;
if (rawDistance >= minWaterLevel) return 0;
return map(rawDistance, maxWaterLevel, minWaterLevel, 100, 0);
}
// Control the pump based on the water level
void controlPump() {
if (pumpState) {
// Pump is manually turned on
digitalWrite(RELAY_PIN, HIGH);
} else if (currentWaterLevel <= 10) {
// Automatically turn off pump if near max
digitalWrite(RELAY_PIN, LOW);
} else if (currentWaterLevel >= 90) {
// Automatically turn on pump if near min
digitalWrite(RELAY_PIN, HIGH);
}
// Update pump active time
if (digitalRead(RELAY_PIN) == HIGH) {
if (pumpStartTime == 0) pumpStartTime = millis();
} else {
if (pumpStartTime != 0) {
pumpActiveTime += millis() - pumpStartTime;
pumpStartTime = 0;
}
}
}
void saveCalibration() {
preferences.begin("water-tank", false); // Open preferences with namespace "water-tank"
preferences.putInt("maxLevel", maxWaterLevel);
preferences.putInt("minLevel", minWaterLevel);
preferences.end();
}
void loadCalibration() {
preferences.begin("water-tank", true); // Open preferences for read-only
maxWaterLevel = preferences.getInt("maxLevel", 0);
minWaterLevel = preferences.getInt("minLevel", 0);
preferences.end();
// Set default values if not calibrated
if (maxWaterLevel == 0 && minWaterLevel == 0) {
maxWaterLevel = 20; // Default max level in cm
minWaterLevel = 40; // Default min level in cm
}
}
void setup() {
// Pin configurations
pinMode(TRIGGER_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW); // Ensure the relay is off
// Serial Monitor
Serial.begin(115200);
// Load calibration data
loadCalibration();
// WiFi Setup
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
Serial.println(WiFi.localIP());
// Web Server Routes
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/html", INDEX_HTML);
});
server.on("/data", HTTP_GET, [](AsyncWebServerRequest *request) {
String json = "{";
json += "\"waterLevel\":" + String(currentWaterLevel) + ",";
json += "\"waterUsage\":" + String(calculateWaterUsage());
json += "}";
request->send(200, "application/json", json);
});
server.on("/calibrate", HTTP_GET, [](AsyncWebServerRequest *request) {
maxWaterLevel = readRawDistance();
minWaterLevel = maxWaterLevel + 20; // Example: Tank depth is 20 cm
saveCalibration();
request->send(200, "text/plain", "Calibration complete");
});
server.on("/toggle", HTTP_GET, [](AsyncWebServerRequest *request) {
pumpState = !pumpState;
request->send(200, "text/plain", "Pump state toggled");
});
// Start server
server.begin();
}
void loop() {
// Read current water level
int rawDistance = readRawDistance();
currentWaterLevel = calculateWaterLevelPercentage(rawDistance);
// Control the pump based on water level and state
controlPump();
// Debugging
Serial.print("Raw Distance: ");
Serial.print(rawDistance);
Serial.println(" cm");
Serial.print("Water Level: ");
Serial.print(currentWaterLevel);
Serial.println(" %");
Serial.print("Water Usage: ");
Serial.print(calculateWaterUsage());
Serial.println(" liters");
delay(1000); // Wait 1 second
}