DÍA 028 DE 365

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

📖 Teoría Explícita: El Paradigma Head/Tail

1. Productores y Consumidores

En sistemas embebidos, hay dos actores manejando la memoria. El Productor es usualmente el hardware (la Interrupción UART) que inserta bytes tan pronto como llegan. El Consumidor es tu Súper Bucle (main) que extrae esos bytes para analizarlos cuando la CPU está libre.

2. Los Punteros Circulares

Para no pisarnos los pies, usamos dos índices independientes:

3. Detección de Colisiones (Empty vs Full)

El búfer nos dice su estado basándose en la posición de ambos punteros:

UART RX RING BUFFER (LÓGICA HEAD/TAIL) [0] [1] [2] 0xAA [3] 0xBB [4] 0xCC [5] [6] [7] TAIL Consumidor (Main) HEAD Productor (ISR) Aritmética Modular: Índice = (Índice + 1) % 8

⚙️ Ejercicios Prácticos: Código Libre de Bloqueos

Ejercicio 1: Implementaremos el búfer de recepción (RX) del UART para asegurar que ningún comando del ESP32 se pierda. Utilizaremos el modificador volatile extensivamente, protegiendo las variables que comparten el contexto principal y la Interrupción (Recordando el Día 26).


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

// --- 1. DEFINICIONES DEL BÚFER FIFO ---
// El tamaño debe ser suficiente para almacenar el payload más largo del sistema.
// 64 Bytes es un estándar industrial excelente para comandos UART.
#define RX_BUFFER_SIZE 64

// Encapsulamos la lógica en una estructura global
typedef struct {
    volatile uint8_t data[RX_BUFFER_SIZE]; // El arreglo físico en SRAM
    volatile uint8_t head;                 // Índice donde escribirá la ISR
    volatile uint8_t tail;                 // Índice donde leerá el Main
} UART_RingBuffer_t;

UART_RingBuffer_t rx_fifo = { .head = 0, .tail = 0 };

// --- 2. EL PRODUCTOR (Hardware / ISR) ---
// Esta función es disparada por el pin físico UART_RX cuando llega 1 byte.
void UART_RX_Interrupt_Handler(uint8_t byte_recibido) {
    
    // Calculamos matemáticamente cuál sería la SIGUIENTE posición de la cabeza
    uint8_t siguiente_head = (rx_fifo.head + 1) % RX_BUFFER_SIZE;
    
    // Verificamos colisión (Buffer Lleno)
    if (siguiente_head != rx_fifo.tail) {
        // Hay espacio. Escribimos el byte en la RAM.
        rx_fifo.data[rx_fifo.head] = byte_recibido;
        // Avanzamos oficialmente la cabeza
        rx_fifo.head = siguiente_head;
    } else {
        // ERROR: Buffer Overflow Asíncrono (Hardware más rápido que Software)
        // El byte se pierde para proteger la integridad de los datos antiguos.
    }
}

// --- 3. EL CONSUMIDOR (Software / Súper Bucle) ---
// Esta función la llamas tú, cuando la CPU esté disponible.
bool Leer_Byte_Del_Buffer(uint8_t* byte_destino) {
    
    // Si la cabeza y la cola están en el mismo cajón, el búfer está vacío.
    if (rx_fifo.head == rx_fifo.tail) {
        return false; 
    }
    
    // Extraemos el byte más antiguo (el que señala la cola)
    *byte_destino = rx_fifo.data[rx_fifo.tail];
    
    // Avanzamos la cola matemáticamente persiguiendo a la cabeza
    rx_fifo.tail = (rx_fifo.tail + 1) % RX_BUFFER_SIZE;
    
    return true; // Lectura exitosa
}

// =====================================================================
// SIMULACIÓN EN LA MÁQUINA DE ESTADOS
// =====================================================================
int main(void) {
    
    uint8_t byte_entrante = 0;
    
    while (1) {
        // En cada vuelta del Súper Bucle, vaciamos la cola si hay datos.
        // Mientras el búfer tenga datos (Read == true), procesamos.
        while (Leer_Byte_Del_Buffer(&byte_entrante)) {
            
            // Si el ESP32 mandó el comando 0xFF (Falla), reaccionamos.
            if (byte_entrante == 0xFF) {
                // Activar protocolos de seguridad de las válvulas
            }
        }
        
        // Continuamos monitoreando la presión del gas sin habernos bloqueado
        // nunca a "esperar" que el ESP32 terminara de hablar.
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Recepción Telemetría Asíncrona (Non-Blocking UART).

Un despachador de gas que utiliza funciones como UART_Receive_Wait() está condenado al fracaso: si el ESP32 se congela y no envía el último byte, el STM32 se quedará bloqueado esperando infinitamente, ignorando el flujo del gas. Al usar el paradigma Head/Tail en un Ring Buffer, has desacoplado el hardware del software. El hardware atrapa los datos en microsegundos y el software los lee a su propio ritmo seguro. Esta es la base de un RTOS (Real Time Operating System) construida a mano en C puro.

📝 Resumen del Día