Public Access
Document the printing subsystem
Covers the roll/job colour rule that caused the "change it to Monochrome media" failures, the tri-state health result, and why network status is unavailable on this hardware.
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
# Badge printing
|
||||
|
||||
The app renders the badge and sends it to the Brother QL-820NWB over TCP port
|
||||
9100, speaking Brother's raster command language directly. No CUPS, no driver,
|
||||
no print dialog, and no Python.
|
||||
|
||||
## Why not AirPrint
|
||||
|
||||
AirPrint from the kiosk browser means iPadOS renders the badge from print CSS
|
||||
and picks the page geometry from what the printer advertises. That costs exact
|
||||
control of the 60.96 mm print width, makes two-colour DK-22251 output
|
||||
impossible, and cannot suppress the print dialog — so a staff member has to tap
|
||||
"Print" for every visitor. Fine for a staffed desk, useless for unattended
|
||||
sign-in.
|
||||
|
||||
Doing it server-side also means the kiosk device stops mattering. An iPad, an
|
||||
old Android tablet, a browser on a NUC: they all just POST.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
src/printing/
|
||||
ql-raster.js command language: media, cut, expanded mode, bit packing
|
||||
ql-status.js decoder for the 32-byte status frames
|
||||
ql-transport.js TCP client for port 9100
|
||||
ql-print.js printPng / printerHealth, plus the serial queue
|
||||
src/printer.js badge layout and rendering (unchanged by this work)
|
||||
```
|
||||
|
||||
Protocol details follow Brother's *Raster Command Reference, QL-800/810W/820NWB
|
||||
v1.01*. Where that manual and pklaus/brother_ql disagree, the manual wins;
|
||||
brother_ql targets older models and several of its defaults are wrong for this
|
||||
hardware (200-byte invalidate instead of 400, the print-quality bit set during
|
||||
two-colour jobs, the media-length valid bit asserted for continuous tape).
|
||||
|
||||
## The roll setting is not cosmetic
|
||||
|
||||
`sites.printer_label` must match the roll physically in the machine:
|
||||
|
||||
| Roll | Setting |
|
||||
|---|---|
|
||||
| DK-22205, 62 mm white | `62` |
|
||||
| DK-22251, 62 mm black/red | `62red` |
|
||||
|
||||
This is enforced by the printer, not by us. A monochrome job sent to a
|
||||
black/red roll is refused outright, and vice versa. It applies even when the
|
||||
badge has no red on it — with `62red` set, every job is built as two-colour and
|
||||
the red plane is simply empty.
|
||||
|
||||
The printer's own error message is misleading here. It says "change it to
|
||||
Monochrome media", which points at a driver setting that doesn't exist in this
|
||||
setup. The fix is the roll dropdown in the admin console, or swapping the roll.
|
||||
|
||||
## Status, and why there isn't much
|
||||
|
||||
Manual section 5.9: over a network connection the print data is sent as-is and
|
||||
nothing comes back. Status frames only arrive over USB. brother_ql documents the
|
||||
same limitation for its network backend.
|
||||
|
||||
So `printerHealth` returns a tri-state `ready`:
|
||||
|
||||
- `true` — the printer answered and everything is fine
|
||||
- `false` — it answered and something is wrong
|
||||
- `null` — reachable, but it won't say
|
||||
|
||||
`null` is the normal answer here. Show it as amber/unknown in the admin console;
|
||||
colouring it green claims something we don't know. Likewise `printPng` returns
|
||||
`confirmed: false` when no status came back: the bytes were delivered, but
|
||||
whether paper moved is unknown.
|
||||
|
||||
Consequence worth accepting: a jammed or empty printer cannot be detected. The
|
||||
practical signal is a visitor saying no badge appeared. That is tolerable
|
||||
because the evacuation record is written before printing is attempted. If real
|
||||
roll monitoring is ever needed, the lead to follow is SNMP — the Brother Status
|
||||
Monitor on Windows gets roll state over the network somehow, and it is not
|
||||
using port 9100.
|
||||
|
||||
## Concurrency
|
||||
|
||||
The QL accepts one TCP connection at a time and has no job spooler worth the
|
||||
name. Two visitors signing in together would otherwise produce a refused
|
||||
connection, half a label, or both. Every job goes through a serial queue keyed
|
||||
on printer host, in `ql-print.js`.
|
||||
|
||||
That queue is in-process. Running more than one app instance against one printer
|
||||
would need a lock in SQLite instead.
|
||||
|
||||
## Geometry
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Print head | 720 pins, 60.96 mm at 300 dpi |
|
||||
| 62 mm roll printable | 696 pins, 58.9 mm |
|
||||
| Margins | 12 pins each side |
|
||||
| Feed margin | 35 dots (3 mm), the documented minimum |
|
||||
| Length | 150 to 11811 raster lines (12.7 mm to 1 m) |
|
||||
|
||||
Every raster line sent is the full 90 bytes regardless of the roll. Narrower
|
||||
media just means more pins miss the paper.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Reachability and roll state:
|
||||
|
||||
```bash
|
||||
docker compose exec visitor-signin node -e "
|
||||
import('./src/printing/ql-print.js').then(async m =>
|
||||
console.log(await m.printerHealth({ host: '10.142.177.169', label: '62red' })));
|
||||
"
|
||||
```
|
||||
|
||||
If a label never appears, work down this list:
|
||||
|
||||
**Nothing at all, `Cannot reach printer`.** Routing or firewall between the
|
||||
container and the printer. Try `nc -vz IP 9100` from the Ubuntu host; if that
|
||||
works but the container doesn't, it is Docker networking.
|
||||
|
||||
**Connects but silent.** Normal. See above.
|
||||
|
||||
**Media mismatch error.** The roll setting and the physical roll disagree.
|
||||
|
||||
**Labels come out blank.** Roll in backwards, or a non-Brother roll with no end
|
||||
sensor. The printer reports no error for this.
|
||||
|
||||
**Everything mirrored.** The bit packing is wrong. The head clocks each raster
|
||||
line out right-to-left; see `packRow`.
|
||||
|
||||
**First label after a power cycle is misaligned.** Feed one label from the front
|
||||
panel before the first sign-in of the day.
|
||||
|
||||
## Known rough edge
|
||||
|
||||
Photos are not dithered. `drawBadge` draws them straight and the plane
|
||||
conversion thresholds at luminance 180. A thermal head has no grey, so faces
|
||||
come out blotchy. The fix is Floyd–Steinberg on the photo region before it goes
|
||||
on the canvas, inside `drawBadge`.
|
||||
|
||||
---
|
||||
|
||||
Created by: Jess Rogerson (yelling commands at Claude.AI)
|
||||
Reference in New Issue
Block a user