// MODUL 4 - Smart Home + Water Level Monitoring
// Platform: ESP32 | Simulator: Wokwi
// =======================================================
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <ArduinoJson.h>
// ---- PIN DEFINITIONS ----
#define DHT_PIN 33 // Sesuai modifikasimu
#define LDR_PIN 34
#define PIR_PIN 15
#define BUZZER_PIN 14
#define LED_AC 26
#define LED_LAMP 27
#define LED_ALARM 25
// PIN HC-SR04 (Tugas 36)
#define TRIG_PIN 5
#define ECHO_PIN 18
#define DHT_TYPE DHT22
// ---- THRESHOLDS ----
#define TEMP_THRESHOLD 28.0
#define LIGHT_THRESHOLD 2000 // Sudah diperbaiki logikanya
#define TANK_DEPTH 400 // cm (Jarak saat tangki kosong)
#define TANK_FULL 10 // cm (Jarak batas air penuh)
// ---- NETWORK CONFIG ----
const char* SSID = "Wokwi-GUEST";
const char* PASSWORD = "";
const char* BROKER = "broker.hivemq.com";
const char* BASE_TOPIC = "smarthome/lab/2330205030056/";
// ---- OBJECTS ----
DHT dht(DHT_PIN, DHT_TYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2);
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
// ---- SYSTEM STATE ----
struct SmartHomeState {
float temperature = 0;
float humidity = 0;
int lightLevel = 0;
bool motionDetected = false;
float waterDistance = 0;
int waterPercentage = 0; // Kapasitas air dalam %
bool acActive = false;
bool lampActive = false;
bool alarmActive = false;
bool awayMode = false;
} state;
unsigned long lastSensorRead = 0;
unsigned long lastMQTTPublish = 0;
unsigned long lastLCDUpdate = 0;
// ---- MQTT CALLBACK ----
void mqttCallback(char* topic, byte* payload, unsigned int len) {
String msg = "";
String t = String(topic);
for (int i = 0; i < len; i++) msg += (char)payload[i];
Serial.println("[IN] " + t + ": " + msg);
StaticJsonDocument<128> doc;
if (deserializeJson(doc, msg)) return;
if (t.endsWith("control")) {
if (doc.containsKey("ac")) state.acActive = (String(doc["ac"].as<String>()) == "on");
if (doc.containsKey("lamp")) state.lampActive = (String(doc["lamp"].as<String>()) == "on");
if (doc.containsKey("alarm")) state.alarmActive = (String(doc["alarm"].as<String>()) == "on");
if (doc.containsKey("mode")) state.awayMode = (String(doc["mode"].as<String>()) == "away");
}
}
// ---- SENSOR READING ----
void readSensors() {
// DHT & Cahaya & Gerak
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t) && !isnan(h)) {
state.temperature = t;
state.humidity = h;
}
state.lightLevel = analogRead(LDR_PIN);
state.motionDetected = digitalRead(PIR_PIN);
// HC-SR04 Reading
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000);
if (duration > 0) {
state.waterDistance = duration * 0.034 / 2;
// Ubah jarak cm menjadi persentase 0-100%
state.waterPercentage = map(state.waterDistance, TANK_DEPTH, TANK_FULL, 0, 100);
state.waterPercentage = constrain(state.waterPercentage, 0, 100); // Kunci di 0-100
}
}
// ---- AUTOMATION LOGIC ----
void runAutomation() {
state.acActive = (state.temperature > TEMP_THRESHOLD);
state.lampActive = (state.lightLevel > LIGHT_THRESHOLD);
if (state.awayMode && state.motionDetected) {
state.alarmActive = true;
}
if (state.waterPercentage >= 95) { // Jika air mencapai 95% atau lebih
state.alarmActive = true;
Serial.println("[ALARM] Toren Air Hampir Penuh!");
}
digitalWrite(LED_AC, state.acActive ? HIGH : LOW);
digitalWrite(LED_LAMP, state.lampActive ? HIGH : LOW);
digitalWrite(LED_ALARM, state.alarmActive ? HIGH : LOW);
if (state.alarmActive) tone(BUZZER_PIN, 2000, 100);
else noTone(BUZZER_PIN);
}
// ---- LCD DISPLAY ----
void updateLCD() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("T:"); lcd.print(state.temperature, 1);
lcd.print("C ");
lcd.print("Air:"); lcd.print(state.waterPercentage); lcd.print("%");
lcd.setCursor(0, 1);
lcd.print(state.acActive ? "AC:ON " : "AC:OFF ");
lcd.print(state.lampActive ? "L:ON " : "L:OFF ");
lcd.print(state.awayMode ? "[AWY]" : "[HOM]");
}
// ---- MQTT PUBLISH ----
void publishData() {
StaticJsonDocument<300> doc;
doc["temperature"] = state.temperature;
doc["humidity"] = state.humidity;
doc["light"] = state.lightLevel;
doc["motion"] = state.motionDetected;
doc["water_level"] = state.waterPercentage; // Data baru dikirim!
doc["ac"] = state.acActive ? "on" : "off";
doc["lamp"] = state.lampActive ? "on" : "off";
doc["alarm"] = state.alarmActive ? "on" : "off";
doc["mode"] = state.awayMode ? "away" : "home";
char payload[300];
serializeJson(doc, payload);
String topic = String(BASE_TOPIC) + "status";
mqtt.publish(topic.c_str(), payload, true);
}
// ---- SETUP ----
void setup() {
Serial.begin(115200);
dht.begin();
pinMode(PIR_PIN, INPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_AC, OUTPUT);
pinMode(LED_LAMP, OUTPUT);
pinMode(LED_ALARM, OUTPUT);
lcd.init();
lcd.backlight();
lcd.print("System Booting..");
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) { delay(500); }
mqtt.setServer(BROKER, 1883);
mqtt.setCallback(mqttCallback);
}
// ---- MAIN LOOP ----
void loop() {
if (!mqtt.connected()) {
String clientId = "ESP32-" + String(random(0xffff), HEX);
if (mqtt.connect(clientId.c_str())) {
mqtt.subscribe((String(BASE_TOPIC) + "control").c_str());
}
}
mqtt.loop();
unsigned long now = millis();
if (now - lastSensorRead > 2000) {
readSensors();
runAutomation();
lastSensorRead = now;
}
if (now - lastLCDUpdate > 1000) {
updateLCD();
lastLCDUpdate = now;
}
if (now - lastMQTTPublish > 6000) { // Delay 6 detik sesuai permintaanmu
publishData();
lastMQTTPublish = now;
}
}