first commit

This commit is contained in:
Ghaida 2025-07-31 12:31:17 +07:00
commit 01772b32e9
2 changed files with 749 additions and 0 deletions

530
sketch_jul31a.ino Normal file
View File

@ -0,0 +1,530 @@
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <SoftwareSerial.h>
// Pin Definitions
#define DHT_PIN 2
#define DHT_TYPE DHT22
// Relay pins
#define RELAY_POMPA_A 3
#define RELAY_POMPA_B 4
#define RELAY_POMPA_PH_UP 5
#define RELAY_POMPA_PH_DOWN 6
#define RELAY_GROW_LIGHT 7
#define RELAY_AERATOR 8
#define RELAY_KIPAS 9
#define RELAY_FOGGER 10
// Sensor pins
#define PH_SENSOR_PIN A1
#define TDS_SENSOR_PIN A0
// *** TAMBAHAN UNTUK KOMUNIKASI ESP32 ***
#define ESP32_TX_PIN 1 // Pin TX Arduino (D1) -> RX ESP32 (GPIO 16)
#define ESP32_RX_PIN 0 // Pin RX Arduino (tidak digunakan dalam kasus ini)
// Gunakan Serial hardware untuk komunikasi ke ESP32
// SoftwareSerial esp32Serial(ESP32_RX_PIN, ESP32_TX_PIN); // Alternatif jika butuh software serial
// Constants moved to PROGMEM to save RAM
const float VOLTAGE_REF = 5.0;
// pH Calibration constants (dari kalibrasi 2 titik)
const float PH_SLOPE = -4.0787;
const float PH_OFFSET = 16.764;
// Timing constants
const unsigned long SENSOR_INTERVAL = 3000;
const unsigned long DHT_INTERVAL = 5000;
const unsigned long DISPLAY_INTERVAL = 1000;
const unsigned long DEBUG_INTERVAL = 5000;
const unsigned long ESP32_SEND_INTERVAL = 2000; // *** TAMBAHAN: Interval kirim data ke ESP32 ***
const unsigned long PUMP_DURATION = 5000;
const unsigned long GROW_LIGHT_ON_TIME = 43200000UL;
const unsigned long GROW_LIGHT_OFF_TIME = 43200000UL;
// Initialize components
DHT dht(DHT_PIN, DHT_TYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Sensor variables
float humidity = 0;
float temperature = 0;
float phValue = 0;
float tdsValue = 0;
int phRawADC = 0;
int tdsRawADC = 0;
// Timing variables
unsigned long lastSensorRead = 0;
unsigned long lastDHTRead = 0;
unsigned long lastDisplayUpdate = 0;
unsigned long lastDebugOutput = 0;
unsigned long lastDHTStatus = 0;
unsigned long lastESP32Send = 0; // *** TAMBAHAN: Timer untuk kirim data ***
unsigned long pumpStartTime = 0;
unsigned long growLightStartTime = 0;
// Control flags
struct {
bool pumpPhUpRunning : 1;
bool pumpPhDownRunning : 1;
bool growLightState : 1;
bool aeratorState : 1;
bool debugMode : 1;
bool dhtError : 1;
bool phError : 1;
bool tdsError : 1;
bool esp32CommError : 1; // *** TAMBAHAN: Flag error komunikasi ESP32 ***
} flags = {false, false, false, true, true, false, false, false, false};
// DHT22 monitoring variables
int dhtErrorCount = 0;
int dhtSuccessCount = 0;
void setup() {
Serial.begin(9600); // Untuk komunikasi dengan ESP32
Serial.println(F("=== HYDROPONIC SYSTEM STARTUP ==="));
// Initialize LCD
Wire.begin();
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print(F("Init LCD..."));
delay(1000);
// Initialize relay pins
pinMode(RELAY_POMPA_A, OUTPUT);
pinMode(RELAY_POMPA_B, OUTPUT);
pinMode(RELAY_POMPA_PH_UP, OUTPUT);
pinMode(RELAY_POMPA_PH_DOWN, OUTPUT);
pinMode(RELAY_GROW_LIGHT, OUTPUT);
pinMode(RELAY_AERATOR, OUTPUT);
pinMode(RELAY_KIPAS, OUTPUT);
pinMode(RELAY_FOGGER, OUTPUT);
// Set relay initial states (HIGH = OFF untuk relay aktif LOW)
digitalWrite(RELAY_POMPA_A, HIGH);
digitalWrite(RELAY_POMPA_B, HIGH);
digitalWrite(RELAY_POMPA_PH_UP, HIGH);
digitalWrite(RELAY_POMPA_PH_DOWN, HIGH);
digitalWrite(RELAY_GROW_LIGHT, HIGH);
digitalWrite(RELAY_AERATOR, HIGH);
digitalWrite(RELAY_KIPAS, HIGH);
digitalWrite(RELAY_FOGGER, HIGH);
lcd.clear();
lcd.print(F("Init Relays..."));
delay(1000);
// Initialize DHT sensor
lcd.clear();
lcd.print(F("Init DHT22..."));
dht.begin();
delay(2000);
// Test DHT22
bool dhtOK = false;
for (int attempt = 1; attempt <= 5; attempt++) {
float testTemp = dht.readTemperature();
float testHum = dht.readHumidity();
if (!isnan(testTemp) && !isnan(testHum) &&
testTemp > -40 && testTemp < 80 &&
testHum > 0 && testHum < 100) {
dhtOK = true;
temperature = testTemp;
humidity = testHum;
break;
}
delay(1000);
}
if (!dhtOK) {
flags.dhtError = true;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("DHT22 ERROR!"));
lcd.setCursor(0, 1);
lcd.print(F("Check wiring"));
delay(3000);
} else {
flags.dhtError = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("DHT22 OK!"));
delay(2000);
}
// Nyalakan aerator
if (!flags.dhtError) {
digitalWrite(RELAY_AERATOR, LOW);
flags.aeratorState = true;
}
// Initialize timers
growLightStartTime = millis();
lastSensorRead = millis();
lastDHTRead = millis();
lastDisplayUpdate = millis();
lastDebugOutput = millis();
lastDHTStatus = millis();
lastESP32Send = millis(); // *** TAMBAHAN ***
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("System Ready!"));
delay(2000);
lcd.clear();
// *** TAMBAHAN: Test komunikasi ESP32 ***
Serial.println(F("Testing ESP32 communication..."));
delay(1000);
}
void loop() {
unsigned long currentTime = millis();
// Read sensors
if (currentTime - lastSensorRead >= SENSOR_INTERVAL) {
readOtherSensors();
lastSensorRead = currentTime;
}
if (currentTime - lastDHTRead >= DHT_INTERVAL) {
readDHTSensor();
lastDHTRead = currentTime;
}
// *** TAMBAHAN: Kirim data ke ESP32 ***
if (currentTime - lastESP32Send >= ESP32_SEND_INTERVAL) {
sendDataToESP32();
lastESP32Send = currentTime;
}
// Control systems
controlHumidity();
controlPH();
controlTDS();
controlGrowLight();
controlAerator();
controlPumps();
// Update display
if (currentTime - lastDisplayUpdate >= DISPLAY_INTERVAL) {
updateDisplay();
lastDisplayUpdate = currentTime;
}
// Debug output (comment out jika tidak perlu untuk mengurangi interference)
// if (flags.debugMode && (currentTime - lastDebugOutput >= DEBUG_INTERVAL)) {
// debugOutput();
// lastDebugOutput = currentTime;
// }
// Monitor DHT22 health
monitorDHT();
delay(50);
}
// *** FUNGSI BARU: Kirim data ke ESP32 ***
void sendDataToESP32() {
// Format: temperature,humidity,pH,TDS
// Pastikan data valid sebelum dikirim
float tempToSend = flags.dhtError ? -999 : temperature;
float humToSend = flags.dhtError ? -999 : humidity;
float phToSend = flags.phError ? -999 : phValue;
float tdsToSend = flags.tdsError ? -999 : tdsValue;
// Kirim data dengan format CSV
Serial.print(tempToSend, 2);
Serial.print(",");
Serial.print(humToSend, 2);
Serial.print(",");
Serial.print(phToSend, 2);
Serial.print(",");
Serial.print(tdsToSend, 0);
Serial.println(); // Newline sebagai delimiter
// Optional: Indikator di LCD bahwa data terkirim
// lcd.setCursor(15, 0);
// lcd.print(">");
}
// Fungsi baca DHT22 yang diperbaiki
void readDHTSensor() {
bool aeratorWasOn = (digitalRead(RELAY_AERATOR) == LOW);
bool kipasWasOn = (digitalRead(RELAY_KIPAS) == LOW);
if (aeratorWasOn) {
digitalWrite(RELAY_AERATOR, HIGH);
}
if (kipasWasOn) {
digitalWrite(RELAY_KIPAS, HIGH);
}
delay(100);
float h = NAN, t = NAN;
int retries = 0;
const int maxRetries = 3;
while (retries < maxRetries) {
h = dht.readHumidity();
t = dht.readTemperature();
if (!isnan(h) && !isnan(t) &&
t > -40 && t < 80 &&
h > 0 && h < 100) {
break;
}
retries++;
if (retries < maxRetries) {
delay(500);
}
}
if (aeratorWasOn) {
digitalWrite(RELAY_AERATOR, LOW);
}
if (kipasWasOn) {
digitalWrite(RELAY_KIPAS, LOW);
}
if (retries >= maxRetries || isnan(h) || isnan(t)) {
flags.dhtError = true;
dhtErrorCount++;
} else {
flags.dhtError = false;
humidity = h;
temperature = t;
dhtSuccessCount++;
dhtErrorCount = 0;
}
}
void readOtherSensors() {
phValue = readPH();
flags.phError = (phValue < 0 || phValue > 14);
tdsValue = readTDS();
flags.tdsError = (tdsValue < 0);
// DEBUG: Tampilkan nilai TDS ke Serial Monitor
Serial.print(F("[DEBUG] TDS Value: "));
Serial.print(tdsValue);
Serial.println(F(" ppm"));
}
float getAverageVoltage(int pin, int samples = 20) {
long total = 0;
for (int i = 0; i < samples; i++) {
total += analogRead(pin);
delay(5);
}
float avgADC = total / (float)samples;
float voltage = avgADC * (5.0 / 1023.0);
return voltage;
}
float readPH() {
float voltage = getAverageVoltage(PH_SENSOR_PIN, 20);
if (voltage < 0.1 || voltage > 4.9) {
return -1;
}
float esp32_equivalent_voltage = voltage * (3.3/5.0) * (4095.0/1023.0);
float ph = PH_SLOPE * esp32_equivalent_voltage + PH_OFFSET;
if (ph < 0) ph = 0;
if (ph > 14) ph = 14;
return ph;
}
float readTDS() {
int buffer_arr[10];
for (int i = 0; i < 10; i++) {
buffer_arr[i] = analogRead(TDS_SENSOR_PIN);
delay(30);
}
// Urutkan nilai buffer
for (int i = 0; i < 9; i++) {
for (int j = i + 1; j < 10; j++) {
if (buffer_arr[i] > buffer_arr[j]) {
int temp = buffer_arr[i];
buffer_arr[i] = buffer_arr[j];
buffer_arr[j] = temp;
}
}
}
// Hitung rata-rata dari nilai tengah (tanpa outlier)
int avgval = 0;
for (int i = 2; i < 8; i++) {
avgval += buffer_arr[i];
}
float voltage = (float)avgval * 5.0 / 1023.0 / 6.0;
// Konversi tegangan ke TDS dengan rumus kalibrasi dan faktor
float calibrationFactor = 0.71;
float tds = (133.42 * pow(voltage, 3)
- 255.86 * pow(voltage, 2)
+ 857.39 * voltage) * 0.5;
tds *= calibrationFactor;
if (tds < 0 || tds > 2000) {
return -1;
}
return tds;
}
void controlHumidity() {
if (flags.dhtError) {
digitalWrite(RELAY_FOGGER, HIGH);
digitalWrite(RELAY_KIPAS, HIGH);
return;
}
if (humidity < 85.0) {
digitalWrite(RELAY_FOGGER, LOW);
digitalWrite(RELAY_KIPAS, HIGH);
} else if (humidity > 95.0) {
digitalWrite(RELAY_FOGGER, HIGH);
digitalWrite(RELAY_KIPAS, LOW);
} else {
digitalWrite(RELAY_FOGGER, HIGH);
digitalWrite(RELAY_KIPAS, HIGH);
}
}
void controlPH() {
if (flags.phError) return;
if (phValue > 6.5 && !flags.pumpPhDownRunning && !flags.pumpPhUpRunning) {
digitalWrite(RELAY_POMPA_PH_DOWN, LOW);
flags.pumpPhDownRunning = true;
pumpStartTime = millis();
} else if (phValue < 5.0 && !flags.pumpPhUpRunning && !flags.pumpPhDownRunning) {
digitalWrite(RELAY_POMPA_PH_UP, LOW);
flags.pumpPhUpRunning = true;
pumpStartTime = millis();
}
}
void controlTDS() {
if (flags.tdsError) {
digitalWrite(RELAY_POMPA_A, HIGH);
digitalWrite(RELAY_POMPA_B, HIGH);
return;
}
if (tdsValue < 1000.0) {
digitalWrite(RELAY_POMPA_A, LOW);
digitalWrite(RELAY_POMPA_B, LOW);
} else {
digitalWrite(RELAY_POMPA_A, HIGH);
digitalWrite(RELAY_POMPA_B, HIGH);
}
}
void controlGrowLight() {
unsigned long currentTime = millis();
unsigned long elapsedTime = currentTime - growLightStartTime;
if (!flags.growLightState && elapsedTime >= GROW_LIGHT_OFF_TIME) {
digitalWrite(RELAY_GROW_LIGHT, LOW);
flags.growLightState = true;
growLightStartTime = currentTime;
} else if (flags.growLightState && elapsedTime >= GROW_LIGHT_ON_TIME) {
digitalWrite(RELAY_GROW_LIGHT, HIGH);
flags.growLightState = false;
growLightStartTime = currentTime;
}
}
void controlAerator() {
if (flags.aeratorState && !flags.dhtError) {
digitalWrite(RELAY_AERATOR, LOW);
} else {
digitalWrite(RELAY_AERATOR, HIGH);
}
}
void controlPumps() {
unsigned long currentTime = millis();
if (flags.pumpPhUpRunning && (currentTime - pumpStartTime >= PUMP_DURATION)) {
digitalWrite(RELAY_POMPA_PH_UP, HIGH);
flags.pumpPhUpRunning = false;
}
if (flags.pumpPhDownRunning && (currentTime - pumpStartTime >= PUMP_DURATION)) {
digitalWrite(RELAY_POMPA_PH_DOWN, HIGH);
flags.pumpPhDownRunning = false;
}
}
void updateDisplay() {
lcd.clear();
lcd.setCursor(0, 0);
if (flags.dhtError) {
lcd.print(F("T:ERR H:ERR"));
} else {
lcd.print(F("T:"));
lcd.print(temperature, 1);
lcd.print(F("C H:"));
lcd.print(humidity, 1);
lcd.print(F("%"));
}
lcd.setCursor(0, 1);
if (flags.phError) {
lcd.print(F("pH:ERR "));
} else {
lcd.print(F("pH:"));
lcd.print(phValue, 1);
lcd.print(F(" "));
}
if (flags.tdsError) {
lcd.print(F("TDS:ERR"));
} else {
lcd.print(F("TDS:"));
lcd.print(tdsValue, 0);
}
}
void monitorDHT() {
static unsigned long lastDHTCheck = 0;
if (millis() - lastDHTCheck >= 30000) {
if (flags.dhtError && dhtErrorCount >= 5) {
dht.begin();
delay(2000);
float testT = dht.readTemperature();
float testH = dht.readHumidity();
if (!isnan(testT) && !isnan(testH)) {
flags.dhtError = false;
dhtErrorCount = 0;
temperature = testT;
humidity = testH;
}
}
lastDHTCheck = millis();
}
}

