====== Control de un jardín con piscina con ESP32 ====== * [[ESP32]] {{ :acbfda0a7d6e3e04.png?600 |}} #include #include #include #include #include #include #include #include // --- CONFIGURACIÓN DE REDES --- WiFiMulti wifiMulti; const char* ssid1 = "Nombre del WiFi Principal"; const char* pass1 = "Clave del WiFi principal"; const char* ssid2 = "Nombre del WiFi secundario"; const char* pass2 = "Clave del WiFi secundario"; const char* usuarioSeguro = "admin"; // Usuario const char* passwordSegura = "1234"; // Contraseña // --- CONFIGURACIÓN NTP (Hora de Internet) --- const char* ntpServer = "pool.ntp.org"; const char* timeZone = "CET-1CEST,M3.5.0,M10.5.0/3"; // --- ASIGNACIÓN DE PINES --- const int pinesCanales[] = {13, 14, 27, 26, 16, 33}; const int pinValvulaMaestra = 32; const int pinLuces = 23; const int pinDepuradora = 19; const int pinServo = 18; const int pinLDR = 35; const int pinRelayPresion = 4; const int pinSensorPresion = 34; // --- CONFIGURACIÓN DEL DISPLAY Y SERVO --- LiquidCrystal_I2C lcd(0x27, 16, 2); Servo miServo; const int posReposo = 0; const int posPresion = 20; unsigned long tiempoServoPulsado = 0; bool servoEnMovimiento = false; // Variables para clonar el texto del LCD a la Web String lcdLinea1 = ""; String lcdLinea2 = ""; // --- VARIABLES DE ESTADO --- bool estados[8] = {false, false, false, false, false, false, false, false}; bool hayLuz = false; bool riegoEnProgreso = false; unsigned long tiempoFinAccesorios[2] = {0, 0}; int canalesParaRegar[6], totalCanalesRiego = 0, indiceRiegoActual = -1; unsigned long tiempoInicioZona = 0, duracionZonaMs = 0; // --- CONTADORES DE TIEMPO POR CANAL Y ACCESORIOS (SEGUNDOS DE USO DIARIO) --- // Índices: 0-5 = Canales C1-C6, 6 = Luz, 7 = Depuradora unsigned long tiempoUsoSegundos[8] = {0, 0, 0, 0, 0, 0, 0, 0}; unsigned long ultimoCalculoUso[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int ultimoDiaMilitar = -1; float ultimaPresion = 0.0; bool midiendoPresion = false; unsigned long tiempoInicioMedida = 0; const unsigned long TIEMPO_ESPERA_PRESION = 1500; unsigned long ultimaRotacion = 0; int pantallaActual = 0; const int intervaloRotacion = 5000; unsigned long ultimaVezCheckWiFi = 0; const unsigned long intervaloCheckWiFi = 15000; unsigned long ultimaActualizacionLCD = 0; WebServer server(80); // --- FUNCIONES DE HARDWARE Y CONTADORES --- void actualizarHardware() { bool algunRiegoActivo = false; unsigned long ahora = millis(); for (int i = 0; i < 6; i++) { digitalWrite(pinesCanales[i], estados[i] ? LOW : HIGH); if (estados[i]) { algunRiegoActivo = true; if (ultimoCalculoUso[i] == 0) ultimoCalculoUso[i] = ahora; } else { if (ultimoCalculoUso[i] > 0) { tiempoUsoSegundos[i] += (ahora - ultimoCalculoUso[i]) / 1000; ultimoCalculoUso[i] = 0; } } } digitalWrite(pinValvulaMaestra, (algunRiegoActivo || riegoEnProgreso) ? LOW : HIGH); // Luz (Índice 6) digitalWrite(pinLuces, estados[6] ? LOW : HIGH); if (estados[6]) { if (ultimoCalculoUso[6] == 0) ultimoCalculoUso[6] = ahora; } else { if (ultimoCalculoUso[6] > 0) { tiempoUsoSegundos[6] += (ahora - ultimoCalculoUso[6]) / 1000; ultimoCalculoUso[6] = 0; } } // Depuradora (Índice 7) digitalWrite(pinDepuradora, estados[7] ? LOW : HIGH); if (estados[7]) { if (ultimoCalculoUso[7] == 0) ultimoCalculoUso[7] = ahora; } else { if (ultimoCalculoUso[7] > 0) { tiempoUsoSegundos[7] += (ahora - ultimoCalculoUso[7]) / 1000; ultimoCalculoUso[7] = 0; } } if (midiendoPresion) { digitalWrite(pinRelayPresion, LOW); } else { digitalWrite(pinRelayPresion, HIGH); } } void actualizarContadoresTiempo() { unsigned long ahora = millis(); for (int i = 0; i < 8; i++) { if (estados[i] && ultimoCalculoUso[i] > 0) { unsigned long transcurrido = (ahora - ultimoCalculoUso[i]) / 1000; if (transcurrido >= 1) { tiempoUsoSegundos[i] += transcurrido; ultimoCalculoUso[i] = ahora; } } } struct tm timeinfo; if (getLocalTime(&timeinfo)) { if (ultimoDiaMilitar == -1) { ultimoDiaMilitar = timeinfo.tm_mday; } else if (ultimoDiaMilitar != timeinfo.tm_mday) { for (int i = 0; i < 8; i++) { tiempoUsoSegundos[i] = 0; if (estados[i]) ultimoCalculoUso[i] = ahora; } ultimoDiaMilitar = timeinfo.tm_mday; // Serial.println("Contadores reseteados a las 00:00"); } } } void realizarLecturaPresion() { long suma = 0; for(int i=0; i<25; i++) { suma += analogRead(pinSensorPresion); delay(2); } float promedio = suma / 25.0; float voltaje = promedio * (3.3 / 4095.0); ultimaPresion = (voltaje - 0.5) * (12.0 / 4.0); if (ultimaPresion < 0) ultimaPresion = 0; } void imprimirLinea(int fila, String texto) { while (texto.length() < 16) texto += " "; if (texto.length() > 16) texto = texto.substring(0, 16); lcd.setCursor(0, fila); lcd.print(texto); if (fila == 0) lcdLinea1 = texto; else if (fila == 1) lcdLinea2 = texto; } String obtenerHoraFormateada() { struct tm timeinfo; if (!getLocalTime(&timeinfo)) return "Sincronizando.."; char buffer[16]; strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo); return String(buffer); } String obtenerFechaFormateada() { struct tm timeinfo; if (!getLocalTime(&timeinfo)) return "--/--/----"; char buffer[16]; strftime(buffer, sizeof(buffer), "%d/%m/%Y", &timeinfo); return String(buffer); } void actualizarLCD() { unsigned long ahora = millis(); if (servoEnMovimiento) { imprimirLinea(0, "EJECUTANDO:"); imprimirLinea(1, "PULSACION SERVO"); return; } if (midiendoPresion) { imprimirLinea(0, "MIDIENDO..."); imprimirLinea(1, "RELE 8 ACTIVO"); return; } bool mostrarRiego = (riegoEnProgreso && indiceRiegoActual >= 0); bool mostrarLuz = estados[6]; bool mostrarDep = estados[7]; bool mostrarManual = (!riegoEnProgreso && (estados[0]||estados[1]||estados[2]||estados[3]||estados[4]||estados[5])); bool mostrarPresion = (ultimaPresion > 0.05); int activas = 1; if (mostrarRiego) activas++; if (mostrarLuz) activas++; if (mostrarDep) activas++; if (mostrarManual) activas++; if (mostrarPresion) activas++; if (ahora - ultimaRotacion >= intervaloRotacion) { ultimaRotacion = ahora; pantallaActual++; } int vista = pantallaActual % activas; int contadorVista = 0; if (contadorVista == vista) { imprimirLinea(0, "HORA: " + obtenerHoraFormateada()); imprimirLinea(1, "FECHA:" + obtenerFechaFormateada()); return; } contadorVista++; if (mostrarRiego) { if (contadorVista == vista) { imprimirLinea(0, "RIEGO: Zona C" + String(canalesParaRegar[indiceRiegoActual] + 1)); unsigned long r = (duracionZonaMs > (ahora - tiempoInicioZona)) ? (duracionZonaMs - (ahora - tiempoInicioZona)) / 1000 : 0; imprimirLinea(1, "Restan: " + String(r) + " seg"); return; } contadorVista++; } if (mostrarPresion) { if (contadorVista == vista) { imprimirLinea(0, "PRESION RED:"); imprimirLinea(1, String(ultimaPresion, 2) + " BAR"); return; } contadorVista++; } if (mostrarLuz) { if (contadorVista == vista) { imprimirLinea(0, "ACC: LUZ ON"); unsigned long rest = (tiempoFinAccesorios[0] > ahora) ? (tiempoFinAccesorios[0] - ahora) / 1000 : 0; imprimirLinea(1, "Off: " + String(rest / 60) + "m " + String(rest % 60) + "s"); return; } contadorVista++; } if (mostrarDep) { if (contadorVista == vista) { imprimirLinea(0, "ACC: DEPURAD. ON"); unsigned long rest = (tiempoFinAccesorios[1] > ahora) ? (tiempoFinAccesorios[1] - ahora) / 1000 : 0; imprimirLinea(1, "Off: " + String(rest / 60) + "m " + String(rest % 60) + "s"); return; } contadorVista++; } if (mostrarManual) { if (contadorVista == vista) { imprimirLinea(0, "ZONAS MANUALES:"); String lista = ""; for(int i=0; i<6; i++) { if(estados[i]) lista += String(i+1) + " "; } imprimirLinea(1, lista); } } } void detenerTodo() { riegoEnProgreso = false; indiceRiegoActual = -1; midiendoPresion = false; digitalWrite(pinRelayPresion, HIGH); for (int i = 0; i < 8; i++) estados[i] = false; tiempoFinAccesorios[0] = 0; tiempoFinAccesorios[1] = 0; actualizarHardware(); } // --- INTERFAZ WEB --- const char PAGE_HTML[] PROGMEM = R"=====( Control de Riego

