DÍA 334 DE 365

Fase 5: Diseño Industrial y Hardware

📖 Teoría a Estudiar

El Pecado del Blocking Wait (`HAL_Delay`)

A nivel de ensamblador, la función HAL_Delay(1000) del ecosistema STM32 atrapa a la CPU en un bucle ciego: "Anota la hora actual, y gira en círculos sin hacer absolutamente nada hasta que el reloj avance 1000 milisegundos". Durante ese tiempo, la CPU que corre a 84 MHz desperdicia 84 millones de ciclos de reloj. Si un sensor I2C exige un retardo de 50ms antes de poder recibir su primer comando, usar HAL_Delay(50) es un crimen contra la concurrencia.

El Paradigma del Arranque Paralelo (Asynchronous Boot)

El silicio del procesador STM32 (Master) arranca casi instantáneamente (< 2 milisegundos). Los cuellos de botella son los periféricos (Esclavos) que tienen sus propios microcontroladores internos lentos. La estrategia de Boot Paralelo consiste en que el STM32 envíe la señal de encendido eléctrico (Enable Pin) a todos los periféricos simultáneamente en el milisegundo cero, y luego dedique su tiempo a leer la EEPROM, arrancar los relojes y calcular criptografía. Solo interrogará a los periféricos cuando hayan transcurrido sus respectivos tiempos de gracia biológicos.

ARQUITECTURA DE ARRANQUE: SECUENCIAL VS PARALELO 0 ms 1000 ms 2000 ms 3000 ms SECUENCIAL (MAL) STM32 BOOT HAL_Delay(2000) - ESP32 WARMUP AT+EN HAL_Delay(500) LCD ON Tiempo Total: > 2500 ms (Bloqueante) PARALELO (FSM) PWR ON PWR ON CRC Check, EEPROM Read, Vars Init LCD_INIT AT+EN APP RUNNING La lógica arranca en milisegundos. Se ignora el ESP32 hasta que esté listo.

⚙️ Procedimiento Práctico: Hardware Power-Sequencing

Paso 1: Pines de "Enable" de Hardware.
Para controlar el arranque, tu STM32 debe ser el maestro absoluto de la energía. En el diseño de tu placa (KiCad), los periféricos grandes (ESP32, Backlight del LCD) no deben estar conectados permanentemente a la línea de 3.3V. Deben alimentarse a través de un Transistor MOSFET canal P (Load Switch), cuya compuerta esté conectada a un pin GPIO del STM32. Así, el STM32 decide exactamente en qué milisegundo reciben voltaje.

Paso 2: Borrar todos los `HAL_Delay()` del código.
Busca en todo tu proyecto (Ctrl+Shift+F) la palabra clave `HAL_Delay`. Deberás reemplazarlos con temporizadores de software basados en el reloj maestro HAL_GetTick(), transformando tu inicialización lineal en una Máquina de Estados (State Machine).

💻 Firmware: El Boot State Machine (Non-Blocking)

En lugar de bloquear la CPU esperando al Wi-Fi, dispararemos el temporizador y el sistema principal seguirá girando en el while(1). Cuando el temporizador marque que han pasado 2000 milisegundos, el código pasará al siguiente estado y enviará los comandos AT.


/**
 * @file system_boot_optimizer.c
 * @brief Máquina de Estados de Arranque No Bloqueante (Asynchronous Boot FSM)
 */

#include "stm32f4xx_hal.h"
#include <stdbool.h>
#include <stdio.h>

// =========================================================================
// DEFINICIÓN DE ESTADOS Y TIMEOUTS
// =========================================================================
typedef enum {
    BOOT_STATE_INIT_HARDWARE = 0,
    BOOT_STATE_WAIT_LCD,
    BOOT_STATE_WAIT_WIFI,
    BOOT_STATE_ALL_SYSTEMS_GO
} BootState_t;

BootState_t Current_Boot_State = BOOT_STATE_INIT_HARDWARE;

// Tiempos de calentamiento requeridos por el datasheet de los periféricos
#define LCD_WARMUP_TIME_MS   100
#define WIFI_WARMUP_TIME_MS  2000

uint32_t Boot_Start_Tick = 0;

// Variables lógicas del sistema
bool is_lcd_ready = false;
bool is_wifi_ready = false;

