En nuestra Arquitectura de Software, el sistema pasa el 95% de su vida útil en el estado ESTADO_REPOSO. Durante esta fase, las válvulas solenoides están sin energía (Normalmente Cerradas) bloqueando el flujo a más de 120 PSI de presión. El microcontrolador solo debe estar vigilando la pantalla o esperando que el usuario descuelgue la pistola para iniciar una venta.
¿Qué pasa si programamos que "Cualquier pulso en Reposo = Alarma de Fuga"? Tendremos cientos de alarmas falsas. En hidráulica, cuando la válvula se cierra bruscamente al terminar un despacho de gas, la inercia cinética del líquido choca contra la pared de la válvula cerrada y rebota hacia atrás. Esta onda de choque hace que la turbina del caudalímetro gire levemente hacia atrás y hacia adelante generando 1 o 2 pulsos residuales.
Para evitar pánico innecesario, la máquina debe tolerar un Umbral de Ruido Hidráulico (ej. 3 pulsos). Solo si el contador de pulsos en reposo supera ese umbral, el sistema decreta una Alarma Nivel 1: Fuga Detectada.
Cuando la fuga es confirmada, el firmware no puede simplemente mostrar un mensaje y seguir funcionando. Las reglas de diseño seguro exigen un secuestro total de la Máquina de Estados (Lockdown):
ESTADO_BLOQUEO_EMERGENCIA.Circuito Electrónico de Redundancia: La Norma Oficial Mexicana exige que un Despachador de Gas LP tenga al menos dos válvulas en serie. Una es la válvula solenoide de trabajo (rápida, operada cada despacho). La otra es la válvula de corte general (lenta, generalmente de bola motorizada), la cual solo se usa en emergencias y cuando la máquina se apaga por las noches. Nuestra alarma activará el relé de esta segunda válvula.
Ejercicio 1: Integraremos la lógica de detección de fugas dentro de la Tarea de Metrología. Utilizaremos el estándar MISRA-C asegurándonos de que los estados del sistema estén estrictamente definidos mediante un `enum` y que las variables sean tipadas.
/**
* @file fsm_fugas.c
* @brief Lógica de Detección Falsa de Fluido y Bloqueo de Emergencia (MISRA-C)
*/
#include "stm32f401xe.h"
#include "cmsis_os2.h"
#include <stdint.h>
#include <stdbool.h>
// =========================================================================
// DEFINICIONES DEL SISTEMA
// =========================================================================
// Umbral de ruido para ignorar el "Golpe de Ariete" al cerrar la válvula
#define UMBRAL_RUIDO_HIDRAULICO (3U)
typedef enum {
ESTADO_REPOSO = 0U,
ESTADO_DESPACHANDO = 1U,
ESTADO_EMERGENCIA = 2U
} FSM_Estado_t;
// Variable global de estado compartida (Protegida)
static FSM_Estado_t estado_maquina_actual = ESTADO_REPOSO;
// Pulsos interceptados por la ISR
extern volatile uint32_t isr_pulsos_acumulados;
// =========================================================================
// ACCIONES DE MITIGACIÓN (HARDWARE)
// =========================================================================
/**
* @brief Ejecuta el protocolo SIL de cierre total.
*/
static void Activar_Protocolo_Lockdown(void) {
// 1. Asegurar que la válvula solenoide primaria esté apagada
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
// 2. Activar el Relé de la Válvula Motorizada de Corte General (Redundancia)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
// 3. Encender Zumbador (Buzzer)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
// 4. Delegar notificación IoT a la Tarea UART/ESP32
// Enviar_Alarma_Nube(ALERTA_FUGA_DETECTADA);
}
// =========================================================================
// MÁQUINA DE ESTADOS (TAREA PRINCIPAL)
// =========================================================================
void Task_Control_Valvulas(void *argument) {
uint32_t pulsos_locales_previos = 0U;
uint32_t pulsos_en_reposo = 0U;
for(;;) {
// Lectura Atómica (Evitar Data Race)
taskENTER_CRITICAL();
uint32_t snapshot_pulsos = isr_pulsos_acumulados;
taskEXIT_CRITICAL();
// Calcular si hubo incremento desde el último ciclo
uint32_t incremento = snapshot_pulsos - pulsos_locales_previos;
pulsos_locales_previos = snapshot_pulsos;
// FSM Lógica
switch (estado_maquina_actual) {
case ESTADO_REPOSO:
if (incremento > 0U) {
pulsos_en_reposo += incremento;
// ¿Superó el umbral del Golpe de Ariete?
if (pulsos_en_reposo >= UMBRAL_RUIDO_HIDRAULICO) {
// ¡PELIGRO! FLUJO FANTASMA DETECTADO
estado_maquina_actual = ESTADO_EMERGENCIA;
}
}
break;
case ESTADO_DESPACHANDO:
// En despacho es normal que haya pulsos.
// La variable pulsos_en_reposo debe estar reseteada a 0.
pulsos_en_reposo = 0U;
break;
case ESTADO_EMERGENCIA:
// Estado Latch-up (Atrapado). La máquina no puede salir de aquí
// a menos que un técnico gire una llave física de Mantenimiento.
Activar_Protocolo_Lockdown();
break;
default:
// MISRA-C Regla 16.4: Transición segura por corrupción de RAM
estado_maquina_actual = ESTADO_EMERGENCIA;
break;
}
osDelay(pdMS_TO_TICKS(100U)); // Monitorear cada 100ms
}
}
Objetivo del día: Diagnóstico Predictivo y Redundancia Mecánica por Software.
Un ingeniero mecánico sabe que todos los sellos de goma del mundo eventualmente se degradarán (especialmente bajo la presión de 150 PSI del Gas LP líquido). Si la solenoide principal sufre desgaste microscópico, empezará a "gotear" gas hacia la manguera cuando la máquina no esté operando. Si el despachador de la noche deja la pistola en el suelo, ese gas se fugará silenciosamente a la atmósfera creando una nube explosiva.
Al diseñar la lógica de **Alerta de Fuga en Reposo**, has creado una capa de diagnóstico predictivo. El STM32 actúa como un auditor del hardware físico. Si la orden lógica es `CERRADO`, pero la física dice `FLUJO`, el software asume que el hardware primario ha fallado e invoca al hardware secundario (la válvula de corte general motorizada). Esta redundancia cruzada (Software monitoreando al Hardware A para activar el Hardware B) es la base de las certificaciones SIL-3 (Safety Integrity Level) que las grandes petroquímicas (PEMEX, Repsol) exigen antes de comprar e instalar cualquier equipo electrónico en sus despachadoras.
switch-case) debe capturar cualquier fallo de la variable de estado en su bloque default e inyectar al sistema directamente hacia el Estado de Emergencia, protegiendo al equipo contra la corrupción de RAM provocada por interferencias.