A force d'en voir dans des installations d'art numérique, j'ai fini par avoir envie d'avoir ma propre imprimante thermique, histoire de voir ce qu'on peut faire avec.
Je tombe sur une Epson TM-T20II à 35 euros sur Leboncoin. Après une ou deux recherches sur internet j'apprends que :
- Le protocole ESC/POS a l'air d'être le standard pour contrôler les imprimantes thermiques
- Il y a une librairie python qui a l'air bien maintenue qui sait faire de l'ESC/POS par usb
- L'Epson TM-T20II gère ce protocole et a une prise USB
Ça me suffit, je la prends.
Parler à l'imprimante
J'ai reçu l'imprimante, maintenant comment est-ce que je fait pour lui parler ? python-escpos est plutôt bien documenté, je suis les instructions d'installation : je commence par ignorer les instructions à propos des règles udev -> ça ne fonctionne pas -> je décide de ne pas ignorer les instructions à propos des règles udev :
- Je repère le vendorId et le productId de mon imprimante avec
lsub:
$ lsusb
(...)
Bus 001 Device 008: ID 04b8:0e15 Seiko Epson Corp. TM-T20II
(...)
- Dans
/etc/udev/rules.d/50-myusb.rules, j'écris :
SUBSYSTEM=="usb", ATTRS{idVendor}=="04b8", ATTRS{idProduct}=="0e15" GROUP="users", MODE="0666", SYMLINK+="usbtm20"
Ensuite, un coup de sudo service udev restart
Et je me retrouve avec un script python qui parvient à imprimer du texte sur l'imprimante :
from escpos.printer import Usb
text = "..."
device = Usb(0x04B8, 0xE15, 0, profile="TM-T20II")
device.textln(text)
device.cut()
Ok ça a l'air de fonctionner, mais pourquoi ça n'imprime pas sur toute la largeur de mon rouleau de 80mm ? On dirait que la machine crois que je lui ai chargé un rouleau de 58mm ?
Modifier la largeur de la zone d'impression
Epson publie une documentation plutôt détaillée (mais pas forcément évidente à naviguer) sur le protocole ESC/POS et le support de ses commandes par son matériel.
Je finis par tomber sur GS ( E <fn=5> :
A priori, avec a=3, je peux configurer la largeur du papier :

La documentation précise que cette fonction n'a un effet que si l'appareil est en mode "user settings" :

Dans le cas de la fonction GS ( E <fn=1>, l'imprimante n'entre pas directement dans le mode user setting. Il faut attendre qu'elle transmette la valeur 0x372000 avant de lui envoyer de nouvelles commandes.
En fouillant un peu, on trouve la fonction GS ( E <fn=2> qui permet de sortir du mode "user setting".
Je termine avec ce bout de code qui permet de changer la taille du papier configurée dans l'imprimante :
from escpos.printer import Usb
from time import sleep
def enter_settings_mode(d):
# Enter user setting mode
# GS ( E <Function 1>
enter = bytes.fromhex("1D2845030001494E")
d._raw(enter)
# Await confirmation from printer
# that we entered user setting mode
for i in range(100):
res = d._read()
if(len(res) == 0):
sleep(.1)
continue
elif(res[0] == 55 and res[1] == 32):
break
def exit_settings_mode(d):
# Exit user setting mode
# GS ( E <Function 2>
exit = bytes.fromhex("1D28450400024F5554")
d._raw(exit)
def change_paper_size():
d = Usb(0x04b8,0x0e15, 0, profile="TM-T20II")
enter_settings_mode(d)
# Switch to 80mm paper width
# Set config value
# GS ( E <Function 5>
# Set paper width
paper_width = bytes.fromhex("1D2845040005030600")
d._raw(paper_width)
exit_settings_mode()
Les impressions occupent maintenant toute la largeur du papier.
Imprimer des images
Il y a déjà les fonctions nécessaires pour imprimer des images dans python-escpos :
from PIL import Image
from escpos.printer import Usb
im = Image.open("(...)")
d = Usb(0x04b8,0x0e15, 0, profile="TM-T20II")
d.image(
im,
center=True
)
d.cut()
Je suis content, ça imprime mon image, mais il y a des traits qui gachent un peu tout :
En fouillant dans la doc epson, je finis par tomber sur une page qui parle de l'impression d'images à partir de... je sais pas trop, des fichiers xml qu'on mettrait dans un logiciel particulier ? Bref, sur cette page on lit :
If you set to print a raster image in grayscale, the amount of data of the image increases and may be printed intermittently, which causes white streaks on printout.
Donc... s'il y a trop de données à imprimer, l'imprimante pourrait décider de faire de petites pauses, qui entrainent l'apparition de traits blancs. Ça a tout l'air de correspondre au problème qu'on rencontre !
Dans la documentation de la commande ESC *, on remarque qu'un paramètre permet de sélectionner les densités d'impression horizontales et verticales. En mettant le nez dans le code de python-escpos, on remarque les paramètres high_density_vertical et high_density_horizontal, qui agissent justement sur ces paramètres d'ESC *.
Après de petites modifications sur le code d'esc_pos pour redimensionner l'image pour l'impression en faible densité, on imprime une image sans lignes qui gâchent tout !
C'est mieux ! Mais on perd quand même énormément en qualité d'impression... Et en plus il y a encore des lignes parasites.
Mais en fait, la documentation dit que "the amount of data of the image increases and may be printed intermittently". Ça veut dire que l'imprimante a besoin de respirer lorsqu'on lui envoie beaucoup de données ? Peut-être que ça se passerait mieux si on imprimait plus doucement ?
On retourne à la commande GS ( E <fn=5>, puisqu'elle permet aussi de définir une vitesse d'impression ! On fait ça avec ce bout de code :
def change_print_speed():
d = get_device()
enter_settings_mode(d)
speed = bytes.fromhex("1D2845040005060100")
d._raw(speed)
exit_settings_mode(d)
Ça imprime bien moins rapidement, mais l'image rend beaucoup mieux.