Qnack + qnack-mqtt-print: Ein Web-POS an Thermodrucker anbinden
Qnack ist ein webbasiertes Kassensystem für die Gastronomie; qnack-mqtt-print ist der kleine Node-Dienst, der den Browser über MQTT an Thermodrucker anbindet. Die Architektur, das MQTT-Topic-Design und die Fehlerfälle, die wir behandeln mussten.
Ein browserbasiertes POS, das über USB mit einem Thermodrucker spricht, ist ein Nicht-Starter: USB-Zugriff aus einem Browser-Tab ist eingeschränkt, feindliche Umgebungen installieren keine Treiber, und der Drucker kann in der Küche stehen, während die Bestellung an der Bar aufgegeben wird. Wir brauchten einen Transport, den der Browser erreichen konnte und den ein winziger Headless-Dienst nativ sprechen konnte. MQTT war die Antwort.
Die Architektur
Drei Komponenten, jede minimal:
- Qnack-Web-POS. Eine Next.js-App, die auf einem Tablet an der Bar läuft. Bestelleingaben feuern eine HTTPS-Anfrage an das Qnack-Backend mit der Bestell-Payload.
- Qnack-Backend (NestJS). Persistiert die Bestellung, sendet ein „Druck“-Event an ein auf die Venue bezogenes MQTT-Topic.
- qnack-mqtt-print. Ein kleiner Node-Dienst, der auf einem Raspberry Pi neben dem Drucker läuft. Abonniert das Venue-bezogene Topic, formatiert die Bestellung in den ESC/POS-Byte-Stream des Druckers und schreibt ihn über USB auf den Drucker.
Warum MQTT, nicht WebSocket
WebSocket hätte funktioniert, aber Küche und Bar befinden sich im selben physischen Netzwerk wie der Drucker – und nicht im selben Netzwerk wie das Cloud-Backend. MQTT-Broker können lokal auf dem Pi laufen, hinter einer Firewall, ohne eingehende Ports aus dem Internet. Die Bestellung übersteht ein Backend-Deployment, einen kurzen Internetausfall oder einen Tablet-Neustart. Das Publish ist local-first, über Reconnects hinweg dauerhaft und beim Broker bestätigt.
Topic-Design, das skaliert
Das Topic-Design ist der Teil, in den die meisten Teams zu wenig investieren. Unseres:
qnack/{venueId}/printers/{printerId}/jobs– die Warteschlange für einen bestimmten Drucker in einer bestimmten Venue.qnack/{venueId}/printers/{printerId}/status– Last-Will-and-Testament-Heartbeat, damit das POS „Küchendrucker offline“ anzeigen kann, wenn der Pi aufhört zu heartbeatet.
Eine neue Venue ist ein neuer Zweig im Topic-Baum. Der Broker erzwingt Access Control pro Venue, sodass ein falsch konfigurierter Subscriber bei Venue A nicht den Bestell-Stream von Venue B lesen kann.
Fehlerfälle, die wir behandeln mussten
- Pi-Stromzyklus. Der MQTT-Client verbindet sich beim Boot neu, das Last-Will-and-Testament veröffentlicht eine „Drucker offline“-Nachricht, und das POS hört auf, Bestellungen an diesen Drucker zu reihen.
- Drucker ohne Papier. Der ESC/POS-Treiber gibt ein Status-Byte zurück. qnack-mqtt-print veröffentlicht eine „Druckerfehler“-Nachricht auf dem Status-Topic, und das POS zeigt einen Banner.
- Backend unerreichbar, aber POS noch online. Das Qnack-POS reiht Bestellungen lokal ein und wiederholt mit Backoff. Der Küchendrucker bekommt sie trotzdem, sobald sich das Backend erholt.
Was uns das gelehrt hat
Die Lektion verallgemeinert sich über POS hinaus: Wenn Sie Browser-zu-physisches-Gerät-Kommunikation brauchen, ist der Brückendienst der langweilige Teil, den alle unterschätzen. Bauen Sie ihn so, als würde er jeden Frontend-Neuschrieb überleben – denn das wird er.
