En este post te enseñaré cómo enviar bytes ESC POS a una impresora térmica en Windows usando únicamente el lenguaje de programación C.

Hago enfásis en que este tutorial es para Windows porque vamos a usar la API de Windows desde ANSI C. Para Linux probablemente escribiré otro tutorial más adelante ya que ahí debe ser tan fácil como escribir a cualquier archivo.

Requisitos de impresora

Sin importar el modelo y marca de tu impresora debes instalarla como genérica y compartirla usando un nombre usando solo letras.

En mi blog ya tengo un tutorial para que impresora acepte comandos ESC POS

Impresora térmica con C

El código queda como se ve a continuación.

Por favor toma en cuenta que DESKTOP-PLGLS8D es el nombre de mi computadora, y PT es el nombre de mi impresora ya compartida (aunque está compartida no accedo a ella por red, ya que está conectada directamente por USB pero se debe dejar de esta manera para que funcione)

Debe existir alguna manera de obtener el hostname desde el código tal vez usando GetComputerNameA pero eso te lo dejo a ti, mientras tanto para pruebas puedes dejar definido el hostname e impresora directamente en el código.


#include <windows.h>
#include <winspool.h>
#include <stdio.h>
#include <string.h>
#define ESC 0x1b
int main()
{
    HANDLE manejadorImpresora;
    DOC_INFO_1 docInfo;
    DWORD bytesEscritos;
    // No olvides cambiar aquí tu hostname y nombre de impresora
    if (!OpenPrinter("\\\\DESKTOP-PLGLS8D\\PT", &manejadorImpresora, NULL))
    {
        printf("No se pudo abrir la impresora: %lu\n", GetLastError());
        return 1;
    }

    docInfo.pDocName = "Mi documento";
    docInfo.pOutputFile = NULL;
    docInfo.pDatatype = "RAW";

    if (StartDocPrinter(manejadorImpresora, 1, (LPBYTE)&docInfo) == 0)
    {
        printf("Error StartDocPrinter: %lu\n", GetLastError());
        ClosePrinter(manejadorImpresora);
        return 1;
    }

    if (!StartPagePrinter(manejadorImpresora))
    {
        printf("Error StartPagePrinter %lu\n", GetLastError());
        ClosePrinter(manejadorImpresora);
        return 1;
    }

    const unsigned char bytesEscPos[] = {
        ESC, 0x40, // ESC @ limpiar búfer y reiniciar impresora
        'H', 'o', 'l', 'a', ' ', 'm', 'u', 'n', 'd', 'o', '\n', '\n',
        ESC, 'E', 0x01, // ESC E 0x01 habilitar enfatizado
        'T', 'e', 'x', 't', 'o', ' ', 'e', 'n', 'f', 'a', 't', 'i', 'z', 'a', 'd', 'o', '\n',
        // Ahora un feed ESC d líneas
        ESC, 'd', 2};

    WINBOOL escritoCorrectamente = WritePrinter(manejadorImpresora, (LPVOID)bytesEscPos, sizeof(bytesEscPos), &bytesEscritos);
    if (!escritoCorrectamente)
    {
        printf("Error WritePrinter %lu\n", GetLastError());
    }

    EndPagePrinter(manejadorImpresora);
    EndDocPrinter(manejadorImpresora);
    ClosePrinter(manejadorImpresora);

    printf("Bytes escritos: %lu\n", bytesEscritos);
    return 0;
}

Y así de simple es como podemos enviar comandos ESC POS a una impresora térmica en C sobre el sistema operativo Windows.

Esto nos permite controlar todavía más la impresión, pues podemos abrir el cajón de dinero conectado a la impresora, hacer cortes de papel, avanzar el papel, etcétera.

Por cierto, se compila así:

gcc escpos2.c -o e2.exe -lwinspool -Wall

En donde escpos2.c es el código fuente y e2 es el nombre del ejecutable. Es muy importante indicar el -lwinspool

Nota: también puedes hacer esto con C#

Código C sin API de Windows

Antes de usar Winspool yo intenté abrir la impresora como un archivo normal y escribirle bytes normalmente, así:

#include <stdio.h>
#include <string.h>

int main()
{
    FILE *archivo = fopen("\\\\DESKTOP-PLGLS8D\\PT", "a");
    if (archivo == NULL)
    {
        printf("Error abriendo archivo");
        return 1;
    }
    char *datos = "\x1b@Hola mundo\n\n s";
    size_t bytesEscritos = fwrite(datos, sizeof 'a', strlen(datos), archivo);
    fclose(archivo);
    printf("Escritosss: %lld\n", bytesEscritos);
}

Lo cual sí funcionaba, pero me dejaba mi impresora en un estado raro donde ya no se podía avanzar el papel. Y lo más curioso es que cuando yo hacía el printf de los bytes escritos se imprimía en la impresora.

Es decir: printf debe imprimir en la terminal, pero en realidad ese printf se mostraba tanto en mi terminal como en la impresora térmica físicamente impreso.

Por cierto, estoy muy seguro de que ese fragmento de código sí debe funcionar en Linux solo cambiando el archivo a /dev/usb/lp1 ya que funciona en mi tutorial para instalar impresora térmica en Linux

APIs de Windows no suenan tan mal

Usar las APIs de Windows para las impresoras no me está disgustando. Todo está bien documentado y el hecho de poderlas usar en C es una delicia.

Con esta API también podemos cancelar un trabajo de impresión

Prueba mi plugin de impresión

Hago estos tutoriales porque me gusta imprimir en impresoras térmicas desde cualquier lenguaje de programación y sistema operativo, justamente por ello he desarrollado un plugin para impresoras térmicas que expone una API JSON para que tú no tengas que preocuparte de armar los bytes y solo le envíes las operaciones que debe realizar.

Dicha API también se puede consumir en C usando cURL en C

Si el post ha sido de tu agrado te invito a que me sigas para saber cuando haya escrito un nuevo post, haya actualizado algún sistema o publicado un nuevo software. Facebook | X | Instagram | Telegram | También estoy a tus órdenes para cualquier contratación en mi página de contacto