first commit
This commit is contained in:
commit
01772b32e9
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue