When people imagine a modern restaurant system, they picture a screen in the kitchen. Vitrina, our platform for cafés, does not have one yet. It prints. Orders reach the cooks as paper tickets from a thermal printer, the way they have reached kitchens for decades. For a small café that is a reasonable place to start: paper needs no tablet, survives a greasy kitchen and can be stuck on the rail.
A kitchen display with routing by station is on our roadmap. This post is about what runs today.
The problem: the printer is behind the café's router
Vitrina's backend runs in the cloud. The printers sit on the café's local network, behind its router, with local addresses such as 192.168.1.50. The cloud cannot open a connection to them.
So the connection goes the other way. A small program, the print agent, runs on a device in the café, such as a Raspberry Pi or a Mac mini. Every few seconds it asks the backend for new print jobs and sends each job to the right printer over the local network.
What gets printed
The system prints several kinds of paper:
- the kitchen ticket for a new order;
- a kitchen ticket for items added at the till;
- a replacement ticket when an accepted order changes, so the kitchen sees what changed instead of guessing;
- the guest receipt for an online order;
- the receipt and the bill printed at the till.
Which printer gets which paper is set in the admin panel, not in code: each printer has a role (kitchen or cashier), a source of orders (online or the till) and, where it matters, a hall.
When a job is created
A kitchen ticket must not appear for an order that has not been paid. So the moment depends on how the guest pays: for an online card payment the job is created when the payment provider confirms the payment; for an order paid on receipt, or an order placed at a table, it is created together with the order.
Printing once, even when the network is not perfect
The agent claims up to ten jobs at a time. The claim is atomic on the server: a job moves from pending to in flight in one step, so two agents, or one agent that has just restarted, cannot take the same job.
Each job goes to the printer as ESC/POS bytes over a raw TCP connection on port 9100, and the agent reports it as done or failed. A failed job is retried up to three times before it is marked as failed for good.
Delivery is at least once, which means a job can come back. The agent keeps a local list of the jobs it has already printed, and a job that comes back is acknowledged without being printed again. A cook does not get the same order twice because the Wi-Fi blinked.
When the internet goes down
The tablet POS keeps selling when the café is offline. Its kitchen tickets then go to the print agent directly over the local network, without the cloud. When the connection is back, the sales are synchronised, and a ticket that the tablet could not print is printed by the server with a clear "late ticket" mark, so the kitchen knows it is not a new order.
Ukrainian letters on thermal printers
Thermal printers do not speak UTF-8. They use code pages, and which page holds the Ukrainian letters (і, ї, є, ґ) depends on the model. The agent has a test command that prints the candidate pages on the actual printer; you read the tape, find the block where the letters come out right and set it once in the agent's settings. We tested it with a Citizen CT-S310II for the kitchen and a Birch CPQ5 for the till.
Why paper first
A screen in the kitchen is a good tool when there are several stations, many cooks and many orders. For a café with one kitchen, a printer that never misses or doubles a ticket matters more. That is the part we built first, and it is what Vitrina runs today.
If you are building something that has to talk to hardware in a place you do not control, we have done that before.