DÍA 136 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Protocolo ESC/POS

Una impresora térmica no es más que un microcontrolador conectado a un motor de pasos (que avanza el papel) y un cabezal de resistencias cerámicas que se calientan y queman papel químico. Para comunicarnos con ella, utilizamos el puerto UART (típicamente a 9600 o 115200 baudios). Si envías letras normales (ej. "Hola"), la impresora quema el papel dibujando esas letras con su fuente interna.

Pero, ¿cómo le dices a la impresora que quieres el texto en Negritas, o que active la Guillotina para cortar el papel? Utilizando secuencias de escape. El estándar global ESC/POS utiliza bytes de control, siendo los más famosos ESC (0x1B) y GS (0x1D). Cuando la impresora recibe un 0x1B, sabe que los siguientes bytes no son para imprimir, sino que son comandos de configuración.

Las Trampas de enviar Binarios por UART

En el Día 131 creamos una función uart_escribir_cadena(const char *texto). Esta función se detiene cuando encuentra un byte nulo (\0 o 0x00). ¡Cuidado! Muchos comandos ESC/POS requieren enviar bytes nulos como parte de su configuración. Si intentas usar tu función de cadenas (strings) para enviar comandos, el código C cortará la transmisión a la mitad. Necesitamos una nueva función Bare-Metal que envíe Arreglos de Bytes (Byte Arrays) indicando exactamente la longitud matemática a transferir.

UART Y ESC/POS: DEL CÓDIGO HEXADECIMAL AL PAPEL TÉRMICO STM32 UART TX (115200) 0x1B, 'a', 0x01 Alineación: Centro 0x1B, 'E', 0x01 Negritas: ON "GAS LP", 0x0A Print & Line Feed 0x1D, 'V', 0x42, 0x00 Corte de Papel (Cut) Impresora Térmica 58mm ESC/POS Decoder Thermal Head ✂ GAS LP 10.00 Lts $105.00 2026-09-10 Motor Stepper

⚙️ Ejercicios Prácticos

Ejercicio 1: Utilizaremos un puerto USART adicional (ej. `USART1` o `USART6`) si el `USART2` ya está ocupado con RS485. Crearemos la función uart_enviar_arreglo(uint8_t *datos, uint16_t longitud), y un envoltorio para enviar comandos ESC/POS limpios. Ensamblaremos el ticket extrayendo las fechas del RTC y los litros de la SRAM.


/**
 * @file printer_escpos.c
 * @brief Driver Bare-Metal para Impresora Térmica Serial (Protocolo ESC/POS)
 */

#include "stm32f401xe.h"

// --- COMANDOS UNIVERSALES ESC/POS ---
#define ESC 0x1B
#define GS  0x1D
#define LF  0x0A

const uint8_t CMD_INIT[]       = {ESC, '@'};                  // Resetear impresora
const uint8_t CMD_ALIGN_CENTER[] = {ESC, 'a', 1};             // Alinear centro
const uint8_t CMD_ALIGN_LEFT[]   = {ESC, 'a', 0};             // Alinear izquierda
const uint8_t CMD_BOLD_ON[]    = {ESC, 'E', 1};               // Negrita activada
const uint8_t CMD_BOLD_OFF[]   = {ESC, 'E', 0};               // Negrita desactivada
const uint8_t CMD_CUT_PAPER[]  = {GS, 'V', 66, 0};            // Guillotina (Corte Parcial)
const uint8_t CMD_SIZE_DOUBLE[]= {GS, '!', 0x11};             // Texto Doble Alto/Ancho
const uint8_t CMD_SIZE_NORMAL[]= {GS, '!', 0x00};             // Texto Normal

// Dependencia de la UART dedicada a la impresora
extern void uart_enviar_caracter(char c);

/**
 * @brief Envia un arreglo de bytes crudos (Útil para bytes nulos 0x00)
 */
void uart_enviar_arreglo(const uint8_t *datos, uint16_t longitud) {
    for (uint16_t i = 0; i < longitud; i++) {
        uart_enviar_caracter((char)datos[i]);
    }
}

/**
 * @brief Envia una cadena ASCII y añade un salto de línea (Print & Feed)
 */
