Generación de códigos QR en impresoras térmicas con comandos ESC/POS

16 de Octubre de 2022

Descripción del Entorno

Esta guía rápida fue escrita en base a pruebas en la terminal de Linux con la impresora Epson TM-T20X.

Breve explicación de los comandos de la impresora

Cabe mencionar que para las funciones se convierte el valor decimal a hexadecimal: es por esto que a cada número le antecede un \x.

Para más información, se adjunta la url del manual de la impresora.

GS ( k <fn=165>
# Seleccionar el modelo
# (función 165) https://www.epson-biz.com/modules/ref_escpos/index.php?content_id=140
# (se toman los valores por defecto para n1 (50 = QR Model 2) y n2 (0))
#GS	(	k	pL	pH	cn	fn	n1	n2
#1D	28	6B	04	00	31	41	50	00
'x1D\x28\x6B\x04\x00\x31\x41\x50\x00'
GS ( k <fn=167>
# Establecer el tamaño del módulo 
# (función 167) https://www.epson-biz.com/modules/ref_escpos/index.php?content_id=141
# (el valor de n para el modelo TM-T20 es 3, ref: https://www.novopos.ch/client/EPSON/TM-T20/TM-T20_spc_f.pdf pág 168)
#GS	(	k	pL	pH	cn	fn	n
#1D	28	6B	03	00	31	43	03
'x1D\x28\x6B\x03\x00\x31\x43\x03'
GS ( k <fn=169>
# Seleccionar el nivel de corrección de error 
# (función 169) https://www.epson-biz.com/modules/ref_escpos/index.php?content_id=142
# ([30 -> 7%, nivel L] [31-> 15%, nivel M] [32 -> 25%, nivel Q] [33 -> 30%, nivel H]; el valor por defecto es 30 (7% aprox. de capacidad de recuperación))
#GS	(	k	pL	pH	cn	fn	n
#1D	28	6B	03	00	31	45	30
'x1D\x28\x6B\x03\x00\x31\x45\x30'
GS ( k <fn=180>
# Almacenar los datos en el área de almacenamiento de símbolos
# (función 180) https://www.epson-biz.com/modules/ref_escpos/index.php?content_id=143
# La longitud del QR se obtiene sumando 3 a la longitud original (en HEX) de los datos: QRL = longitud(QR) + 3
# pL se obtiene con el módulo a 256 de la longitud del QR: pL = QRL % 256
# pH se obtiene dividiendo a 256 la longitud del QR: pH = QRL / 256
# d1...dk es la información que se enviará, ej.: un string con una url. d1…dk = cadena_QR
#GS	(	k	pL	pH	cn	fn	m	d1...dk
#1D	28	6B	...	...	31	50	30	..........
'x1D\x28\x6B<pL><pH>\x31\x50\x30<cadena_QR>'
GS ( k <fn=181>
# Imprimir los datos en el área de almacenamiento de símbolos
# (función 181) https://www.epson-biz.com/modules/ref_escpos/index.php?content_id=144
#GS	(	k	pL	pH	cn	fn	m
#1D	28	6B	03	00	31	51	30 
'x1D\x28\x6B\x03\x00\x31\x51\x30'
GS ( k <fn=182>
# Transmitir la información del tamaño de los datos de los símbolos en el área de almacenamiento de símbolos
# (función 182) https://www.epson-biz.com/modules/ref_escpos/index.php?content_id=145
#GS	(	k	pL	pH	cn	fn	m
#1D	28	6B	03	00	31	52	30
'x1D\x28\x6B\x03\x00\x31\x52\x30'

Explicación de los comandos en la terminal

  • echo -en

echo escribe cada una de las cadenas dadas en su salida estándar, con un espacio en blanco entre cada una y un caracter “salto de línea” después de la última cadena.
(información de man echo)

La opción -e activa la interpretación de los escapes de barra invertida (como \n, \r, etc.), y -n evita que se envíe el caracter “salto de línea” al final.

  • lp -d

lp imprime un archivo. Con la opción -d se indica el destino, el cual es el nombre de la impresora.


Con echo -en <cadena> | lp -d <nombre_de_impresora> se envía la cadena en cuestión a la impresora seleccionada. Para fines prácticos, aquí la impresora tiene el nombre thermal (térmica).

Ejemplos

Algunas expresiones están explicadas a través de los comentarios.

# Ej.: imprimir la url https://www.oracle.com/index.html como QR; longitud = 33 + 3 (\x24), pL = 36(\x24), pH = 0
echo -en '\x1D\x28\x6B\x04\x00\x31\x41\x50\x00' | lp -d thermal
echo -en '\x1D\x28\x6B\x03\x00\x31\x43\x03' | lp -d thermal
echo -en '\x1D\x28\x6B\x03\x00\x31\x45\x30' | lp -d thermal
echo -en '\x1D\x28\x6B\x24\x00\x31\x50\x30https://www.oracle.com/index.html' | lp -d thermal
echo -en '\x1D\x28\x6B\x03\x00\x31\x51\x30' | lp -d thermal
echo -en '\x1D\x28\x6B\x03\x00\x31\x52\x30' | lp -d thermal
#Mismo ejemplo en una línea, con salto de tres líneas (ESC d 3) y corte parcial de hoja, con un punto sin cortar (ESC i)
(echo -en '\x1D\x28\x6B\x04\x00\x31\x41\x50\x00'; echo -en '\x1D\x28\x6B\x03\x00\x31\x43\x03'; echo -en '\x1D\x28\x6B\x03\x00\x31\x45\x30'; echo -en '\x1D\x28\x6B\x24\x00\x31\x50\x30https://www.oracle.com/index.html'; echo -en '\x1D\x28\x6B\x03\x00\x31\x51\x30'; echo -en '\x1B\x64\x03'; echo -en '\x1B\x69') | lp -d thermal

# ídem, con dobles comillas
(echo -en "\x1D\x28\x6B\x04\x00\x31\x41\x50\x00"; echo -en "\x1D\x28\x6B\x03\x00\x31\x43\x03"; echo -en "\x1D\x28\x6B\x03\x00\x31\x45\x30"; echo -en "\x1D\x28\x6B\x24\x00\x31\x50\x30https://www.oracle.com/index.html"; echo -en "\x1D\x28\x6B\x03\x00\x31\x51\x30"; echo -en "\x1B\x64\x03"; echo -en "\x1B\x69") | lp -d thermal
#Mismo ejemplo (con otra cadena) y con variables:
CADENA_QR=https://www.set.gov.py/portal/PARAGUAY-SET/detail?content-id=/repository/collaboration/sites/PARAGUAY-SET/documents/facturacion-electronica/Presentacion%20Lanzamiento%20del%20Plan%20Piloto%20del%20SIFEN.pdf
LONG_QR_ORIG=${#CADENA_QR} #el "#" delante de la variable obtiene la longitud de la cadena
LONG_QR=$(($LONG_QR_ORIG+3)) #doble paréntesis para operaciones aritméticas
PL=$(printf "%x" $(($LONG_QR % 256))) #printf "%x" convierte el valor a hexadecimal
PH=$(printf "%x" $(($LONG_QR / 256)))

# (declaración de variables en una línea)
CADENA_QR=https://www.set.gov.py/portal/PARAGUAY-SET/detail?content-id=/repository/collaboration/sites/PARAGUAY-SET/documents/facturacion-electronica/Presentacion%20Lanzamiento%20del%20Plan%20Piloto%20del%20SIFEN.pdf; LONG_QR_ORIG=${#CADENA_QR} ; LONG_QR=$(($LONG_QR_ORIG+3)) ; PL=$(printf "%x" $(($LONG_QR % 256))) ; PH=$(printf "%x" $(($LONG_QR / 256)))

# envío de comandos
(echo -en '\x1D\x28\x6B\x04\x00\x31\x41\x50\x00'; echo -en '\x1D\x28\x6B\x03\x00\x31\x43\x03'; echo -en '\x1D\x28\x6B\x03\x00\x31\x45\x30'; echo -en '\x1D\x28\x6B\x'$PL'\x'$PH'\x31\x50\x30'$CADENA_QR; echo -en '\x1D\x28\x6B\x03\x00\x31\x51\x30'; echo -en '\x1B\x64\x03'; echo -en '\x1B\x69') | lp -d thermal

# envío de comandos, con dobles comillas
(echo -en "\x1D\x28\x6B\x04\x00\x31\x41\x50\x00"; echo -en "\x1D\x28\x6B\x03\x00\x31\x43\x03"; echo -en "\x1D\x28\x6B\x03\x00\x31\x45\x30"; echo -en "\x1D\x28\x6B\x"$PL"\x"$PH"\x31\x50\x30"$CADENA_QR; echo -en "\x1D\x28\x6B\x03\x00\x31\x51\x30"; echo -en "\x1B\x64\x03"; echo -en "\x1B\x69") | lp -d thermal

¿Cómo nació la guía?

A mediados de 2022, en el trabajo nos designaron la tarea de generar códigos QR directamente a través de los comandos ESC/POS de la impresora, en lugar de generarlos con alguna librería de un lenguaje de programación. Todo esto es debido a que el sistema de facturación electrónica en Paraguay (SIFEN) exige que en las facturas se imprima el código QR en el KuDE (la versión gráfica del documento electrónico).

Notas Finales

Si encuentran errores o notan que faltan comentarios que aporten mayor información, no duden en avisarme :)
Agradecimientos especiales a J.V. y G.K. por la orientación inicial para la impresión desde la terminal de Linux.

Hugo Theme: "Bulma Hugo Resume", basado en  Hugo Resume