control_de_un_jardin_con_piscina_con_esp32
Diferencias
Muestra las diferencias entre dos versiones de la página.
| control_de_un_jardin_con_piscina_con_esp32 [2026/09/07 18:00] – creado hispa | control_de_un_jardin_con_piscina_con_esp32 [2026/09/07 18:00] (actual) – editor externo (Fecha desconocida) 127.0.0.1 | ||
|---|---|---|---|
| Línea 1: | Línea 1: | ||
| + | <WRAP round box> | ||
| + | ====== Control de un jardín con piscina con ESP32 ====== | ||
| + | </ | ||
| + | <WRAP tabs> | ||
| + | * [[ESP32]] | ||
| + | </ | ||
| + | {{ : | ||
| + | <code c> | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | // --- CONFIGURACIÓN DE REDES --- | ||
| + | WiFiMulti wifiMulti; | ||
| + | const char* ssid1 = " | ||
| + | const char* pass1 = "Clave del WiFi principal"; | ||
| + | const char* ssid2 = " | ||
| + | const char* pass2 = "Clave del WiFi secundario"; | ||
| + | const char* usuarioSeguro = " | ||
| + | const char* passwordSegura = " | ||
| + | |||
| + | // --- CONFIGURACIÓN NTP (Hora de Internet) --- | ||
| + | const char* ntpServer = " | ||
| + | const char* timeZone = " | ||
| + | |||
| + | // --- 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], | ||
| + | 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], | ||
| + | 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, | ||
| + | | ||
| + | // Luz (Índice 6) | ||
| + | digitalWrite(pinLuces, | ||
| + | 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, | ||
| + | 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, | ||
| + | } else { | ||
| + | digitalWrite(pinRelayPresion, | ||
| + | } | ||
| + | } | ||
| + | |||
| + | 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(& | ||
| + | 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(" | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | 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, | ||
| + | |||
| + | lcd.setCursor(0, | ||
| + | lcd.print(texto); | ||
| + | |||
| + | if (fila == 0) lcdLinea1 = texto; | ||
| + | else if (fila == 1) lcdLinea2 = texto; | ||
| + | } | ||
| + | |||
| + | String obtenerHoraFormateada() { | ||
| + | struct tm timeinfo; | ||
| + | if (!getLocalTime(& | ||
| + | char buffer[16]; | ||
| + | strftime(buffer, | ||
| + | return String(buffer); | ||
| + | } | ||
| + | |||
| + | String obtenerFechaFormateada() { | ||
| + | struct tm timeinfo; | ||
| + | if (!getLocalTime(& | ||
| + | char buffer[16]; | ||
| + | strftime(buffer, | ||
| + | return String(buffer); | ||
| + | } | ||
| + | |||
| + | void actualizarLCD() { | ||
| + | unsigned long ahora = millis(); | ||
| + | if (servoEnMovimiento) { | ||
| + | imprimirLinea(0, | ||
| + | imprimirLinea(1, | ||
| + | return; | ||
| + | } | ||
| + | if (midiendoPresion) { | ||
| + | imprimirLinea(0, | ||
| + | imprimirLinea(1, | ||
| + | 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, | ||
| + | imprimirLinea(1, | ||
| + | return; | ||
| + | } | ||
| + | contadorVista++; | ||
| + | |||
| + | if (mostrarRiego) { | ||
| + | if (contadorVista == vista) { | ||
| + | imprimirLinea(0, | ||
| + | unsigned long r = (duracionZonaMs > (ahora - tiempoInicioZona)) ? (duracionZonaMs - (ahora - tiempoInicioZona)) / 1000 : 0; | ||
| + | imprimirLinea(1, | ||
| + | return; | ||
| + | } | ||
| + | contadorVista++; | ||
| + | } | ||
| + | if (mostrarPresion) { | ||
| + | if (contadorVista == vista) { | ||
| + | imprimirLinea(0, | ||
| + | imprimirLinea(1, | ||
| + | return; | ||
| + | } | ||
| + | contadorVista++; | ||
| + | } | ||
| + | if (mostrarLuz) { | ||
| + | if (contadorVista == vista) { | ||
| + | imprimirLinea(0, | ||
| + | unsigned long rest = (tiempoFinAccesorios[0] > ahora) ? (tiempoFinAccesorios[0] - ahora) / 1000 : 0; | ||
| + | imprimirLinea(1, | ||
| + | return; | ||
| + | } | ||
| + | contadorVista++; | ||
| + | } | ||
| + | if (mostrarDep) { | ||
| + | if (contadorVista == vista) { | ||
| + | imprimirLinea(0, | ||
| + | unsigned long rest = (tiempoFinAccesorios[1] > ahora) ? (tiempoFinAccesorios[1] - ahora) / 1000 : 0; | ||
| + | imprimirLinea(1, | ||
| + | return; | ||
| + | } | ||
| + | contadorVista++; | ||
| + | } | ||
| + | if (mostrarManual) { | ||
| + | if (contadorVista == vista) { | ||
| + | imprimirLinea(0, | ||
| + | String lista = ""; | ||
| + | for(int i=0; i<6; i++) { if(estados[i]) lista += String(i+1) + " "; } | ||
| + | imprimirLinea(1, | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void detenerTodo() { | ||
| + | riegoEnProgreso = false; | ||
| + | indiceRiegoActual = -1; | ||
| + | midiendoPresion = false; | ||
| + | digitalWrite(pinRelayPresion, | ||
| + | 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" | ||
| + | < | ||
| + | <html lang=' | ||
| + | < | ||
| + | <meta charset=' | ||
| + | < | ||
| + | < | ||
| + | * { box-sizing: border-box; margin: 0; padding: 0; } | ||
| + | body { font-family: | ||
| + | h1 { text-align: center; color: #0056b3; font-size: 20px; margin: 10px 0; } | ||
| + | .card { background: white; padding: 12px; border-radius: | ||
| + | h2 { font-size: 15px; margin-bottom: | ||
| + | .grid { display: grid; grid-template-columns: | ||
| + | button { padding: 10px 2px; cursor: pointer; border: 1px solid #ddd; border-radius: | ||
| + | button.active { background: #d9534f; color: white; border-color: | ||
| + | .special-btn { min-width: 100px; padding: 14px 5px; font-size: 11px; } | ||
| + | .overlay { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, | ||
| + | .sensor-container { display: flex; gap: 8px; margin-top: 5px; } | ||
| + | .btn-servo { width: 66%; background: #6f42c1; color: white; border: none; padding: 14px 5px; border-radius: | ||
| + | .sensor-status { width: 34%; pointer-events: | ||
| + | .status-active { background: #28a745; color: white; border-color: | ||
| + | .row { display: flex; align-items: | ||
| + | .time-label { min-width: 45px; font-size: 12px; font-weight: | ||
| + | .riego-grid { display: grid; grid-template-columns: | ||
| + | .riego-item { display: flex; align-items: | ||
| + | input[type=" | ||
| + | .btn-main { background: #007bff; color: white; border: none; width: 100%; padding: 14px; margin-top: 10px; border-radius: | ||
| + | .btn-stop { background: #ff0000; color: white; border: none; width: 100%; padding: 16px; border-radius: | ||
| + | .btn-presion { background: #17a2b8; color: white; border: none; width: 66%; padding: 14px 5px; border-radius: | ||
| + | | ||
| + | .lcd-box { background: #222; color: #00ff00; font-family: | ||
| + | </ | ||
| + | </ | ||
| + | <body onload=" | ||
| + | < | ||
| + | | ||
| + | <div class=' | ||
| + | <div id=' | ||
| + | <div id=' | ||
| + | </ | ||
| + | |||
| + | <div class=' | ||
| + | <div id=' | ||
| + | < | ||
| + | <div class=' | ||
| + | <button id=" | ||
| + | <button id=" | ||
| + | <button id=" | ||
| + | <button id=" | ||
| + | <button id=" | ||
| + | <button id=" | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | <div class=' | ||
| + | < | ||
| + | <div class=' | ||
| + | <button id=" | ||
| + | <input type=" | ||
| + | <span class=" | ||
| + | </ | ||
| + | <div class=' | ||
| + | <button id=" | ||
| + | <input type=" | ||
| + | <span class=" | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | <div class=' | ||
| + | <div class=' | ||
| + | | ||
| + | <div class=' | ||
| + | < | ||
| + | <div class=' | ||
| + | <div class=' | ||
| + | <div class=' | ||
| + | <div class=' | ||
| + | <div class=' | ||
| + | <div class=' | ||
| + | <div class=' | ||
| + | </ | ||
| + | <div class=' | ||
| + | <label style=" | ||
| + | <input type=" | ||
| + | <span class=" | ||
| + | </ | ||
| + | <button class=' | ||
| + | </ | ||
| + | | ||
| + | <div class=' | ||
| + | |||
| + | < | ||
| + | function fmtSeg(s) { | ||
| + | if (s < 60) return s + ' | ||
| + | let m = Math.floor(s / 60); | ||
| + | let seg = s % 60; | ||
| + | return m + 'm ' + seg + ' | ||
| + | } | ||
| + | |||
| + | function actualizarEstados() { | ||
| + | fetch('/ | ||
| + | data.estados.forEach((st, | ||
| + | const btn = document.getElementById(' | ||
| + | if(btn) st ? btn.classList.add(' | ||
| + | }); | ||
| + | | ||
| + | document.getElementById(' | ||
| + | | ||
| + | const ldr = document.getElementById(' | ||
| + | if(data.luz) { ldr.innerText = " | ||
| + | else { ldr.innerText = " | ||
| + | | ||
| + | const pVal = document.getElementById(' | ||
| + | pVal.innerText = data.presion.toFixed(2) + " BAR"; | ||
| + | if(data.midiendo) { document.getElementById(' | ||
| + | else { document.getElementById(' | ||
| + | | ||
| + | if(data.lcd1 !== undefined) document.getElementById(' | ||
| + | if(data.lcd2 !== undefined) document.getElementById(' | ||
| + | |||
| + | if(data.usoSegundos) { | ||
| + | // Actualizar etiquetas en botones C1-C6 | ||
| + | for(let i = 0; i < 6; i++) { | ||
| + | const btn = document.getElementById(' | ||
| + | if(btn) btn.innerText = ' | ||
| + | } | ||
| + | // Actualizar etiqueta Luz | ||
| + | const btnLuz = document.getElementById(' | ||
| + | if(btnLuz) btnLuz.innerText = 'LUZ (' + fmtSeg(data.usoSegundos[6]) + ' | ||
| + | | ||
| + | // Actualizar etiqueta Depuradora | ||
| + | const btnDep = document.getElementById(' | ||
| + | if(btnDep) btnDep.innerText = 'DEP (' + fmtSeg(data.usoSegundos[7]) + ' | ||
| + | } | ||
| + | }); | ||
| + | } | ||
| + | |||
| + | function medirP() { fetch('/ | ||
| + | function toggle(id) { fetch('/ | ||
| + | function toggleAcc(id, | ||
| + | function pulsar() { fetch('/ | ||
| + | function stopAll() { fetch('/ | ||
| + | function sendRiego() { let sel = []; document.querySelectorAll(' | ||
| + | setInterval(actualizarEstados, | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | )====="; | ||
| + | |||
| + | // --- HANDLERS --- | ||
| + | |||
| + | void handleStatus() { | ||
| + | if (!server.authenticate(usuarioSeguro, | ||
| + | int lecturaLDR = analogRead(pinLDR); | ||
| + | hayLuz = (lecturaLDR > 20); | ||
| + | | ||
| + | String json = " | ||
| + | ", | ||
| + | ", | ||
| + | ", | ||
| + | ", | ||
| + | ", | ||
| + | ", | ||
| + | for (int i = 0; i < 8; i++) { | ||
| + | json += String(tiempoUsoSegundos[i]); | ||
| + | if (i < 7) json += ","; | ||
| + | } | ||
| + | json += " | ||
| + | for (int i = 0; i < 8; i++) { json += estados[i] ? " | ||
| + | json += " | ||
| + | server.send(200, | ||
| + | } | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(9600); | ||
| + | Wire.begin(21, | ||
| + | lcd.init(); lcd.backlight(); | ||
| + | imprimirLinea(0, | ||
| + | |||
| + | auto initPinSeguro = [](int p) { | ||
| + | digitalWrite(p, | ||
| + | pinMode(p, OUTPUT); | ||
| + | digitalWrite(p, | ||
| + | }; | ||
| + | |||
| + | for(int i=0; i<6; i++) initPinSeguro(pinesCanales[i]); | ||
| + | initPinSeguro(pinValvulaMaestra); | ||
| + | initPinSeguro(pinLuces); | ||
| + | initPinSeguro(pinDepuradora); | ||
| + | initPinSeguro(pinRelayPresion); | ||
| + | |||
| + | pinMode(pinLDR, | ||
| + | pinMode(pinSensorPresion, | ||
| + | | ||
| + | miServo.attach(pinServo); | ||
| + | actualizarHardware(); | ||
| + | |||
| + | wifiMulti.addAP(ssid1, | ||
| + | while (wifiMulti.run() != WL_CONNECTED) { delay(500); } | ||
| + | | ||
| + | configTzTime(timeZone, | ||
| + | |||
| + | MDNS.begin(" | ||
| + | |||
| + | server.on("/", | ||
| + | server.on("/ | ||
| + | server.on("/ | ||
| + | server.on("/ | ||
| + | | ||
| + | server.on("/ | ||
| + | if(!midiendoPresion) { | ||
| + | digitalWrite(pinRelayPresion, | ||
| + | midiendoPresion = true; | ||
| + | tiempoInicioMedida = millis(); | ||
| + | } | ||
| + | server.send(200); | ||
| + | }); | ||
| + | |||
| + | server.on("/ | ||
| + | server.on("/ | ||
| + | server.on("/ | ||
| + | if (riegoEnProgreso) return; | ||
| + | for (int i = 0; i < 6; i++) estados[i] = false; | ||
| + | actualizarHardware(); | ||
| + | String ids = server.arg(" | ||
| + | totalCanalesRiego = 0; char str[ids.length() + 1]; strcpy(str, ids.c_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, | ||
| + | midiendoPresion = false; | ||
| + | // Serial.println(" | ||
| + | } | ||
| + | } | ||
| + | |||
| + | 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); | ||
| + | } | ||
| + | </ | ||
| + | <WRAP collarge justify> | ||
| + | |||
| + | </ | ||
| + | |||
| + | {{tag> cacharreo C++ ESP32 control_de_riego}} | ||
