#include <Arduino.h> // Necesario para funciones básicas de Arduino como pinMode, digitalWrite, analogRead, delay, Serial.print, etc.
// --- CLASES DEL SISTEMA ---
// Clase Barrera
// Representa una barrera de seguridad vehicular.
// Encapsula su estado y las acciones de abrir/cerrar.
class Barrera {
public:
String id;
String estado; // Estados: "CERRADA", "ABIERTA", "ABRIENDO", "CERRANDO"
int pinMotorDir1; // Pin para controlar la dirección 1 del motor
int pinMotorDir2; // Pin para controlar la dirección 2 del motor
int pinEnableMotor; // Pin para habilitar el motor (PWM para velocidad, o HIGH/LOW)
// Nota: Para una barrera real, necesitarías sensores de fin de carrera
// para saber cuándo está completamente abierta o cerrada. Aquí se simula con delay.
Barrera(String id_barrera, int pMotorDir1, int pMotorDir2, int pEnableMotor) {
id = id_barrera;
estado = "CERRADA";
pinMotorDir1 = pMotorDir1;
pinMotorDir2 = pMotorDir2;
pinEnableMotor = pEnableMotor;
pinMode(pinMotorDir1, OUTPUT);
pinMode(pinMotorDir2, OUTPUT);
pinMode(pinEnableMotor, OUTPUT);
digitalWrite(pinMotorDir1, LOW); // Asegurarse de que el motor esté parado al inicio
digitalWrite(pinMotorDir2, LOW);
digitalWrite(pinEnableMotor, LOW); // Motor deshabilitado
Serial.print("Barrera '");
Serial.print(id);
Serial.println("' inicializada. Estado: CERRADA");
}
// Funciones para controlar el motor
void _moverMotor(String direccion) {
if (direccion == "ARRIBA") {
digitalWrite(pinMotorDir1, HIGH);
digitalWrite(pinMotorDir2, LOW);
} else if (direccion == "ABAJO") {
digitalWrite(pinMotorDir1, LOW);
digitalWrite(pinMotorDir2, HIGH);
} else { // Detener
digitalWrite(pinMotorDir1, LOW);
digitalWrite(pinMotorDir2, LOW);
}
digitalWrite(pinEnableMotor, HIGH); // Habilitar el motor
}
void _detenerMotor() {
digitalWrite(pinEnableMotor, LOW); // Deshabilitar el motor
digitalWrite(pinMotorDir1, LOW);
digitalWrite(pinMotorDir2, LOW);
}
bool abrir() {
if (estado == "CERRADA") {
Serial.print("Barrera '");
Serial.print(id);
Serial.println("': Recibiendo orden de ABRIR.");
estado = "ABRIENDO";
Serial.print("Barrera '");
Serial.print(id);
Serial.print("': Estado -> ");
Serial.println(estado);
_moverMotor("ARRIBA");
delay(2000); // Simula el tiempo que toma abrir (2 segundos)
_detenerMotor();
estado = "ABIERTA";
Serial.print("Barrera '");
Serial.print(id);
Serial.print("': Estado -> ");
Serial.println(estado);
return true;
} else if (estado == "ABIERTA") {
Serial.print("Barrera '");
Serial.print(id);
Serial.println("': Ya está ABIERTA.");
return true;
} else {
Serial.print("Barrera '");
Serial.print(id);
Serial.print("': No se puede abrir. Estado actual: ");
Serial.println(estado);
return false;
}
}
bool cerrar() {
if (estado == "ABIERTA") {
Serial.print("Barrera '");
Serial.print(id);
Serial.println("': Recibiendo orden de CERRAR.");
estado = "CERRANDO";
Serial.print("Barrera '");
Serial.print(id);
Serial.print("': Estado -> ");
Serial.println(estado);
_moverMotor("ABAJO");
delay(2000); // Simula el tiempo que toma cerrar (2 segundos)
_detenerMotor();
estado = "CERRADA";
Serial.print("Barrera '");
Serial.print(id);
Serial.print("': Estado -> ");
Serial.println(estado);
return true;
} else if (estado == "CERRADA") {
Serial.print("Barrera '");
Serial.print(id);
Serial.println("': Ya está CERRADA.");
return true;
} else {
Serial.print("Barrera '");
Serial.print(id);
Serial.print("': No se puede cerrar. Estado actual: ");
Serial.println(estado);
return false;
}
}
String obtener_estado() {
return estado;
}
};
// Clase SensorPresencia
// Simula una bobina detectora de vehículos (o cualquier sensor de presencia).
class SensorPresencia {
public:
String id;
int pinSensor; // Pin analógico o digital donde está conectado el sensor
int umbralDeteccion; // Umbral para detectar vehículo
bool vehiculoDetectado; // Para detectar cambios de estado (llegó/se fue)
SensorPresencia(String id_sensor, int pSensor, int umbral = 600) {
id = id_sensor;
pinSensor = pSensor;
umbralDeteccion = umbral;
vehiculoDetectado = false;
// Si es analógico (ADC), no se necesita pinMode en ESP32 para lectura.
// Si fuera digital (botón, sensor de proximidad con salida digital), sería pinMode(pinSensor, INPUT);
Serial.print("Sensor de Presencia '");
Serial.print(id);
Serial.print("' inicializado. Pin: ");
Serial.print(pinSensor);
Serial.print(", Umbral: ");
Serial.println(umbralDeteccion);
}
// Método para leer el sensor. Retorna True si hay vehículo, False si no.
// En un Arduino real, valorAnalogicoSimulado sería analogRead(pinSensor)
bool leerValorSensor(int valorAnalogicoSimulado) {
// En un ESP32 real: int lecturaActual = analogRead(pinSensor);
// Asegúrate que pinSensor es un pin ADC válido en ESP32 (ej. GPIO32-39).
// Usamos valorAnalogicoSimulado para la demostración.
Serial.print(" Sensor '");
Serial.print(id);
Serial.print("': Leyendo -> ");
Serial.println(valorAnalogicoSimulado);
if (valorAnalogicoSimulado > umbralDeteccion) {
if (!vehiculoDetectado) {
vehiculoDetectado = true;
Serial.print(" Sensor '");
Serial.print(id);
Serial.println("': ¡VEHÍCULO DETECTADO!");
}
return true;
} else {
if (vehiculoDetectado) {
vehiculoDetectado = false;
Serial.print(" Sensor '");
Serial.print(id);
Serial.println("': Vehículo DESAPARECIDO.");
}
return false;
}
}
};
// ControladorAcceso (simplificado para C++ y Arduino)
// Orquesta la lógica de acceso y salida del estacionamiento.
class ControladorAcceso {
public:
Barrera* barreraEntrada;
SensorPresencia* sensorLlegadaEntrada;
SensorPresencia* sensorPasoEntrada;
Barrera* barreraSalida;
SensorPresencia* sensorLlegadaSalida;
SensorPresencia* sensorPasoSalida;
// Para simular "base de datos" de RFID/Tickets.
// En un ESP32 real, esto podría involucrar EEPROM, SPIFFS/LittleFS,
// o comunicación Wi-Fi con un servidor/base de datos.
String rfidValidoSimulado = "RFID123";
String ticketGeneradoSimulado = "";
bool ticketPagadoSimulado = false;
ControladorAcceso(Barrera* be, SensorPresencia* sle, SensorPresencia* spe,
Barrera* bs, SensorPresencia* sls, SensorPresencia* sps) {
barreraEntrada = be;
sensorLlegadaEntrada = sle;
sensorPasoEntrada = spe;
barreraSalida = bs;
sensorLlegadaSalida = sls;
sensorPasoSalida = sps;
Serial.println("Controlador de Acceso inicializado para Entrada y Salida.");
}
// Funciones auxiliares privadas (simulaciones)
bool _simularValidacionRFID(String id_rfid) {
Serial.print(" [Controlador]: Simulación RFID: Validando '");
Serial.print(id_rfid);
Serial.println("'...");
delay(500); // Simula tiempo de validación
if (id_rfid == rfidValidoSimulado) {
Serial.print(" [Controlador]: RFID '");
Serial.print(id_rfid);
Serial.println("' Válido. Registrando entrada en servidor (simulado).");
return true;
} else {
Serial.print(" [Controlador]: RFID '");
Serial.print(id_rfid);
Serial.println("' NO Válido o desconocido.");
return false;
}
}
String _simularEmisionTicket() {
// Genera un ID simple, en un sistema real sería un ID único de BD/hardware
ticketGeneradoSimulado = "TICKET-ESP32";
Serial.print(" [Controlador]: Simulación Ticket: Emitiendo ticket '");
Serial.print(ticketGeneradoSimulado);
Serial.println("' con QR y foto de matrícula (simulado)...");
delay(1000); // Simula tiempo de emisión
Serial.print(" [Controlador]: Ticket '");
Serial.print(ticketGeneradoSimulado);
Serial.println("' emitido y registrado. Favor retirarlo.");
return ticketGeneradoSimulado;
}
void _monitorearPasoYCerrar(Barrera* barrera, SensorPresencia* sensorPaso) {
bool vehiculoCruzando = false;
bool vehiculoPasoCompleto = false;
Serial.print(" [Controlador]: Monitoreando sensor de paso '");
Serial.print(sensorPaso->id);
Serial.println("' en la barrera para el cierre...");
unsigned long tiempoInicioPaso = millis();
const unsigned long TIEMPO_CRUZANDO = 2000; // Simula tiempo que el vehículo tarda en activar el sensor de paso
const unsigned long TIEMPO_DESAPARECIDO = 3000; // Simula tiempo que el vehículo tarda en pasar completamente
while (!vehiculoPasoCompleto) {
// Simulación del sensor de paso
// En un sistema real, leerías: int lecturaSensorActual = analogRead(sensorPaso->pinSensor);
// y pasarías lecturaSensorActual a sensorPaso->leerValorSensor().
// Para la simulación automática, simulamos la detección y luego la ausencia.
if (!vehiculoCruzando && millis() - tiempoInicioPaso > 1000) { // Simula detección después de 1 segundo
Serial.println(" [Controlador]: (Simulando vehículo detectado bajo la barrera...)");
sensorPaso->leerValorSensor(800); // Simula valor alto para detección
vehiculoCruzando = true;
tiempoInicioPaso = millis(); // Reiniciar contador para el tiempo de cruce
} else if (vehiculoCruzando && millis() - tiempoInicioPaso > TIEMPO_CRUZANDO) { // Simula que el vehículo ya pasó
Serial.println(" [Controlador]: (Simulando vehículo saliendo del sensor de paso...)");
sensorPaso->leerValorSensor(100); // Simula valor bajo para no detección
vehiculoPasoCompleto = true;
}
delay(100); // Pequeña pausa para evitar un bucle muy rápido
}
Serial.print("\n [Controlador]: Vehículo ha pasado. Ordenando a la barrera '");
Serial.print(barrera->id);
Serial.println("' que baje.");
barrera->cerrar();
Serial.println(" [Controlador]: Registro de salida en 'base de datos' (si aplica).");
}
void procesarEntradaVehiculo(bool tieneRfidSimulado) {
Serial.println("\n--- INICIO PROCESO DE ENTRADA ---");
Serial.print("Esperando detección de vehículo en sensor de llegada '");
Serial.print(sensorLlegadaEntrada->id);
Serial.println("'...");
bool vehiculoEnLlegada = false;
unsigned long tiempoInicioEspera = millis();
const unsigned long TIEMPO_ESPERA_VEHICULO = 3000; // Simula tiempo hasta que el vehículo llega al sensor
while (!vehiculoEnLlegada) {
// En un ESP32 real, aquí leerías el sensor: int lecturaActual = analogRead(sensorLlegadaEntrada->pinSensor);
// y pasarías lecturaActual a sensorLlegadaEntrada->leerValorSensor().
// Para la simulación automática, activamos la detección después de un tiempo
if (millis() - tiempoInicioEspera > TIEMPO_ESPERA_VEHICULO) {
Serial.println(" [Controlador]: (Simulando vehículo llegando al sensor de entrada...)");
vehiculoEnLlegada = sensorLlegadaEntrada->leerValorSensor(800); // Valor alto para simular detección
}
delay(100); // Pequeña pausa para no saturar el Serial
}
Serial.println("\nVehículo detectado en la zona de llegada.");
bool accesoConcedido = false;
String identificadorVehiculo = "";
if (tieneRfidSimulado) {
// En un ESP32 real, aquí leerías el RFID del módulo lector (ej. RC522)
// String idLeidoRfid = rfidReader.readCard(); // Ejemplo de llamada a librería RFID
String idLeidoRfid = rfidValidoSimulado; // Usamos el RFID válido simulado
if (_simularValidacionRFID(idLeidoRfid)) {
accesoConcedido = true;
identificadorVehiculo = idLeidoRfid;
}
} else {
Serial.println(" [Controlador]: Cliente no tiene RFID o prefiere ticket.");
identificadorVehiculo = _simularEmisionTicket();
if (identificadorVehiculo != "") {
accesoConcedido = true;
}
}
if (accesoConcedido) {
Serial.print("\n [Controlador]: Acceso validado para '");
Serial.print(identificadorVehiculo);
Serial.println("'. Procediendo a abrir barrera.");
if (barreraEntrada->abrir()) {
Serial.println(" [Controlador]: Barrera de entrada subiendo. Esperando paso del vehículo...");
// En C++, registrar la hora exacta de entrada requeriría un módulo RTC o NTP (para ESP32 con Wi-Fi)
// Serial.print(" [Controlador]: Hora de entrada registrada para '"); Serial.print(identificadorVehiculo); Serial.println("'.");
_monitorearPasoYCerrar(barreraEntrada, sensorPasoEntrada);
} else {
Serial.println(" [Controlador]: Fallo al abrir la barrera de entrada. Acceso denegado.");
}
} else {
Serial.println("\n [Controlador]: Acceso denegado (RFID inválido o error en emisión de ticket). Barrera de entrada permanece CERRADA.");
}
Serial.println("\n--- FIN PROCESO DE ENTRADA ---\n");
}
bool _simularPagoTaquilla(String ticket_id) {
Serial.print("\n [Controlador]: Cliente presenta ticket '");
Serial.print(ticket_id);
Serial.println("'.");
// En un sistema real, aquí habría lógica de cálculo de tarifa y procesamiento de pago.
Serial.println(" [Controlador]: (Simulando cálculo de tarifa y proceso de pago...)");
delay(2000); // Simula el tiempo de pago
// Para simulación, si el ticket coincide con el generado, se marca como pagado.
if (ticket_id == ticketGeneradoSimulado) {
ticketPagadoSimulado = true;
Serial.print(" [Controlador]: Ticket '");
Serial.print(ticket_id);
Serial.println("' marcado como PAGADO. Código QR inutilizado.");
return true;
} else {
Serial.println(" [Controlador]: Ticket no encontrado o no válido para pago.");
return false;
}
}
void procesarSalidaVehiculo(String identificadorVehiculoSimulado, bool esRfidSalida) {
Serial.println("\n--- INICIO PROCESO DE SALIDA ---");
Serial.print("Esperando detección de vehículo en sensor de llegada '");
Serial.print(sensorLlegadaSalida->id);
Serial.println("'...");
bool vehiculoEnLlegadaSalida = false;
unsigned long tiempoInicioEspera = millis();
const unsigned long TIEMPO_ESPERA_VEHICULO = 3000; // Simula tiempo hasta que el vehículo llega al sensor
while (!vehiculoEnLlegadaSalida) {
if (millis() - tiempoInicioEspera > TIEMPO_ESPERA_VEHICULO) {
Serial.println(" [Controlador]: (Simulando vehículo llegando al sensor de salida...)");
vehiculoEnLlegadaSalida = sensorLlegadaSalida->leerValorSensor(800);
}
delay(100);
}
Serial.println("\nVehículo detectado en la zona de llegada de salida.");
bool salidaPermitida = false;
if (esRfidSalida) {
Serial.print(" [Controlador]: Cliente usa RFID para salir: '");
Serial.print(identificadorVehiculoSimulado);
Serial.println("'.");
if (identificadorVehiculoSimulado == rfidValidoSimulado) {
Serial.print(" [Controlador]: RFID '");
Serial.print(identificadorVehiculoSimulado);
Serial.println("' válido. Registrando salida en servidor.");
// En un sistema real, aquí actualizarías el registro de salida en la "base de datos".
salidaPermitida = true;
} else {
Serial.print(" [Controlador]: RFID '");
Serial.print(identificadorVehiculoSimulado);
Serial.println("' no válido para salida.");
}
} else {
Serial.print(" [Controlador]: Cliente usa ticket para salir: '");
Serial.print(identificadorVehiculoSimulado);
Serial.println("'.");
if (identificadorVehiculoSimulado == ticketGeneradoSimulado && ticketPagadoSimulado) {
Serial.print(" [Controlador]: Ticket '");
Serial.print(identificadorVehiculoSimulado);
Serial.println("' VÁLIDO y PAGADO.");
// En un sistema real, aquí actualizarías el registro de salida y lo removerías de los tickets activos.
salidaPermitida = true;
} else {
Serial.print(" [Controlador]: Ticket '");
Serial.print(identificadorVehiculoSimulado);
Serial.println("' NO PAGADO o inválido. Barrera cerrada.");
Serial.println(" [Controlador]: Por favor, diríjase a la taquilla para pagar.");
salidaPermitida = false;
}
}
if (salidaPermitida) {
Serial.println("\n [Controlador]: Salida validada. Procediendo a abrir barrera de salida.");
Serial.println(" [Controlador]: Cámara verifica matrícula (opcional para seguridad)."); // Simulado
if (barreraSalida->abrir()) {
Serial.println(" [Controlador]: Barrera de salida subiendo. Esperando paso del vehículo...");
_monitorearPasoYCerrar(barreraSalida, sensorPasoSalida);
} else {
Serial.println(" [Controlador]: Fallo al abrir la barrera de salida. Salida denegada.");
}
} else {
Serial.println("\n [Controlador]: Salida denegada. Barrera de salida permanece CERRADA.");
}
Serial.println("\n--- FIN PROCESO DE SALIDA ---\n");
}
};
// --- INSTANCIAS GLOBALES Y FUNCIONES DE ARDUINO ---
// PINES DE EJEMPLO para ESP32 (¡Ajustar según tu hardware real y el pinout de tu placa!)
// Evita GPIOs usados para Flash/Boot (0, 2, 5, 12, 15) para evitar problemas al cargar.
// Los pines 34-39 son solo INPUT (ADC), no OUTPUT.
// Barrera de Entrada (Ejemplos de pines OUTPUT seguros para motores)
const int PIN_BE_DIR1 = 16;
const int PIN_BE_DIR2 = 17;
const int PIN_BE_ENABLE = 18;
// Sensores de Entrada (Ejemplos de pines INPUT/ADC)
const int PIN_SLE_SENSOR = 35; // Pin ADC
const int PIN_SPE_SENSOR = 36; // Pin ADC
// Barrera de Salida
const int PIN_BS_DIR1 = 19;
const int PIN_BS_DIR2 = 21;
const int PIN_BS_ENABLE = 22;
// Sensores de Salida
const int PIN_SLS_SENSOR = 37; // Pin ADC
const int PIN_SPS_SENSOR = 38; // Pin ADC
// Objetos (instancias de las clases) - ¡Declarados globalmente para que sean accesibles en setup() y loop()!
Barrera barreraEntrada("Entrada Principal", PIN_BE_DIR1, PIN_BE_DIR2, PIN_BE_ENABLE);
SensorPresencia sensorLlegadaEntrada("Bobina Entrada (Detección)", PIN_SLE_SENSOR);
SensorPresencia sensorPasoEntrada("Bobina Entrada (Bajo Barrera)", PIN_SPE_SENSOR);
Barrera barreraSalida("Salida Principal", PIN_BS_DIR1, PIN_BS_DIR2, PIN_BS_ENABLE);
SensorPresencia sensorLlegadaSalida("Bobina Salida (Detección)", PIN_SLS_SENSOR);
SensorPresencia sensorPasoSalida("Bobina Salina (Bajo Barrera)", PIN_SPS_SENSOR);
ControladorAcceso controlador(&barreraEntrada, &sensorLlegadaEntrada, &sensorPasoEntrada,
&barreraSalida, &sensorLlegadaSalida, &sensorPasoSalida);
// Variables para controlar la simulación de escenarios en el loop
int escenarioActual = 0;
unsigned long tiempoUltimoEscenario = 0;
const unsigned long DELAY_ENTRE_ESCENARIOS = 10000; // 10 segundos entre escenarios simulados
// La función setup() se ejecuta una vez cuando el ESP32 se enciende
void setup() {
Serial.begin(115200); // Es común usar baudios más altos (ej. 115200) con ESP32 por su mayor velocidad
Serial.println("\n--- INICIANDO SIMULACIÓN COMPLETA DEL SISTEMA DE ESTACIONAMIENTO (ESP32 C++) ---\n");
delay(2000); // Pequeña pausa para inicialización
}
// La función loop() se ejecuta repetidamente (continuamente)
void loop() {
// Esta sección simula el flujo de escenarios automáticamente para demostrar la lógica.
// En una aplicación real, no tendrías esta lógica secuencial en loop(),
// sino que la lógica del controlador reaccionaría a las lecturas de los sensores en tiempo real.
if (millis() - tiempoUltimoEscenario > DELAY_ENTRE_ESCENARIOS) {
tiempoUltimoEscenario = millis();
escenarioActual++;
switch (escenarioActual) {
case 1:
Serial.println("\n##### ESCENARIO 1: Vehículo entra SIN RFID (necesita ticket) #####");
controlador.procesarEntradaVehiculo(false); // false = no RFID
break;
case 2:
Serial.println("\n##### ESCENARIO 2: Vehículo sale (con pago de ticket) #####");
// Asegurarse de que el ticket simulado se haya generado y esté "pagado"
// En un sistema real, el pago se manejaría por separado y actualizaría el estado.
controlador._simularPagoTaquilla(controlador.ticketGeneradoSimulado);
if (controlador.ticketPagadoSimulado) {
controlador.procesarSalidaVehiculo(controlador.ticketGeneradoSimulado, false); // false = es ticket
} else {
Serial.println(" [Simulación]: No se pudo simular la salida porque el ticket no fue 'pagado'.");
}
break;
case 3:
Serial.println("\n##### ESCENARIO 3: Vehículo entra CON RFID VÁLIDO #####");
controlador.procesarEntradaVehiculo(true); // true = tiene RFID
break;
case 4:
Serial.println("\n##### ESCENARIO 4: Vehículo sale (con RFID) #####");
controlador.procesarSalidaVehiculo(controlador.rfidValidoSimulado, true); // true = es RFID
break;
default:
Serial.println("\n--- SIMULACIÓN DE ESCENARIOS COMPLETADA. Reinicia el ESP32 para repetir. ---");
while(true) delay(1000); // Detener el loop después de los escenarios
break;
}
}
}