// ================================================================
// SmartLoo — IoT Autonomous Toilet Maintenance System
// Wokwi Simulation Version
// Board : ESP32 Dev Module
// Author : SmartLoo Project
// ================================================================
//
// PIN MAP
// -------
// GPIO 14 → PIR #1 (Entry sensor)
// GPIO 27 → PIR #2 (Exit sensor)
// GPIO 34 → MQ-135 (Gas / Odor — Analog)
// GPIO 26 → DHT22 (Temp & Humidity) ← Wokwi uses DHT22
// GPIO 5 → HC-SR04 TRIG (Water level)
// GPIO 18 → HC-SR04 ECHO (Water level)
// GPIO 13 → Servo (Freshener spray)
// GPIO 21 → LCD SDA (I2C)
// GPIO 22 → LCD SCL (I2C)
// GPIO 25 → LED Red (simulates Relay→Pump)
// GPIO 33 → LED Green (simulates Relay→Fan)
// GPIO 32 → LED Blue (simulates Relay→Spare)
// ================================================================
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <ESP32Servo.h>
// ── PIN DEFINITIONS ─────────────────────────────────────────────
#define PIR_ENTRY 14
#define PIR_EXIT 27
#define MQ135_PIN 34
#define DHT_PIN 26
#define TRIG_PIN 5
#define ECHO_PIN 18
#define SERVO_PIN 13
#define LED_PUMP 25 // Simulates water pump relay
#define LED_FAN 33 // Simulates exhaust fan relay
#define LED_SPARE 32 // Simulates occupied indicator
// ── THRESHOLDS ──────────────────────────────────────────────────
#define GAS_THRESHOLD 400 // Above this = bad odor detected
#define WATER_LOW_CM 15 // Distance > 15cm = tank is low
#define FLUSH_DELAY_MS 10000 // 10 sec after exit → auto flush
#define FLUSH_DURATION 5000 // Pump runs 5 seconds
#define MAX_DAILY_USES 50 // Alert at 50 uses per day
// ── DHT TYPE (Wokwi uses DHT22 component for simulation) ────────
#define DHT_TYPE DHT22
// ── OBJECTS ─────────────────────────────────────────────────────
LiquidCrystal_I2C lcd(0x27, 16, 2);
DHT dht(DHT_PIN, DHT_TYPE);
Servo sprayServo;
// ── STATE VARIABLES ─────────────────────────────────────────────
int usageCount = 0;
bool isOccupied = false;
bool flushPending = false;
unsigned long exitTimestamp = 0;
unsigned long lastLCDUpdate = 0;
unsigned long lastSerialLog = 0;
// ════════════════════════════════════════════════════════════════
// HELPER FUNCTIONS
// ════════════════════════════════════════════════════════════════
// Read distance from HC-SR04 ultrasonic sensor (returns cm)
float getDistance() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // 30ms timeout
if (duration == 0) return 999; // No echo = error
return (duration * 0.034) / 2.0;
}
// Trigger auto-flush (pump runs for 5 seconds)
void triggerFlush() {
Serial.println("╔══════════════════════════════╗");
Serial.println("║ AUTO FLUSH TRIGGERED! ║");
Serial.println("╚══════════════════════════════╝");
lcd.clear();
lcd.setCursor(0, 0); lcd.print("AUTO CLEANING...");
lcd.setCursor(0, 1); lcd.print("Flushing: 5 sec ");
digitalWrite(LED_PUMP, HIGH); // Pump ON (relay LOW in real)
Serial.println("[PUMP] Water pump ON — GPIO25 → Relay IN1 LOW");
delay(FLUSH_DURATION); // Run for 5 seconds
digitalWrite(LED_PUMP, LOW); // Pump OFF
Serial.println("[PUMP] Water pump OFF — flush complete!");
Serial.println("-------------------------------");
}
// Trigger freshener spray (servo sweeps 0° → 90° → 0°)
void triggerSpray() {
Serial.println("[SERVO] GPIO13 PWM → 90° (spray press)");
sprayServo.write(90);
delay(800);
sprayServo.write(0);
Serial.println("[SERVO] GPIO13 PWM → 0° (released)");
}
// Update the 16×2 LCD display
void updateLCD(int gasLevel, float temp, float humidity) {
lcd.clear();
lcd.setCursor(0, 0);
if (isOccupied) {
lcd.print("Status:OCCUPIED ");
} else if (flushPending) {
lcd.print("Status:CLEANING ");
} else {
lcd.print("Status: VACANT ");
}
lcd.setCursor(0, 1);
lcd.print("U:");
lcd.print(usageCount);
lcd.print(" G:");
lcd.print(gasLevel);
lcd.print(" T:");
lcd.print((int)temp);
lcd.print("C");
}
// ════════════════════════════════════════════════════════════════
// SETUP
// ════════════════════════════════════════════════════════════════
void setup() {
Serial.begin(115200);
delay(500);
// Welcome banner
Serial.println("");
Serial.println("╔════════════════════════════════════╗");
Serial.println("║ SmartLoo IoT System v1.0 ║");
Serial.println("║ Autonomous Toilet Maintenance ║");
Serial.println("╚════════════════════════════════════╝");
Serial.println("[BOOT] Initializing all pins...");
// Input pins
pinMode(PIR_ENTRY, INPUT);
pinMode(PIR_EXIT, INPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
// Output pins (simulate relays with LEDs)
pinMode(LED_PUMP, OUTPUT);
pinMode(LED_FAN, OUTPUT);
pinMode(LED_SPARE, OUTPUT);
// All outputs OFF at start
digitalWrite(LED_PUMP, LOW);
digitalWrite(LED_FAN, LOW);
digitalWrite(LED_SPARE, LOW);
// Initialize DHT sensor
dht.begin();
Serial.println("[DHT] DHT22 sensor initialized on GPIO26");
// Initialize LCD
Wire.begin(21, 22);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0); lcd.print(" SmartLoo v1.0 ");
lcd.setCursor(0, 1); lcd.print(" Initializing ");
Serial.println("[LCD] 16x2 I2C display initialized (0x27)");
// Initialize servo
sprayServo.attach(SERVO_PIN);
sprayServo.write(0);
Serial.println("[SERVO] SG90 servo attached to GPIO13");
Serial.println("[BOOT] All systems ready!");
Serial.println("-------------------------------");
Serial.println(" PIR Entry → GPIO 14");
Serial.println(" PIR Exit → GPIO 27");
Serial.println(" MQ-135 → GPIO 34 (Analog)");
Serial.println(" DHT22 → GPIO 26");
Serial.println(" Ultrasonic → TRIG:5 ECHO:18");
Serial.println(" Servo → GPIO 13");
Serial.println(" Pump LED → GPIO 25 (Relay IN1)");
Serial.println(" Fan LED → GPIO 33 (Relay IN2)");
Serial.println("-------------------------------");
delay(2000);
lcd.clear();
lcd.setCursor(0, 0); lcd.print("Status: VACANT ");
lcd.setCursor(0, 1); lcd.print("Uses:0 Gas:--- ");
}
// ════════════════════════════════════════════════════════════════
// MAIN LOOP
// ════════════════════════════════════════════════════════════════
void loop() {
// ── Read all sensors ────────────────────────────────────────
int entryDetected = digitalRead(PIR_ENTRY);
int exitDetected = digitalRead(PIR_EXIT);
int gasLevel = analogRead(MQ135_PIN);
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
float waterDist = getDistance();
// Protect against NaN from DHT
if (isnan(temperature)) temperature = 0;
if (isnan(humidity)) humidity = 0;
// ── ENTRY DETECTION ─────────────────────────────────────────
if (entryDetected == HIGH && !isOccupied) {
isOccupied = true;
flushPending = false;
usageCount++;
digitalWrite(LED_SPARE, HIGH); // Occupied indicator ON
Serial.println("");
Serial.println(">>> PIR #1 HIGH — GPIO14 — PERSON ENTERED!");
Serial.print (" Usage count today: ");
Serial.println(usageCount);
if (usageCount >= MAX_DAILY_USES) {
Serial.println("!!! ALERT: 50 uses reached — manual cleaning needed!");
}
}
// ── EXIT DETECTION ──────────────────────────────────────────
if (exitDetected == HIGH && isOccupied) {
isOccupied = false;
flushPending = true;
exitTimestamp = millis();
digitalWrite(LED_SPARE, LOW); // Occupied indicator OFF
Serial.println("");
Serial.println(">>> PIR #2 HIGH — GPIO27 — PERSON EXITED!");
Serial.println(" Auto-flush scheduled in 10 seconds...");
}
// ── AUTO FLUSH after 10 seconds ─────────────────────────────
if (flushPending && (millis() - exitTimestamp >= FLUSH_DELAY_MS)) {
flushPending = false;
triggerFlush();
}
// ── ODOR CONTROL ────────────────────────────────────────────
if (gasLevel > GAS_THRESHOLD) {
digitalWrite(LED_FAN, HIGH); // Fan ON
// Only spray if fan was previously off (avoid repeated sprays)
static bool sprayCooldown = false;
if (!sprayCooldown) {
Serial.print("[MQ135] Gas level HIGH: ");
Serial.println(gasLevel);
Serial.println("[FAN] GPIO33 → Relay IN2 LOW — FAN ON");
triggerSpray();
sprayCooldown = true;
}
} else {
if (digitalRead(LED_FAN) == HIGH) {
Serial.print("[MQ135] Gas cleared: ");
Serial.println(gasLevel);
Serial.println("[FAN] GPIO33 → Relay IN2 HIGH — FAN OFF");
}
digitalWrite(LED_FAN, LOW); // Fan OFF
// Reset spray cooldown when air is clean
static bool sprayCooldown = false;
sprayCooldown = false;
}
// ── WATER LEVEL CHECK ───────────────────────────────────────
static bool waterAlerted = false;
if (waterDist > WATER_LOW_CM && waterDist < 500) {
if (!waterAlerted) {
Serial.print("[WATER] Tank LOW! Distance: ");
Serial.print(waterDist);
Serial.println("cm — refill needed!");
waterAlerted = true;
}
} else {
waterAlerted = false;
}
// ── SERIAL LOG every 3 seconds ──────────────────────────────
if (millis() - lastSerialLog >= 3000) {
lastSerialLog = millis();
Serial.println("─────────── SENSOR READINGS ───────────");
Serial.print (" MQ-135 gas : "); Serial.print(gasLevel);
Serial.println(gasLevel > GAS_THRESHOLD ? " [ODOR!]" : " [GOOD]");
Serial.print (" DHT22 temp : "); Serial.print(temperature, 1);
Serial.println(" C");
Serial.print (" DHT22 humid : "); Serial.print(humidity, 1);
Serial.println(" %");
Serial.print (" HC-SR04 water : "); Serial.print(waterDist, 1);
Serial.println(" cm");
Serial.print (" Status : ");
Serial.println(isOccupied ? "OCCUPIED" : flushPending ? "CLEANING" : "VACANT");
Serial.print (" Usage count : "); Serial.println(usageCount);
Serial.println("────────────────────────────────────────");
}
// ── UPDATE LCD every 2 seconds ──────────────────────────────
if (millis() - lastLCDUpdate >= 2000) {
lastLCDUpdate = millis();
updateLCD(gasLevel, temperature, humidity);
}
delay(300); // Loop runs every 300ms
}