#include "DHT.h"
#include <WiFi.h>
#include <PubSubClient.h>
// =============================
// CONFIGURACIÓN DE PINES
// =============================
#define DHTPIN 25
#define DHTTYPE DHT22
#define SOIL_PIN 34
#define RELAY_PIN 0
#define LED_PIN 2
#define PIN_BUTTON 27
#define PIN_BUTTON_MANUAL 26
#define POT_EVAPO 32
#define POT_CALOR 33
// =============================
// OBJETOS Y VARIABLES
// =============================
DHT dht(DHTPIN, DHTTYPE);
// Estado del sistema
bool SIMULACION = true;
bool lastButtonState = HIGH;
bool lastButtonManual = HIGH;
// Umbrales de humedad suelo (% C.C.)
const float HUM_ON = 60.0;
const float HUM_OFF = 80.0;
// Seguridad: tiempo máximo de riego (30 min = 1800000 ms)
const unsigned long MAX_RIEGO_MS = 1800000;
// Variables de estado
float soilHumidity = 70.0;
float temp = 18.0;
float humAmb = 50.0;
bool bombaEncendida = false;
bool controlManual = false;
long tiempoInicioRiego = 0;
// =============================
// CONFIGURACIÓN WIFI Y MQTT
// =============================
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqtt_server = "test.mosquitto.org";
WiFiClient espClient;
PubSubClient client(espClient);
// =============================
// PROTOTIPOS DE FUNCIONES
// =============================
void setupWifi();
void reconnect();
void callback(char* topic, byte* message, unsigned int length);
void publishEstado();
// =============================
// SETUP
// =============================
void setup() {
Serial.begin(115200);
dht.begin();
pinMode(RELAY_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(PIN_BUTTON, INPUT_PULLUP);
pinMode(PIN_BUTTON_MANUAL, INPUT_PULLUP);
digitalWrite(RELAY_PIN, LOW);
digitalWrite(LED_PIN, LOW);
Serial.println("===== Sistema de Riego Automático ESP32 =====");
setupWifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
// =============================
// LOOP PRINCIPAL
// =============================
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long now = millis();
checkButton();
checkManualButton(now);
if (!controlManual) {
if (SIMULACION) {
simularParcela(now);
} else {
leerSensores();
controlRiego(now);
}
}
// Publicar estado cada 5s
static unsigned long lastPublish = 0;
if (millis() - lastPublish > 5000) {
publishEstado();
lastPublish = millis();
}
}
// =============================
// FUNCIONES DE WIFI
// =============================
void setupWifi() {
delay(10);
Serial.println();
Serial.print("Conectando a ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ WiFi conectado");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
// =============================
// FUNCIONES DE MQTT
// =============================
void callback(char* topic, byte* message, unsigned int length) {
String msg;
for (unsigned int i = 0; i < length; i++) {
msg += (char)message[i];
}
Serial.print("[MQTT] Mensaje en ");
Serial.print(topic);
Serial.print(": ");
Serial.println(msg);
if (String(topic) == "riego/control") {
if (msg == "ON") {
encenderBomba(millis());
controlManual = true;
Serial.println("[MQTT] Bomba encendida remotamente");
} else if (msg == "OFF") {
apagarBomba();
controlManual = false;
Serial.println("[MQTT] Bomba apagada remotamente");
}
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("[MQTT] Intentando conexión...");
if (client.connect("ESP32RiegoClient")) {
Serial.println("Conectado ✅");
client.subscribe("riego/control"); // Suscripción a control remoto
} else {
Serial.print("Error, rc=");
Serial.print(client.state());
Serial.println(" Reintentando en 5s...");
delay(5000);
}
}
}
void publishEstado() {
String payload = "{";
payload += "\"temp\":" + String(temp) + ",";
payload += "\"humAmb\":" + String(humAmb) + ",";
payload += "\"humSuelo\":" + String(soilHumidity) + ",";
payload += "\"bomba\":\"" + String(bombaEncendida ? "ON" : "OFF") + "\"";
payload += "}";
client.publish("riego/estado", payload.c_str());
Serial.println("[MQTT] Estado publicado: " + payload);
}
// =============================
// FUNCIONES DEL SISTEMA DE RIEGO
// =============================
void checkButton() {
bool buttonState = digitalRead(PIN_BUTTON);
if (buttonState == LOW && lastButtonState == HIGH) {
SIMULACION = !SIMULACION;
Serial.print("[MODO] Cambiado a: ");
Serial.println(SIMULACION ? "SIMULACIÓN" : "REAL");
delay(300);
}
lastButtonState = buttonState;
}
void checkManualButton(unsigned long now) {
bool buttonState = digitalRead(PIN_BUTTON_MANUAL);
if (buttonState == LOW && lastButtonManual == HIGH) {
if (!controlManual) {
controlManual = true;
encenderBomba(now);
Serial.println("[MANUAL] Bomba encendida con botón.");
} else {
controlManual = false;
apagarBomba();
Serial.println("[MANUAL] Bomba apagada, vuelve automático.");
}
delay(300);
}
lastButtonManual = buttonState;
}
void leerSensores() {
humAmb = dht.readHumidity();
temp = dht.readTemperature();
if (isnan(humAmb) || isnan(temp)) {
Serial.println("[ERROR] Fallo DHT22");
}
int rawValue = analogRead(SOIL_PIN);
soilHumidity = map(rawValue, 0, 4095, 100, 0);
logEstado("REAL");
}
void simularParcela(unsigned long now) {
float evapFactor = (analogRead(POT_EVAPO) / 4095.0) - 0.5;
float calorFactor = (analogRead(POT_CALOR) / 4095.0) - 0.5;
if (bombaEncendida) {
soilHumidity += 2.0;
temp -= 0.2;
} else {
soilHumidity -= (0.5 + evapFactor * 2.0);
}
temp += (calorFactor * 1.0);
if (soilHumidity > 100) soilHumidity = 100;
if (soilHumidity < 0) soilHumidity = 0;
if (temp < -5) temp = -5;
if (temp > 60) temp = 60;
logEstado("SIM");
controlRiego(now);
}
void controlRiego(unsigned long now) {
if (!bombaEncendida && soilHumidity <= HUM_ON) {
encenderBomba(now);
} else if (bombaEncendida && soilHumidity >= HUM_OFF) {
apagarBomba();
}
if (bombaEncendida && (now - tiempoInicioRiego >= MAX_RIEGO_MS)) {
Serial.println("[ALERTA] Tiempo máximo alcanzado → Bomba apagada.");
apagarBomba();
}
if (temp < 9.0 || temp > 20.0) {
Serial.print("[ALERTA] Temp fuera de rango: ");
Serial.println(temp);
}
}
void encenderBomba(unsigned long now) {
digitalWrite(RELAY_PIN, HIGH);
digitalWrite(LED_PIN, HIGH);
bombaEncendida = true;
tiempoInicioRiego = now;
Serial.println("[RIEGO] 💧 Bomba ENCENDIDA");
}
void apagarBomba() {
digitalWrite(RELAY_PIN, LOW);
digitalWrite(LED_PIN, LOW);
bombaEncendida = false;
Serial.println("[RIEGO] ✅ Bomba APAGADA");
}
void logEstado(String modo) {
unsigned long timestamp = millis() / 1000;
Serial.print("[LOG][");
Serial.print(timestamp);
Serial.print("s][");
Serial.print(modo);
Serial.print("] Temp=");
Serial.print(temp);
Serial.print("°C | HumAmb=");
Serial.print(humAmb);
Serial.print("% | HumSuelo=");
Serial.print(soilHumidity);
Serial.print("% | Bomba=");
Serial.println(bombaEncendida ? "ON" : "OFF");
}