DÍA 161 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Contrato de Software (Header Files)

En el lenguaje C, cuando un archivo necesita usar una función que está en otro lado, necesita saber su "firma" (qué recibe y qué devuelve) antes de que el compilador la acepte. El archivo `.h` (Header File) actúa como un contrato público o un menú de restaurante. Te dice qué platillos (funciones) puedes pedir, pero no te dice cómo se cocinaron.

El archivo `.c` (Source File) es la cocina. Ahí es donde vive el código real (los bucles `while`, los IFs y el acceso a los registros del hardware).

Guardas de Inclusión (Include Guards)

Si el archivo `modbus.h` incluye a `valvula.h`, y el `main.c` incluye a ambos, el compilador procesará el texto de `valvula.h` dos veces. Esto generará un error fatal de "Redefinición de Estructura". Para evitarlo, todo archivo `.h` del planeta debe estar envuelto en macros de seguridad llamadas Include Guards (#ifndef, #define, #endif). Ellas garantizan que el compilador solo copie el contrato una sola vez por compilación.

Encapsulamiento con `static`

En lenguajes como C++ o Java usamos la palabra private para esconder variables peligrosas. En C, usamos la palabra static a nivel global. Si declaras static float totalizador_litros = 0; dentro de `metrologia.c`, esa variable se vuelve invisible para el resto del proyecto. Nadie, ni siquiera usando `extern`, podrá leerla o modificarla desde `main.c`. Esto evita que un programador novato borre el totalizador por error al usar el mismo nombre de variable en otro archivo.

ARQUITECTURA MODULAR: COMPILACIÓN SEPARADA Y ENCAPSULAMIENTO Mal: Código Monolítico main.c (15,000 líneas) int timer_count; void LCD_Init() {...} void PID_Calc() {...} int modbus_state; void Flash_Write() {...} Spaghetti Code Las variables globales chocan entre sí. Bien: Arquitectura Modular (Módulos Aislados) Módulo Metrología metrologia.h (El Contrato) #ifndef METRO_H #define METRO_H float Metro_GetLitros(); metrologia.c (La Cocina) Muralla de Privacidad static float litros = 0; float Metro_GetLitros() { return litros; } Aplicación Principal main.c #include "metrologia.h" void main() { float L = Metro_GetLitros(); } Incluye Enlazador (Linker) Construyendo el Firmware Final (.bin) metrologia.o main.o El Linker une los objetos compilados y verifica que las firmas concuerden con los contratos (.h).

⚙️ Ejercicios Prácticos

Ejercicio 1: Vamos a refactorizar el código de nuestro controlador PID (Día 151). Lo sacaremos del `main.c` y crearemos su propia Unidad de Traducción: pid_valvula.h y pid_valvula.c. Protegeremos las variables internas del PID usando el modificador static, exponiendo únicamente las funciones de Inicialización y Actualización para que el Súper Bucle las utilice.

Archivo: pid_valvula.h (El Contrato Público)


/**
 * @file pid_valvula.h
 * @brief Interfaz Pública para el controlador PID de Válvulas Proporcionales
 */

// 1. LAS GUARDAS DE INCLUSIÓN (Include Guards)
// Evitan que este archivo se copie más de una vez si varios .c lo incluyen.
#ifndef PID_VALVULA_H
#define PID_VALVULA_H

#include <stdint.h>

// Opcional: Estructuras de datos públicas que otros módulos necesiten conocer
typedef struct {
    float kp;
    float ki;
    float kd;
} PID_Config_t;

// 2. PROTOTIPOS DE FUNCIONES PÚBLICAS (API)
// El main() podrá invocar estas funciones.
void PID_Valvula_Init(PID_Config_t *configuracion);
float PID_Valvula_Update(float setpoint, float medicion, float dt);

#endif /* PID_VALVULA_H */
        

Archivo: pid_valvula.c (La Implementación Oculta)


/**
 * @file pid_valvula.c
 * @brief Implementación y lógica privada del Controlador PID
 */

// Incluimos nuestro propio contrato para que el compilador verifique firmas
#include "pid_valvula.h"

// 1. VARIABLES PRIVADAS (Encapsulamiento)
// Al añadir 'static', estas variables SOLO existen dentro de este archivo.
// Ningún otro archivo en el proyecto puede leerlas o sobrescribirlas.
static float error_previo = 0.0f;
static float integrador = 0.0f;
static float limite_vueltas = 100.0f;

static PID_Config_t params_internos;

// 2. FUNCIONES PRIVADAS (Ocultas)
// Funciones de uso interno que el Súper Bucle no necesita conocer.
static float aplicar_anti_windup(float val_integral) {
    if (val_integral > limite_vueltas) return limite_vueltas;
    if (val_integral < -limite_vueltas) return -limite_vueltas;
    return val_integral;
}

// 3. IMPLEMENTACIÓN DE FUNCIONES PÚBLICAS
void PID_Valvula_Init(PID_Config_t *config) {
    // Copiamos la configuración a nuestra estructura privada
    params_internos.kp = config->kp;
    params_internos.ki = config->ki;
    params_internos.kd = config->kd;
    
    error_previo = 0.0f;
    integrador = 0.0f;
}

float PID_Valvula_Update(float setpoint, float medicion, float dt) {
    
    if (dt <= 0.0f) return 0.0f; 
    
    float error = setpoint - medicion;
    float P = params_internos.kp * error;
    
    integrador += error * dt;
    integrador = aplicar_anti_windup(integrador); // Llamada a func privada
    float I = params_internos.ki * integrador;
    
    float D = params_internos.kd * ((error - error_previo) / dt);
    
    error_previo = error;
    
    float salida = P + I + D;
    
    // Clamp de seguridad 0-100%
    if(salida > 100.0f) salida = 100.0f;
    if(salida < 0.0f) salida = 0.0f;
    
    return salida;
}
        

Archivo: main.c (Aplicación Limpia)


/**
 * @file main.c
 * @brief Archivo Maestro (Súper Bucle) refactorizado
 */

#include "stm32f401xe.h"
#include "pid_valvula.h" // Incluimos el contrato del PID
// #include "modbus.h"
// #include "lcd_i2c.h"

int main(void) {
    
    // El main() ahora está inmaculado. Solo coordina a los gerentes.
    
    PID_Config_t calibracion = { .kp = 15.0f, .ki = 2.5f, .kd = 0.5f };
    PID_Valvula_Init(&calibracion);
    
    while(1) {
        
        // El main() no sabe qué es el Anti-Windup ni le importan 
        // las variables internas de error_previo. Solo pasa datos.
        float salida = PID_Valvula_Update(20.0f, 18.5f, 0.01f);
        
        // aplicar_pwm(salida);
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Mantenibilidad del Código a Nivel Empresarial.

Si alguna vez intentas que tu placa de circuito impreso (PCB) sea revisada por una empresa certificadora o si contratas a otro ingeniero para que te ayude a programar una nueva función de cobro bancario, lo primero que exigirán será un código legible. Nadie invertirá dinero en una máquina donde el controlador de la válvula, el protocolo Modbus RS485 y el dibujado de la pantalla LCD están todos amontonados en un archivo `main.c` infinito.

Al implementar la **Refactorización en Headers y Módulos**, has creado un "Firmware Base". En el futuro, si decides cambiar la pantalla LCD por una Táctil a color, simplemente borras `lcd_i2c.c`, programas un nuevo `pantalla_tactil.c`, y al incluir su Header en el `main()`, tu Despachador seguirá funcionando. El Súper Bucle principal ha dejado de ser un albañil, y se ha convertido en el verdadero Arquitecto: solo da órdenes a sus gerentes delegados y coordina la música industrial.

📝 Resumen del Día