En el software de consumo (como una app de celular), si algo falla, simplemente se ignora o se lanza una advertencia. En la metrología legal, un fallo o incongruencia mecánica es motivo de inhabilitación. La "Desviación Métrica" ocurre cuando la expectativa matemática (Software) choca con la realidad (Hardware).
Existen dos tipos clásicos de desviación en estaciones de gas:
Cuando un Watchdog (Perro Guardián) de software detecta esta desviación, debe ejecutar una secuencia de bloqueo: Primero, apagar toda la potencia (cerrar válvulas). Segundo, marcar el registro BKP (SRAM respaldada por batería) con un código de error. Tercero, rechazar cualquier intento de nueva venta y levantar la "Bandera de Excepción" en el mapa de memoria Modbus para que el Servidor SCADA envíe al equipo de mantenimiento.
Ejercicio 1: Integraremos un Watchdog Metrológico en el Súper Bucle. Revisaremos asincrónicamente el delta de pulsos del caudalímetro. Si el sistema está en estado REPOSO, el límite de tolerancia será muy bajo (ej. 3 pulsos). Si este límite se rompe, activaremos la función de Bloqueo_Legal(), la cual registrará el error en los registros BKP de la batería (Día 142) y alterará la tabla de memoria Modbus (Día 139) para que el Servidor SCADA se entere del fraude.
/**
* @file metrology_watchdog.c
* @brief Monitor de Desviación Métrica y Bloqueo Legal (NOM-005)
*/
#include "stm32f401xe.h"
#include <stdbool.h>
#include <stdint.h>
// Tolerancias Legales (Calibración)
#define MAX_PULSOS_FUGA 5 // Pulsos permitidos por inercia tras cerrar la válvula
#define TIMEOUT_FLUJO_VACIO 5000 // Milisegundos esperando flujo antes de abortar
// Códigos de Error Legales (Sobreviven en BKP RAM)
#define BKP_REG_ERROR_LEGAL RTC->BKP13R
#define ERR_NINGUNO 0x0000
#define ERR_FLUJO_NO_AUTORIZ 0xF001
#define ERR_VENTA_EN_SECO 0xF002
#define ERR_TAMPER_FISICO 0xF003 // Del Día 143
// Dependencias de nuestro firmware
extern uint32_t pulsos_caudalimetro_crudos; // Contador EXTI bruto
extern volatile uint32_t ms_ticks; // Base de tiempo SysTick
extern uint16_t holding_registers[10]; // Mapa Modbus (Día 139)
extern void set_apertura_pwm_hardware(float); // Válvula (Día 152)
// Estados de Operación Lógica
typedef enum {
ESTADO_REPOSO = 0,
ESTADO_DESPACHANDO,
ESTADO_BLOQUEADO
} EstadoMaquina_t;
EstadoMaquina_t estado_dispensador = ESTADO_REPOSO;
/**
* @brief Ejecuta el protocolo de cierre de emergencia y sella el sistema.
*/
void Bloqueo_Legal_Fatal(uint32_t codigo_error) {
// 1. Apagar hardware de potencia INMEDIATAMENTE
set_apertura_pwm_hardware(0.0f);
// motor_bomba_off();
// 2. Transición al estado irrecuperable
estado_dispensador = ESTADO_BLOQUEADO;
// 3. Sellar el error en la memoria RAM respaldada por batería (BKP)
// Esto requiere desbloquear el PWR_CR_DBP (Omitido aquí por brevedad, ver Día 142)
BKP_REG_ERROR_LEGAL = codigo_error;
// 4. Alertar a la red industrial (Servidor Central / SCADA)
// Mapeamos el Registro Modbus 0x02 (Estado) a "Bloqueado"
holding_registers[2] = 0x9999;
// Mapeamos el Registro Modbus 0x03 (Código Error)
holding_registers[3] = codigo_error;
// 5. Bloquear Interfaz HMI
// lcd_borrar();
// lcd_escribir("BLOQUEO METROLOGICO");
}
/**
* @brief Perro Guardián que vigila las incongruencias mecánicas vs lógicas.
* Se llama cíclicamente desde el main().
*/
void Vigilar_Desviacion_Metrica(void) {
static uint32_t pulsos_anteriores = 0;
static uint32_t timestamp_ultima_revision = 0;
static uint32_t temporizador_flujo_seco = 0;
// Revisar cada 200 ms
if ((ms_ticks - timestamp_ultima_revision) >= 200) {
uint32_t pulsos_actuales = pulsos_caudalimetro_crudos;
uint32_t delta_pulsos = pulsos_actuales - pulsos_anteriores;
switch (estado_dispensador) {
case ESTADO_REPOSO:
// La máquina no debería estar despachando gas.
// Si el sensor emite pulsos continuos, hay un robo o fuga.
if (delta_pulsos > MAX_PULSOS_FUGA) {
Bloqueo_Legal_Fatal(ERR_FLUJO_NO_AUTORIZ);
}
break;
case ESTADO_DESPACHANDO:
// La válvula está abierta, pero no hay flujo (Tanque vacío / Bomba dañada)
if (delta_pulsos == 0) {
temporizador_flujo_seco += 200;
if (temporizador_flujo_seco >= TIMEOUT_FLUJO_VACIO) {
Bloqueo_Legal_Fatal(ERR_VENTA_EN_SECO);
}
} else {
temporizador_flujo_seco = 0; // Reset si hay flujo
}
break;
case ESTADO_BLOQUEADO:
// Forzar válvulas a 0 continuamente por seguridad
set_apertura_pwm_hardware(0.0f);
break;
}
pulsos_anteriores = pulsos_actuales;
timestamp_ultima_revision = ms_ticks;
}
}
int main(void) {
// inicializar_sistema();
// 1. REVISIÓN POST-MORTEM (Al arrancar tras un apagón)
if (BKP_REG_ERROR_LEGAL != ERR_NINGUNO) {
// La máquina arranca pre-bloqueada si no ha sido reseteada
// por un comando de administrador autenticado.
estado_dispensador = ESTADO_BLOQUEADO;
holding_registers[2] = 0x9999;
holding_registers[3] = BKP_REG_ERROR_LEGAL;
}
while(1) {
// El Vigía silencioso
Vigilar_Desviacion_Metrica();
if (estado_dispensador != ESTADO_BLOQUEADO) {
// procesar_comunicaciones();
// actualizar_pantallas();
}
// Dormir CPU
// __WFI();
}
return 0;
}
Objetivo del día: Cumplimiento de NOM-005 Antifraude y Telemetría SCADA.
Uno de los mayores dolores de cabeza para los dueños de gasolineras es el robo hormiga. Un operador en el turno nocturno podría golpear la electroválvula para "atascarla" abierta un milímetro, logrando que el gas fluya lentamente hacia un cilindro portátil sin que el software inicie una venta oficial. En equipos viejos, el microcontrolador estaba ciego a esto porque su bucle principal solo sumaba litros durante una venta activa.
Al implementar el **Watchdog Metrológico** y vincularlo con el mapa de memoria **Modbus**, has cerrado la última brecha de seguridad de tu máquina. El STM32 ahora vigila asincrónicamente el hardware las 24 horas. Si el sensor de flujo emite pulsos en la madrugada cuando el PWM está apagado, el procesador despierta, entra en pánico controlado, cierra todos los contactores por seguridad física, y escribe `0xF001` (Flujo No Autorizado) en la bóveda de la batería. A la mañana siguiente, el software administrativo de la estación leerá el registro Modbus RS485 `Holding_Register[3]` y le notificará al dueño exactamente a qué hora y qué tipo de fraude se intentó en el despachador #4.
Holding Registers), permitiendo al sistema central SCADA reaccionar y alertar a la gerencia.