void impresora_imprimir_linea(const char* texto) {
    while (*texto) {
        uart_enviar_caracter(*texto++);
    }
    uart_enviar_caracter(LF); // La impresora solo "quema" al recibir el Line Feed
}

// Dependencias de variables globales de nuestro sistema
extern uint32_t litros_despachados_x1000;
extern uint32_t precio_total_centavos;
extern void u32_a_string(uint32_t valor, char* buffer); // Creada en el Día 131

/**
 * @brief Ensambla y transmite el comprobante fiscal metrológico
 */
void impresora_generar_ticket_venta(void) {
    
    // 1. Inicialización y Encabezado
    uart_enviar_arreglo(CMD_INIT, sizeof(CMD_INIT));
    uart_enviar_arreglo(CMD_ALIGN_CENTER, sizeof(CMD_ALIGN_CENTER));
    
    uart_enviar_arreglo(CMD_SIZE_DOUBLE, sizeof(CMD_SIZE_DOUBLE));
    impresora_imprimir_linea("GAS LP EL SOL");
    
    uart_enviar_arreglo(CMD_SIZE_NORMAL, sizeof(CMD_SIZE_NORMAL));
    uart_enviar_arreglo(CMD_BOLD_ON, sizeof(CMD_BOLD_ON));
    impresora_imprimir_linea("Estacion: Centro 01");
    uart_enviar_arreglo(CMD_BOLD_OFF, sizeof(CMD_BOLD_OFF));
    
    impresora_imprimir_linea("------------------------");
    
    // 2. Extracción de Datos
    char buffer_num[12];
    
    uart_enviar_arreglo(CMD_ALIGN_LEFT, sizeof(CMD_ALIGN_LEFT));
    uart_enviar_caracter('L'); uart_enviar_caracter('i'); uart_enviar_caracter('t'); uart_enviar_caracter('r'); uart_enviar_caracter('o'); uart_enviar_caracter('s'); uart_enviar_caracter(':'); uart_enviar_caracter(' ');
    
    // Convertimos los litros (Punto Fijo x1000)
    // Para simplificar sin floats, imprimimos la parte entera, un punto, y los decimales
    u32_a_string(litros_despachados_x1000 / 1000, buffer_num);
    while(*buffer_num) uart_enviar_caracter(*buffer_num++); // (En la práctica usarías una rutina mejorada para los decimales)
    
    impresora_imprimir_linea(""); // Enter
    
    // 3. Pie de página y Corte
    impresora_imprimir_linea("------------------------");
    uart_enviar_arreglo(CMD_ALIGN_CENTER, sizeof(CMD_ALIGN_CENTER));
    impresora_imprimir_linea("Conserve este recibo");
    impresora_imprimir_linea("para cualquier queja.");
    
    // Feed de espacio extra para que salga de la ranura
    uart_enviar_caracter(LF); uart_enviar_caracter(LF); uart_enviar_caracter(LF);
    
    // Disparar guillotina automática
    uart_enviar_arreglo(CMD_CUT_PAPER, sizeof(CMD_CUT_PAPER));
}

int main(void) {
    
    // inicializar_usart_impresora();
    
    while(1) {
        // (Flujo de máquina de estados...)
        
        // Cuando el cliente termina de cargar e ingresa el PIN o confirma
        if (bandera_venta_terminada) {
            impresora_generar_ticket_venta();
            bandera_venta_terminada = false;
        }
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Materialización de la Auditoría y Cumplimiento NOM-005.

De acuerdo a las leyes de comercio y carburación, el cliente final no puede llevarse simplemente el gas y creer en la palabra del empleado; la transacción debe dejar un rastro físico inmediato. La máquina debe poseer una impresora homologada capaz de generar un ticket con folio, fecha, litros reales compensados a 20°C y precio total.

Al programar el protocolo **ESC/POS mediante UART**, has vinculado tu algoritmo matemático metrológico directamente con un cabezal de papel térmico. Al abstenerte de usar las pesadas librerías de "Arduino Printer", tu procesador ensambla la tira de bytes de configuración, el string del comprobante y los comandos de guillotina enviándolos casi instantáneamente por el puerto serie. La impresora hace el trabajo duro de dibujar los píxeles gracias a su procesador interno, permitiendo que tu STM32 regrese rápidamente a sus tareas de supervisión.

📝 Resumen del Día