#include #include #include #include #include #include #include #define SDA1 21 #define SCL1 22 #define SDA2 16 #define SCL2 17 #define RELAY_KIPAS 27 #define RELAY_POMPA 26 const char* ssid = "Oppo12"; const char* password = "12345678"; #define BOT_TOKEN "8628991486:AAEWwreXf8lXAFrb0fokQcrS_pqY6-C19_o" #define CHAT_ID "6963743941" #define FIREBASE_HOST "database-ta-cbb47-default-rtdb.asia-southeast1.firebasedatabase.app" #define FIREBASE_AUTH "hCpBMPxOKsHLVv5Unyfd5YikGtFy11Z01Olx7477" #define SUHU_ON 28.0 #define SUHU_OFF 27.5 #define RH_ON 80.0 #define RH_OFF 90.0 #define MS_BACA 2000 #define MS_PERINTAH 1000 #define MS_UPDATE 30000 #define MS_FIREBASE 25000 #define MS_KIRIM 3000 #define QUEUE_SIZE 5 String msgQueue[QUEUE_SIZE]; int qHead=0, qTail=0, qCount=0; void queuePush(String msg) { if (qCount >= QUEUE_SIZE) { qHead=(qHead+1)%QUEUE_SIZE; qCount--; } msgQueue[qTail] = msg; qTail = (qTail+1) % QUEUE_SIZE; qCount++; Serial.printf("[QUEUE] Antrian: %d\n", qCount); } bool queuePop(String &msg) { if (qCount==0) return false; msg = msgQueue[qHead]; qHead = (qHead+1) % QUEUE_SIZE; qCount--; return true; } FirebaseData fbData; FirebaseConfig fbConfig; FirebaseAuth fbAuth; Adafruit_AHTX0 sensor1; Adafruit_AHTX0 sensor2; bool kipasState=false, pompaState=false; bool prevKipas=false, prevPompa=false; bool modeManual=false; bool sensor1_ok=false, sensor2_ok=false; float suhu1=0,rh1=0,suhu2=0,rh2=0,avgSuhu=0,avgRH=0; unsigned long lastBaca=0,lastPerintah=0,lastUpdate=0; unsigned long lastFirebase=0,lastKirim=0,dataCount=0; long lastMsgId = 0; void setKipas(bool nyala) { kipasState = nyala; digitalWrite(RELAY_KIPAS, nyala ? LOW : HIGH); Serial.printf("[RELAY] Kipas %s (GPIO%d)\n", nyala?"ON":"OFF", RELAY_KIPAS); } void setPompa(bool nyala) { pompaState = nyala; digitalWrite(RELAY_POMPA, nyala ? LOW : HIGH); Serial.printf("[RELAY] Pompa %s (GPIO%d)\n", nyala?"ON":"OFF", RELAY_POMPA); } String getKondisi() { if (avgSuhu>SUHU_ON && avgRHSUHU_ON) return "Suhu Tinggi"; if (avgRH 0; Serial.printf("[TELEGRAM] %s (%s)\n", ok?"OK":"GAGAL", statusLine.substring(9,12).c_str()); client.stop(); return ok; } void prosesPerintah() { if (WiFi.status() != WL_CONNECTED) return; String url = "/bot" + String(BOT_TOKEN) + "/getUpdates?offset=" + String(lastMsgId+1) + "&limit=5&timeout=1"; WiFiClientSecure client; client.setInsecure(); client.setTimeout(10000); if (!client.connect("api.telegram.org", 443)) { Serial.println("[BOT] Gagal konek getUpdates"); return; } client.printf("GET %s HTTP/1.1\r\n", url.c_str()); client.println("Host: api.telegram.org"); client.println("Connection: close"); client.println(); unsigned long t = millis(); while (client.connected() && millis()-t < 8000) { String line = client.readStringUntil('\n'); if (line == "\r") break; } String json = ""; t = millis(); while (client.available() && millis()-t < 5000) { json += client.readString(); } client.stop(); if (json.length() < 10) return; DynamicJsonDocument doc(2048); if (deserializeJson(doc, json)) { Serial.println("[BOT] JSON error"); return; } JsonArray results = doc["result"].as(); for (JsonObject upd : results) { lastMsgId = upd["update_id"].as(); String chat_id = upd["message"]["chat"]["id"].as(); String teks = upd["message"]["text"].as(); if (chat_id != CHAT_ID) continue; Serial.printf("[BOT] Perintah: %s\n", teks.c_str()); if (teks=="/start"||teks=="/status") queuePush(formatStatus()); else if (teks=="/help") queuePush(formatHelp()); else if (teks=="/kipason") { modeManual=true; setKipas(true); queuePush("Mode Manual\nKipas DC ON"); } else if (teks=="/kipasoff") { modeManual=true; setKipas(false); queuePush("Mode Manual\nKipas DC OFF"); } else if (teks=="/pompaon") { modeManual=true; setPompa(true); queuePush("Mode Manual\nPompa Air ON"); } else if (teks=="/pompaoff") { modeManual=true; setPompa(false); queuePush("Mode Manual\nPompa Air OFF"); } else if (teks=="/auto") { modeManual=false; queuePush("Mode Otomatis Aktif"); } else queuePush("Perintah tidak dikenal."); } } void bacaSensor() { sensors_event_t hum, tmp; if (sensor1_ok) { sensor1.getEvent(&hum, &tmp); if (tmp.temperature>-22 &&tmp.temperature<28&& hum.relative_humidity>=80&&hum.relative_humidity<90) { suhu1=tmp.temperature; rh1=hum.relative_humidity; } else { Serial.println("[SENSOR1] Nilai tidak valid, reinit..."); sensor1_ok = sensor1.begin(&Wire); } } if (sensor2_ok) { sensor2.getEvent(&hum, &tmp); if (tmp.temperature>-22&&tmp.temperature<28&& hum.relative_humidity>=80&&hum.relative_humidity<90) { suhu2=tmp.temperature; rh2=hum.relative_humidity; } else { Serial.println("[SENSOR2] Nilai tidak valid, reinit..."); Wire1.begin(SDA2, SCL2, 400000); sensor2_ok = sensor2.begin(&Wire1); } } if (sensor1_ok&&sensor2_ok) { avgSuhu=(suhu1+suhu2)/2.0; avgRH=(rh1+rh2)/2.0; } else if (sensor1_ok) { avgSuhu=suhu1; avgRH=rh1; } else if (sensor2_ok) { avgSuhu=suhu2; avgRH=rh2; } } void kendaliRelay() { if (modeManual) return; prevKipas=kipasState; prevPompa=pompaState; if (avgSuhu > SUHU_ON) setKipas(true); else if (avgSuhu < SUHU_OFF) setKipas(false); if (avgRH < RH_ON) setPompa(true); else if (avgRH > RH_OFF) setPompa(false); if (kipasState != prevKipas) { String notif = ""; if (kipasState) { notif += "\xF0\x9F\x92\xA7 Suhu berubah : " + String(avgSuhu,1) + " C\n\n"; } notif += formatStatus(); queuePush(notif); } if (pompaState != prevPompa) { String notif = ""; notif += "\xF0\x9F\x92\xA7 RH berubah : " + String(avgRH,1) + " %\n\n"; notif += formatStatus(); queuePush(notif); } } void kirimFirebase() { if (!Firebase.ready()) { Serial.println("[FIREBASE] Tidak siap"); return; } Firebase.setFloat(fbData, "/kumbung/realtime/sensor1/suhu", suhu1); Firebase.setFloat(fbData, "/kumbung/realtime/sensor1/rh", rh1); Firebase.setFloat(fbData, "/kumbung/realtime/sensor2/suhu", suhu2); Firebase.setFloat(fbData, "/kumbung/realtime/sensor2/rh", rh2); Firebase.setFloat(fbData, "/kumbung/realtime/avg/suhu", avgSuhu); Firebase.setFloat(fbData, "/kumbung/realtime/avg/rh", avgRH); Firebase.setBool(fbData, "/kumbung/realtime/relay/kipas", kipasState); Firebase.setBool(fbData, "/kumbung/realtime/relay/pompa", pompaState); Firebase.setString(fbData,"/kumbung/realtime/kondisi", getKondisi()); dataCount++; char path[50]; sprintf(path,"/kumbung/history/data_%04lu",dataCount); FirebaseJson json; json.set("s1t",suhu1); json.set("s1rh",rh1); json.set("s2t",suhu2); json.set("s2rh",rh2); json.set("avgt",avgSuhu); json.set("avgrh",avgRH); json.set("kipas",kipasState?"ON":"OFF"); json.set("pompa",pompaState?"ON":"OFF"); json.set("kondisi",getKondisi()); Firebase.setJSON(fbData, path, json) ? Serial.printf("[FIREBASE] OK -> %s\n", path) : Serial.printf("[FIREBASE] Gagal: %s\n", fbData.errorReason().c_str()); } void setup() { Serial.begin(115200); delay(1000); Serial.println("\n=== MONITORING JAMUR TIRAM v7.3 FIX (ESP32) ===\n"); Serial.println("[INFO] RELAY_KIPAS = GPIO" + String(RELAY_KIPAS)); Serial.println("[INFO] RELAY_POMPA = GPIO" + String(RELAY_POMPA)); pinMode(RELAY_KIPAS, OUTPUT); digitalWrite(RELAY_KIPAS, HIGH); pinMode(RELAY_POMPA, OUTPUT); digitalWrite(RELAY_POMPA, HIGH); Serial.println("[OK] Relay OFF"); Wire.begin(SDA1, SCL1); delay(200); sensor1_ok = sensor1.begin(&Wire); Serial.println(sensor1_ok?"[OK] Sensor 1 (Wire GPIO21/22)":"[GAGAL] Sensor 1"); Wire1.begin(SDA2, SCL2, 400000); delay(200); sensor2_ok = sensor2.begin(&Wire1); if (sensor2_ok) { Wire1.begin(SDA2, SCL2, 400000); Serial.println("[OK] Sensor 2 (Wire1 GPIO16/17)"); } else { Serial.println("[GAGAL] Sensor 2"); } WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.printf("[INFO] WiFi: %s\n", ssid); int r=0; while (WiFi.status()!=WL_CONNECTED && r<40) { delay(500); Serial.print("."); r++; } Serial.println(WiFi.status()==WL_CONNECTED ? "\n[OK] WiFi: " + WiFi.localIP().toString() : "\n[GAGAL] WiFi!"); fbConfig.host = FIREBASE_HOST; fbConfig.signer.tokens.legacy_token = FIREBASE_AUTH; Firebase.begin(&fbConfig, &fbAuth); Firebase.reconnectWiFi(true); fbData.setResponseSize(2048); Serial.println(Firebase.ready()?"[OK] Firebase":"[GAGAL] Firebase"); delay(500); bacaSensor(); kendaliRelay(); String boot = "\xE2\x9C\x85 ESP32 Monitoring Jamur Online\n\n"; boot += "WiFi : " + String(ssid) + "\n"; boot += "Sensor 1 : " + String(sensor1_ok?"OK":"Gagal") + "\n"; boot += "Sensor 2 : " + String(sensor2_ok?"OK":"Gagal") + "\n"; boot += "Firebase : " + String(Firebase.ready()?"OK":"Gagal") + "\n\n"; boot += formatStatus(); queuePush(boot); lastBaca=lastPerintah=lastUpdate=lastFirebase=lastKirim=millis(); Serial.println("\n[INFO] Sistem siap!\n"); } void loop() { unsigned long now = millis(); if (now - lastKirim >= MS_KIRIM && qCount > 0) { lastKirim = now; String msg; if (queuePop(msg)) { bool ok = kirimTelegram(msg); if (!ok) { queuePush(msg); lastKirim = now + 10000; Serial.println("[TELEGRAM] Coba lagi 10 detik lagi"); } } } if (now - lastBaca >= MS_BACA) { lastBaca = now; bacaSensor(); kendaliRelay(); Serial.printf("[S] S1:%.1fC %.1f%% S2:%.1fC %.1f%% AVG:%.1fC %.1f%% K:%s P:%s\n", suhu1,rh1,suhu2,rh2,avgSuhu,avgRH, kipasState?"ON":"OFF",pompaState?"ON":"OFF"); } if (now - lastPerintah >= MS_PERINTAH && qCount == 0) { lastPerintah = now; prosesPerintah(); } if (now - lastUpdate >= MS_UPDATE) { lastUpdate = now; Serial.println("[INFO] Update otomatis -> queue"); queuePush(formatStatus()); } if (now - lastFirebase >= MS_FIREBASE && qCount == 0) { lastFirebase = now; kirimFirebase(); } if (WiFi.status() != WL_CONNECTED) { Serial.println("[WARNING] WiFi putus!"); WiFi.disconnect(); delay(1000); WiFi.begin(ssid, password); int r=0; while (WiFi.status()!=WL_CONNECTED && r<20) { delay(500); r++; } Serial.println(WiFi.status()==WL_CONNECTED?"[OK] Reconnect!":"[GAGAL] Reconnect!"); } yield(); }