#include <Arduino.h>
#include <WiFi.h>
#include <FirebaseESP32.h>
#include <NewPing.h>
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
#define FIREBASE_HOST "aquasphere-d14e3-default-rtdb.asia-southeast1.firebasedatabase.app"
#define FIREBASE_AUTH "AIzaSyBRUgCYegiehinVsrZgmACr0k7Ktwco624"
#define TANK_NAME "Tanka"
FirebaseData firebaseData;
FirebaseConfig firebaseConfig;
FirebaseAuth firebaseAuth;
FirebaseJson json;
#define PH_PIN_IN 34
#define TURBIDITY_PIN_IN 33
#define TDS_PIN_IN 32
#define PH_MAX_RANGE 14.0
#define NTU_MAX_RANGE 1000.0
#define PPM_MAX_RANGE 1000.0
#define ADC_MAX_VALUE 4095.0
#define TRIG_PIN 26
#define ECHO_PIN 27
#define MAX_DISTANCE_CM 400
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE_CM);
#define DIST_FULL_CM 20.0
#define DIST_EMPTY_CM 390.0
#define TOTAL_RANGE_CM (DIST_EMPTY_CM - DIST_FULL_CM)
#define MOTOR_PIN 0
#define BUTTON_PIN 3
bool motorState = false;
int lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
unsigned long lastUpdateTime = 0;
const long updateInterval = 30000;
void connectWiFi();
float readAnalogSensor(int pin, float max_range);
float readWaterLevelPercent();
void updateMotorStatus(bool newStatus);
void sendDataToFirebase();
void handleButtonPress();
void setup() {
Serial.begin(115200);
Serial.println("--- Aquasphere IoT Node Initializing ---");
analogReadResolution(12);
analogSetAttenuation(ADC_11db);
pinMode(MOTOR_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
digitalWrite(MOTOR_PIN, LOW);
connectWiFi();
firebaseConfig.host = FIREBASE_HOST;
firebaseConfig.api_key = FIREBASE_AUTH;
Firebase.begin(&firebaseConfig, &firebaseAuth);
Firebase.reconnectWiFi(true);
if (Firebase.ready()) {
Serial.println("Firebase initialized successfully.");
} else {
Serial.println("Firebase initialization failed.");
}
if (Firebase.ready()) {
String motorStatusPath = "/tanks/" TANK_NAME "/motorStatus";
if (Firebase.getString(firebaseData, motorStatusPath)) {
motorState = (firebaseData.dataType() == "string" && firebaseData.stringData() == "on");
digitalWrite(MOTOR_PIN, motorState ? HIGH : LOW);
Serial.printf("Initial motor state from DB: %s\n", motorState ? "on" : "off");
} else {
Serial.println("Motor status not found or failed to retrieve, defaulting to OFF.");
updateMotorStatus(false);
}
} else {
updateMotorStatus(false);
}
}
void loop() {
handleButtonPress();
if (WiFi.isConnected() && Firebase.ready() && (millis() - lastUpdateTime >= updateInterval)) {
sendDataToFirebase();
lastUpdateTime = millis();
}
delay(10);
}
void connectWiFi() {
Serial.printf("Connecting to %s\n", WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.printf("\nWiFi connected, IP: %s\n", WiFi.localIP().toString().c_str());
}
float readAnalogSensor(int pin, float max_range) {
int rawADC = analogRead(pin);
float sensorValue = ((float)rawADC / ADC_MAX_VALUE) * max_range;
return sensorValue;
}
float readWaterLevelPercent() {
unsigned int uS = sonar.ping();
float distanceToWater = sonar.convert_cm(uS);
if (distanceToWater == 0 || distanceToWater > MAX_DISTANCE_CM) {
distanceToWater = DIST_EMPTY_CM;
}
float levelPercent = map(distanceToWater, DIST_FULL_CM, DIST_EMPTY_CM, 100.0, 0.0);
return constrain(levelPercent, 1.0, 100.0);
}
void updateMotorStatus(bool newStatus) {
motorState = newStatus;
digitalWrite(MOTOR_PIN, motorState ? HIGH : LOW);
const char* statusStr = motorState ? "on" : "off";
String path = "/tanks/" TANK_NAME "/motorStatus";
if (Firebase.ready() && Firebase.setString(firebaseData, path.c_str(), statusStr)) {
Serial.printf("Motor state updated to '%s' in DB.\n", statusStr);
} else if (!Firebase.ready()) {
Serial.println("Firebase not ready. Motor state updated locally only.");
} else {
Serial.printf("Failed to update motor state: %s\n", firebaseData.errorReason().c_str());
}
}
void handleButtonPress() {
int reading = digitalRead(BUTTON_PIN);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != lastButtonState && reading == LOW) {
Serial.println("Button Pressed - Toggling Motor Status...");
updateMotorStatus(!motorState);
}
}
lastButtonState = reading;
}
void sendDataToFirebase() {
Serial.println("--- Syncing Data to Firebase ---");
float phValue = readAnalogSensor(PH_PIN_IN, PH_MAX_RANGE);
float turbidityValue = readAnalogSensor(TURBIDITY_PIN_IN, NTU_MAX_RANGE);
float tdsValue = readAnalogSensor(TDS_PIN_IN, PPM_MAX_RANGE);
float waterLevel = readWaterLevelPercent();
bool alertStatus = (tdsValue > 800) || (phValue < 6.5 || phValue > 8.5) || (turbidityValue > 100);
Serial.printf("W.Level: %.1f%% | PH: %.1f | Turb: %.0f NTU | TDS: %.0f PPM | Alert: %s\n",
waterLevel, phValue, turbidityValue, tdsValue, alertStatus ? "TRUE" : "FALSE");
json.clear();
json.set("waterLevelPercent", waterLevel);
json.set("waterQuality/TDS", (int)tdsValue);
json.set("waterQuality/pH", phValue);
json.set("waterQuality/turbidity", turbidityValue);
json.set("waterQuality/alert", alertStatus);
json.set("motorStatus", motorState ? "on" : "off");
String path = "/tanks/" TANK_NAME;
if (Firebase.updateNode(firebaseData, path.c_str(), json)) {
Serial.println("Data synced successfully.");
} else {
Serial.printf("Data sync failed: %s\n", firebaseData.errorReason().c_str());
}
}