/*
BluEcoInVET Smart Water / Digital Twin Demonstrator
Wokwi ESP32 -> Firebase Realtime Database -> Web Dashboard
Hardware:
- ESP32 DevKit C v4
- DHT22 data: GPIO 15
- Water-level potentiometer signal: GPIO 34
- Turbidity potentiometer signal: GPIO 35
Security note:
No Gemini/API key is stored on the ESP32 or in this public project.
*/
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <DHTesp.h>
constexpr uint8_t DHT_PIN = 15;
constexpr uint8_t WATER_LEVEL_PIN = 34;
constexpr uint8_t TURBIDITY_PIN = 35;
constexpr unsigned long SEND_INTERVAL_MS = 5000;
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASSWORD = "";
const char* FIREBASE_DATABASE_URL =
"https://studio-2928557024-15735-default-rtdb.firebaseio.com";
const char* FIREBASE_DEVICE_PATH =
"/bluecoinvet/devices/esp32_wokwi_01/latest.json";
DHTesp dhtSensor;
unsigned long lastSendAt = 0;
void connectToWiFi() {
if (WiFi.status() == WL_CONNECTED) {
return;
}
Serial.print("Connecting to Wokwi WiFi");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD, 6);
unsigned long startedAt = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startedAt < 15000) {
delay(300);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.print("WiFi connected. IP: ");
Serial.println(WiFi.localIP());
} else {
Serial.println();
Serial.println("WiFi connection timed out. Retrying later.");
}
}
String classifyWaterQuality(float turbidityNtu) {
if (turbidityNtu < 5.0) {
return "GOOD";
}
if (turbidityNtu < 50.0) {
return "MODERATE";
}
return "POOR";
}
bool sendToFirebase(const String& payload) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Firebase skipped: WiFi is offline.");
return false;
}
WiFiClientSecure secureClient;
secureClient.setInsecure();
HTTPClient http;
String endpoint = String(FIREBASE_DATABASE_URL) + FIREBASE_DEVICE_PATH;
if (!http.begin(secureClient, endpoint)) {
Serial.println("Firebase error: HTTPS connection could not start.");
return false;
}
http.setTimeout(12000);
http.addHeader("Content-Type", "application/json");
int httpCode = http.PUT(payload);
String response = http.getString();
Serial.print("Firebase HTTP status: ");
Serial.println(httpCode);
if (response.length() > 0) {
Serial.print("Firebase response: ");
Serial.println(response);
}
http.end();
return httpCode >= 200 && httpCode < 300;
}
void setup() {
Serial.begin(115200);
delay(500);
Serial.println();
Serial.println("============================================");
Serial.println(" BluEcoInVET Smart Water Digital Twin");
Serial.println(" Wokwi ESP32 + Firebase RTDB");
Serial.println("============================================");
analogReadResolution(12);
pinMode(WATER_LEVEL_PIN, INPUT);
pinMode(TURBIDITY_PIN, INPUT);
dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
connectToWiFi();
}
void loop() {
if (millis() - lastSendAt < SEND_INTERVAL_MS) {
delay(50);
return;
}
lastSendAt = millis();
connectToWiFi();
TempAndHumidity climate = dhtSensor.getTempAndHumidity();
int waterLevelRaw = analogRead(WATER_LEVEL_PIN);
int turbidityRaw = analogRead(TURBIDITY_PIN);
float waterLevelPct = (waterLevelRaw / 4095.0f) * 100.0f;
float turbidityNtu = (turbidityRaw / 4095.0f) * 1000.0f;
String waterQuality = classifyWaterQuality(turbidityNtu);
bool climateValid =
!isnan(climate.temperature) && !isnan(climate.humidity);
if (!climateValid) {
Serial.println("DHT22 read failed. Sending null values for safe JSON.");
}
String temperatureJson =
climateValid ? String(climate.temperature, 1) : "null";
String humidityJson =
climateValid ? String(climate.humidity, 1) : "null";
String payload = "{";
payload += "\"deviceId\":\"esp32_wokwi_01\",";
payload += "\"project\":\"BluEcoInVET\",";
payload += "\"source\":\"Wokwi ESP32\",";
payload += "\"sensorStatus\":\"" +
String(climateValid ? "OK" : "DHT_ERROR") + "\",";
payload += "\"temperatureC\":" + temperatureJson + ",";
payload += "\"humidityPct\":" + humidityJson + ",";
payload += "\"waterLevelPct\":" + String(waterLevelPct, 1) + ",";
payload += "\"turbidityNtu\":" + String(turbidityNtu, 1) + ",";
payload += "\"waterQuality\":\"" + waterQuality + "\",";
payload += "\"waterLevelRaw\":" + String(waterLevelRaw) + ",";
payload += "\"turbidityRaw\":" + String(turbidityRaw) + ",";
payload += "\"localUptimeMs\":" + String(millis()) + ",";
payload += "\"timestamp\":{\".sv\":\"timestamp\"}";
payload += "}";
Serial.println();
Serial.println("--- Sensor sample ---");
Serial.print("Temperature: ");
Serial.print(climate.temperature, 1);
Serial.println(" C");
Serial.print("Humidity: ");
Serial.print(climate.humidity, 1);
Serial.println(" %");
Serial.print("Water level: ");
Serial.print(waterLevelPct, 1);
Serial.println(" %");
Serial.print("Turbidity: ");
Serial.print(turbidityNtu, 1);
Serial.println(" NTU");
Serial.print("Water quality: ");
Serial.println(waterQuality);
Serial.print("JSON payload: ");
Serial.println(payload);
bool sent = sendToFirebase(payload);
Serial.println(sent
? "Upload result: SUCCESS"
: "Upload result: FAILED - check Firebase rules or connectivity");
}