DÍA 030 DE 365

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

📖 Teoría Explícita: El Patrón "Super Loop" con Máquina de Estados

Un sistema embebido industrial rara vez termina su ejecución; corre en un ciclo infinito evaluando condiciones y reaccionando a eventos externos. En esta integración, vamos a combinar tres pilares fundamentales que hemos desarrollado en la Fase 1:

ARQUITECTURA DE FIRMWARE: ESQUELETO CENTRAL Teclado 4x4 Capacitivo Polling / I2C Flujómetro (Hall) Interrupción HW (EXTI) Botón Emergencia Interrupción NMI MÁQUINA DE ESTADOS - ESTADO_REPOSO - ESTADO_TECLADO - ESTADO_DESPACHANDO - ESTADO_TICKET Cálculo Punto Fijo & Struct_t (Volumen, Precio, Total, Empaquetado) Válvula / Bomba (Control GPIO) UART / ESP32 (Serialización)

⚡ Interconexión: Tabla de Asignación de Hardware

A nivel de hardware, el diseño del sistema orquesta entradas críticas asíncronas con rutinas secuenciales.

Periférico (Capa Física) Tipo de Señal Rol en la Máquina de Estados
Teclado 4x4 Capacitivo I2C / GPIO Polling (3.3V) Configurar precio unitario, ingresar límite monetario de venta, iniciar/detener transacción.
Sensor Flujómetro (Hall) Pulsos Digitales (EXTI) Incremento de volumen asíncrono. No detiene la FSM; alimenta la variable de control.
Relé Estado Sólido (Bomba) Salida Digital (GPIO) Ejecuta la acción del estado `DESPACHANDO`. Se apaga en `TICKET` o `EMERGENCIA`.

⚙️ Código C: El Bucle Principal (main.c)

Este es el esqueleto donde las interrupciones alimentan las variables globales, y la FSM procesa las decisiones con base en el teclado capacitivo y las matemáticas escaladas.


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

// --- 1. DEFINICIONES DE MÁQUINA DE ESTADOS ---
typedef enum {
    ESTADO_REPOSO,
    ESTADO_CONFIG_PRECIO,
    ESTADO_DESPACHANDO,
    ESTADO_IMPRIMIR_TICKET,
    ESTADO_EMERGENCIA
} EstadoDespachador_t;

// --- 2. ESTRUCTURAS Y CONSTANTES (De días anteriores) ---
#define ESCALA_VOLUMEN 1000
#define PULSOS_POR_LITRO 450

typedef struct __attribute__((packed)) {
    uint32_t id_transaccion;
    uint32_t volumen_ml;
    uint32_t precio_litro_cent;
    uint32_t total_pagar_cent;
} Ticket_t;

// Variables Globales Volátiles (Modificadas en ISR)
volatile uint32_t contador_pulsos = 0;
volatile bool bandera_emergencia = false;

// Variables de Estado
EstadoDespachador_t estado_actual = ESTADO_REPOSO;
Ticket_t ticket_actual;
uint32_t precio_actual_centavos = 2050; // $20.50 MXN inicial

// --- 3. PROTOTIPOS HARDWARE/PERIFÉRICOS ---
void GPIO_Bomba_Encender(void);
void GPIO_Bomba_Apagar(void);
char leer_teclado_capacitivo_4x4(void); // Driver del teclado (I2C/Matricial)
void enviar_ticket_uart(const Ticket_t *ticket);

// --- 4. RUTINAS DE INTERRUPCIÓN (ISR) ---
void EXTI_Flujometro_Callback(void) {
    if(estado_actual == ESTADO_DESPACHANDO) {
        contador_pulsos++;
    }
}

void EXTI_BotonEmergencia_Callback(void) {
    bandera_emergencia = true; // Flag atómica
}

