#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
---
---
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; }
}