// =========================================================================
// LA MÁQUINA DE ESTADOS DEL ARRANQUE (Se llama en el while(1))
// =========================================================================
void System_Async_Boot_Manager(void) {
    
    uint32_t current_tick = HAL_GetTick();

    switch (Current_Boot_State) {
        
        case BOOT_STATE_INIT_HARDWARE:
            printf("[BOOT] Energizando periféricos simultáneamente...\r\n");
            
            // 1. Dar poder eléctrico físico a los periféricos al mismo tiempo
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); // LCD Enable Pin
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET); // ESP32 Enable Pin
            
            // 2. Registrar la marca de tiempo de inicio
            Boot_Start_Tick = current_tick;
            
            // 3. Avanzar al estado de espera no bloqueante
            Current_Boot_State = BOOT_STATE_WAIT_LCD;
            break;

        case BOOT_STATE_WAIT_LCD:
            // Comprobar si ya pasaron los 100ms requeridos por la LCD
            if ((current_tick - Boot_Start_Tick) >= LCD_WARMUP_TIME_MS) {
                // Ahora sí es seguro enviar comandos I2C/SPI
                printf("[BOOT] LCD Caliente. Inicializando controlador...\r\n");
                LCD_Send_Init_Sequence();
                is_lcd_ready = true;
                
                // Avanzar a esperar al módulo más lento
                Current_Boot_State = BOOT_STATE_WAIT_WIFI;
            }
            // Mientras tanto, la CPU retorna inmediatamente y puede ejecutar otras tareas
            break;

        case BOOT_STATE_WAIT_WIFI:
            // La pantalla ya está lista, mostramos el logo mientras esperamos al Wi-Fi
            if (is_lcd_ready) {
                LCD_Print_Loading_Animation();
            }

            // Comprobar si ya pasaron los 2000ms requeridos por el ESP32
            if ((current_tick - Boot_Start_Tick) >= WIFI_WARMUP_TIME_MS) {
                printf("[BOOT] ESP32 Caliente. Enviando comando AT de Sincronización...\r\n");
                ESP32_Send_Command("AT+RST\r\n");
                is_wifi_ready = true;
                
                Current_Boot_State = BOOT_STATE_ALL_SYSTEMS_GO;
            }
            break;

        case BOOT_STATE_ALL_SYSTEMS_GO:
            // El proceso de arranque finalizó. El sistema cede el control a la FSM principal de ventas.
            break;
    }
}

// =========================================================================
// EJEMPLO DEL BUCLE PRINCIPAL (MAIN)
// =========================================================================
int main(void) {
    HAL_Init();
    SystemClock_Config();
    
    // Configuraciones nativas (SRAM) que toman microsegundos se hacen aquí
    Metrology_Variables_Init();
    
    while (1) {
        // En lugar de detenerse 2 segundos, el bucle gira miles de veces por segundo.
        // El Boot_Manager avanza de estado solo cuando los relojes biológicos coinciden.
        if (Current_Boot_State != BOOT_STATE_ALL_SYSTEMS_GO) {
            System_Async_Boot_Manager();
        } else {
            // Lógica principal de venta de Gas LP
            Process_Gas_Dispenser_Sales();
        }
        
        // Otras tareas críticas que no se pueden detener nunca (ej. Anti-Fraude)
        Security_Fraud_Detection_Task(); 
    }
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Disponibilidad Extrema y Reducción del Tiempo de Recuperación (MTTR).

En el Día 325 implementamos el sistema de "Dying Breath" para sobrevivir a los apagones guardando la venta de gas en la memoria de respaldo de la batería. Pero cuando la energía de CFE regresa, el operador y el cliente están parados frente a la máquina con gran ansiedad. Si tu firmware usa retardos bloqueantes (`HAL_Delay`), la pantalla de la máquina permanecerá en negro total durante casi 3 segundos mientras espera al Wi-Fi. El cliente asumirá que su dinero se perdió, y el operador presionará botones desesperadamente, arriesgándose a cancelar la venta rescatada.

Al implementar el **Arranque Asíncrono Paralelo (Boot State Machine)**, has logrado que tu Despachador de Gas cobre vida instantáneamente. El microcontrolador despierta, energiza a los periféricos esclavos, recupera la venta de la memoria BKP y dibuja la interfaz en la pantalla LCD en menos de **120 milisegundos** (la suma del boot del ARM + calentamiento del LCD). El cliente ve su dinero en la pantalla casi en el instante en que regresa la luz. Y mientras el cliente se relaja y el operador levanta la pistola de la manguera, el módulo Wi-Fi ESP32 termina silenciosamente sus lentos 2 segundos de calentamiento en el fondo (Background) sin que nadie lo note. Esta es la diferencia entre un código que simplemente "funciona" y una experiencia de usuario industrial impecable.

📝 Resumen del Día