View File

@ -0,0 +1,219 @@
#include <WiFi.h>
#include <FirebaseESP32.h> // Gunakan library "Firebase ESP32 Client" by Mobizt
#define RXD2 16 // RX pin ESP32
#define TXD2 17 // TX pin ESP32
// WiFi credentials
#define WIFI_SSID "hydrop"
#define WIFI_PASSWORD "123123123"
// Firebase config
#define FIREBASE_HOST "https://hydrop-a1e64-default-rtdb.firebaseio.com"
#define FIREBASE_AUTH "4lJpDHPo9LGh6WCYpjs1aRB6ABJZwHLOXkaCoLAV"
// Firebase objects
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
struct SensorData {
float temperature;
float humidity;
float ph;
float tds;
bool tempValid;
bool humValid;
bool phValid;
bool tdsValid;
unsigned long lastUpdate;
} sensorData;
struct CommStats {
unsigned long totalReceived;
unsigned long validData;
unsigned long errorData;
unsigned long lastReceived;
} commStats = {0, 0, 0, 0};
const unsigned long DATA_TIMEOUT = 10000;
const unsigned long STATS_INTERVAL = 30000;
void setup() {
Serial.begin(115200);
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
// Koneksi WiFi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("\nWiFi connected!");
// Konfigurasi Firebase
config.database_url = FIREBASE_HOST;
config.signer.tokens.legacy_token = FIREBASE_AUTH;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
Serial.println("Firebase initialized.");
sensorData = {0, 0, 0, 0, false, false, false, false, 0};
Serial.println("=== ESP32 Hydroponic Receiver ===");
Serial.println("Menunggu data dari Arduino...");
}
void loop() {
if (Serial2.available()) {
String rawData = Serial2.readStringUntil('\n');
rawData.trim();
commStats.totalReceived++;
commStats.lastReceived = millis();
if (parseAndValidateData(rawData)) {
commStats.validData++;
sensorData.lastUpdate = millis();
displaySensorData();
sendToFirebase();
} else {
commStats.errorData++;
Serial.print("ERROR: Invalid data format: ");
Serial.println(rawData);
}
}
checkDataTimeout();
displayStats();
delay(100);
}
bool parseAndValidateData(String data) {
if (data.length() < 5) return false;
int idx1 = data.indexOf(',');
int idx2 = data.indexOf(',', idx1 + 1);
int idx3 = data.indexOf(',', idx2 + 1);
if (idx1 <= 0 || idx2 <= 0 || idx3 <= 0) return false;
String tempStr = data.substring(0, idx1);
String humStr = data.substring(idx1 + 1, idx2);
String phStr = data.substring(idx2 + 1, idx3);
String tdsStr = data.substring(idx3 + 1);
float temp = tempStr.toFloat();
float hum = humStr.toFloat();
float ph = phStr.toFloat();
float tds = tdsStr.toFloat();
sensorData.temperature = temp;
sensorData.humidity = hum;
sensorData.ph = ph;
sensorData.tds = tds;
sensorData.tempValid = validateTemperature(temp);
sensorData.humValid = validateHumidity(hum);
sensorData.phValid = validatePH(ph);
sensorData.tdsValid = validateTDS(tds);
return (sensorData.tempValid || sensorData.humValid || sensorData.phValid || sensorData.tdsValid);
}
void sendToFirebase() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi disconnected, skipping Firebase upload.");
return;
}
FirebaseJson json;
json.set("temperature", sensorData.tempValid ? sensorData.temperature : -999);
json.set("humidity", sensorData.humValid ? sensorData.humidity : -999);
json.set("ph", sensorData.phValid ? sensorData.ph : -999);
json.set("tds", sensorData.tdsValid ? sensorData.tds : -999);
json.set("timestamp", millis());
if (Firebase.updateNode(fbdo, "/sensorData", json)) {
Serial.println("Data sent to Firebase.");
} else {
Serial.print("Firebase failed: ");
Serial.println(fbdo.errorReason());
}
}
bool validateTemperature(float temp) {
return temp != -999 && temp >= -10 && temp <= 60;
}
bool validateHumidity(float hum) {
return hum != -999 && hum >= 0 && hum <= 100;
}
bool validatePH(float ph) {
return ph != -999 && ph >= 0 && ph <= 14;
}
bool validateTDS(float tds) {
return tds != -999 && tds >= 0 && tds <= 3000;
}
void displaySensorData() {
Serial.println("=== SENSOR DATA RECEIVED ===");
Serial.print("Temperature: ");
Serial.println(sensorData.tempValid ? String(sensorData.temperature, 2) + " °C" : "INVALID");
Serial.print("Humidity: ");
Serial.println(sensorData.humValid ? String(sensorData.humidity, 2) + " %" : "INVALID");
Serial.print("pH: ");
Serial.println(sensorData.phValid ? String(sensorData.ph, 2) : "INVALID");
Serial.print("TDS: ");
Serial.println(sensorData.tdsValid ? String(sensorData.tds, 0) + " ppm" : "INVALID");
Serial.println("===========================");
}
void checkDataTimeout() {
static unsigned long lastTimeoutCheck = 0;
if (millis() - lastTimeoutCheck >= 5000) {
if (sensorData.lastUpdate > 0 && (millis() - sensorData.lastUpdate) > DATA_TIMEOUT) {
Serial.println("⚠️ WARNING: No valid data for 10 seconds!");
sensorData.tempValid = false;
sensorData.humValid = false;
sensorData.phValid = false;
sensorData.tdsValid = false;
}
lastTimeoutCheck = millis();
}
}
void displayStats() {
static unsigned long lastStatsDisplay = 0;
if (millis() - lastStatsDisplay >= STATS_INTERVAL) {
Serial.println("=== STATISTIK KOMUNIKASI ===");
Serial.print("Total diterima: ");
Serial.println(commStats.totalReceived);
Serial.print("Valid: ");
Serial.println(commStats.validData);
Serial.print("Error: ");
Serial.println(commStats.errorData);
if (commStats.totalReceived > 0) {
float success = (float)commStats.validData / commStats.totalReceived * 100;
Serial.print("Success rate: ");
Serial.print(success, 1);
Serial.println(" %");
}
Serial.print("Terakhir diterima: ");
Serial.println((millis() - commStats.lastReceived) / 1000);
Serial.println("=============================");
lastStatsDisplay = millis();
}
}