#include <Arduino.h>
#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 33
#define PIN_BUTTON_MANUAL 26
// =============================
// OBJETOS Y VARIABLES
// =============================
DHT dht(DHTPIN, DHTTYPE);
// Estado del sistema
bool lastButtonManual = HIGH;
// Umbrales de humedad suelo (% C.C.)
const float HUM_ON = 60.0; // umbral mínimo de humedad (%)
const float HUM_OFF = 80.0; // máximo de humedad (%)
// 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;
// =============================
// VARIABLES DE CONTROL DE VOLUMEN
// =============================
const float volumenObjetivo_L = 2667.0; // 2.667 m3 = 2667 litros
const float caudalTotal_Lps = 20.0 / 60.0; // 20 L/min = 0.333 L/s
float volumenAcumulado = 0.0;
unsigned long tiempoAnteriorVol = 0;
const unsigned long intervaloVol = 1000; // 1s
// =============================
// 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();
void controlVolumen(unsigned long now);
// =============================
// SETUP
// =============================
void setup() {
Serial.begin(115200);
dht.begin();
pinMode(RELAY_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
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);
tiempoAnteriorVol = 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 DEL SISTEMA DE RIEGO
// =============================
void encenderBomba(unsigned long now) {
digitalWrite(RELAY_PIN, HIGH);
digitalWrite(LED_PIN, HIGH);
bombaEncendida = true;
tiempoInicioRiego = now;
tiempoAnteriorVol = now;
Serial.println("[RIEGO] 💧 Bomba ENCENDIDA");
}
void apagarBomba() {
digitalWrite(RELAY_PIN, LOW);
digitalWrite(LED_PIN, LOW);
bombaEncendida = false;
Serial.println("[RIEGO] ✅ Bomba APAGADA");
}
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(100);
}
lastButtonManual = buttonState;
}
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);
}
}
// =============================
// CONTROL DE VOLUMEN POR EVENTO
// =============================
void controlVolumen(unsigned long now) {
if (bombaEncendida && (now - tiempoAnteriorVol >= intervaloVol)) {
unsigned long delta = now - tiempoAnteriorVol;
volumenAcumulado += caudalTotal_Lps*(delta/1000.0);
tiempoAnteriorVol = now;
Serial.print("[VOLUMEN] Acumulado: ");
Serial.print(volumenAcumulado);
Serial.println(" L");
if (volumenAcumulado >= volumenObjetivo_L) {
Serial.println("[RIEGO] ✅ Volumen objetivo alcanzado (2.667 m3). Apagando bomba y reiniciando contador...");
apagarBomba();
volumenAcumulado = 0.0;
}
}
}
void logEstado() {
unsigned long timestamp = millis() / 1000;
Serial.print("[LOG][");
Serial.print(timestamp);
Serial.print("s] Temp=");
Serial.print(temp);
Serial.print("°C | HumAmb=");
Serial.print(humAmb);
Serial.print("% | HumSuelo=");
Serial.print(soilHumidity);
Serial.print(" | Volumen=");
Serial.print(volumenAcumulado);
Serial.println(" L");
Serial.print("% | Bomba=");
Serial.println(bombaEncendida ? "ON" : "OFF");
}
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();
}
// =============================
// 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 += "\"volumen\":" + String(volumenAcumulado);
payload += "}";
client.publish("riego/estado", payload.c_str(), true);
Serial.println("[MQTT] Estado publicado: " + payload);
}
// =============================
// LOOP PRINCIPAL
// =============================
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long now = millis();
checkManualButton(now);
if (!controlManual) {
leerSensores();
controlRiego(now);
}
controlVolumen(now);
static unsigned long lastPublish = 0;
if (millis() - lastPublish > 5000) {
publishEstado();
lastPublish = millis();
}
}