// --- 5. BUCLE PRINCIPAL (SUPER LOOP) ---
int main(void) {
    // Hardware_Init(); // Relojes, GPIO, I2C, UART
    
    char tecla;
    
    while(1) {
        // --- A. GESTIÓN DE EMERGENCIA PRIORITARIA ---
        if(bandera_emergencia) {
            estado_actual = ESTADO_EMERGENCIA;
        }

        // --- B. MÁQUINA DE ESTADOS PRINCIPAL ---
        switch(estado_actual) {
            
            case ESTADO_REPOSO:
                GPIO_Bomba_Apagar();
                contador_pulsos = 0;
                
                tecla = leer_teclado_capacitivo_4x4();
                if(tecla == '*') {
                    estado_actual = ESTADO_CONFIG_PRECIO;
                } else if(tecla == '#') {
                    // Inicializar estructura del ticket
                    ticket_actual.id_transaccion++;
                    ticket_actual.volumen_ml = 0;
                    ticket_actual.precio_litro_cent = precio_actual_centavos;
                    estado_actual = ESTADO_DESPACHANDO;
                }
                break;
                
            case ESTADO_CONFIG_PRECIO:
                // Lógica para capturar dígitos numéricos del teclado capacitivo
                // y actualizar la variable 'precio_actual_centavos'
                
                tecla = leer_teclado_capacitivo_4x4();
                if(tecla == 'D') { // Tecla D = Confirmar o Guardar
                    estado_actual = ESTADO_REPOSO;
                }
                break;
                
            case ESTADO_DESPACHANDO:
                GPIO_Bomba_Encender();
                
                // Matemática de Punto Fijo en tiempo real (Protegida)
                uint32_t pulsos_snap = contador_pulsos; 
                ticket_actual.volumen_ml = (pulsos_snap * ESCALA_VOLUMEN) / PULSOS_POR_LITRO;
                
                // Actualizar Display (LCD/OLED)
                // Display_Actualizar(ticket_actual.volumen_ml);
                
                tecla = leer_teclado_capacitivo_4x4();
                if(tecla == '#') { // Detener despacho
                    GPIO_Bomba_Apagar();
                    estado_actual = ESTADO_IMPRIMIR_TICKET;
                }
                break;
                
            case ESTADO_IMPRIMIR_TICKET:
                // Cálculo de precio final sin floats y casteo a 64 bits para evitar overflow
                uint64_t importe_temp = ((uint64_t)ticket_actual.volumen_ml * ticket_actual.precio_litro_cent) / ESCALA_VOLUMEN;
                ticket_actual.total_pagar_cent = (uint32_t)importe_temp;
                
                enviar_ticket_uart(&ticket_actual);
                estado_actual = ESTADO_REPOSO;
                break;
                
            case ESTADO_EMERGENCIA:
                GPIO_Bomba_Apagar();
                // Bloqueo total, requiere reinicio por llave de supervisor
                break;
        }
    }
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Cierre Fase 1: Arquitectura de Control Unificada.

Al llegar al final de este bloque, tu sistema dejó de ser un puñado de rutinas matemáticas y se convirtió en una entidad operativa. Has integrado un diseño de hardware industrial con la agilidad del software C: al integrar la lectura del panel de usuario a través del teclado capacitivo dentro de la Máquina de Estados central, garantizas que ni los rebotes mecánicos de un botón clásico ni las pulsaciones humanas bloqueen el cálculo de punto fijo en tiempo real. Este esqueleto es determinista, seguro y está listo para escalar a sistemas operativos en tiempo real (RTOS) en la siguiente etapa.

📝 Resumen del Día

❓ Cuestionario de Autoevaluación

1. En el código del Súper Bucle, la variable `contador_pulsos` se copia a una variable local `pulsos_snap` antes de realizar el cálculo matemático. ¿Por qué es fundamental esta acción en C embebido?
Respuesta: Debido al riesgo de inconsistencia. Al ser `contador_pulsos` una variable volátil modificada por una interrupción, el flujómetro podría enviar un nuevo pulso *mientras* la CPU está haciendo la multiplicación y división. Al tomar una "captura" (`snap`), congelamos el valor para las matemáticas de ese ciclo del bucle, asegurando un cálculo íntegro.