A Go CLI and library for the Phomemo M02X pocket thermal printer. Connects over Bluetooth Low Energy, renders text / images / barcodes / weather / Markdown / etc. into 1-bit bitmaps, and prints them.
The M02X uses a vendor-specific protocol that's similar to but not compatible with the older M02 / M02 Pro / M02S / T02 family — it's not ESC/POS and not the cat-printer framing. The full wire format is documented in PROTOCOL.md, reverse-engineered from a PacketLogger capture of the official iOS app.
- ✅ M02X — the protocol the code was built against; verified working.
- ⚠ Older M02 / M02 Pro / M02S — not currently supported. The bones of ESC/POS framing exist in earlier commits if you want to build that path.
- 🐉 Other Phomemo / re-branded thermal printers — unknown; capture and diff their protocol against ours.
Requires Go 1.26+ and macOS or Linux with a working Bluetooth stack.
go install github.com/sgrankin/phomemo@latestOr from a clone:
git clone https://github.com/sgrankin/phomemo.git
cd phomemo
go build -o phomemo .The first BLE scan on macOS will trigger a Bluetooth permission prompt for whichever terminal you're running from.
phomemo <subcommand> [flags] [args...]
phomemo image photo.jpg # PNG/JPEG/GIF/WebP/BMP/TIFF, dithered
phomemo text "Hello world" # text; reads stdin if arg is "-"
phomemo qr "https://example.com" # QR code
phomemo pdf417 "wide URL" # PDF417 (iPhone Camera reads it)
phomemo notify "DOORBELL" "Package on porch"
phomemo weather # IP-geolocated forecast w/ icon + sparkline
phomemo wifi MyHomeNet secretpw # WIFI: QR — phone scans to join
phomemo vcard "Sergey Grankin" -phone +15550100 -email me@example.com
phomemo url "https://news.ycombinator.com" # QR + page <title> caption
phomemo md NOTES.md # render Markdown (headings, lists, code)phomemo selftest # text + QR + timestamp
phomemo qualitytest # bars + gradient + dithered photo
phomemo fonts # one labeled line per vendored font
phomemo fonts -list # print family names + sizes (no print)
phomemo bar # solid or striped bar
phomemo pattern # stripes / checkerboards / diagonals
phomemo curve hilbert # Hilbert space-filling curve (try -order 5..8)
phomemo curve dragon # Heighway dragon (try -iter 8..14)
phomemo curve gosper # flowsnake / hexagonal space-filling
phomemo curve rule30 # Wolfram 1-D CA (try -rule 30, 90, 110, 184)Persistent BLE connection so prints don't pay the scan latency:
phomemo serve -addr 127.0.0.1:8080Then from anywhere:
curl -d "Hello!" http://localhost:8080/print/text
curl -d '{"title":"DOORBELL","body":"Package on porch"}' \
-H "Content-Type: application/json" \
http://localhost:8080/print/notify
curl --data-binary @photo.webp -H "Content-Type: image/webp" \
http://localhost:8080/print/imageGET / on the running server returns the full endpoint list. Image
endpoints accept any format the CLI does (PNG / JPEG / GIF / WebP / BMP /
TIFF), driven by Content-Type.
phomemo scan # list nearby BLE devices
phomemo ping # connect + feed paper
phomemo info # serial, firmware, battery, paper, cover, temperature
phomemo discover # enumerate GATT services / characteristics
phomemo raw "1b 40" "Hello\n" "1b 64 04" # send raw hex bytes (multi-arg)-name NAME BLE local name; default $PHOMEMO_NAME or "M02X"
-feed N blank lines after content; default 4
-density N head energy 1..15 (default 2 — what the iOS app uses)
-rotate N rotate output by 0/90/180/270
-font NAME body font family (helvetica, times, geneva, chicago,
monaco, courier, spleen, sungallant)
-codefont NAME monospace family for code blocks (monaco, courier, spleen)
-no-print write rendered PNG to stdout instead of printing
-v log every BLE write to stderr
main.go, serve.go CLI dispatcher + HTTP daemon (module root)
printer/ BLE driver + M02X wire-format protocol
render/ Image rendering (text, QR, PDF417, dither, compose, fonts)
content/ Data sources (weather, wifi, vcard, url, markdown)
internal/smolfont/ Vendored Apple + Spleen + Sun Gallant bitmap fonts
Tests are golden-PNG comparisons in render/testdata/ and
content/testdata/. Run go test ./render -update to regenerate after
intentional rendering changes.
Protocol reverse-engineering inheritance:
- vivier/phomemo-tools — the reference for the M02-family ESC/POS protocol.
- theacodes/phomemo_m02s —
Python library; mapped the
1F 11 NNquery family. - jeffrafter/phomemo — Swift port; decoded the notification status table that we re-confirmed for M02X.
- ryo-endo/phomemo-printer — clearest BLE-specific implementation for the M02 Pro.
Bitmap fonts come from the smol collection (Apple classic Mac fonts, Spleen, Sun Gallant).
MIT — see LICENSE.