#include <MentorBit.h>
#include <MentorBitMotores.h>
#include <MentorBitPantallas.h>
#include <MentorBitVL53.h>
#include <MentorBitIR.h>
#include <MentorBitDisplay.h>
#include <MentorBitSensores.h>
#include <MentorBitLDR.h>
#include <MentorBitDHT11.h>
#include <MentorBitPir.h>
#include <MentorBitRGB.h>
#include <MentorBitPuertos.h>
#include <MentorBitMQ.h>
#include <MentorBitDataLogger.h>
// --- DEFINICIÓN DE PINES (¡AJUSTA ESTO SEGÚN TUS CONEXIONES!) ---
// Actuadores
#define LED_PIN_1 MentorBitPuertos::PUERTO_MODULO_2_DIGITAL
// Pin para el Sensor IR de entrada
#define PIN_IR MentorBitPuertos::PUERTO_GENERICO_1_DIGITAL_5
// Pin para el Micro Servo SG90 (puerta auxiliar)
#define SERVO MentorBitMotores::SERVOMOTOR_1
// --- INSTANCIACIÓN DE OBJETOS ---
MentorBit miMentorBit;
MentorBitMotores DriverMotores;
MentorBitDisplay Display;
MentorBitPantallas DriverPantallas;
MentorBitPuertos miModuloPuertos;
MentorBitVL53 laser;
MentorBitDataLogger miDataLogger;
// Sensores
MentorBitPir miPIR(miModuloPuertos.PUERTO_GENERICO_1_DIGITAL_2);
MentorBitDHT11 miDHT(miModuloPuertos.PUERTO_MODULO_1_DIGITAL);
MentorBitIR miIR_Entrada(PIN_IR);
MentorBitMQ2 miMQ(miMQ.co, miModuloPuertos.PUERTO_GENERICO_1_ANALOG_1);
// Declaramos el objeto de los RGBs
MentorBitRGB led_rgb1(LED_PIN_1);
// --- VARIABLES GLOBALES ---
unsigned long tiempoUltimaPresencia = 0;
const unsigned long TIMEOUT_PRESENCIA = 10000; // 10 segundos sin presencia para apagar luces (ajustable)
const int UMBRAL_HUMEDAD_ALTA = 70; // % RH (Humedad Relativa)
const int UMBRAL_TEMP_ALTA = 28; // Grados Celsius
const int UMBRAL_LUMINOSIDAD_BAJA = 700; // Valor del LDR para considerar que hay poca luz
const int UMBRAL_LUMINOSIDAD_ALTA = 200; // Valor del LDR para considerar que hay mucha luz
const int UMBRAL_DISTANCIA_COCHE = 40; // Distancia en mm para detectar coche aparcado (85cm si no hay objeto)
const unsigned long TIEMPO_MOVIMIENTO_PUERTA = 4000; // Tiempo en ms que tarda la puerta en abrir/cerrar
const unsigned long TIEMPO_PUERTA_ABIERTA = 5000; // Tiempo en ms que la puerta permanece abierta
// Número de pasos para el motor para abrir/cerrar completamente
const int PASOS_MOTOR_GARAGE = 300;
// Estados de la puerta del garaje
enum EstadoPuerta { CERRADA, ABRIENDO, ABIERTA, CERRANDO };
EstadoPuerta estadoActualPuerta = CERRADA;
unsigned long tiempoUltimoCambioEstadoPuerta = 0; // Para gestionar temporizadores
// --- VARIABLES PARA EL PULSADOR DE LA PUERTA AUXILIAR ---
bool puertaAuxiliarAbierta = false; // Estado de la puerta auxiliar (false = cerrada, true = abierta)
bool lastButtonStateDigital = HIGH; // Estado anterior del pulsador digital (HIGH por INPUT_PULLUP)
unsigned long lastDebounceTimeDigital = 0; // Última vez que se detectó un cambio válido en el pulsador digital
const unsigned long DEBOUNCE_DELAY = 50; // Retardo en ms para el anti-rebotes
// Umbral de PPM para considerar la concentración de CO como peligrosa
const float CO_ALTO = 1.5; //5 ppm, puedes ajustar este valor
const float GLP_ALTO = 1; //0 ppm
const float H2_BAJO = 1; //1 ppm
const float ALCOHOL_ALTO = 1; //0 ppm
const float PROPANO_ALTO = 1; //0 ppm
// Variables para el parpadeo de la alarma
unsigned long lastBlinkTime = 0;
const unsigned long BLINK_INTERVAL = 200; // Intervalo de parpadeo en ms (200ms ON, 200ms OFF)
bool alarmState = false; // true = alarma activa (sonando y parpadeando)
// Variable con el nombre del Archivo que vamos a emplear
String archivo = "Alarma.txt";
// --- FUNCIONES AUXILIARES ---
void establecerColorRGB(int r, int g, int b) {
miMentorBit.encenderRGB(r, g, b);
}
// Función para actualizar la pantalla OLED
void actualizarOLED(String linea1, String linea2 = "", String linea3 = "", String linea4 = "",
String linea5 = "", String linea6 = "") {
DriverPantallas.oled.clearDisplay(); // Limpia la pantalla
DriverPantallas.oled.setCursor(0, 0); DriverPantallas.oled.print(linea1);
DriverPantallas.oled.setCursor(0, 10); DriverPantallas.oled.print(linea2);
DriverPantallas.oled.setCursor(0, 20); DriverPantallas.oled.print(linea3);
DriverPantallas.oled.setCursor(0, 30); DriverPantallas.oled.print(linea4);
DriverPantallas.oled.setCursor(0, 40); DriverPantallas.oled.print(linea5);
DriverPantallas.oled.setCursor(0, 50); DriverPantallas.oled.print(linea6);
DriverPantallas.oled.display(); // Muestra el contenido en la pantalla
}
// NUEVA FUNCIÓN QUE REEMPLAZA A activarAlarmaVisualAuditiva y desactivarAlarma
void gestionarAlarma(bool hayAlarma, float concentracion, const String& nombreGas,
String& linea5, String& linea6) {
if (hayAlarma) {
// --- Activar alarma física ---
unsigned long currentTime = millis();
if (currentTime - lastBlinkTime >= BLINK_INTERVAL) {
lastBlinkTime = currentTime;
alarmState = !alarmState; // Alternar estado (ON/OFF)
if (alarmState) {
digitalWrite(miMentorBit.LED_ROJO, HIGH);
digitalWrite(miMentorBit.LED_AZUL, LOW);
tone(miMentorBit.ZUMBADOR, 1000, 300);
} else {
digitalWrite(miMentorBit.LED_ROJO, LOW);
noTone(miMentorBit.ZUMBADOR);
}
}
// --- Preparar mensajes para el OLED ---
linea5 = "ALARMA: " + nombreGas;
linea6 = String(concentracion, 1) + " PPM. ¡VENTILE!";
// --- Registrar en SD (DataLogger) ---
// (Esta lógica se mantiene aquí para no repetirla en el loop)
Serial.print("ALARMA DE GAS " + nombreGas + ": ");
Serial.print(concentracion, 1);
Serial.println(" ppm");
String fecha_hora_actual = miDataLogger.obtenerHoraRTC();
String fecha_actual = fecha_hora_actual.substring(0, 10);
String hora_actual = fecha_hora_actual.substring(11);
String registro = "Alarma por " + nombreGas +
" el " + fecha_actual +
" a las " + hora_actual +
" (" + String(concentracion, 1) + " ppm)";
miDataLogger.escribirEnArchivo(archivo, registro);
Serial.println(registro);
} else {
// --- Desactivar alarma física ---
digitalWrite(miMentorBit.LED_AZUL, HIGH);
noTone(miMentorBit.ZUMBADOR);
alarmState = false;
// --- Preparar mensajes de estado normal ---
linea5 = "Calidad del Aire: OK";
linea6 = "";
}
}
void setup() {
Serial.begin(9600);
Serial.println("Iniciando Casa Domótica...");
pinMode(miModuloPuertos.PUERTO_GENERICO_1_ANALOG_2, INPUT); //LDR
// Inicializar sensor IR de entrada
pinMode(PIN_IR, INPUT);
// Inicializar servo
DriverMotores.inicializarServo(SERVO);
DriverMotores.moverServo(SERVO, 0);
// Inicializamos la pantalla LCD
DriverPantallas.inicializarLCD();
DriverPantallas.lcd.backlight();
DriverPantallas.lcd.clear();
DriverPantallas.lcd.setCursor(0, 0);
DriverPantallas.lcd.print("Casa Domotica");
DriverPantallas.lcd.setCursor(0, 1);
DriverPantallas.lcd.print("Iniciando...");
// Inicializar pantalla OLED
DriverPantallas.inicializarOLED();
DriverPantallas.oled.setTextSize(1);
DriverPantallas.oled.setTextColor(SSD1306_WHITE);
actualizarOLED("Casa Domotica", "Iniciando...");
delay(2000);
// Inicializamos el Display 7 Segmentos
Display.inicializarDisplay();
Display.imprimir(0); // Mostrar 0 al inicio
// Inicializar pin del zumbador apagado
noTone(miMentorBit.ZUMBADOR);
// Inicializar LED RGB del MentorBit
miMentorBit.apagarRGB();
led_rgb1.begin(); // INICIALIZA la librería
led_rgb1.cambiarBrillo(50);
digitalWrite(miMentorBit.LED_ROJO, LOW);
digitalWrite(miMentorBit.LED_VERDE, LOW);
digitalWrite(miMentorBit.LED_AZUL, LOW);
// Inicializar sensor de distancia láser
laser.begin();
actualizarOLED("Casa Domotica", "Listo para operar");
establecerColorRGB(0, 255, 0); // Verde - Listo
delay(1000);
// Calibrar el sensor MQ-2
Serial.println("Calibrando sensor MQ-2. Esto puede tardar 20-30 segundos...");
actualizarOLED("Calibrando MQ-2...", "Mantener en aire limpio");
miMQ.calibrar();
Serial.println("Calibracion MQ-2 completa.");
actualizarOLED("Calibracion OK", "Sistema Listo!");
delay(1000);
// Inicalizamos el RTC
miDataLogger.inicializarRTC();
// Configuramos el RTC con la fecha y la hora del PC
miDataLogger.ajustarHoraRTC();
// Guardamos un primer mensaje distintivo al comienzo de la cuenta
miDataLogger.escribirEnArchivo(archivo, "---- Registro de alarmas ----");
// Esperamos un poco antes de empezar
delay(2000);
// Limpiamos la pantalla antes de empezar
DriverPantallas.lcd.clear();
}
void loop() {
// --- Variables para la pantalla OLED ---
String oledL1, oledL2, oledL3, oledL4, oledL5, oledL6;
// --- 1. Lectura de Sensores ---
int temperatura_dht = miDHT.obtenerTemperatura();
int humedad_dht = miDHT.obtenerHumedad();
int luminosidad_ldr = analogRead(miModuloPuertos.PUERTO_GENERICO_1_ANALOG_2);
int lectura_pir = miPIR.lecturaDigital(); // 1 si hay presencia, 0 si no
int velocidad_potenciometro = analogRead(miMentorBit.POTENCIOMETRO); // Lectura del potenciómetro (0-1023)
// --- 2. Control de Ventilación (Motor N20 DC) ---
int velocidad_ventilador_pwm = 0; // Velocidad del ventilador (0-255 para PWM)
String estadoVentilador = "OFF";
if (humedad_dht > UMBRAL_HUMEDAD_ALTA || temperatura_dht > UMBRAL_TEMP_ALTA) {
velocidad_ventilador_pwm = map(velocidad_potenciometro, 0, 1023, 50, 255); // Mínimo 50 para que gire
estadoVentilador = "ON";
DriverMotores.moverMotorDC(velocidad_ventilador_pwm, 0);
}
else{
DriverMotores.moverMotorDC(0, 0);
}
int nivel_ventilacion_display = map(velocidad_ventilador_pwm, 0, 255, 0, 9);
Display.imprimir(nivel_ventilacion_display);
// --- 3. Control de Iluminación y Presencia (LDR, PIR, RGB Externo) ---
bool hayPresencia = (miPIR.lecturaDigital());
if (hayPresencia) {
tiempoUltimaPresencia = millis();
}
if (hayPresencia || (millis() - tiempoUltimaPresencia < TIMEOUT_PRESENCIA)) {
digitalWrite(miMentorBit.LED_ROJO, HIGH);
digitalWrite(miMentorBit.LED_VERDE, HIGH);
digitalWrite(miMentorBit.LED_AZUL, HIGH);
} else {
digitalWrite(miMentorBit.LED_ROJO, LOW);
digitalWrite(miMentorBit.LED_VERDE, LOW);
digitalWrite(miMentorBit.LED_AZUL, LOW);
}
int brilloRGB = 50;
if (luminosidad_ldr > UMBRAL_LUMINOSIDAD_BAJA) {
brilloRGB = 255;
led_rgb1.cambiarBrillo(brilloRGB);
} else if (luminosidad_ldr > UMBRAL_LUMINOSIDAD_ALTA) {
brilloRGB = 100;
led_rgb1.cambiarBrillo(brilloRGB);
} else {
brilloRGB = 10;
led_rgb1.cambiarBrillo(brilloRGB);
}
led_rgb1.cambiarColor(255, 255, 255);
// --- 4. Mostrar en Pantalla LCD ---
DriverPantallas.lcd.clear();
DriverPantallas.lcd.setCursor(0, 0);
DriverPantallas.lcd.print("Temp:");
DriverPantallas.lcd.print(temperatura_dht);
DriverPantallas.lcd.print("C Hum:");
DriverPantallas.lcd.print(humedad_dht);
DriverPantallas.lcd.print("%");
DriverPantallas.lcd.setCursor(0, 1);
DriverPantallas.lcd.print("Vent:");
DriverPantallas.lcd.print(estadoVentilador);
DriverPantallas.lcd.print(" LDR:");
DriverPantallas.lcd.print(luminosidad_ldr);
// --- Lógica del Garaje ---
bool vehiculoEnEntrada = miIR_Entrada.objetoPresente();
float distancia_coche_garaje = laser.leerDistancia();
// Manejo de estados de la puerta principal del garaje
switch (estadoActualPuerta) {
case CERRADA:
if (vehiculoEnEntrada) {
if (distancia_coche_garaje > UMBRAL_DISTANCIA_COCHE) {
DriverMotores.moverMotorPP(300, 0);
delay(2);
estadoActualPuerta = ABRIENDO;
tiempoUltimoCambioEstadoPuerta = millis();
}
}
break;
case ABRIENDO:
if (millis() - tiempoUltimoCambioEstadoPuerta >= TIEMPO_MOVIMIENTO_PUERTA) {
estadoActualPuerta = ABIERTA;
tiempoUltimoCambioEstadoPuerta = millis();
}
break;
case ABIERTA:
if (millis() - tiempoUltimoCambioEstadoPuerta >= TIEMPO_PUERTA_ABIERTA) {
DriverMotores.moverMotorPP(300, 1);
delay(2);
estadoActualPuerta = CERRANDO;
tiempoUltimoCambioEstadoPuerta = millis();
}
break;
case CERRANDO:
if (millis() - tiempoUltimoCambioEstadoPuerta >= TIEMPO_MOVIMIENTO_PUERTA) {
estadoActualPuerta = CERRADA;
}
break;
}
// Preparar texto para las líneas 1-4 del OLED (Garaje)
oledL1 = (estadoActualPuerta == CERRADA) ? "Garaje Cerrado" : (estadoActualPuerta == ABRIENDO ? "Abriendo Puerta..." : (estadoActualPuerta == ABIERTA ? "Puerta Abierta" : "Cerrando Puerta..."));
oledL2 = (estadoActualPuerta == CERRADA) ? "Plaza Libre: Si" : (estadoActualPuerta == ABRIENDO ? "Vehiculo Detectado" : (estadoActualPuerta == ABIERTA ? "Esperando cierre..." : ""));
oledL3 = "Dist: " + String(distancia_coche_garaje) + "mm";
oledL4 = (distancia_coche_garaje > 0 && distancia_coche_garaje <= UMBRAL_DISTANCIA_COCHE) ? "Coche Aparcado!" : (distancia_coche_garaje == 0) ? "Sensor Fuera Rango" : "Plaza Libre";
// Control de Acceso Auxiliar (Servo)
int reading = digitalRead(miMentorBit.PULSADOR_PULLUP_DIGITAL);
if (reading != lastButtonStateDigital && (millis() - lastDebounceTimeDigital > DEBOUNCE_DELAY)) {
lastDebounceTimeDigital = millis();
if (reading == LOW) {
if (puertaAuxiliarAbierta) {
DriverMotores.moverServo(SERVO, 0);
puertaAuxiliarAbierta = false;
} else {
DriverMotores.moverServo(SERVO, 90);
puertaAuxiliarAbierta = true;
}
}
}
lastButtonStateDigital = reading;
// --- Monitorización y Alarma de Gas ---
miMQ.cambiarDeteccionGas(miMQ.co);
float concentracion_co_ppm = miMQ.obtenerLecturaMQ();
miMQ.cambiarDeteccionGas(miMQ.glp);
float concentracion_glp_ppm = miMQ.obtenerLecturaMQ();
miMQ.cambiarDeteccionGas(miMQ.h2);
float concentracion_h2_ppm = miMQ.obtenerLecturaMQ();
miMQ.cambiarDeteccionGas(miMQ.alcohol);
float concentracion_alcohol_ppm = miMQ.obtenerLecturaMQ();
miMQ.cambiarDeteccionGas(miMQ.propano);
float concentracion_propano_ppm = miMQ.obtenerLecturaMQ();
// Preparar texto para las líneas 5 y 6 del OLED (Alarma)
if (concentracion_co_ppm > CO_ALTO) {
gestionarAlarma(true, concentracion_co_ppm, "CO", oledL5, oledL6);
}
else if (concentracion_glp_ppm > GLP_ALTO) {
gestionarAlarma(true, concentracion_glp_ppm, "GLP", oledL5, oledL6);
}
else if (concentracion_h2_ppm < H2_BAJO) {
gestionarAlarma(true, concentracion_h2_ppm, "H2", oledL5, oledL6);
}
else if (concentracion_alcohol_ppm > ALCOHOL_ALTO) {
gestionarAlarma(true, concentracion_alcohol_ppm, "ALCOHOL", oledL5, oledL6);
}
else if (concentracion_propano_ppm > PROPANO_ALTO) {
gestionarAlarma(true, concentracion_propano_ppm, "PROPANO", oledL5, oledL6);
}
else {
gestionarAlarma(false, 0, "", oledL5, oledL6);
}
// --- ACTUALIZACIÓN ÚNICA DE LA PANTALLA OLED ---
actualizarOLED(oledL1, oledL2, oledL3, oledL4, oledL5, oledL6);
delay(500);
}