DÍA 060 DE 365

Fase 1: Fundamentos de C y Lógica de Software

📖 Teoría a Estudiar

Evaluación Fase 1: Consolidación de la Máquina de Estados

¡Llegaste al final de la Fase 1! Durante estos 60 días has construido un arsenal de herramientas en C puro: punteros, manejo de bits, arquitecturas bare-metal, Watchdogs, rutinas de estado seguro y manipulación de memoria. Ahora es el momento de orquestar todo esto bajo el patrón de diseño más importante en la ingeniería de sistemas embebidos: la Máquina de Estados Finita (FSM - Finite State Machine).

Una FSM es un modelo matemático de computación. Define que un sistema solo puede estar en un único estado a la vez. En lugar de tener un código secuencial (espagueti) que se bloquea esperando eventos, la FSM se evalúa de forma cíclica y asíncrona. Si ocurre un evento válido (transición), el sistema cambia de estado; si ocurre un evento crítico, salta inmediatamente a un estado de error, garantizando control absoluto en cada microsegundo.

ST_REPOSO Btn Start = 1 ST_INIT Válvulas OK ST_DESPACHO Límite Vol Alcanzado ST_ERROR Timeout / Paro

Estructura de la FSM en C

  1. Enumeración (Enum): Se define un tipo de dato que contiene todos los estados posibles del sistema. Esto elimina los "números mágicos" de estado.
  2. Variable de Estado: Una variable global (o estática) que guarda el estado actual.
  3. Despachador (Switch-Case): Una función no bloqueante que evalúa la variable de estado mediante un switch y ejecuta solo el fragmento de código correspondiente al estado actual, saliendo rápidamente para permitir que otras tareas del Súper Bucle se sigan ejecutando.

⚙️ Proyecto Práctico: El Núcleo de Control

Ejercicio 1: Integra los conceptos de Timeout, control predictivo de volumen y manejo de estados en una estructura de firmware profesional para la gestión del flujo principal.


#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

// --- 1. DEFINICIÓN DE ESTADOS ---
typedef enum {
    ST_REPOSO,          // Esperando cliente/teclado
    ST_INICIANDO,       // Abriendo válvulas, validando sensores
    ST_DESPACHANDO,     // Leyendo flujo y procesando volumen
    ST_FINALIZANDO,     // Cerrando válvulas compensando inercia
    ST_ERROR            // Bloqueo por timeout, sobrepresión o fallo
} EstadoMaquina_t;

// Variable global de la FSM
EstadoMaquina_t estado_actual = ST_REPOSO;

// Mocks de variables del sistema
uint32_t volumen_objetivo_ml = 20000;
uint32_t volumen_actual_ml = 0;
bool boton_inicio_presionado = false;
bool sensor_error_critico = false;


// --- 2. MOTOR DE LA MÁQUINA DE ESTADOS ---
// Esta función se llama miles de veces por segundo desde el Súper Bucle
void procesar_maquina_despacho(void) {
    
    // Evaluar interrupciones críticas asíncronas globales
    if (sensor_error_critico && estado_actual != ST_ERROR) {
        estado_actual = ST_ERROR;
    }

    switch (estado_actual) {
        
        case ST_REPOSO:
            // hardware_valvula_set(LOW);
            if (boton_inicio_presionado) {
                printf("Transicion: REPOSO -> INICIANDO\n");
                volumen_actual_ml = 0; 
                boton_inicio_presionado = false; // Consumir evento
                estado_actual = ST_INICIANDO;
            }
            break;

        case ST_INICIANDO:
            // hardware_valvula_set(HIGH);
            // Aquí iría el Watchdog de flujo (Día 52)
            printf("Transicion: INICIANDO -> DESPACHANDO\n");
            estado_actual = ST_DESPACHANDO;
            break;

        case ST_DESPACHANDO:
            // Simulación de lectura continua del caudalímetro
            volumen_actual_ml += 50; 
            
            // Barrera de software (Día 53)
            if (volumen_actual_ml >= (volumen_objetivo_ml - 150)) { // 150ml de inercia
                printf("Transicion: DESPACHANDO -> FINALIZANDO\n");
                estado_actual = ST_FINALIZANDO;
            }
            break;

        case ST_FINALIZANDO:
            // hardware_valvula_set(LOW);
            // generar_ticket_json(); (Día 58)
            printf("Despacho Completado. Transicion a REPOSO.\n\n");
            estado_actual = ST_REPOSO;
            break;

        case ST_ERROR:
            // ESTADO SEGURO DE BLOQUEO (Día 54)
            // hardware_apagar_todo_emergencia();
            // printf("SISTEMA BLOQUEADO. REQUIERE REINICIO MANUAL.\n");
            break;
            
        default:
            // Buena práctica MISRA-C: Manejar estados indefinidos
            estado_actual = ST_ERROR;
            break;
    }
}


int main(void) {
    printf("--- INICIANDO FIRMWARE DESPACHADOR ---\n");
    
    // Simulación del Súper Bucle
    boton_inicio_presionado = true; // Forzar el inicio
    
    for (int i = 0; i < 5; i++) {
        procesar_maquina_despacho();
        // Al no tener delays, el procesador puede hacer otras cosas aquí
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Orquestación Lógica Total.

Con esta estructura base, has transformado fragmentos de código aislados en un cerebro industrial coordinado. En tu sistema de medición de gas con los microcontroladores que elegiste (arquitecturas ARM STM32 o Xtensa ESP32), la máquina de estados es la que garantiza que sea físicamente imposible empezar a leer los pulsos del caudalímetro si la máquina sigue en el estado ST_REPOSO, o que las válvulas solenoides jamás se abrirán si el equipo saltó al ST_ERROR por una sobrepresión.

Este es el hito final de tu Fase 1 de Fundamentos de Lógica de Software. A partir de ahora, todo el control complejo (pantallas, botones, cálculos de punto fijo y protección de EEPROM) girará en torno a esta sólida espina dorsal. Estás listo para dejar atrás la teoría pura de C y sumergirte de lleno en los registros físicos y el silicio.

📝 Resumen de la Fase 1

❓ Cuestionario Final de Evaluación

1. ¿Qué beneficio crucial ofrece el caso `default:` en el `switch` de una FSM, incluso si crees que definiste todos los estados en el `enum`?
Respuesta: Es una barrera defensiva (exigida por MISRA-C). Si una interferencia electromagnética severa corrompe la variable estado_actual en la memoria RAM asignándole un número que no existe en tu enum (ej. 99), el código caería en el default, el cual atrapa la ejecución y la redirige forzosamente hacia ST_ERROR, apagando los motores en lugar de dejar el software vagando en la nada.

2. ¿Por qué la función de la máquina de estados jamás debe contener un bucle `while()` infinito dentro de uno de sus `case`?
Respuesta: Porque destruiría la naturaleza concurrente del Súper Bucle. Si el case ST_INICIANDO: tuviera un while(esperando_gas), el microcontrolador se quedaría atrapado ahí. El teclado no respondería y la pantalla se congelaría. La FSM debe evaluarse y salir (Return) inmediatamente, dejando que las variables globales dicten si en la próxima pasada sigue en el mismo estado o avanza.