// ============================================================================
// >>> [FUNCIÓN 1 DE 5] <<< LÍNEAS 1 A 75
// RÓTULO PRINCIPAL: CONTROLADORES DE COBRE, HARDWARE DHT22 Y MAPEO DE PINES
// ============================================================================
#include "DHT.h" // Controlador oficial para el sensor de clima DHT22
#include <Wire.h> // Protocolo I2C para manejar la pantalla OLED
#include <U8g2lib.h> // Motor gráfico industrial para el display SH1106
#include <ESP32Servo.h> // Librería experta para mover el servo sin tirones
// Constructor de la pantalla OLED SH1106 por bus I2C (Pines nativos SDA y SCL)
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
#define DHTPIN 4 // Cable de datos del sensor DHT22 conectado al Pin 4
#define DHTTYPE DHT22 // Definimos que el sensor físico real es un DHT22
DHT dht(DHTPIN, DHTTYPE); // Inicializamos el objeto del sensor de clima
Servo miServo; // Creamos el motor virtual para la cuna basculante
#define SERVO_PIN 13 // Cable de control PWM del servo clavado en el Pin 13
// Pines de relés de potencia (Fuerza a 5V)
#define RELE_CALENTADOR 25 // Pin que comanda el optoacoplador de la resistencia
#define RELE_VENTILADOR 26 // Pin del extractor (¡NUESTRO PULMÓN CONTINUO HIGH!)
#define RELE_HUMIDIFICADOR 27 // Pin que gatilla la pastilla ultrasónica de vapor
#define RELE_ILUMINACION 33 // Pin que enciende el Kit Redondito del techo
#define PIN_BUZZER 15 // Pin de salida digital para el Módulo Buzzer activo
// Pines del Mando Rotativo (Encoder Bourns Industrial)
#define PIN_ENCODER_CLK 14 // Canal A del encoder para capturar el giro
#define PIN_ENCODER_DT 12 // Canal B del encoder para definir el sentido de marcha
#define PIN_ENCODER_SW 32 // Pulsador central del encoder (Clic de confirmación)
// Parámetros biológicos de trabajo (Registros vivos)
float temperaturaDeseada = 37.5; // Meta de calor por defecto para la incubación
float humidityDeseada = 50.0; // Meta de humedad por defecto para la incubación
int diasTotales = 21; // Duración estándar del ciclo (21 días de gallina)
int diasPasados = 0; // Cronómetro de días acumulados (Arranca en cero)
bool estadoLuzInterna = false; // Estado de la luz ojo de buey (Encendido/Apagado)
// Backups de memoria para el Modo Manual Experto (Para no leer ceros fatales)
int diasIncubacionManual = 21; // Días de incubación cargados por el operario
int diasEclosionManual = 2; // Días de nacimiento cargados por el operario
float tempEclosionManual = 36.9; // Calor final de eclosión fijado en el menú
float humEclosionManual = 65.0; // Vapor final de eclosión fijado en el menú
float tempManualBase = 37.5; // Calor inicial de incubación fijado en el menú
float humManualBase = 50.0; // Vapor inicial de incubación fijado en el menú
// ============================================================================
// >>> [FUNCIÓN 2 DE 5] <<< LÍNEAS 76 A 155
// RÓTULO PRINCIPAL: MEMORIAS DE CRISIS, ÁNGULOS DEL SERVO Y DICCIONARIOS OLED
// ============================================================================
unsigned long tiempoCalorEncendidoCiego = 20000; // Milisegundos de calor en modo ciego
unsigned long tiempoCalorApagadoCiego = 40000; // Milisegundos de apagado en modo ciego
int especieSeleccionada = -1; // Especie (-1=Ninguna, 0=Gallina, 1=Pato, 2=Codorniz, 3=Manual)
bool modoEclosionActivo = false; // Alerta de fase final de nacimiento (Apaga volteo)
bool modoEdicionActivo = false; // Indica si parpadea el '?' para cambiar un número
bool incubadoraYaIniciada = false; // Estado general de marcha del nido biológico
bool enFaseDeEnfriamientoActiva = false; // Estado del aireado automático para patos
int contadorErroresSensor = 0; // Contador de lecturas defectuosas del DHT22
bool fallaSensorActiva = false; // Disparador de pánico y modo ciego por sensor roto
bool alarmaCalorCritico = false; // Pánico por sobrecalentamiento interno excesivo
bool alarmaVentiladorRoto = false; // Alerta por temperatura disparada sin control
bool alarmaFrioCritico = false; // Alerta de congelamiento o resistencia quemada
bool alarmaHumedadCritica = false; // Alerta de caída leve de humedad en la celda
bool alarmaHumidificadorRoto = false; // Alerta de sequedad extrema por falta de agua
bool fallaServoActiva = false; // Alerta por bloqueo mecánico del motor de volteo
bool hayAlgunaCrisisActiva = false; // Centralizador general de pánico en la placa
bool buzzerSilenciadoManualmente = false; // Silenciador temporal activado por el operario
int anguloServoActual = 90; // Posición en grados del brazo del servo
int anguloObjetivoDestino = 90; // Destino final del viaje suave de la cuna
bool moviendoServoSuave = false; // Habilita el movimiento paso a paso por software
unsigned long ultimoTiempoPasoServo = 0; // Temporizador para regular la velocidad del servo
unsigned long ultimoTiempoVolteoBloqueo = 0; // Temporizador del intervalo de volteo de 2 horas
bool modoTestCunaActivo = false; // Bandera para el volteo manual desde el menú de pruebas
int etapaTestCuna = 0; // Secuenciador interno de giros del testeo mecánico
int anguloOrigenAntesDelTest = 90; // Memoria de posición previa para retornar la cuna
int pantallaActual = 0; // Pantalla activa (0=Main, 1=Vivo, 2=Especies, 3=Test, 4=Aduana, 5=Manual)
int opcionSeleccionada = 0; // Índice del renglón donde apunta el cursor '>' o '?'
int botonOrigenAduana = 0; // Memoria para saber de qué menú venías al ir a advertencia
unsigned long tiempoUltimaActividadEncoder = 0; // Temporizador para regreso automático al monitor
const unsigned long TIEMPO_ESPERA_MENU = 20000; // Tiempo límite de inactividad (20 segundos)
int melodiaArpegio[] = { 349, 0, 523, 523, 523, 523, 523, 523, 0, 587, 0, 880 }; // Notas musicales de bienvenida
int tiemposArpegio[] = { 1000, 100, 60, 60, 60, 60, 60, 60, 200, 1000, 100, 1000 }; // Tiempos de reproducción de notas
const int OP_MAIN = 6; // Cantidad de opciones en el menú de inicio
const char* menuMain[OP_MAIN] = { "SELECCIONAR ESPECIE", "INICIAR NIDO", "REINICIAR NIDO", "CAMBIAR ESPECIE", "TESTEOS", "LUZ" };
const int OP_ESPECIES = 5; // Cantidad de renglones en el selector bio
const char* menuEspecies[OP_ESPECIES] = { "GALLINA (21 DIAS)", "PATO (28 DIAS)", "CODORNIZ (17 DIAS)", "MODO MANUAL", "VOLVER ATRAS" };
const int OP_MANUAL_EDIT = 8; // Cantidad de campos editables en modo experto
const char* menuManualEdit[OP_MANUAL_EDIT] = { "DIAS INCUB: ", "DIAS ECLO: ", "TEMP: ", "HUM: ", "TEMP ECLO: ", "HUM ECLO: ", "GUARDAR CONFIG", "VOLVER ATRAS" };
const int OP_TEST = 6; // Cantidad de pruebas disponibles en el banco
const char* menuTest[OP_TEST] = { "TEST CALOR", "TEST VENTILA", "TEST HUMIDIF", "TEST CUNA", "TEST BUZZER", "VOLVER MENU" };
const int OP_CONFIRMAR = 2; // Opciones en la aduana de advertencia
const char* menuConfirmar[OP_CONFIRMAR] = { "NO, VOLVER", "SI, INICIAR" };
int ultimoEstadoCLK; // Guarda la posición anterior de la perilla del encoder
unsigned long ultimoTiempoClick = 0; // Antirebote de tiempo para el pulsador central
int backupDiasIncub = 21, backupDiasEclo = 2; // Memorias temporales mientras editás en pantalla
float backupTempObj = 37.5, backupHumObj = 50.0, backupTempEclo = 36.9, backupHumEclo = 65.0;
void actualizarOLED(); // Prototipo: Dibuja las pantallas estáticas de menús
void refrescarPantallaVivo(float t, float h); // Prototipo: Dibuja las temperaturas dinámicas
void ejecutarAccionClic(); // Prototipo: Ejecuta órdenes cuando apretás el botón
void calcularValoresClimaActual(); // Prototipo: Ajusta metas climatizadoras automáticas
unsigned long ultimoCicloEnfriamientoPato = 0; // Almacena el inicio del aireado para el huevo de pato
unsigned long marcaTiempoInicioInyeccionVirgen = 0; // Ventana de gracia para el arranque inicial en frío
// ============================================================================
// >>> [FUNCIÓN 3 DE 5] <<< LÍNEAS 156 A 215
// RÓTULO PRINCIPAL: CONFIGURACIÓN DE PINES, AUDIO DE ENTRADA E INICIO DE PANTALLA
// ============================================================================
void setup() {
Serial.begin(115200); // Inicializa monitor serie para diagnóstico en taller
dht.begin(); // Enciende las lecturas físicas del sensor de clima
pinMode(RELE_CALENTADOR, OUTPUT); // Configura borne de fuerza de la resistencia
pinMode(RELE_VENTILADOR, OUTPUT); // Configura borne de fuerza del extractor continuo
pinMode(RELE_HUMIDIFICADOR, OUTPUT); // Configura borne de fuerza del vaporizador
pinMode(RELE_ILUMINACION, OUTPUT); // Configura borne de fuerza del ojo de buey del techo
pinMode(PIN_BUZZER, OUTPUT); // Configura salida de la sirena de pánico
pinMode(PIN_ENCODER_CLK, INPUT_PULLUP); // Entrada de giro canal A con filtro interno
pinMode(PIN_ENCODER_DT, INPUT_PULLUP); // Entrada de giro canal B con filtro interno
pinMode(PIN_ENCODER_SW, INPUT_PULLUP); // Entrada de pulsador central con filtro interno
ultimoEstadoCLK = digitalRead(PIN_ENCODER_CLK); // Captura estado base de la perilla antes de girar
digitalWrite(RELE_CALENTADOR, LOW); // Asegura calentador apagado al arrancar la placa
digitalWrite(RELE_HUMIDIFICADOR, LOW); // Asegura humidificador apagado al arrancar la placa
digitalWrite(PIN_BUZZER, LOW); // Silencia la sirena de pánico en el inicio
digitalWrite(RELE_VENTILADOR, LOW); // Mantiene extractor en reposo antes del arranque real
digitalWrite(RELE_ILUMINACION, LOW); // Apaga la luz redonda del techo al iniciar
miServo.attach(SERVO_PIN, 500, 2400); // Monta el servo MG90S con sus pulsos calibrados
miServo.write(anguloServoActual); // Clava el bracito de la cuna en los 90 grados iniciales
u8g2.begin(); // Inicializa los controladores gráficos de la OLED
int totalNotas = sizeof(melodiaArpegio) / sizeof(int); // Calcula cuántas notas tiene el audio
for (int estaNota = 0; estaNota < totalNotas; estaNota++) { // Lazo para reproducir la bienvenida
int duracionMilisegundos = tiemposArpegio[estaNota]; // Toma el tiempo de cada nota musical
if (melodiaArpegio[estaNota] != 0) { // Si hay sonido real en la celda...
tone(PIN_BUZZER, melodiaArpegio[estaNota]); // Enciende la bocina en esa frecuencia fija
delay(duracionMilisegundos); // Sostiene el silbido los milisegundos de fábrica
} else { // Si es un silencio de partitura...
noTone(PIN_BUZZER); // Corta el audio del pin del buzzer
delay(duracionMilisegundos); // Espera el tiempo muerto antes de la siguiente
}
noTone(PIN_BUZZER); // Asegura limpiar el pin al terminar la nota
}
actualizarOLED(); // Dibuja el Menú Principal en la pantalla apenas se apaga la música
}
void loop() {
int estadoCLK = digitalRead(PIN_ENCODER_CLK); // Lee el estado real de la perilla giratoria
if (hayAlgunaCrisisActiva == false) { // Cortafuegos: Si hay pánico, congela los giros normales
if ((estadoCLK != ultimoEstadoCLK && estadoCLK == LOW) || (digitalRead(PIN_ENCODER_SW) == LOW && pantallaActual == 1)) {
if (pantallaActual == 1) { // Si estás en el monitor vivo y movés o tocás la perilla...
pantallaActual = 0; opcionSeleccionada = 1; // Te saca del vivo y te manda al menú de inicio
actualizarOLED(); // Refresca el visor gráfico en el acto
tiempoUltimaActividadEncoder = millis(); // Reinicia el timer para el retorno automático
}
}
if (estadoCLK != ultimoEstadoCLK && estadoCLK == LOW) { // Detecta movimiento de giro
tiempoUltimaActividadEncoder = millis(); // Reinicia temporizador de inactividad
bool rumboDerecha = (digitalRead(PIN_ENCODER_DT) != estadoCLK); // Sentido de giro
if (modoEdicionActivo == true) { // Si estás modificando un número adentro del Manual Experto
if (pantallaActual == 5) {
if (opcionSeleccionada == 0) { backupDiasIncub += rumboDerecha ? 1 : -1; backupDiasIncub = constrain(backupDiasIncub, 0, 45); }
else if (opcionSeleccionada == 1) { backupDiasEclo += rumboDerecha ? 1 : -1; backupDiasEclo = constrain(backupDiasEclo, 0, 10); }
else if (opcionSeleccionada == 2) { backupTempObj += rumboDerecha ? 0.1 : -0.1; backupTempObj = constrain(backupTempObj, 0.0, 42.0); }
else if (opcionSeleccionada == 3) { backupHumObj += rumboDerecha ? 1.0 : -1.0; backupHumObj = constrain(backupHumObj, 0.0, 90.0); }
else if (opcionSeleccionada == 4) { backupTempEclo += rumboDerecha ? 0.1 : -0.1; backupTempEclo = constrain(backupTempEclo, 0.0, 42.0); }
else if (opcionSeleccionada == 5) { backupHumEclo += rumboDerecha ? 1.0 : -1.0; backupHumEclo = constrain(backupHumEclo, 0.0, 90.0); }
}
actualizarOLED(); // Muestra el número recién modificado en la pantalla
} else { // Navegación común por los renglones de menús
int limiteOpciones = (pantallaActual == 0) ? OP_MAIN : ((pantallaActual == 2) ? OP_ESPECIES : ((pantallaActual == 3) ? OP_TEST : ((pantallaActual == 4) ? OP_CONFIRMAR : OP_MANUAL_EDIT)));
if (pantallaActual != 1) {
if (rumboDerecha) { opcionSeleccionada++; if (opcionSeleccionada >= limiteOpciones) opcionSeleccionada = 0; }
else { opcionSeleccionada--; if (opcionSeleccionada < 0) opcionSeleccionada = limiteOpciones - 1; }
actualizarOLED(); // Mueve la flecha '>' de arriba a abajo
}
}
}
}
ultimoEstadoCLK = estadoCLK; // Almacena posición para la siguiente vuelta
if (digitalRead(PIN_ENCODER_SW) == LOW) { // Escanea el pulsador central del encoder
unsigned long tiempoInicioPresionado = millis();
while(digitalRead(PIN_ENCODER_SW) == LOW) { delay(10); } // Retén de dedo puesto
unsigned long duracionPresionado = millis() - tiempoInicioPresionado;
if (duracionPresionado > 2000) { // 🔒 CLIC LARGO (2 SEGUNDOS): Reset manual de pánicos
botonOrigenAduana = 99;
alarmaCalorCritico = false; alarmaVentiladorRoto = false; alarmaFrioCritico = false;
alarmaHumedadCritica = false; alarmaHumidificadorRoto = false; fallaSensorActiva = false;
hayAlgunaCrisisActiva = false;
digitalWrite(RELE_CALENTADOR, LOW); digitalWrite(RELE_HUMIDIFICADOR, LOW); noTone(PIN_BUZZER);
ejecutarAccionClic(); // Fuerza el salto para resetear el nido a cero
}
else if (duracionPresionado > 50) { // Clic corto común
if (millis() - ultimoTiempoClick > 300) {
ultimoTiempoClick = millis(); tiempoUltimaActividadEncoder = millis();
if (hayAlgunaCrisisActiva == true) { buzzerSilenciadoManualmente = true; digitalWrite(PIN_BUZZER, LOW); }
else { ejecutarAccionClic(); }
}
}
}
static unsigned long ultimoTiempoClima = 0;
if (millis() - ultimoTiempoClima >= 2000) { // Toma el pulso al microclima cada 2 segundos netos
ultimoTiempoClima = millis();
float humidity_read = dht.readHumidity(); float temp_read = dht.readTemperature();
if (isnan(humidity_read) || isnan(temp_read)) { // Desconexión de cable de datos del dht
contadorErroresSensor++;
if (contadorErroresSensor >= 3) { fallaSensorActiva = true; temp_read = 0.0; humidity_read = 0.0; }
else { return; }
} else { contadorErroresSensor = 0; fallaSensorActiva = false; }
if (incubadoraYaIniciada == true) {
calcularValoresClimaActual();
if (diasPasados > diasTotales) { // 🔒 DISYUNTOR FIN DE CICLO: Apagón absoluto de fuerza
digitalWrite(RELE_CALENTADOR, LOW); digitalWrite(RELE_HUMIDIFICADOR, LOW); digitalWrite(RELE_VENTILADOR, LOW);
alarmaCalorCritico = false; alarmaVentiladorRoto = false; alarmaFrioCritico = false; alarmaHumedadCritica = false; alarmaHumidificadorRoto = false;
hayAlgunaCrisisActiva = false;
return;
}
digitalWrite(RELE_VENTILADOR, HIGH); // 🔒 LEY DEL PULMÓN CONTINUO: Extractor fijo las 24hs
if (fallaSensorActiva == true) { // CORTAFUEGOS MODO CIEGO AUTOMÁTICO POR TIEMPO
static unsigned long ultimoCambioCiego = 0; static bool estadoCalorCiego = false;
unsigned long intervaloCiego = 40000;
if (estadoCalorCiego == true) {
if (especieSeleccionada == 0) intervaloCiego = 20000;
else if (especieSeleccionada == 1) intervaloCiego = 25000;
else if (especieSeleccionada == 2) intervaloCiego = 12000;
else intervaloCiego = 20000;
} else {
if (especieSeleccionada == 0) intervaloCiego = 40000;
else if (especieSeleccionada == 1) intervaloCiego = 35000;
else if (especieSeleccionada == 2) intervaloCiego = 48000;
else intervaloCiego = 40000;
}
if (millis() - ultimoCambioCiego >= intervaloCiego) { ultimoCambioCiego = millis(); estadoCalorCiego = !estadoCalorCiego; }
digitalWrite(RELE_CALENTADOR, estadoCalorCiego ? HIGH : LOW); digitalWrite(RELE_HUMIDIFICADOR, LOW);
}
else { // CONTROL BIOLÓGICO CON SENSOR DHT22 ONLINE
bool esMomentoEnfriarPato = (especieSeleccionada == 1 && diasPasados >= 10 && diasPasados <= 25);
static unsigned long marcaTiempoFinEnfriamiento = 0;
float temperaturaObjetivoActual = temperaturaDeseada; float profesionalHumedadActual = humidityDeseada;
if (esMomentoEnfriarPato == true) { // AIREADO EXCLUSIVO DEL PATO
if (ultimoCicloEnfriamientoPato == 0) ultimoCicloEnfriamientoPato = millis();
if (millis() - ultimoCicloEnfriamientoPato < 900000) {
enFaseDeEnfriamientoActiva = true; temperaturaObjetivoActual = 33.0; profesionalHumedadActual = 65.0; marcaTiempoFinEnfriamiento = 0;
} else {
if (enFaseDeEnfriamientoActiva == true) { enFaseDeEnfriamientoActiva = false; marcaTiempoFinEnfriamiento = millis(); }
if (millis() - ultimoCicloEnfriamientoPato >= 3600000) ultimoCicloEnfriamientoPato = millis();
}
} else { enFaseDeEnfriamientoActiva = false; ultimoCicloEnfriamientoPato = 0; marcaTiempoFinEnfriamiento = 0; }
if (temp_read <= 25.0) { enFaseDeEnfriamientoActiva = false; }
alarmaCalorCritico = false; alarmaVentiladorRoto = false; alarmaFrioCritico = false; alarmaHumedadCritica = false; alarmaHumidificadorRoto = false;
float limiteFrioCritico = temperaturaObjetivoActual - 3.5; float limiteCalorCritico = temperaturaObjetivoActual + 1.0; float limiteVentiladorRoto = temperaturaObjetivoActual + 2.0; float limiteHumedadCritica = profesionalHumedadActual - 10.0; float limiteHumidificadorRoto = profesionalHumedadActual - 20.0;
bool enVentanaGraciaRecuperacion = (marcaTiempoFinEnfriamiento > 0 && (millis() - marcaTiempoFinEnfriamiento < 600000)); bool enVentanaGraciaArranqueInicial = (marcaTiempoInicioInyeccionVirgen > 0 && (millis() - marcaTiempoInicioInyeccionVirgen < 900000));
bool romperGraciaPorFuegoReal = (temp_read >= 41.5); bool romperEnfriamientoPorCongelacionReal = (temp_read <= 0);
if (temp_read >= limiteVentiladorRoto && temp_read > temperaturaObjetivoActual && (enVentanaGraciaArranqueInicial == false || romperGraciaPorFuegoReal == true)) alarmaVentiladorRoto = true;
else if (temp_read >= limiteCalorCritico && temp_read > temperaturaObjetivoActual && (enVentanaGraciaArranqueInicial == false || romperGraciaPorFuegoReal == true)) alarmaCalorCritico = true;
else if (temp_read <= limiteFrioCritico && (enVentanaGraciaRecuperacion == false && enVentanaGraciaArranqueInicial == false || romperEnfriamientoPorCongelacionReal == true)) { alarmaFrioCritico = true; }
if (humidity_read <= limiteHumidificadorRoto && enVentanaGraciaRecuperacion == false && enVentanaGraciaArranqueInicial == false) alarmaHumidificadorRoto = true;
else if (humidity_read <= limiteHumedadCritica && enVentanaGraciaRecuperacion == false && enVentanaGraciaArranqueInicial == false) alarmaHumedadCritica = true;
if (temp_read <= 35.0 && romperEnfriamientoPorCongelacionReal == false) {
alarmaVentiladorRoto = false; alarmaCalorCritico = false;
if (alarmaFrioCritico == false && alarmaHumedadCritica == false && alarmaHumidificadorRoto == false && fallaServoActiva == false) { hayAlgunaCrisisActiva = false; }
}
if (alarmaVentiladorRoto || alarmaCalorCritico) { digitalWrite(RELE_CALENTADOR, LOW); }
else { // 🌡️ HISTÉRESIS DE CALEFACCION REAL
if (temp_read > (temperaturaObjetivoActual + 0.1)) { digitalWrite(RELE_CALENTADOR, LOW); }
else if (temp_read < (temperaturaObjetivoActual - 0.2)) { if (diasPasados <= diasTotales) digitalWrite(RELE_CALENTADOR, HIGH); }
} // 🌫️ HISTÉRESIS DE HUMEDAD REAL
if (alarmaHumidificadorRoto || alarmaHumedadCritica || humidity_read < (profesionalHumedadActual - 2.0)) { if (diasPasados <= diasTotales) digitalWrite(RELE_HUMIDIFICADOR, HIGH); }
else if (humidity_read >= profesionalHumedadActual) { digitalWrite(RELE_HUMIDIFICADOR, LOW); }
}
}
bool crisisDetectadaAhora = (fallaSensorActiva || alarmaVentiladorRoto || alarmaCalorCritico || alarmaFrioCritico || alarmaHumidificadorRoto || alarmaHumedadCritica || fallaServoActiva);
if (crisisDetectadaAhora == true && incubadoraYaIniciada == true && diasPasados <= diasTotales) {
if (hayAlgunaCrisisActiva == false) { modoEdicionActivo = false; hayAlgunaCrisisActiva = true; buzzerSilenciadoManualmente = false; }
refrescarPantallaVivo(temp_read, humidity_read);
}
else {
if (hayAlgunaCrisisActiva == true) { hayAlgunaCrisisActiva = false; buzzerSilenciadoManualmente = false; actualizarOLED(); }
if (pantallaActual == 1 && incubadoraYaIniciada == true) { refrescarPantallaVivo(temp_read, humidity_read); }
}
if (incubadoraYaIniciada == true && pantallaActual != 1 && modoEdicionActivo == false && hayAlgunaCrisisActiva == false) {
if (millis() - tiempoUltimaActividadEncoder >= TIEMPO_ESPERA_MENU) { pantallaActual = 1; actualizarOLED(); }
}
}
if (incubadoraYaIniciada == true && hayAlgunaCrisisActiva == true && diasPasados <= diasTotales && buzzerSilenciadoManualmente == false) { tone(PIN_BUZZER, 1000); } else { noTone(PIN_BUZZER); }
if (incubadoraYaIniciada == true && modoEclosionActivo == false && modoTestCunaActivo == false && (millis() - ultimoTiempoVolteoBloqueo >= 7200000 || ultimoTiempoVolteoBloqueo == 0)) {
ultimoTiempoVolteoBloqueo = millis(); moviendoServoSuave = true; // MECIDO AUTOMÁTICO DE CUNA CADA 2 HORAS
if (anguloObjetivoDestino == 45 || anguloObjetivoDestino == 90) { anguloObjetivoDestino = 135; } else { anguloObjetivoDestino = 45; }
}
if (moviendoServoSuave == true && millis() - ultimoTiempoPasoServo >= 200) { // Cinemática del servo suave
ultimoTiempoPasoServo = millis();
if (anguloServoActual < anguloObjetivoDestino) { anguloServoActual++; miServo.write(anguloServoActual); }
else if (anguloServoActual > anguloObjetivoDestino) { anguloServoActual--; miServo.write(anguloServoActual); }
else {
if (modoTestCunaActivo == true) {
if (etapaTestCuna == 1) { delay(1000); etapaTestCuna = 2; anguloObjetivoDestino = anguloOrigenAntesDelTest; }
else if (etapaTestCuna == 2) { modoTestCunaActivo = false; etapaTestCuna = 0; moviendoServoSuave = false; }
} else { moviendoServoSuave = false; }
}
}
}
void ejecutarAccionClic() {
if (pantallaActual == 0) { // Clics desde el Menú de Inicio principal
if (opcionSeleccionada == 0) { pantallaActual = 2; opcionSeleccionada = 0; actualizarOLED(); }
else if (opcionSeleccionada == 1) { // Filtro anti-ceros fatales antes de iniciar marcha
if (especieSeleccionada == -1) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); u8g2.drawStr(5, 35, "SELECCIONAR ESPECIE"); u8g2.sendBuffer(); delay(2000); actualizarOLED(); }
else if (especieSeleccionada == 3 && (diasTotales == 0 || temperaturaDeseada == 0.0 || humidityDeseada == 0.0)) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); u8g2.drawStr(15, 35, "VALORES EN CERO"); u8g2.sendBuffer(); delay(2000); actualizarOLED(); }
else { marcaTiempoInicioInyeccionVirgen = millis(); calcularValoresClimaActual(); pantallaActual = 1; incubadoraYaIniciada = true; digitalWrite(RELE_VENTILADOR, HIGH); if (estadoLuzInterna) digitalWrite(RELE_ILUMINACION, HIGH); actualizarOLED(); }
} // Aduanas de protección para reinicios o cambios sobre la marcha
else if (opcionSeleccionada == 2) { if (incubadoraYaIniciada == false) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); u8g2.drawStr(15, 35, "NIDO NO INICIADO"); u8g2.sendBuffer(); delay(2000); actualizarOLED(); } else { botonOrigenAduana = 2; pantallaActual = 4; opcionSeleccionada = 0; actualizarOLED(); } }
else if (opcionSeleccionada == 3) { if (incubadoraYaIniciada == false) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); u8g2.drawStr(15, 35, "NIDO NO INICIADO"); u8g2.sendBuffer(); delay(2000); actualizarOLED(); } else { botonOrigenAduana = 3; pantallaActual = 4; opcionSeleccionada = 0; actualizarOLED(); } }
else if (opcionSeleccionada == 4) { pantallaActual = 3; opcionSeleccionada = 0; actualizarOLED(); }
else if (opcionSeleccionada == 5) { estadoLuzInterna = !estadoLuzInterna; digitalWrite(RELE_ILUMINACION, estadoLuzInterna ? HIGH : LOW); actualizarOLED(); }
}
else if (pantallaActual == 2) { // Clics desde el Menú de Especies
if (opcionSeleccionada == 3) { pantallaActual = 5; opcionSeleccionada = 0; backupDiasIncub = diasIncubacionManual; backupDiasEclo = diasEclosionManual; backupTempObj = tempManualBase; backupHumObj = humManualBase; backupTempEclo = tempEclosionManual; backupHumEclo = humEclosionManual; actualizarOLED(); }
else if (opcionSeleccionada == 4) { pantallaActual = 0; opcionSeleccionada = 0; actualizarOLED(); }
else { especieSeleccionada = opcionSeleccionada; calcularValoresClimaActual(); botonOrigenAduana = 0; pantallaActual = 4; opcionSeleccionada = 0; actualizarOLED(); }
}
else if (pantallaActual == 3) { // 🛠️ SUBMENÚ DE TESTEO MANUAL EN FRÍO DE COMPONENTES
if (opcionSeleccionada == 0) {
u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); u8g2.drawStr(10, 20, "PROBANDO CALOR..."); u8g2.sendBuffer(); digitalWrite(RELE_CALENTADOR, HIGH);
for (int i = 0; i < 7; i++) { float tReal = dht.readTemperature(); u8g2.clearBuffer(); u8g2.drawStr(10, 20, "PROBANDO CALOR..."); u8g2.setCursor(10, 45); u8g2.print("TEMP VIVO: "); if(isnan(tReal)) u8g2.print("0.0 C"); else { u8g2.print(tReal, 1); u8g2.print(" C"); } u8g2.sendBuffer(); delay(1000); }
digitalWrite(RELE_CALENTADOR, LOW); actualizarOLED();
}
else if (opcionSeleccionada == 1) { digitalWrite(RELE_VENTILADOR, HIGH); delay(2000); digitalWrite(RELE_VENTILADOR, LOW); }
else if (opcionSeleccionada == 2) { digitalWrite(RELE_HUMIDIFICADOR, HIGH); delay(3000); digitalWrite(RELE_HUMIDIFICADOR, LOW); }
else if (opcionSeleccionada == 3) { if (moviendoServoSuave == false && modoTestCunaActivo == false) { modoTestCunaActivo = true; etapaTestCuna = 1; anguloOrigenAntesDelTest = anguloServoActual; if (anguloOrigenAntesDelTest == 45 || anguloOrigenAntesDelTest == 90) anguloObjetivoDestino = 135; else anguloObjetivoDestino = 45; moviendoServoSuave = true; } }
else if (opcionSeleccionada == 4) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); u8g2.drawStr(10, 20, "PROBANDO BUZZER..."); u8g2.sendBuffer(); tone(PIN_BUZZER, 1000); delay(2000); noTone(PIN_BUZZER); actualizarOLED(); }
else if (opcionSeleccionada == 5) { pantallaActual = 0; opcionSeleccionada = 4; actualizarOLED(); }
}
else if (pantallaActual == 4 || (pantallaActual == 1 && botonOrigenAduana == 99)) {
if (botonOrigenAduana == 99) { // Escape de pánico total por clic largo
if (especieSeleccionada == 3) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); u8g2.drawStr(10, 28, "ESCAPE DE CRISIS"); u8g2.drawStr(15, 45, "RESET DE ESPECIE"); u8g2.drawHLine(0, 52, 126); u8g2.sendBuffer(); delay(2000); incubadoraYaIniciada = false; especieSeleccionada = -1; modoEclosionActivo = false; alarmaCalorCritico = false; alarmaVentiladorRoto = false; alarmaFrioCritico = false; alarmaHumedadCritica = false; alarmaHumidificadorRoto = false; fallaSensorActiva = false; hayAlgunaCrisisActiva = false; buzzerSilenciadoManualmente = false; digitalWrite(RELE_CALENTADOR, LOW); digitalWrite(RELE_HUMIDIFICADOR, LOW); noTone(PIN_BUZZER); botonOrigenAduana = 0; pantallaActual = 0; opcionSeleccionada = 0; actualizarOLED(); }
else { botonOrigenAduana = 0; actualizarOLED(); }
}
else if (opcionSeleccionada == 0) { if (incubadoraYaIniciada == true) { pantallaActual = 1; actualizarOLED(); } else { especieSeleccionada = -1; pantallaActual = 2; opcionSeleccionada = 0; actualizarOLED(); } }
else if (opcionSeleccionada == 1) {
// 🔒 REPARACIÓN FIJA DE MARA Y JUANJO: El bypass manual muerde origen cero y destraba el vivo al instante
if (incubadoraYaIniciada == true) {
if (botonOrigenAduana == 2) { modoEclosionActivo = false; ultimoCicloEnfriamientoPato = 0; calcularValoresClimaActual(); pantallaActual = 1; actualizarOLED(); }
else if (botonOrigenAduana == 3) { incubadoraYaIniciada = false; especieSeleccionada = -1; modoEclosionActivo = false; digitalWrite(RELE_CALENTADOR, LOW); digitalWrite(RELE_HUMIDIFICADOR, LOW); pantallaActual = 2; opcionSeleccionada = 0; actualizarOLED(); }
else if (botonOrigenAduana == 0) { calcularValoresClimaActual(); pantallaActual = 1; actualizarOLED(); }
}
else { calcularValoresClimaActual(); pantallaActual = 1; incubadoraYaIniciada = true; digitalWrite(RELE_VENTILADOR, HIGH); if (estadoLuzInterna) digitalWrite(RELE_ILUMINACION, HIGH); actualizarOLED(); }
}
}
else if (pantallaActual == 5) { // Edición Manual Experta sin ceros fatales
if (opcionSeleccionada >= 0 && opcionSeleccionada <= 5) { modoEdicionActivo = !modoEdicionActivo; actualizarOLED(); }
else if (opcionSeleccionada == 6) {
if (backupDiasIncub == 0 || backupTempObj == 0.0 || backupHumObj == 0.0 || backupDiasEclo == 0 || backupTempEclo == 0.0 || backupHumEclo == 0.0) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); u8g2.drawStr(15, 35, "VALORES EN CERO"); u8g2.sendBuffer(); delay(2000); actualizarOLED(); }
else { diasIncubacionManual = backupDiasIncub; diasEclosionManual = backupDiasEclo; diasTotales = backupDiasIncub + backupDiasEclo; temperaturaDeseada = backupTempObj; humidityDeseada = backupHumObj; tempEclosionManual = backupTempEclo; humEclosionManual = backupHumEclo; tempManualBase = backupTempObj; humManualBase = backupHumObj; especieSeleccionada = 3; calcularValoresClimaActual(); botonOrigenAduana = 0; pantallaActual = 4; opcionSeleccionada = 0; actualizarOLED(); }
}
else if (opcionSeleccionada == 7) { pantallaActual = 2; opcionSeleccionada = 3; modoEdicionActivo = false; actualizarOLED(); }
}
}
void calcularValoresClimaActual() { // Ajuste automático de metas climáticas
if (especieSeleccionada == 0) { diasTotales = 21; int puntoEclosion = diasTotales - 2; if (diasPasados > puntoEclosion) modoEclosionActivo = true; else modoEclosionActivo = false; if (modoEclosionActivo) { temperaturaDeseada = 36.9; humidityDeseada = 65.0; } else { temperaturaDeseada = 37.5; humidityDeseada = 50.0; } tiempoCalorEncendidoCiego = 20000; tiempoCalorApagadoCiego = 40000; }
else if (especieSeleccionada == 1) { diasTotales = 28; int puntoEclosion = diasTotales - 3; if (diasPasados > puntoEclosion) modoEclosionActivo = true; else modoEclosionActivo = false; if (modoEclosionActivo) { temperaturaDeseada = 36.5; humidityDeseada = 75.0; } else { temperaturaDeseada = 37.2; humidityDeseada = 55.0; } tiempoCalorEncendidoCiego = 25000; tiempoCalorApagadoCiego = 35000; }
else if (especieSeleccionada == 2) { diasTotales = 17; int puntoEclosion = diasTotales - 2; if (diasPasados > puntoEclosion) modoEclosionActivo = true; else modoEclosionActivo = false; if (modoEclosionActivo) { temperaturaDeseada = 37.1; humidityDeseada = 65.0; } else { temperaturaDeseada = 37.7; humidityDeseada = 45.0; } tiempoCalorEncendidoCiego = 12000; tiempoCalorApagadoCiego = 48000; }
else if (especieSeleccionada == 3) { if (diasPasados >= diasIncubacionManual) modoEclosionActivo = true; else modoEclosionActivo = false; if (modoEclosionActivo) { temperaturaDeseada = tempEclosionManual; humidityDeseada = humEclosionManual; } else { temperaturaDeseada = tempManualBase; humidityDeseada = humManualBase; } tiempoCalorEncendidoCiego = 20000; tiempoCalorApagadoCiego = 40000; }
}
void actualizarOLED() {
u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tr); char ind = modoEdicionActivo ? '?' : '>';
if (pantallaActual == 0) { u8g2.drawStr(7, 12, "INCUBADORA DE AVES"); u8g2.drawHLine(0, 15, 122); u8g2.setCursor(0, 30); u8g2.print(ind); u8g2.print(" "); u8g2.print(menuMain[opcionSeleccionada]); if (opcionSeleccionada == 5) { u8g2.print(": "); u8g2.print(estadoLuzInterna ? "ON" : "OFF"); } int o1 = (opcionSeleccionada + 1) % OP_MAIN; u8g2.setCursor(10, 45); u8g2.print(menuMain[o1]); if (o1 == 5) { u8g2.print(": "); u8g2.print(estadoLuzInterna ? "ON" : "OFF"); } int o2 = (opcionSeleccionada + 2) % OP_MAIN; u8g2.setCursor(10, 60); u8g2.print(menuMain[o2]); if (o2 == 5) { u8g2.print(": "); u8g2.print(estadoLuzInterna ? "ON" : "OFF"); } }
else if (pantallaActual == 1) return;
else if (pantallaActual == 2) { u8g2.drawStr(4, 12, "SELECCIONAR ESPECIE"); u8g2.drawHLine(0, 15, 122); u8g2.setCursor(0, 30); u8g2.print(ind); u8g2.print(" "); u8g2.print(menuEspecies[opcionSeleccionada]); int o1 = (opcionSeleccionada + 1) % OP_ESPECIES; u8g2.setCursor(10, 45); u8g2.print(menuEspecies[o1]); int o2 = (opcionSeleccionada + 2) % OP_ESPECIES; u8g2.setCursor(10, 60); u8g2.print(menuEspecies[o2]); }
else if (pantallaActual == 3) { u8g2.drawStr(4, 12, "TEST DE COMPONENTES"); u8g2.drawHLine(0, 15, 122); u8g2.setCursor(0, 30); u8g2.print(ind); u8g2.print(" "); u8g2.print(menuTest[opcionSeleccionada]); int o1 = (opcionSeleccionada + 1) % OP_TEST; u8g2.setCursor(10, 45); u8g2.print(menuTest[o1]); int o2 = (opcionSeleccionada + 2) % OP_TEST; u8g2.setCursor(10, 60); u8g2.print(menuTest[o2]); }
else if (pantallaActual == 4) { u8g2.drawStr(16, 12, "! ADVERTENCIA !"); u8g2.drawHLine(0, 15, 122); if (incubadoraYaIniciada == true) { if (botonOrigenAduana == 3) u8g2.drawStr(5, 32, "CAMBIAR LA ESPECIE?"); else u8g2.drawStr(5, 32, "REINICIAR NIDO?"); } else { u8g2.drawStr(5, 32, "DESEA INICIAR NIDO?"); } u8g2.setCursor(15, 48); u8g2.print(opcionSeleccionada == 0 ? ind : ' '); u8g2.print(" NO, VOLVER"); u8g2.setCursor(15, 62); u8g2.print(opcionSeleccionada == 1 ? ind : ' '); u8g2.print(" SI, INICIAR"); }
else if (pantallaActual == 5) { u8g2.drawStr(4, 12, "MODO MANUAL EXPERTO"); u8g2.drawHLine(0, 15, 122); u8g2.setCursor(0, 30); u8g2.print(ind); u8g2.print(" "); u8g2.print(menuManualEdit[opcionSeleccionada]); if(opcionSeleccionada==0)u8g2.print(backupDiasIncub);else if(opcionSeleccionada==1)u8g2.print(backupDiasEclo);else if(opcionSeleccionada==2)u8g2.print(backupTempObj,1);else if(opcionSeleccionada==3)u8g2.print(backupHumObj,0);else if(opcionSeleccionada==4)u8g2.print(backupTempEclo,1);else if(opcionSeleccionada==5)u8g2.print(backupHumEclo,0); int o1 = (opcionSeleccionada + 1) % OP_MANUAL_EDIT; u8g2.setCursor(10, 45); u8g2.print(menuManualEdit[o1]); if(o1==0)u8g2.print(backupDiasIncub);else if(o1==1)u8g2.print(backupDiasEclo);else if(o1==2)u8g2.print(backupTempObj,1);else if(o1==3)u8g2.print(backupHumObj,0);else if(o1==4)u8g2.print(backupTempEclo,1);else if(o1==5)u8g2.print(backupHumEclo,0); int o2 = (opcionSeleccionada + 2) % OP_MANUAL_EDIT; u8g2.setCursor(10, 60); u8g2.print(menuManualEdit[o2]); if(o2==0)u8g2.print(backupDiasIncub);else if(o2==1)u8g2.print(backupDiasEclo);else if(o2==2)u8g2.print(backupTempObj,1);else if(o2==3)u8g2.print(backupHumObj,0);else if(o2==4)u8g2.print(backupTempEclo,1);else if(o2==5)u8g2.print(backupHumEclo,0); }
u8g2.sendBuffer();
}
void refrescarPantallaVivo(float t, float h) {
u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x12_tf);
if (diasPasados > diasTotales && incubadoraYaIniciada == true) { u8g2.drawStr(13, 12, "NIDO TERMINADO"); u8g2.drawHLine(0, 15, 122); u8g2.drawStr(16, 32, "CICLO CONCLUIDO"); u8g2.drawStr(10, 45, "SISTEMA: APAGADO"); u8g2.drawStr(7, 62, "[CLICK LARGO EXIT]"); }
else if (hayAlgunaCrisisActiva == true) {
if (fallaSensorActiva) { u8g2.drawStr(5, 12, "!!! CRISIS TOTAL !!!"); u8g2.drawStr(0, 30, "FALLO: SENSOR DHT22"); u8g2.drawStr(0, 45, "MODO CIEGO: ACTIVO"); u8g2.drawStr(0, 60, "EMERGENCIA X TIEMPO"); }
else if (alarmaVentiladorRoto) { u8g2.drawStr(5, 12, "!!! DANGER CRITICO!!"); u8g2.drawStr(0, 30, "FALLO: VENTILADOR"); u8g2.setCursor(0, 45); u8g2.print("TEMP ACTUAL: "); u8g2.print(t,1); u8g2.drawStr(0, 60, "REVISAR VENTILADOR!"); }
else if (alarmaCalorCritico) { u8g2.drawStr(5, 12, "!!! ALERTA CRITICA!!"); u8g2.drawStr(0, 30, "FALLO: CALEFACCION"); u8g2.setCursor(0, 45); u8g2.print("TEMP ALTA: "); u8g2.print(t,1); u8g2.drawStr(0, 60, "ESPERANDO ENFRIAR..."); }
else if (alarmaFrioCritico) { u8g2.drawStr(5, 12, "!!! DANGER CRITICO!!"); u8g2.drawStr(0, 30, "FALLO: CALEFACCION"); u8g2.setCursor(0, 45); u8g2.print("FRIO EXTREMO: "); u8g2.print(t,1); u8g2.drawStr(0, 60, "REVISAR CALEFACCION!"); }
else if (alarmaHumidificadorRoto) { u8g2.drawStr(5, 12, "!!! DANGER CRITICO!!"); u8g2.drawStr(0, 30, "FALLO: HUMIDIFICADOR"); u8g2.setCursor(0, 45); u8g2.print("HUMEDAD: "); u8g2.print(h,0); u8g2.print("% SEC"); u8g2.drawStr(0, 60, "REVISAR AGUA/CABLE"); }
else if (alarmaHumedadCritica) { u8g2.drawStr(5, 12, "!!! ALERTA CRITICA!!"); u8g2.drawStr(0, 30, "FALLO: HUMEDAD BAJA"); u8g2.setCursor(0, 45); u8g2.print("HUMEDAD CRIT: "); u8g2.print(h,0); u8g2.print("%"); u8g2.drawStr(0, 60, "INYECTANDO VAPOR..."); }
if (buzzerSilenciadoManualmente == false && (millis() % 1000 < 500)) { u8g2.setFont(u8g2_font_unifont_t_symbols); u8g2.drawGlyph(110, 13, 0x266a); }
}
else {
if (especieSeleccionada == 0) u8g2.drawStr(10, 12, "INCUBANDO GALLINA"); else if (especieSeleccionada == 1) u8g2.drawStr(19, 12, "INCUBANDO PATO"); else if (especieSeleccionada == 2) u8g2.drawStr(7, 12, "INCUBANDO CODORNIZ"); else if (especieSeleccionada == 3) u8g2.drawStr(13, 12, "INCUBANDO MANUAL"); else u8g2.drawStr(10, 12, "INCUBADORA DE AVES");
u8g2.drawHLine(0, 15, 122); u8g2.setCursor(5, 26); u8g2.print("T: "); u8g2.print(t, 1); u8g2.print(" C"); u8g2.setCursor(75, 26); u8g2.print("H: "); u8g2.print(h, 0); u8g2.print("%");
if (especieSeleccionada == 3 && hayAlgunaCrisisActiva) { u8g2.drawStr(5, 38, "FALLO: DETECTADO"); u8g2.drawStr(5, 50, "REVISAR VALORES"); }
else {
if (modoEclosionActivo) { // Dibujo de fase de nacimiento con Conteo Humano de días en la OLED
int totalDiasDeLaEclo = (especieSeleccionada == 0) ? 2 : ((especieSeleccionada == 1) ? 3 : ((especieSeleccionada == 2) ? 2 : diasEclosionManual)); int baseInicioEclo = (especieSeleccionada == 3) ? diasIncubacionManual : ((especieSeleccionada == 0) ? (21 - 2) : ((especieSeleccionada == 1) ? (28 - 3) : (17 - 2)));
int diasTranscurridosEclo = diasPasados - baseInicioEclo; if (diasTranscurridosEclo <= 0) diasTranscurridosEclo = 1; if (diasTranscurridosEclo > totalDiasDeLaEclo) diasTranscurridosEclo = totalDiasDeLaEclo;
int diasRealesAcumulados = baseInicioEclo + diasTranscurridosEclo; if (diasRealesAcumulados > diasTotales) diasRealesAcumulados = diasTotales;
u8g2.setCursor(5, 38); u8g2.print("INCUBADO: "); u8g2.print(baseInicioEclo); u8g2.print("/"); u8g2.print(baseInicioEclo); u8g2.setCursor(5, 50); u8g2.print("DIAS ECLO: "); u8g2.print(diasTranscurridosEclo); u8g2.print("/"); u8g2.print(totalDiasDeLaEclo); u8g2.setCursor(5, 62); u8g2.print("DIA ACTUAL: "); u8g2.print(diasRealesAcumulados); u8g2.print("/"); u8g2.print(diasTotales);
} else { // 🔒 LÓGICA DE JUANJO: Se le suma 1 al texto para informar el "DÍA ACTUAL: 1" desde el arranque
u8g2.setCursor(5, 38); u8g2.print("DIA ACTUAL: "); u8g2.print(diasPasados + 1); u8g2.print("/"); u8g2.print(diasTotales); if (enFaseDeEnfriamientoActiva == true) u8g2.drawStr(5, 50, "ENFRIANDO PATO"); else u8g2.drawStr(5, 50, "VOLTEO: ACTIVO");
}
}
} u8g2.sendBuffer();
}
void dibujarPantallaCrisis(const char* t1, const char* t2, const char* t3, float val, const char* uni) { u8g2.drawStr(5, 12, t1); u8g2.drawStr(0, 30, t2); u8g2.setCursor(0, 45); u8g2.print("TEMP ACTUAL: "); u8g2.print(val, 1); u8g2.print(uni); u8g2.drawStr(0, 60, t3); }
void apagarAlarmasTermicas() { alarmaCalorCritico = alarmaVentiladorRoto = alarmaFrioCritico = alarmaHumedadCritica = alarmaHumidificadorRoto = false; }