¿Cómo sabes que tu HardFault Logger del Día 150 realmente atrapará un desbordamiento de RAM? ¿Cómo garantizas que el Watchdog Metrológico del Día 158 cerrará las válvulas si detecta una fuga?
Esperar a que estos errores fatales ocurran naturalmente en campo es irresponsable. Forzarlos cortando cables físicamente o haciendo cortocircuitos en tu laboratorio es destructivo y peligroso.
La ingeniería industrial de alto nivel exige Unit Tests (Pruebas Unitarias) y Fault Injection (Inyección de Fallas) por software. Consiste en crear un "Modo de Prueba" dentro del firmware que intercepta la lectura del hardware real (Mocks) y la reemplaza por un escenario de caos controlado creado matemáticamente.
En lugar de que tu Súper Bucle lea directamente el registro del Timer TIM2->CNT, debe llamar a una función envoltorio como Sensor_GetPulsos(). En modo producción, esta función devuelve el valor del hardware. En Modo Test, esta función devuelve valores simulados. Si simulas que los pulsos aumentan mientras la válvula está apagada, tu Watchdog debería activarse. Así pruebas toda la cadena de software lógica sin derramar una sola gota de gas.
Ejercicio 1: Haremos uso de las directivas del preprocesador de C (#ifdef). Crearemos una capa unit_tests.c. Cuando compilemos el código con la bandera de pruebas encendida, inyectaremos un comando remoto vía RS485 para que la máquina "simule" una fuga de gas o un HardFault. Comprobaremos a través de la terminal serial que los bloqueos legales actúan a la perfección.
/**
* @file fault_injector.c
* @brief Suite de Unit Tests y Simulación de Fallas de Hardware
*/
#include "stm32f401xe.h"
#include <stdint.h>
#include <stdbool.h>
// =========================================================================
// INTERRUPTOR MAESTRO DE PRUEBAS (Descomentar para habilitar)
// ¡NUNCA COMPILAR PRODUCCIÓN CON ESTA LÍNEA ACTIVA!
// =========================================================================
#define DEBUG_TEST_MODE
// Dependencias de hardware real
extern uint32_t pulsos_caudalimetro_crudos;
extern void Bloqueo_Legal_Fatal(uint32_t codigo_error); // Día 158
#ifdef DEBUG_TEST_MODE
// Variables inyectoras
static bool simular_fuga_activa = false;
static uint32_t pulsos_simulados = 0;
/**
* @brief Mock (Envoltorio) para la lectura del sensor de litros.
* Remplaza la llamada directa a variables globales en el Súper Bucle.
*/
uint32_t Mock_GetPulsosCaudalimetro(void) {
if (simular_fuga_activa) {
// Inyectamos 5 pulsos falsos cada vez que nos leen
pulsos_simulados += 5;
return pulsos_simulados;
} else {
// Comportamiento passthrough normal
return pulsos_caudalimetro_crudos;
}
}
/**
* @brief Mock para provocar un HardFault intencional y probar el Logger (Día 150)
*/
void Test_Inject_HardFault(void) {
// 1. Crear un puntero hacia una dirección de memoria reservada / inválida
volatile uint32_t *puntero_muerte = (volatile uint32_t *)0x1FFFFFFF;
// 2. Intentar escribir en ella. El hardware ARM Cortex-M lanzará
// inmediatamente la excepción HardFault_Handler.
*puntero_muerte = 0xDEADBEEF;
}
/**
* @brief Función que atiende comandos ocultos de diagnóstico por Modbus
* Ej. Enviar Función 0x42 para desatar el caos controlado.
*/
void Procesar_Comandos_Test(uint8_t comando_test) {
switch (comando_test) {
case 0x01:
// Simular Fuga Mecánica
// Esto engañará al Watchdog (Día 158) para que bloquee el sistema
// asumiendo que la válvula está cerrada pero hay flujo.
simular_fuga_activa = true;
break;
case 0x02:
// Simular corrupción de memoria (Puntero loco)
Test_Inject_HardFault();
break;
case 0x03:
// Simular falla de batería principal para probar Standby (Día 145)
// ...
break;
}
}
#else
// =========================================================================
// MODO PRODUCCIÓN (Transparente y Seguro)
// Si DEBUG_TEST_MODE está comentado, estas macros se reemplazan por
// lectura directa de hardware. Cero costo de procesamiento (Zero Overhead).
// =========================================================================
#define Mock_GetPulsosCaudalimetro() (pulsos_caudalimetro_crudos)
#endif
int main(void) {
// inicializar_sistema();
while(1) {
// 1. ZONA DE PRUEBAS
// Si recibimos un comando RS485 especial (Ej. ID 99)
// if (recibi_comando_diagnostico) {
// #ifdef DEBUG_TEST_MODE
// Procesar_Comandos_Test(payload[0]);
// #endif
// }
// 2. APLICACIÓN LÓGICA (SUT)
// El Vigía lee a través del Mock. No sabe ni le importa
// si los datos vienen del hardware o del simulador.
uint32_t pulsos = Mock_GetPulsosCaudalimetro();
// if (valvula_cerrada && pulsos_han_aumentado(pulsos)) {
// Bloqueo_Legal_Fatal(0xF001);
// }
}
return 0;
}
Objetivo del día: Certificación de Software e Integración Continua (SIL).
Cuando lleves tu placa a las oficinas de certificación (PROFECO / OIML), el auditor te pedirá demostrar que la máquina se bloquea en caso de fuga. Si no tuvieras un modo de simulación, tendrías que conectar tu tarjeta a una bomba real, presurizarla a 200 PSI, y aflojar una tuerca intencionalmente para causar una fuga peligrosa en el laboratorio de pruebas.
Con la arquitectura de **Mocks y Fault Injection**, presentas tu tesis de forma impecable. Conectas la máquina a tu laptop por RS485. Abres una terminal serial, envías el código hex `0x42 0x01` (Activar Simulador de Fuga). El auditor ve inmediatamente cómo la pantalla LCD del Despachador se pone en rojo, y el sistema transmite la alarma `ERR_FLUJO_NO_AUTORIZ` a la computadora sin haber derramado una sola gota de gas real. Has demostrado que tu Watchdog metrológico del Día 158 y tu HardFault Logger del Día 150 no son teoría; operan perfectamente y puedes probarlos a voluntad. Esto es ingeniería de software madura.
#ifdef) para inyectar este código. Esto garantiza que la lógica de prueba sea extirpada por completo del binario final de producción, ahorrando memoria Flash y eliminando vulnerabilidades de seguridad.GPIOA->IDR); en su lugar, llama a una función (LeerBoton()) que el compilador enrutará hacia el hardware real o hacia el inyector de caos.HardFault escribiendo en direcciones de memoria protegidas o inexistentes es la forma oficial de validar que tus rutinas de rescate de pila (Stack Dump) post-mortem funcionen correctamente.