I shared my thermal printer with the internet. What could go wrong?

Remember those old website guestbooks—where you’d scribble a note to the webmaster and it’d proudly show up on the homepage? It sounds risky now, but back in the early naive days of the internet, it was a badge of honor for any blog.

I got hit by a wave of nostalgia (and I happened to have a thermal printer lying around), so I thought: why not bring the guestbook into the real world? What if anyone visiting my site could send me a message that pops out on a little slip of paper right on my desk?

A video showing a user submitting a message in the website. On the computer's side there is a printer that prints the message.
When the user submits a message it gets instantly printed.

Interfacing with the printer

Thermal printers are usually compatible with ESC/POS Protocol and this was the chosen method to interface with it. Since my printer does not have an official Linux driver, I am using the python-escpos library.

The first step, is to identify your Vendor ID, Product ID, Input Enable, and Output Enable values. Using lsusb -vvv you can get that information:

Bus 001 Device 010: ID 6868:0200 gxmc # VendorID:ProductId
Device Descriptor:
  ...
  idVendor           0x6868
  idProduct          0x0200
  ...
  Configuration Descriptor:
    ...
    Interface Descriptor:
      ...
      Endpoint Descriptor:
        bEndpointAddress     0x03  EP 3 OUT
        ...
      Endpoint Descriptor:
		...
        bEndpointAddress     0x81  EP 1 IN

These constants can be used to initialize escpos configuration. I am using a generic profile that is closer to my printer’s specification (paper size, resolution and capabilities).

from escpos.printer import Usb
printer = Usb(0x6868, 0x0200, in_ep=0x81, out_ep=0x03, profile="POS-5890")

Probably you are going to get a permission error the first time you print. This happens because udev restricts access to USB devices by default.

Error: Device not found (Unable to open USB printer on (26728, 512): [Errno 13] Access denied (insufficient permissions))

To fix that, you can allow your users to directly access the printer by creating an udev rule. Replace idVendor and idProduct with your printer’s constants.

$ # create a new rules file in udev
$ echo 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="6868", ATTRS{idProduct}=="0200", GROUP="users", MODE="0666"' > /etc/udev/rules.d/50-printer.rules

$ # reload rules
$ udevadm control --reload-rules && udevadm trigger

Now your Thermal Guestbook can actually print!

Bringing the printer to the web

With a newborn at home Aider is what keeps my hobby alive. Aider is an AI pair programming tool and it was used to write more than 50% of the application code. The app has three components: the frontend, webserver, and worker.

The frontend is a simple HTML, CSS, and Vanilla JS page. The web API is implemented with FastAPI and it also serves the frontend. Every print request is added to a MQTT queue that is listened by the worker. Messages are saved in a SQLite database “for archaeological reasons”.

This webserver is running in a personal computer and it is accessible on the internet via Cloudflare Tunnels .

The webpage that displays a thermal printer with a slip of paper. Inside the slip of paper there is a form with title, image, and message fields.
The final webpage. If I am running the printer you’ll be able to access it in https://printer.tncardoso.com/

So what happened?

I posted the link to the printer (or fax reborn, as my wife dubbed it) and waited to see what happened. The results were surprising. After almost 100 messages, contrary to every expectation, most messages were fun, thoughtful and heartwarming. Notes from old friends, people that I am not in touch anymore, hacking attempts from colleagues, or just strangers sending messages.

No Title

pls give food!

Morning at the beach.

Hello world!

message

It started!

Who is going to win the Conclave?

Bayes' theorem

Updating beliefs:
P(A|B) = P(B | A) . P(A) / P(B)

{{7*8}}

<img src=X onerror=alert(1)>

Shrek (x 40)

Once upon a time there was a lovely princess. But she had an enchantment upon her of a fearful sort, which could only be broken by Love's first kiss

Shrek

You could add a rate limit, sorry

Shrek

really didn't mean to, the first message was on purpose, the second and the 10 subsequente were expected. But the other ones were a misconfig for my end. #goplanet

Wanna leave your mark? Sign the guestbook at https://printer.tncardoso.com/ or peek at the code on GitHub .


See Also


comments powered by Disqus