45 lines
5.7 KiB
Markdown
45 lines
5.7 KiB
Markdown
---
|
|
title: Phomemo Thermal Printing On MacOS
|
|
date: 2023-02-03T10:00:00+01:00
|
|
tags:
|
|
- printer
|
|
categories:
|
|
- software
|
|
---
|
|
|
|
My wife bought another set of mini printers for scrapbooking, including the [Phomemo M02 mini printer](https://phomemo.com/collections/phomemo-m02). Phomemo is a Chinese brand I've never heard of before, and sadly, as I expected, it requires the use of a proprietary mobile app in order to send something to the printer. That very much stinks---and I've complained about this before in 2021 when I replaced my mini printer [with a HP Sprocket one](/post/2021/09/hp-sprocket-mini-printer). I just don't get it why a simple "share via Bluetooth" functionality can't be implemented. It's just a few lines of extra code in that hardware. But no.
|
|
|
|
Anyway, I thought I'd put on my hacking hat and try to see if I could come up with some code that would be able to send stuff to the thermal printer, without the usage of their app. A quick search on the good ol' web taught me [vivier at GitHub](https://github.com/vivier/phomemo-tools) already wrote a set of Phomemo tools that connects it to a CUPS printer server on Linux using a few Python filters---great! But does it work on macOS? Drivers being hardware-dependent, the answer is a probable no. After an hour of trying, the answer was a definite no.
|
|
|
|
The problem is twofold:
|
|
|
|
1. vivier's scripts use Python's deprecated Pybluez, that has been forked a couple of times, but ultimately, doesn't work on macOS (at least not on my M1 with Ventura 13.2). Mac's Bluetooth drivers, [CoreBluetooth](https://developer.apple.com/documentation/corebluetooth?language=objc), aren't properly supported. Great.
|
|
2. The part where the script is connected to a virtual printer driver doesn't work on macOS. Granted, Ventura also runs a CUPS service; but its file/driver locations are different, as is its structure. Great.
|
|
|
|
The first problem was fixed by turning to Go and utilizing [TinyGo's Bluetooth modules](https://pkg.go.dev/tinygo.org/x/bluetooth#section-readme) that sit on top of CoreBluetooth[^corebl] and is also compatible with Linux, Windows, and bare metal microcontrollers. I had no idea how Bluetooth---or a printer driver---works, and I still barely do, but "it just works". `adapter.Scan()` found our Phomemo M02, connecting to the MAC works, discovering services works (there's only one), and finding characteristics works (`0000ff01-x` for reading results, `0000ff02-x` for writing, and `0000ff03-x` for... no idea!).
|
|
|
|
[^corebl]: I later learned that, in Python, through PyObjC, you can also call CoreBluetooth directly. I would probably have preferred to keep everything in Python.
|
|
|
|
The source code is available at my Git repository called [phomemo-printer](https://git.brainbaking.com/wgroeneveld/phomemo-printer). It's very hacky and works for our specific situation but could do with a week of fine-tuning---which I don't have. For the second problem, I've had to cut many corners. For instance; I wanted my wife to be able to use it as a "printer" to select in a generic print dialog instead of running a CLI script. The options were:
|
|
|
|
- Try to get it to integrate with Mac's version of CUPS. My knowledge and time is just too limited to do that.
|
|
- Create a virtual printer driver using for example IPP or the [Internet Printing Protocol](https://istopwg.github.io/ipp/ippguide.html). That involves implementing a lot of HTTP endpoints and would probably work but the effort is just too high. I only found existing IPP clients, not servers (one in Java, though).
|
|
- In macOS, you can modify the drop-down menu of the "PDF" print button; these are called "PDF Services" and are apps/scripts/folders located in `~/Library/PDF\ Services`. I read about it [on John M. Simpson's blog](https://jms1.net/osx-pdf-services.shtml), but of course, that doesn't work anymore in modern macOS versions, because `printtool` [runs in a sandbox](https://apple.stackexchange.com/questions/423478/fix-restore-print-to-pdf-to-preset-location-in-big-sur) and has access to pretty much nothing. I gave up after two hours of debugging and digging through Mac system logs. I couldn't get past:
|
|
|
|
```
|
|
[kernel] Sandbox: zsh(34634) deny(1) file-read-data /Users/user/.zshenv
|
|
[printtool] Workflow (344546) stopped with status 1
|
|
```
|
|
|
|
Using Automator to wrap the script in a "native" workflow or app folder didn't work, granting `printtool` disc access didn't work, adding `Sandboxing Relaxed` to a mysterious cupsd config file didn't work.
|
|
|
|
I settled with a sup-par but working solution: drop images in `~/Downloads/phomemo`, and have a cron job---I'm sorry, [a pretty XML launchd job](https://alvinalexander.com/mac-os-x/mac-osx-startup-crontab-launchd-jobs/)---pick up, print, then delete these files every minute. I'm annoyed I have to settle with this, but it's been a constant struggle between putting in enough time and going (too) deep trying to understand how every moving part works. It turned out to be surprisingly complex.
|
|
|
|
The most important part was already done by vivier. You can't simply send a JPG/PNG byte stream to the printer: it expects EPSON ESC/POS commands, and thus a header, a footer, and every block of 255 bytes, a block marker in 16-bit little-endian. Images can't be more than 384 points per line, so scaling and transforming is needed as well, as is grayscale conversion. I currently simply run the Python script from within my Go script; but of course it should have been ported; yet the "batteries included" part of Python's image processing library PIL is just too difficult to quickly do that.
|
|
|
|
Still, at least _something_ rolls out of the printer with the help of some code I wrote:
|
|
|
|
![](../phomemo.jpg "The Phomemo M02, after printing a test image.")
|
|
|
|
Yay! Feel free to improve/steal/hack away.
|