#define BLYNK_TEMPLATE_ID "TMPL6V2iqc0Zy"
#define BLYNK_TEMPLATE_NAME "Water Quality Monitoring and Alert System"
#define BLYNK_AUTH_TOKEN "bceVvoNOHFvwpQEtskMXKn6jFRxkJ8oW"
#include <WiFi.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <BlynkSimpleEsp32.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
#include <SPI.h>
#include "time.h"
// WiFi & MQTT Settings (Fixed for Wokwi)
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// WiFi & MQTT Client
WiFiClient espClient;
PubSubClient client(espClient);
// Blynk Authentication
BlynkTimer timer;
// 🏷️ Sensor Pins
#define TURBIDITY_PIN 39 // VP (GPIO39)
#define TDS_PIN 36 // VN (GPIO36)
#define DO_PIN 34 // GPIO34
#define ONE_WIRE_BUS 4 // DS18B20 Temperature Sensor (GPIO4)
// DS18B20 Temperature Sensor Setup
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// Display setup
#define TFT_CS 5
#define TFT_DC 17
#define TFT_RST 4
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
// WiFi & MQTT Setup
void setup() {
Serial.begin(115200);
// Connect to WiFi (Fixed for Wokwi)
WiFi.begin(ssid, password);
unsigned long startAttemptTime = millis();
const unsigned long wifiTimeout = 10000; // 10 seconds
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < wifiTimeout) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\WiFi Connected!");
} else {
Serial.println("\WiFi Connection Failed. Will retry in loop...");
}
// Set NTP server
configTime(0, 0, "pool.ntp.org");
while (!time(nullptr)) {
delay(1000);
Serial.println("Waiting for time...");
}
Serial.println("Time synchronized");
// Initialize DS18B20 Sensor
sensors.begin();
// Initialize Display
tft.begin();
tft.setRotation(0); // Portraite
tft.fillScreen(ILI9341_BLACK);
tft.setTextSize(1);
tft.setFont(&FreeSans12pt7b);
// Connect to Blynk
Blynk.config(BLYNK_AUTH_TOKEN);
Blynk.connect(); // will not hang if Blynk server is offline
// Run sensor data every 5 seconds
timer.setInterval(5000L, sendSensorData);
}
// Read Sensors & Update Dashboard
void sendSensorData() {
sensors.requestTemperatures();
float temperature = sensors.getTempCByIndex(0);
float turbidity = analogRead(TURBIDITY_PIN) * (5.0 / 4095.0);
float tds = analogRead(TDS_PIN) * (5.0 / 4095.0);
float dissolved_oxygen = analogRead(DO_PIN) * (5.0 / 4095.0);
// Display Data on Serial Monitor
Serial.print("Temperature: "); Serial.print(temperature); Serial.println(" °C");
Serial.print("Turbidity: "); Serial.print(turbidity); Serial.println(" V");
Serial.print("TDS: "); Serial.print(tds); Serial.println(" V");
Serial.print("Dissolved O₂: "); Serial.print(dissolved_oxygen); Serial.println(" V");
// Update TFT Display
tft.fillScreen(ILI9341_BLACK);
// Show status message (big font)
tft.setFont(&FreeSansBold18pt7b);
tft.setTextColor(ILI9341_RED); // Default to red (unsafe)
tft.setCursor(10, 50);
bool isSafe = (turbidity <= 3.5 && dissolved_oxygen >= 2.0 && tds <= 2.0 && temperature >= 15 && temperature <= 35);
if (isSafe) {
tft.setTextColor(ILI9341_GREEN);
tft.println("Safe to Swim");
} else {
tft.setTextColor(ILI9341_RED);
tft.println("Water is NOT SAFE");
}
// Show sensor readings (small font)
tft.setFont(&FreeSans12pt7b);
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(10, 130);
tft.print("Turbidity: "); tft.print(turbidity, 2); tft.println(" V");
tft.setCursor(10, 160);
tft.print("TDS: "); tft.print(tds, 2); tft.println(" V");
tft.setCursor(10, 190);
tft.print("DO: "); tft.print(dissolved_oxygen, 2); tft.println(" V");
tft.setCursor(10, 220);
tft.print("Temp: "); tft.print(temperature, 2); tft.println(" °C");
// Get current time
time_t now = time(nullptr);
struct tm* timeinfo = localtime(&now);
if (timeinfo == nullptr) return; // Skip display update if time is not available yet
// Format time
char timeStr[20];
strftime(timeStr, sizeof(timeStr), "%d/%m %H:%M", timeinfo);
// Set small font and print in bottom right corner
tft.setFont(); // Default font
tft.setTextSize(1);
int16_t x1, y1;
uint16_t w, h;
tft.getTextBounds(timeStr, 0, 0, &x1, &y1, &w, &h);
tft.setCursor(tft.width() - w - 10, tft.height() - 10);
tft.setTextColor(ILI9341_YELLOW);
tft.print(timeStr);
// Send Data to Blynk Mobile App
Blynk.virtualWrite(V1, turbidity);
Blynk.virtualWrite(V2, tds);
Blynk.virtualWrite(V3, dissolved_oxygen);
Blynk.virtualWrite(V4, temperature);
// Send Alert if Water Quality is Unsafe
String alertMsg = "";
bool alert = false;
if (turbidity > 3.5) {
alertMsg += "Water too cloudy (" + String(turbidity, 2) + " V)\n"; // Triggers alert when values is above 3.5
alert = true;
}
if (dissolved_oxygen < 2.0) {
alertMsg += "Water oxygen level low (" + String(dissolved_oxygen, 2) + " V)\n"; // Triggers alert when values is below 2.0
alert = true;
}
if (tds > 2.0) {
alertMsg += "High dissolved solids (" + String(tds, 2) + " V)\n"; // Triggers alert when values is above 2.0
alert = true;
}
if (temperature > 35.0 || temperature < 15.0) {
alertMsg += "Abnormal Temp (" + String(temperature, 2) + " °C)\n"; // Triggers alert when values is below 15.0 and above 35.0
alert = true;
}
if (alert) {
Blynk.logEvent("water_quality_alert", alertMsg);
}
}
void loop() {
// Reconnect WiFi if disconnected
if (WiFi.status() != WL_CONNECTED) {
Serial.println("🔄 Reconnecting WiFi...");
WiFi.begin(ssid, password);
delay(500);
}
// Keep running Blynk and scheduled tasks
Blynk.run();
timer.run();
if (!Blynk.connected()) {
Serial.println("⚠️ Blynk not connected.");
}
}
Loading
ds18b20
ds18b20
TDS sensor
DO sensor
Turbidity sensor
Temp sensor
Water Quality Monitering and Alert System