DÍA 047 DE 365

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

📖 Teoría Explícita: HMI Mediante Nodos y Punteros

1. El Problema del Código Espagueti Visual

Cuando diseñas un menú con múltiples pantallas (Inicio -> Configuración -> Cambiar Precio -> Confirmar), usar múltiples switch-case anidados rápidamente se vuelve inmanejable. Además, si el microcontrolador se queda "esperando" dentro de un bucle a que el operador presione "ENTER" en el teclado matricial, la interrupción del flujómetro (metrología) podría sufrir latencia.

2. Abstracción del HMI: Menús como Nodos (Structs)

En el Día 31 implementamos la Máquina de Estados Finitos (FSM) para el control del hardware. Hoy aplicaremos ese mismo patrón a la Pantalla LCD. Cada pantalla del Despachador será un Nodo de Menú (Struct). Este Nodo contendrá:

ÁRBOL DE NAVEGACIÓN (NODOS LIGADOS) NODO_PRINCIPAL Texto: "BIENVENIDO" *Acción_MostrarReloj() [Enter] -> NODO_MENU ENTER NODO_MENU Texto: "1. Venta 2. Conf" *Acción_Vacia() [Atrás] -> NODO_PRINCIPAL Key '1' NODO_VENTA "Ingresa $:" Key '2' NODO_CONF "Admin Pass:" ATRÁS (Esc)

⚙️ Ejercicios Prácticos: Implementación en C Puro

Ejercicio 1: Construiremos la estructura del Nodo HMI utilizando punteros a la misma estructura (Listas Enlazadas Estáticas). Esto nos permitirá movernos infinitamente por el menú simplemente reasignando un puntero maestro llamado Pantalla_Actual.


#include <stdint.h>

// --- 1. DEFINICIÓN DE LA ESTRUCTURA DEL NODO (El ADN del Menú) ---
// Usamos 'typedef struct MenuNode' para permitir referencias circulares
typedef struct MenuNode {
    const char* texto_linea_1;     // Texto fijo alojado en la Flash (Día 27)
    const char* texto_linea_2;     
    void (*funcion_render)(void);  // Callback: Función dinámica (Ej. Dibujar variables)
    
    // Punteros de Navegación: Hacia dónde ir según el botón presionado
    const struct MenuNode* btn_arriba;
    const struct MenuNode* btn_abajo;
    const struct MenuNode* btn_enter;
    const struct MenuNode* btn_atras;
} NodoPantalla_t;


// --- 2. DECLARACIÓN DE LAS FUNCIONES DINÁMICAS (Callbacks) ---
void Render_Nada(void) { /* Para pantallas que solo muestran texto estático */ }

extern uint32_t variable_precio_actual; // Almacenada en SRAM
void Render_MostrarPrecio(void) {
    // Aquí usamos nuestro conversor de entero a cadena (Día 46)
    // y enviamos los caracteres a la LCD I2C (Día 37).
}


// --- 3. CREACIÓN DEL ÁRBOL EN LA MEMORIA FLASH (ROM) ---
// Al declarar toda la estructura como 'const', ahorramos 100% de la SRAM
// Debemos pre-declarar los nodos porque se referencian entre sí (forward declaration)
extern const NodoPantalla_t PANTALLA_PRINCIPAL;
extern const NodoPantalla_t PANTALLA_MENU;
extern const NodoPantalla_t PANTALLA_VENTA;

const NodoPantalla_t PANTALLA_PRINCIPAL = {
    "BIENVENIDO",           // Línea 1
    "PULSE ENTER",          // Línea 2
    Render_Nada,            // Acción especial: Ninguna
    &PANTALLA_PRINCIPAL,    // btn_arriba (No hace nada)
    &PANTALLA_PRINCIPAL,    // btn_abajo  (No hace nada)
    &PANTALLA_MENU,         // btn_enter  -> Salta al Menú
    &PANTALLA_PRINCIPAL     // btn_atras  (No hace nada)
};

const NodoPantalla_t PANTALLA_MENU = {
    "> 1. DESPACHAR",
    "  2. CONFIGURACION",
    Render_MostrarPrecio,   // Acción: Muestra el precio en una esquina
    &PANTALLA_MENU,         
    &PANTALLA_MENU,         
    &PANTALLA_VENTA,        // btn_enter -> Ingresar a Venta
    &PANTALLA_PRINCIPAL     // btn_atras -> Vuelve al inicio
};

// --- (Declarar el resto de nodos...) ---


// --- 4. MOTOR DE RENDERIZADO Y NAVEGACIÓN (SÚPER BUCLE) ---
// El puntero maestro que rastrea en qué nodo estamos parados.
const NodoPantalla_t* Pantalla_Actual = &PANTALLA_PRINCIPAL;

// Evento ficticio del driver del teclado matricial (Keypad)
extern uint8_t Leer_Boton_Presionado(void); 

void Procesar_HMI(void) {
    uint8_t tecla = Leer_Boton_Presionado();
    
    // NAVEGACIÓN: Cambiamos el puntero maestro con O(1) ciclos de CPU.
    if (tecla == 'E') Pantalla_Actual = Pantalla_Actual->btn_enter;
    if (tecla == 'B') Pantalla_Actual = Pantalla_Actual->btn_atras;
    // (Otras teclas...)

    // RENDERIZADO:
    // 1. Dibujar el texto fijo en la LCD
    // LCD_Imprimir(Pantalla_Actual->texto_linea_1);
    
    // 2. Ejecutar la función dinámica del nodo a través de su puntero a función (Día 31)
    if (Pantalla_Actual->funcion_render != 0x00) {
        Pantalla_Actual->funcion_render();
    }
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Interfaz Humano-Máquina Asíncrona.

Con esta arquitectura de Árbol de Nodos, la pantalla LCD I2C de tu Despachador jamás congelará el conteo de los litros del gas. La función Procesar_HMI() del Súper Bucle toma apenas un par de microsegundos en ejecutarse. Simplemente revisa si se presionó una tecla, mueve un puntero de memoria a la siguiente estructura predefinida en Flash, dispara la función de dibujo, y cede el control del CPU inmediatamente para que la máquina pueda seguir leyendo el transductor analógico y calculando el corte metrológico con seguridad milimétrica.

📝 Resumen del Día