A free, open-source CUPS driver for cheap USB thermal label printers that speak TSPL/TSPL2.
Turn a USB-tethered label printer into a shared network printer for every device —
Mac, iPhone, Windows, Linux — driverless.
Runs on Linux & Raspberry Pi (arm64 / armhf), the platforms the vendor drivers leave out.
💙 Maintained for free by Run The Wall. We don't want your money — we give away tools like this to introduce you to Constly, our genuinely great free Markdown editor → constly.com. If it saved you an afternoon, that's the whole payment we wanted. (The driver logs one thank-you line to your CUPS log per job — no tracking, no phone-home.)
Cheap 4×6 USB label printers mostly speak the same TSPL/TSPL2 language and mostly lack an ARM/Linux driver — so one driver covers them. We can only physically test the printers we own, so support is graded honestly:
✅ Confirmed working (verified on real hardware): HZD950-PRO / HERO.
Everything else is 🟢 TSPL-confirmed (vendor/community docs say it speaks TSPL and our command set should drive it) or 🟡 community-reported — not yet verified by us. Got one working? Tell us and we'll move it to Confirmed and add its USB id to auto-detect.
| Printer | dpi | USB id | Status |
|---|---|---|---|
| HZD950-PRO / HERO | 300 | 0fe6:811e |
✅ Tested |
| Munbyn ITPP941 / 941B (941P: see note) | 203 | 09c6:0426 / generic |
🟢 TSPL |
| iDPRT SP410 / SP420 | 203 | 20d1:7008 |
🟢 TSPL |
| HPRT N41 / SL42 | 203 | 20d1 family (inferred) |
🟢 TSPL |
| Beeprt BY-426 (shared OEM engine) | 203 | 09c6:0426 |
🟢 TSPL |
| JADENS JD-168 / JD-268BT | 203 | 09c6:0426 |
🟢 TSPL |
| Polono PL420 | 203 | unknown | 🟡 community |
| Xprinter XP-420B / 460B / 470B | 203 | 2d84:b528 (460B) / varies |
🟢 TSPL |
| Phomemo PM-241 / D520 | 203 | (unverified) | 🟡 community |
Munbyn's vendor specs list the 941 / 941B as 203 dpi TSPL; the 300 dpi "941P 3.0" has no public spec confirming TSPL yet, and the AirPrint "941AP" speaks OPL (excluded below). Polono is grouped with the TSPL clone family by community reports, but the oft-repeated "HPRT rebadge" claim has no public evidence (different FCC grantees), so it stays 🟡 until someone reports one.
Check yours in 10 seconds (prints nothing): cat /sys/class/usbmisc/lp0/device/ieee1284_id —
most TSPL printers self-describe with TSPL in the CMD: / COMMAND SET: field (the HZD950-PRO
reports COMMAND SET:TSPL), no driver needed. You can also
ask the printer itself: printf '~!T\r\n' | sudo tee /dev/usb/lp0 >/dev/null; sudo timeout 2 head -c 32 /dev/usb/lp0 — but many clones are write-only over USB, so no reply proves nothing; go by the id
string or just try a print.
Not this driver (different language): Munbyn AirPrint / "OPL" models (use AirPrint directly), Phomemo M110 / M120 / D30 / M02 & mini printers (ESC/POS), Brother QL / DYMO (proprietary raster), Zebra (ZPL/EPL — already well-supported), and Rollo / OFFNOVA (TSPL, but they ship their own arm64 drivers).
Add it once, then install and upgrade by name like any system package. Signed, hosted free on GitHub Pages.
# Debian / Ubuntu / Raspberry Pi OS
curl -fsSL https://runthewall.github.io/tspl-cups-driver/apt/KEY.gpg | sudo tee /usr/share/keyrings/tspl.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/tspl.gpg] https://runthewall.github.io/tspl-cups-driver/apt ./" | sudo tee /etc/apt/sources.list.d/tspl.list
sudo apt update && sudo apt install tspl-cups-driver# Fedora / RHEL / openSUSE
sudo curl -fsSL https://runthewall.github.io/tspl-cups-driver/rpm/tspl.repo -o /etc/yum.repos.d/tspl.repo
sudo dnf install tspl-cups-drivergit clone https://github.com/RunTheWall/tspl-cups-driver
cd tspl-cups-driver && sudo ./install.shBuilds from source if you have a compiler; otherwise downloads a prebuilt binary for your CPU (no build tools needed). Turns on network sharing + AirPrint on the way.
Packages install the driver files only; create a queue once (the script does this for you). Name it
anything — HZD950 is just the default:
sudo lpadmin -p HZD950 -E -v tspl://auto -P /usr/share/ppd/tspl/tspl-label.ppd \
-o printer-is-shared=true -o media=na_index-4x6_4x6in
# 203 dpi printer? add: -o Resolution=203dpitspl://auto finds a known TSPL printer. If yours has an unlisted USB id, the backend prints the id it
sees — pin it with -v tspl://<vid>:<pid> or -v tspl:///dev/usb/lp0 (and please
open an issue with the id so we auto-detect it).
Also: one-off .deb/.rpm download · Arch (AUR / PKGBUILD) · NixOS
Every release is built by GitHub Actions for amd64 / arm64 / armhf — grab one from Releases:
sudo apt install ./tspl-cups-driver_*_arm64.deb # Debian/Ubuntu/Pi (or _amd64 / _armhf)
sudo dnf install ./tspl-cups-driver-*.x86_64.rpm # Fedora/RHEL (or .aarch64)
# Arch — build the shipped PKGBUILD (AUR listing pending):
( cd tspl-cups-driver/packaging/aur && makepkg -si )
# NixOS — add to your CUPS drivers:
# services.printing.drivers = [ inputs.tspl.packages.${pkgs.system}.default ];
# flake input: github:RunTheWall/tspl-cups-driverPackaging sources live in packaging/ and flake.nix.
The Pi renders, so clients never install a driver — they just add the shared queue.
- Mac / iPhone / iPad — it appears in Add Printer as AirPrint; pick it, done. (
install.shapplies the one server-side fix that makes this work:BrowseDNSSDSubTypes _print,_universalincupsd.conf, so macOS offers driverless AirPrint instead of a wrong "Generic PostScript" driver.) - Windows 10/11 · Linux — auto-discovered as an IPP Everywhere / Mopria printer; add it driverless.
- Helpers —
client/add-printer.command(Mac; auto-discovers over Bonjour) and a downloadable.mobileconfigprofile.
| Option | Values | → TSPL |
|---|---|---|
| Print Mode (halftone) | Default (threshold — crisp text/barcodes) · Gathering (dither — greys/photos) · None · Diffusion · Error Diffusion | — (rendered into the bitmap) |
| Darkness | 0–15 (default 8) |
DENSITY |
| Print Speed | 1–6 in/sec (default 4) · Printer default (sends nothing) |
SPEED |
| Media tracking | Die-cut (gap) · Black-mark · Continuous · Printer setting | GAP / BLINE |
| Resolution | 203 / 300 dpi |
— |
Loaded black-mark or continuous stock instead of die-cut labels? Set it per queue —
-o MediaTracking=BlackMark or Continuous — sending the default gap-sensor command to gapless
media makes the printer hunt for a gap and error out. And after any media change, run the printer's
hold-the-feed-button calibration and make sure the queue's page size matches the physical labels:
TSPL firmwares skip or garble labels when SIZE disagrees with the stock. Note PrinterDefault
sends no boundary command at all — and GAP/BLINE persist in printer memory, so such a queue
inherits whatever the last job set (e.g. GAP 0 from a Continuous queue sharing the printer).
Two queues: crisp labels + a "photo" (Gathering) queue
Print Mode is a halftone choice and the best one depends on content — Default (threshold) for crisp text/barcodes/QR, Gathering (clustered-dot dither) for greys/photos/watermarks. The filter honours each queue's PPD default, so run two queues on the same printer and pick per job:
# crisp labels (Default / threshold)
sudo lpadmin -p HZD950 -E -v tspl://auto -P /usr/share/ppd/tspl/tspl-label.ppd \
-o printer-is-shared=true -o PrintMode=5 -o Darkness=8 -o PrintSpeed=50
# photo / Gathering (greys, watermarks)
sudo lpadmin -p HZD950-Photo -E -v tspl://auto -P /usr/share/ppd/tspl/tspl-label.ppd \
-o printer-is-shared=true -o PrintMode=3 -o Darkness=7 -o PrintSpeed=20Baked into the queue default, this works even for driverless clients (AirPrint/IPP-Everywhere) that can't
show the option menus — they just pick the right queue. Values: PrintMode 5=Default 3=Gathering
0=None 2=Diffusion 4=ErrorDiffusion · Darkness 0–15 · PrintSpeed = in/sec ×10
(0 = leave it to the printer) · MediaTracking Gap/BlackMark/Continuous/PrinterDefault.
How it works · build from source · multiple printers · notes
your app ─► CUPS ─► gstoraster ─► rastertotspl ─► TSPL ─► tspl backend ─► printer
(this repo) (this repo)
rastertotspl(C filter) reads the CUPS raster and emits TSPL —SIZE / GAP|BLINE / DENSITY / SPEED / DIRECTION / REFERENCE / CLS / BITMAP … / PRINT. The 8-bit page is flattened to 1-bit dots with the selected Print Mode dither (bitmaps, not printer fonts — so no per-model font quirks).tspl(shell backend) writes the TSPL straight to the printer'susblpdevice, located by USB id/serial so it survives reboots and coexists with other USB printers. It spools the whole job, then writes it in one burst: byte-capture testing on the HZD950-PRO proved these firmwares silently drop data that trickles in while the printer is already printing — drivers that stream at render speed lose every page after the first on multi-copy and multi-page jobs.
Build from source: needs gcc, make, CUPS dev headers (sudo apt install build-essential libcups2-dev,
or the dnf/pacman/zypper equivalent), then make.
Multiple USB printers? tspl://auto only matches known TSPL ids (never a laser); pin others by
vid:pid/node. Drop in udev/99-tspl-label.rules for a stable
/dev/usb/tspl-label symlink — and with two label printers, pin each queue to its per-unit
/dev/usb/tspl-label-<serial> link (the bare symlink points at whichever enumerated last).
Notes: CUPS 2.4 prints a "printer drivers are deprecated" warning — harmless; classic PPD+filter drivers work for years yet. Reverse-engineered cleanly from the printer's own TSPL output; no vendor code is redistributed.
Coming from another driver where 2 copies printed 1 label, or a multi-page PDF stopped after page one? That's the mid-print data loss described above — cheap TSPL firmwares eat pages that arrive while the printer is busy, and most drivers (including vendor ones) stream. This driver spools since v1.3.1, so all pages print. If you still see missing pages here, that's a bug we want: report it with your model.
It's free, but we do want it to actually work for you — and your report is how the Supported printers list grows. No hoops, just GitHub Issues:
- Your printer works — or doesn't? → Open a printer report.
It asks for your model,
lsusbUSB id, the IEEE-1284 id (or~!Treply), and your distro/arch. Confirmed printers move to ✅ and get added to auto-detect. - Hit a bug? → Open a bug report
with your CUPS error log (
/var/log/cups/error_log— please redact hostnames/IPs). - Just a question? → open an issue anyway; we read them all.
If the driver saved you time, the nicest thank-you is trying Constly 💙.
MIT © Run The Wall. See LICENSE. Built and maintained for free — support us by trying Constly, our free Markdown editor → constly.com.
