DÍA 140 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Choque de Arquitecturas: Endianness

Un registro de Modbus (Holding Register) guarda un número entero de $16$ bits. Como el bus UART transfiere datos en trozos de $8$ bits, tenemos que enviar ese número partido en dos bytes: una mitad "Alta" y una mitad "Baja".

Aquí reside la trampa industrial que destruye los fines de semana de los ingenieros novatos:

Si simplemente copias la memoria RAM directo al puerto UART (usando el DMA para transmitir), el Servidor Central recibirá los bytes al revés. Leerá 0x1A04, y en lugar de cobrar $10.50$ pesos, el software creerá que el gas cuesta $66.60$ pesos. Para evitar la catástrofe, **debemos invertir los bytes en software (Swapping)** antes de enviarlos y al momento de recibirlos.

Función 03: Leer Holding Registers

Cuando el Maestro te envía una trama con Función 03, te está pidiendo que le envíes el contenido de uno o más registros (Arreglos de 16-bits). El esclavo debe responder con: El ID, la Función 03, **la cantidad de bytes que va a mandar en total**, y luego los datos de los registros (partidos en dos bytes cada uno, High seguido de Low).

FUNCIÓN 03 (READ HOLDING REGS) Y ENDIANNESS SWAP Trama Entrante (RX RAM) 01 03 00 00 00 02 CRC Reg[0] Leer 2 Regs Lógica ARM Cortex (Little-Endian) start_addr = (rx[2] << 8) | rx[3]; // = 0 num_regs = (rx[4] << 8) | rx[5]; // = 2 holding_regs[0] = 1050 (0x041A) Trama de Respuesta (TX RAM) Construcción Byte a Byte (Big-Endian forced) tx[0] = 0x01; // ID tx[1] = 0x03; // Función tx[2] = 4; // 2 regs * 2 bytes = 4 bytes datos val = holding_regs[0]; // 0x041A tx[3] = (val >> 8) & 0xFF; // Extrae 0x04 (HIGH) tx[4] = val & 0xFF; // Extrae 0x1A (LOW) tx[7], tx[8] = Modbus_CRC16(tx, 7);

⚙️ Ejercicios Prácticos

Ejercicio 1: Integraremos la respuesta de la Función 03 (Read Holding Registers) y la Función 06 (Write Single Register) en el analizador (Parser) que armamos en el Día 139. Extraeremos las direcciones usando desplazamientos de bits (Bitwise Shifts) para esquivar la trampa del Endianness. Ensamblaremos la respuesta y la enviaremos físicamente usando el pin DE/RE (Día 138).


/**
 * @file modbus_handlers.c
 * @brief Manejadores de Funciones Modbus (FC03 y FC06) con Endianness Control
 */

#include "stm32f401xe.h"
#include <stdint.h>

#define MAX_HOLDING_REGS 10
extern uint16_t holding_registers[MAX_HOLDING_REGS]; // De Día 139
extern void rs485_enviar_trama_bloqueante(uint8_t* buffer, uint16_t longitud); // De Día 138

// (Simulamos la función CRC16 que desentrañaremos mañana en el Día 141)
extern uint16_t calcular_crc16(uint8_t *buffer, uint16_t longitud); 

/**
 * @brief Responde a la petición 0x03 (Leer registros de 16-bits)
 *        Trama RX: [ID][03][Start Hi][Start Lo][Num Hi][Num Lo][CRC_L][CRC_H]
 */
void procesar_fc03_read_holding(uint8_t* rx_buffer, uint16_t rx_len) {
    
    // 1. Extraer datos esquivando el Endianness (Modbus manda el High Byte primero)
    uint16_t start_addr = (rx_buffer[2] << 8) | rx_buffer[3];
    uint16_t num_regs   = (rx_buffer[4] << 8) | rx_buffer[5];
    
    // 2. Validación de Seguridad (Exception 0x02 - Illegal Data Address)
    if ((start_addr + num_regs) > MAX_HOLDING_REGS) {
        // Enviar trama de error Modbus 0x83
        return; 
    }
    
    // 3. Ensamblar Respuesta
    uint8_t tx_buffer[256];
    uint16_t tx_idx = 0;
    
    tx_buffer[tx_idx++] = rx_buffer[0]; // Eco del ID de la máquina (Ej. 0x01)
    tx_buffer[tx_idx++] = 0x03;         // Eco de la Función
    tx_buffer[tx_idx++] = num_regs * 2; // Byte Count (Cantidad de bytes que siguen)
    
    // Inyectar los registros forzando el formato Big-Endian
    for (uint16_t i = 0; i < num_regs; i++) {
        uint16_t valor = holding_registers[start_addr + i];
        
        tx_buffer[tx_idx++] = (valor >> 8) & 0xFF; // High Byte
        tx_buffer[tx_idx++] = valor & 0xFF;        // Low Byte
    }
    
    // 4. Calcular Firma CRC16 para la respuesta
    uint16_t crc = calcular_crc16(tx_buffer, tx_idx);
    
    // Modbus dice que el CRC se envía Little-Endian (Low Byte primero)
    // Es una de las raras excepciones del protocolo.
    tx_buffer[tx_idx++] = crc & 0xFF;        // CRC Low Byte
    tx_buffer[tx_idx++] = (crc >> 8) & 0xFF; // CRC High Byte
    
    // 5. Transmitir por RS485 controlando el pin DIR
    rs485_enviar_trama_bloqueante(tx_buffer, tx_idx);
}

/**
 * @brief Responde a la petición 0x06 (Escribir un solo registro de 16-bits)
 *        Trama RX: [ID][06][Reg Hi][Reg Lo][Val Hi][Val Lo][CRC_L][CRC_H]
 */
void procesar_fc06_write_single(uint8_t* rx_buffer, uint16_t rx_len) {
    
    uint16_t reg_addr  = (rx_buffer[2] << 8) | rx_buffer[3];
    uint16_t reg_valor = (rx_buffer[4] << 8) | rx_buffer[5];
    
    if (reg_addr >= MAX_HOLDING_REGS) {
        return; // Exception 0x02
    }
    
    // ¡LA ESCRITURA SE MATERIALIZA EN EL SISTEMA!
    holding_registers[reg_addr] = reg_valor;
    
    // Opcional: Aquí llamarías a la rutina para guardar el cambio en la EEPROM (Día 120)
    // para que el nuevo precio del gas sobreviva a un apagón.
    
    // La respuesta de la Función 06 es simplemente un eco exacto de la petición
    // Esto le confirma al Servidor Central que el comando fue ejecutado.
    rs485_enviar_trama_bloqueante(rx_buffer, rx_len);
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Sincronización de Base de Datos Bi-Direccional.

Una máquina expendedora en 2026 no puede tener precios codificados estáticamente. Si el precio internacional del gas sube, el administrador desde la oficina central abre su software SCADA y cambia el precio a $11.20 pesos en su pantalla. El SCADA envía silenciosamente una trama Modbus FC 06 dirigida al Holding Register `0`.

Tu Despachador intercepta el paquete mediante el DMA sin bloquear la metrología, extrae el número esquivando el Endianness y sobrescribe el arreglo `holding_registers[0]`. Al instante, el Súper Bucle utiliza ese nuevo precio para calcular la siguiente venta, y la pantalla I2C 20x4 se actualiza sola. El hardware devuelve la confirmación RS485. Acabas de lograr una Integración Vertical Completa, desde los microvoltios del sensor analógico hasta el software de contabilidad de la empresa.

📝 Resumen del Día