Control de Riego

---
---
RIEGO EN CURSO

Zonas Manuales

Equipamiento

1m
1m

Presión de Agua

-- BAR

Control y Sensor Luz

...

Riego Secuencial

C1
C2
C3
C4
C5
C6
1m
)====="; // --- HANDLERS --- void handleStatus() { if (!server.authenticate(usuarioSeguro, passwordSegura)) return; int lecturaLDR = analogRead(pinLDR); hayLuz = (lecturaLDR > 20); String json = "{\"riegoActivo\":" + String(riegoEnProgreso ? "true" : "false") + ",\"luz\":" + String(hayLuz ? "true" : "false") + ",\"presion\":" + String(ultimaPresion) + ",\"midiendo\":" + String(midiendoPresion ? "true" : "false") + ",\"lcd1\":\"" + lcdLinea1 + "\"" + ",\"lcd2\":\"" + lcdLinea2 + "\"" + ",\"usoSegundos\":["; for (int i = 0; i < 8; i++) { json += String(tiempoUsoSegundos[i]); if (i < 7) json += ","; } json += "],\"estados\":["; for (int i = 0; i < 8; i++) { json += estados[i] ? "true" : "false"; if (i < 7) json += ","; } json += "]}"; server.send(200, "application/json", json); } void setup() { Serial.begin(9600); Wire.begin(21, 22); lcd.init(); lcd.backlight(); imprimirLinea(0, "Sistema Listo"); auto initPinSeguro = [](int p) { digitalWrite(p, HIGH); pinMode(p, OUTPUT); digitalWrite(p, HIGH); }; for(int i=0; i<6; i++) initPinSeguro(pinesCanales[i]); initPinSeguro(pinValvulaMaestra); initPinSeguro(pinLuces); initPinSeguro(pinDepuradora); initPinSeguro(pinRelayPresion); pinMode(pinLDR, INPUT); pinMode(pinSensorPresion, INPUT); miServo.attach(pinServo); miServo.write(posReposo); actualizarHardware(); wifiMulti.addAP(ssid1, pass1); wifiMulti.addAP(ssid2, pass2); while (wifiMulti.run() != WL_CONNECTED) { delay(500); } configTzTime(timeZone, ntpServer); MDNS.begin("riego"); server.on("/", []() { if(server.authenticate(usuarioSeguro, passwordSegura)) server.send(200, "text/html", PAGE_HTML); else server.requestAuthentication(); }); server.on("/status", handleStatus); server.on("/toggle", [](){ if(riegoEnProgreso) return; int id = server.arg("id").toInt(); if (id < 6) { estados[id] = !estados[id]; actualizarHardware(); } server.send(200); }); server.on("/acc", [](){ int id = server.arg("id").toInt(); unsigned long min = server.arg("t").toInt(); estados[id] = !estados[id]; if (estados[id]) tiempoFinAccesorios[id-6] = millis() + (min * 60000); actualizarHardware(); server.send(200); }); server.on("/medirP", [](){ if(!midiendoPresion) { digitalWrite(pinRelayPresion, LOW); midiendoPresion = true; tiempoInicioMedida = millis(); } server.send(200); }); server.on("/pulsar", [](){ miServo.write(posPresion); tiempoServoPulsado = millis() + 500; servoEnMovimiento = true; server.send(200); }); server.on("/stop", [](){ detenerTodo(); server.send(200); }); server.on("/riego", [](){ if (riegoEnProgreso) return; for (int i = 0; i < 6; i++) estados[i] = false; actualizarHardware(); String ids = server.arg("ids"); duracionZonaMs = (unsigned long)server.arg("t").toInt() * 60000; totalCanalesRiego = 0; char str[ids.length() + 1]; strcpy(str, ids.c_str()); char* p = strtok(str, ","); while (p != NULL) { canalesParaRegar[totalCanalesRiego++] = atoi(p); p = strtok(NULL, ","); } if (totalCanalesRiego > 0) { riegoEnProgreso = true; indiceRiegoActual = -1; } server.send(200); }); server.begin(); } void loop() { unsigned long ahora = millis(); server.handleClient(); actualizarContadoresTiempo(); if (ahora - ultimaVezCheckWiFi >= intervaloCheckWiFi) { ultimaVezCheckWiFi = ahora; wifiMulti.run(); } if (ahora - ultimaActualizacionLCD >= 500) { ultimaActualizacionLCD = ahora; actualizarLCD(); } if (midiendoPresion) { if (millis() - tiempoInicioMedida >= TIEMPO_ESPERA_PRESION) { realizarLecturaPresion(); digitalWrite(pinRelayPresion, HIGH); midiendoPresion = false; // Serial.println("Medición terminada, relé apagado."); } } if (riegoEnProgreso) { if (indiceRiegoActual == -1 || (ahora - tiempoInicioZona >= duracionZonaMs)) { if (indiceRiegoActual >= 0) estados[canalesParaRegar[indiceRiegoActual]] = false; indiceRiegoActual++; if (indiceRiegoActual >= totalCanalesRiego) { riegoEnProgreso = false; indiceRiegoActual = -1; } else { estados[canalesParaRegar[indiceRiegoActual]] = true; tiempoInicioZona = ahora; } actualizarHardware(); } } if (estados[6] && ahora >= tiempoFinAccesorios[0]) { estados[6] = false; actualizarHardware(); } if (estados[7] && ahora >= tiempoFinAccesorios[1]) { estados[7] = false; actualizarHardware(); } if (servoEnMovimiento && ahora >= tiempoServoPulsado) { miServo.write(posReposo); servoEnMovimiento = false; } }
{{tag> cacharreo C++ ESP32 control_de_riego}}