#include <Arduino.h>
#include <DHT.h>
// ======================================================
// FarmTech Solutions - Fase 2
// ESP32 + Wokwi - Irrigação Inteligente para Goiaba
//
// Simulação didática:
// - N, P e K -> 3 botões (adequado=true / inadequado=false)
// - pH -> LDR (valor analógico mapeado para faixa de pH)
// - Umidade do solo -> DHT22 (proxy didático)
// - Bomba d'água -> Relé azul
// - Chuva prevista -> informada via Serial
// ======================================================
// ---------------------- PINOS ----------------------
#define DHTPIN 15
#define DHTTYPE DHT22
const int PIN_BTN_N = 18;
const int PIN_BTN_P = 19;
const int PIN_BTN_K = 21;
const int PIN_LDR = 34; // ADC
const int PIN_RELAY = 23;
// ---------------------- OBJETOS ----------------------
DHT dht(DHTPIN, DHTTYPE);
// ---------------------- VARIÁVEIS GLOBAIS ----------------------
float rainProbability = 0.0; // %
float rainMmNext12h = 0.0; // mm
bool relayState = false;
unsigned long lastRead = 0;
const unsigned long READ_INTERVAL = 2500;
// ---------------------- FUNÇÕES AUXILIARES ----------------------
float mapFloatValue(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float readPhFromLdr() {
int raw = analogRead(PIN_LDR); // 0 a 4095 no ESP32
float ph = mapFloatValue((float)raw, 0.0, 4095.0, 4.0, 8.0); // faixa didática
return ph;
}
void showHelp() {
Serial.println("[INFO] Comandos aceitos:");
Serial.println(" STATUS -> mostra ajuda");
Serial.println(" CHANCE=75 -> define probabilidade de chuva (%)");
Serial.println(" RAINMM=3.2 -> define chuva prevista em mm");
}
void processSerialCommands() {
if (!Serial.available()) {
return;
}
String cmd = Serial.readStringUntil('\n');
cmd.trim();
cmd.toUpperCase();
if (cmd == "STATUS") {
showHelp();
} else if (cmd.startsWith("CHANCE=")) {
rainProbability = cmd.substring(7).toFloat();
Serial.print("[OK] Probabilidade de chuva atualizada para: ");
Serial.print(rainProbability, 1);
Serial.println("%");
} else if (cmd.startsWith("RAINMM=")) {
rainMmNext12h = cmd.substring(7).toFloat();
Serial.print("[OK] Chuva prevista nas proximas 12h: ");
Serial.print(rainMmNext12h, 2);
Serial.println(" mm");
} else if (cmd.length() > 0) {
Serial.println("[ERRO] Comando nao reconhecido. Digite STATUS para ajuda.");
}
}
bool shouldIrrigate(bool nOk, bool pOk, bool kOk, float ph, float humidity) {
int nutrientScore = (nOk ? 1 : 0) + (pOk ? 1 : 0) + (kOk ? 1 : 0);
bool phIdeal = (ph >= 5.5 && ph <= 7.0);
bool rainForecast = (rainProbability >= 60.0) || (rainMmNext12h >= 2.0);
// Regra 1: solo muito seco -> liga se não houver chuva relevante prevista
if (humidity < 50.0 && !rainForecast) {
return true;
}
// Regra 2: solo moderadamente seco -> exige condições químicas mínimas
if (humidity >= 50.0 && humidity < 65.0 && !rainForecast && phIdeal && nutrientScore >= 2) {
return true;
}
return false;
}
String buildReason(bool nOk, bool pOk, bool kOk, float ph, float humidity) {
int nutrientScore = (nOk ? 1 : 0) + (pOk ? 1 : 0) + (kOk ? 1 : 0);
bool rainForecast = (rainProbability >= 60.0) || (rainMmNext12h >= 2.0);
String reason = "";
if (humidity < 50.0 && !rainForecast) {
reason += "Solo muito seco: irrigacao prioritaria. ";
} else if (humidity >= 50.0 && humidity < 65.0 && !rainForecast) {
reason += "Solo moderadamente seco. ";
} else if (humidity >= 65.0) {
reason += "Umidade suficiente. ";
}
if (rainForecast) {
reason += "Previsao de chuva ativa. ";
} else {
reason += "Sem chuva relevante prevista. ";
}
if (ph < 5.5 || ph > 7.0) {
reason += "pH fora da faixa de referencia. ";
} else {
reason += "pH em faixa aceitavel. ";
}
reason += "NPK adequados: ";
reason += String(nutrientScore);
reason += "/3. ";
reason += "N=" + String(nOk ? "OK" : "BAIXO");
reason += " P=" + String(pOk ? "OK" : "BAIXO");
reason += " K=" + String(kOk ? "OK" : "BAIXO");
return reason;
}
void printCsvLine(unsigned long ts, float humidity, float ph, bool nOk, bool pOk, bool kOk, float chance, float rainMm, bool pump) {
Serial.print("CSV,");
Serial.print(ts);
Serial.print(",");
Serial.print(humidity, 2);
Serial.print(",");
Serial.print(ph, 2);
Serial.print(",");
Serial.print(nOk ? 1 : 0);
Serial.print(",");
Serial.print(pOk ? 1 : 0);
Serial.print(",");
Serial.print(kOk ? 1 : 0);
Serial.print(",");
Serial.print(chance, 2);
Serial.print(",");
Serial.print(rainMm, 2);
Serial.print(",");
Serial.println(pump ? 1 : 0);
}
// ---------------------- SETUP ----------------------
void setup() {
Serial.begin(115200);
delay(500);
dht.begin();
pinMode(PIN_BTN_N, INPUT_PULLUP);
pinMode(PIN_BTN_P, INPUT_PULLUP);
pinMode(PIN_BTN_K, INPUT_PULLUP);
pinMode(PIN_RELAY, OUTPUT);
digitalWrite(PIN_RELAY, LOW);
Serial.println("========================================");
Serial.println("FarmTech Solutions - Fase 2");
Serial.println("Sistema de irrigacao inteligente - Goiaba");
Serial.println("Comandos Serial: CHANCE=75 | RAINMM=3.2 | STATUS");
Serial.println("Saida CSV: CSV,timestamp,humidity,ph,n,p,k,rainChance,rainMm,pump");
Serial.println("========================================");
}
// ---------------------- LOOP ----------------------
void loop() {
processSerialCommands();
if (millis() - lastRead < READ_INTERVAL) {
return;
}
lastRead = millis();
bool nOk = (digitalRead(PIN_BTN_N) == LOW);
bool pOk = (digitalRead(PIN_BTN_P) == LOW);
bool kOk = (digitalRead(PIN_BTN_K) == LOW);
float humidity = dht.readHumidity();
float ph = readPhFromLdr();
if (isnan(humidity)) {
Serial.println("Falha ao ler o DHT22.");
return;
}
relayState = shouldIrrigate(nOk, pOk, kOk, ph, humidity);
digitalWrite(PIN_RELAY, relayState ? HIGH : LOW);
String reason = buildReason(nOk, pOk, kOk, ph, humidity);
Serial.println();
Serial.println("----------- LEITURA ATUAL -----------");
Serial.print("Umidade (proxy solo / DHT22): ");
Serial.print(humidity, 2);
Serial.println(" %");
Serial.print("pH estimado (via LDR): ");
Serial.println(ph, 2);
Serial.print("N: ");
Serial.println(nOk ? "adequado" : "baixo");
Serial.print("P: ");
Serial.println(pOk ? "adequado" : "baixo");
Serial.print("K: ");
Serial.println(kOk ? "adequado" : "baixo");
Serial.print("Chance de chuva: ");
Serial.print(rainProbability, 1);
Serial.println(" %");
Serial.print("Chuva prox. 12h: ");
Serial.print(rainMmNext12h, 2);
Serial.println(" mm");
Serial.print("Bomba/Rele: ");
Serial.println(relayState ? "LIGADO" : "DESLIGADO");
Serial.print("Justificativa: ");
Serial.println(reason);
printCsvLine(millis(), humidity, ph, nOk, pOk, kOk, rainProbability, rainMmNext12h, relayState);
}