Turn one Mac into a WiFi / AirPrint print server for the Brother DCP-T220 — a cheap USB-only ink-tank printer with no wireless and no macOS driver. After you install this, every iPhone, iPad, Android phone, and other Mac on the same WiFi can print to the DCP-T220 as if it were a normal network printer. No Docker, no Brother app, no cloud.
One Mac stays plugged into the printer by USB and acts as the server. Everyone else prints over WiFi.
Out of the box, the DCP-T220 can only be driven by Brother's own iPrint&Scan application. That app talks to the printer over USB using Brother's private protocol; there is no standard macOS/Windows print driver for this model. So on a plain setup, the only supported way to print is:
- install Brother iPrint&Scan on the computer that has the USB cable, and
- open that app every time you want to print (it renders the page itself).
That is exactly the limitation this project removes — but if you just want the official app, here is where to get it:
- macOS — Mac App Store: https://apps.apple.com/us/app/brother-iprint-scan/id1193539993?mt=12
- Windows / Mac (download hub) — Brother's software page: https://www.brother.com/apps/ps/en/
- Step-by-step (Windows or Mac) — https://help.brother-usa.com/app/answers/detail/a_id/174288/
This project reuses the fact that iPrint&Scan works over USB, but replaces the app entirely with a tiny always-on server so that any device can print over WiFi without installing Brother software.
Platform note — macOS only. This tool is built on macOS-specific pieces:
cupsfilter,dns-sd(Bonjour), CUPS sharing, LaunchAgents, andpkgbuild. It will not run on Windows as-is. The technique is portable, though — a Windows version would need to reproduce the same idea (see the byte pipeline): take a PDF, rasterize it to PWG, wrap it in the@PJL ENTER LANGUAGE=PWGRASTERenvelope, and send it to the printer's USB port, then expose that over the network (e.g. via a shared Windows printer or Bonjour Print Services). If you want Windows support, you'd have to build that yourself based on this design.
The DCP-T220 is a stubborn little printer, and every "normal" path to print from a phone is a dead end. Here is what actually happens, and why the obvious solutions fail:
flowchart TD
phone["Phone / tablet / other Mac<br/>wants to print over WiFi"]
subgraph deadends["The paths that DO NOT work"]
direction TB
wifi["Print straight to the printer's WiFi?<br/><b>FAIL</b> — DCP-T220 has no WiFi at all,<br/>USB is the only port"]
drv["Use a Brother macOS driver?<br/><b>FAIL</b> — Brother ships no CUPS<br/>driver for macOS for this model"]
bridge["Use Brother's iPrint&Scan bridge<br/>(HttpToUsb, the tunnel it installs)?<br/><b>FAIL</b> — its IPP endpoint answers<br/>Print-Job with a <i>fake</i> success:<br/>HTTP 200, no job-id, nothing prints.<br/>Proven by packet capture."]
end
phone --> wifi
phone --> drv
phone --> bridge
key["The one real fact we can build on:<br/>the printer only understands Brother's<br/>own page format <b>vnd.brother-hbp</b>,<br/>and the PJL/PWG envelope inside it"]
wifi -.-> key
drv -.-> key
bridge -.-> key
style deadends fill:#3a1f1f,stroke:#a33
style key fill:#1f3a1f,stroke:#3a3
The breakthrough: we discovered (by reverse-engineering the output of Brother's
Linux driver) that the printer accepts a PWG raster image wrapped in a
specific PJL text header. macOS can already produce PWG raster natively with
its built-in cupsfilter. So we build that exact byte stream ourselves and push
it straight down the USB cable. No Brother software runs at all.
Two views: the big picture, then the exact byte pipeline.
flowchart LR
subgraph clients["Any device on the WiFi"]
ios["iPhone / iPad<br/>(AirPrint, built in)"]
droid["Android<br/>(Default Print / Mopria)"]
mac2["Another Mac<br/>(adds it as a normal printer)"]
end
subgraph server["The server Mac (this one)"]
direction TB
bonjour["Bonjour advertisement<br/>'DCP-T220 WIFI'<br/>(dns-sd LaunchAgent)"]
cups["CUPS shared queue<br/>DCP-T220-WiFi<br/>(listens on :631)"]
backend["brhbp CUPS backend<br/>reads print-color-mode<br/>(color vs B&W)"]
daemon["brhbpd daemon<br/>(listens on 127.0.0.1:9101)<br/>runs forever, never sleeps"]
usbq["CUPS raw queue<br/>DCP-T220 → usb://"]
end
printer["Brother DCP-T220<br/>(USB cable)"]
ios & droid & mac2 -->|"1. find printer"| bonjour
ios & droid & mac2 -->|"2. send job (PDF/JPEG)"| cups
cups -->|"3. run brhbp backend"| backend
backend -->|"4. mode hint + PDF"| daemon
daemon -->|"6. raw bytes"| usbq
usbq -->|"7. USB"| printer
style server fill:#16233a,stroke:#38a
style clients fill:#1c2a1c,stroke:#4a4
sequenceDiagram
autonumber
participant C as Client<br/>(phone/Mac)
participant Q as CUPS queue<br/>DCP-T220-WiFi
participant D as brhbpd daemon<br/>:9101
participant F as cupsfilter<br/>(built into macOS)
participant U as CUPS raw queue<br/>DCP-T220
participant P as Printer<br/>(USB)
participant B as brhbp backend<br/>(reads color choice)
C->>Q: IPP Print-Job over WiFi<br/>(PDF + print-color-mode:<br/>color OR monochrome from<br/>the dialog's B&W toggle)
Note over Q: PPD says<br/>"application/pdf 0 -"<br/>= pass the PDF through
Q->>B: run backend with the job<br/>options (incl. color mode)
Note over B: parse print-color-mode<br/>color → "BRHBP1 color"<br/>mono → "BRHBP1 mono"
B->>D: TCP :9101 — send the mode line,<br/>then stream the PDF, close
Note over D: save PDF to temp,<br/>read the mode hint
alt color job (photos)
D->>F: cupsfilter -m image/pwg-raster<br/>ColorModel=RGB, Draft
F-->>D: sRGB 24-bit raster
Note over D: PJL RENDERMODE=COLOR<br/>(~11 ppm)
else monochrome job (documents)
D->>F: cupsfilter -m image/pwg-raster<br/>ColorModel=Gray, Draft
F-->>D: 8-bit gray raster (1/3 the data)
Note over D: PJL RENDERMODE=GRAYSCALE<br/>black head only (~28 ppm)
end
Note over D: wrap in the PJL envelope<br/>@PJL ENTER LANGUAGE=PWGRASTER<br/>(bytes copied from Brother's<br/>own Linux filter)
D->>U: lp -o raw (send finished .prn)
U->>P: raw bytes over USB
P-->>P: printer sees valid<br/>vnd.brother-hbp → prints
Note over C,P: B&W documents print at up to<br/>28 ppm; color photos stay in color
Plain-English summary of the trick:
- The phone sends an ordinary PDF plus its color choice (that is what AirPrint does).
- A small CUPS backend (
brhbp) reads whether the job is color or B&W and forwards the PDF to our daemon over a plain TCP socket, with a one-line hint. - The daemon uses macOS's own
cupsfilterto turn the PDF into PWG raster — color (sRGB) or grayscale, matching the job. Grayscale prints on the black head only, at the printer's full 28 ppm. - The daemon wraps that bitmap in the short PJL text header the DCP-T220
insists on (
@PJL ENTER LANGUAGE=PWGRASTER, withRENDERMODE=COLORorGRAYSCALE). Those exact bytes were lifted from Brother's Linux driver, so the printer can't tell the difference. - The daemon sends the finished bytes to a second, raw CUPS queue that just pipes them down the USB cable.
That is the whole system. No container, no proprietary binary running — just Apple's built-in tools plus ~120 lines of Python.
Everything lives in two places and starts automatically at every login.
flowchart TB
subgraph lib["/Library/PrintServer/brhbp/"]
py["brhbpd.py<br/>the daemon"]
r1["render.ppd<br/>(macOS IPP-Everywhere PPD,<br/>used to make PWG raster)"]
r2["DCP-T220-WiFi.ppd<br/>(PDF-passthrough PPD<br/>for the shared queue)"]
end
subgraph be["/usr/libexec/cups/backend/"]
bk["brhbp<br/>CUPS backend: reads the job's<br/>color mode, forwards to daemon<br/>(root:wheel 0755, as CUPS requires)"]
end
subgraph agents["/Library/LaunchAgents/"]
a1["com.local.brhbpd.plist<br/>starts the daemon under<br/><b>caffeinate -si</b> so the Mac<br/>never idle-sleeps while it runs;<br/>KeepAlive = auto-restart"]
a2["com.local.brhbpd-airprint.plist<br/>runs dns-sd to advertise<br/>ONE AirPrint printer 'DCP-T220 WIFI'"]
end
subgraph cupsq["CUPS queues"]
q1["DCP-T220-WiFi<br/>(shared, what clients see)"]
q2["DCP-T220<br/>(raw USB, internal only)"]
end
pi["postinstall script (runs as root)<br/>• strip com.apple.quarantine<br/>• cupsctl --share-printers<br/> BrowseLocalProtocols=none<br/> (so only OUR one printer shows)<br/>• create the shared queue<br/>• load both LaunchAgents"]
pi --> lib
pi --> be
pi --> agents
pi --> cupsq
Design choices worth knowing:
- Never idle / always ready. The daemon is launched by
caffeinate -si, which keeps the Mac from going to idle sleep while the server is up. Combined withKeepAlive, the print server is always listening. (If the lid is closed on battery the Mac can still sleep; keep it plugged in for a true always-on server.) - Exactly one printer on the network. We turn off CUPS's own Bonjour
broadcasting (
BrowseLocalProtocols=none) and advertise a single, clean AirPrint record ourselves, so users don't see three near-identical printers. - Self-healing USB queue. If the printer is unplugged during install, the
daemon creates the raw
usb://queue automatically the first time it sees the printer, so install never fails just because the cable was out. - No secrets, no proprietary binaries in this repo. The docker-free version reproduces the printer's format with Apple's tools; nothing from Brother is redistributed here.
- Download
DCP-T220-WiFi-Installer.dmgfrom the Releases page. - Open the DMG and double-click the
.pkg. - Click through the installer and enter your Mac password when asked.
That's it. The installer strips Apple's quarantine flag, sets up the queues, and starts the server immediately and at every future login.
Requirements on the server Mac:
- macOS (uses the built-in
python3,cupsfilter,dns-sd). - The DCP-T220 connected by USB and powered on.
- Keep this Mac awake (plugged in) so it can serve prints at any time.
Nothing needs to be installed on the phones or the other Macs.
| Device | Steps |
|---|---|
| iPhone / iPad | Open anything → Share → Print → pick DCP-T220 WIFI → Print. |
| Android | Open a file → menu → Print → pick DCP-T220 WIFI. If it isn't listed, install Mopria Print Service from the Play Store, enable it, and try again. |
| Another Mac | System Settings → Printers & Scanners → Add Printer → pick DCP-T220 WIFI (Bonjour) → Add. |
| The server Mac itself | Just print to DCP-T220 WIFI like any printer. |
All devices must be on the same WiFi as the server Mac.
./build.sh 2.0.1 # produces DCP-T220-WiFi-Installer.dmgbuild.sh uses pkgbuild + productbuild to make the double-click installer,
then hdiutil to wrap it in a DMG. scripts/postinstall is the root script
that wires everything up on the target Mac.
Inkjet speed is set by the printer's mechanical mode, not the computer. Brother rates the DCP-T220 (draft) at 28 ppm in black & white but only 11 ppm in color — color is inherently ~2.5× slower because it uses all four ink heads instead of just the black one.
You don't have to choose once. This setup honors the color choice in the print dialog on every job, from a single printer:
| Print dialog | What happens | Speed |
|---|---|---|
| Black & White turned on | print-color-mode=monochrome → gray raster + RENDERMODE=GRAYSCALE, black head only |
up to 28 ppm |
| Color (default) | print-color-mode=color → sRGB raster + RENDERMODE=COLOR |
up to 11 ppm |
So: flip Black & White on in the Print dialog for documents and they fly at
the printer's top rated speed; leave it off for photos and they print in full
color. Quality is always DRAFT (fewest head passes) — the fastest quality
tier, confirmed by Brother's own spec (draft 28/11 ppm vs normal 16/9 ppm).
- iPhone/iPad: in the Print sheet, expand options and toggle Black & White.
- Android: choose Black & White / Grayscale in the print options.
- Mac: the Print dialog's color/B&W control.
28 ppm is the printer's hardware ceiling in draft — software cannot go faster.
sudo launchctl bootout gui/$(id -u)/com.local.brhbpd
sudo launchctl bootout gui/$(id -u)/com.local.brhbpd-airprint
sudo rm -f /Library/LaunchAgents/com.local.brhbpd*.plist
sudo rm -rf /Library/PrintServer/brhbp
sudo rm -f /usr/libexec/cups/backend/brhbp
lpadmin -x DCP-T220-WiFi
lpadmin -x DCP-T220- Nothing prints, printer plugged in. Check the daemon log:
cat ~/Library/Logs/brhbpd.log. Each job should showjob received,converted, andlp rc=0. - Printer not found on the phone. Confirm both devices are on the same WiFi;
confirm the advertisement is live:
dns-sd -B _ipp._tcp local.should listDCP-T220 WIFI. - Server Mac keeps sleeping. Keep it on power.
caffeinate -siblocks idle sleep but not lid-close sleep on battery. - Wrong paper size. The default is A4. Change
PageSize=A4in the daemon's PJL header and in the PPD, rebuild, reinstall.
The Brother iPrint&Scan app can print over its USB bridge, so we packet-captured
what it sent. The IPP Print-Job calls to the bridge returned HTTP 200 but with
no job-id and produced no paper — a fake success. Meanwhile the printer's real
input format is vnd.brother-hbp, which is just a PJL wrapper around PWG raster.
We took the exact PJL header/trailer from Brother's Linux driver output, learned
that macOS's cupsfilter already emits matching PWG raster (sRGB, 24-bit, 300
dpi), and stitched the two together. The first byte-for-byte reconstruction we
sent over USB made the printer's status flip to processing and a page came out.
Everything above is the productized version of that finding.