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