Phomemo M08F Protocol Reference#
Reverse-engineered protocol specification for the Phomemo M08F portable thermal printer.
Copyright (C) 2026 Paul Bütof, Passionate Bytes Solutions (www.passionate-bytes.com).
This work is licensed under Creative Commons Attribution-ShareAlike 4.0 International. To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/.
Table of Contents#
Methodology and Acknowledgements#
These findings were produced through systematic exploratory BLE interactions: connecting to the printer, enumerating its services and characteristics, sending probe commands across all discovered channels, and observing responses and physical behaviour. Testing was performed using the bleak Python BLE library on Linux (BlueZ).
This work builds on and was informed by prior reverse engineering efforts from the Phomemo open-source community:
vivier/phomemo-tools — CUPS driver with reverse-engineered protocol for M02, M02S, M110, T02. Provided the
1f 11 XXcustom command family and print job footer sequences.theacodes/phomemo_m02s — Python BLE library for M02S. Provided the
ESC Nconcentration command,NAK 15 11alternative prefix, and0x0Abyte escaping approach.gbl/phomemo — Perl CLI utility. Provided GS v 0 raster formatting details and multi-block image handling.
Known differences from M02/M02S/T02#
The M08F shares the Phomemo command dialect but differs from smaller models (M02, M02S, T02) in several ways. This work was conducted with a single M08F unit — findings may apply in part to related Phomemo models, but have not been verified on other hardware.
Area |
M08F |
M02/M02S/T02 (per community projects) |
|---|---|---|
Density control |
|
Both |
NAK prefix ( |
No effect (except |
Used for concentration setting on some models |
Text mode |
Not supported — raster only |
Not supported on most models |
|
Handled correctly (no escaping needed) |
phomemo_m02s escapes |
Connectivity |
BLE only |
Some models support Bluetooth Classic (RFCOMM) |
Print width |
1680 px (A4 variant; a Letter variant exists, untested) |
384 px (M02) / 576 px (M02S) |
Standard ESC/POS queries (GS I, DLE EOT) |
All return generic |
Some models may return meaningful status bytes |
All findings are based on direct observation unless marked [assumed] (inferred from context or external references).
Tested Device Model#
Device Type:
A4 Portable PrinterBrand:
PhomemoModel:
M08F/29002-M08F/2ASRB-M08FManufacturer:
Zhuhai Quin Technology Co., Ltd.BLE MAC: per device; examples in this document use
AA:BB:CC:DD:EE:FFSerial: 15 ASCII characters (via
1f 11 09→1a 08onff01)Firmware:
v1.1.3(via1f 11 07→1a 07 01 01 03onff01)
Architecture Overview#
[Host] ──BLE── [BLE-UART Bridge Chip] ──UART 460800 bps── [Printer MCU]
(ff80 AT interface) (ESC/POS + Phomemo extensions)
(ff00 data relay) (thermal print head, sensors, motor)
The M08F contains two processors:
BLE-UART bridge chip — handles BLE connectivity, exposes an AT command interface on service
ff80, and relays data between BLE and the printer MCU. Runs custom/locked firmware with onlyAT+NAME?andAT+BAUD?readable. The chip identity cannot be determined via AT commands.Printer MCU — receives commands over UART at 460800 bps, controls the thermal print head, paper feed motor, and sensors. Implements a subset of ESC/POS plus Phomemo-specific extensions (
1f 11 XXand1b 4e XX).
The printer is raster-only — it has no built-in font and does not support ESC/POS text mode. All content must be rendered to 1-bit bitmap images on the host.
BLE Transport Layer#
Connection Parameters#
Parameter |
Value |
|---|---|
BLE name |
|
Negotiated MTU |
247 bytes |
Usable ATT payload |
244 bytes |
Maximum write size |
244 bytes per |
Write method |
Write Request ( |
Writes larger than 244 bytes fail with org.bluez.Error.Failed.
Chunking and Flow Control#
Print data must be split into chunks of at most 244 bytes. Flow control comes
from the ATT layer: with Write Request (response=True) the printer delays
its response while its receive buffer is full, so the sender is paced by the
print head and needs no artificial delay. Measured throughput on an A4 page:
~7.4 KiB/s at medium density on sparse content, ~5.7 KiB/s at high density on
solid black, both steady from the first second. The motor-stop signal arrives
within about a second of the last acknowledged write.
Buffer probes (2026-11-02, Write Request, 244-byte writes): white rows with a sheet loaded, solid black at high density, and rows sent with the lid open all ran at a flat 6.5–7.4 KiB/s with ~30 ms median ACK latency and no slowdown up to 430–525 KB. The ACK rate is the ATT round trip on this link, and the printer consumed (or, with the lid open or no paper, silently discarded) everything at that rate, so the MCU-side buffer never filled and its size could not be measured. A real back-pressure from the MCU below that rate remains plausible but is unproven; the earlier 5.7 KiB/s on a dense page was most likely the weak radio link at the time. Data accepted with the lid open is gone: closing the lid afterwards prints nothing.
Write Command (response=False) is accepted but unsafe for long jobs. The
printer does not acknowledge writes on ff01; ff03 echoes 01 01 per write
(see Service 0000ff00), but that echo comes from the BLE bridge and lags the
sender by at most a few chunks even when the printer falls behind, so it is
not a credit signal. At 244 bytes every 20 ms (~11.3 KiB/s) the printer
consumes roughly 7.5 KiB/s and a buffer of around 100 KB, most likely in the
BLE bridge chip, overflows about two thirds into an A4 page. Symptoms of the overflow:
every row after the loss starts late by the number of bytes dropped, so the image shifts right and the overflow wraps to the left edge (a 25 mm shift equals six lost 244-byte chunks);
the next
GS v 0header is misparsed, the job stops, and the LED blinks;the MCU ignores
1f 11 XXqueries until the printer is power-cycled.
A fixed delay cannot fix this: the safe rate depends on content density and the printer starves (visible as stutter) on sparse pages at a delay that is still too fast for dense ones.
Connect Sequence#
On BLE connect, the following notifications fire automatically:
Channel |
Data |
Timing |
Notes |
|---|---|---|---|
|
|
Immediate |
Connect greeting. Purpose of |
|
|
Immediate |
Fixed constant ( |
|
26-byte protobuf |
~100ms |
Tencent SDK identity packet. Last 6 bytes = BLE MAC address. Not required for printing. |
Both ff03 greetings also fire when writes use Write Request; the per-write
01 01 echo on ff03 does not (the ATT response takes its place).
BLE Services and Characteristics#
UUID format note: The
ff00-family service UUIDs use the standard Bluetooth base UUID expansion (0000ff00-0000-1000-8000-00805f9b34fb). However, the characteristics within those services use a non-standard format where the short UUID appears in the last two bytes:00000000-0000-0000-0000-00000000ff01. This is not the standard 16-bit expansion (0000ff01-0000-1000-8000-00805f9b34fb). Driver code must use the non-standard form or Bleak will report “Characteristic not found”.
Service 0000ff00 — Print Service (primary)#
The main communication channel for printing and status.
Short UUID |
Properties |
Handle |
Role |
|---|---|---|---|
|
write-without-response, write |
41 |
Command/data write channel — all print data and commands go here |
|
notify |
43 |
Device event channel — sensor events, query responses, print completion |
|
notify |
46 |
Status echo channel — returns |
Service 0000ff80 — AT Command Interface#
Exposes the BLE bridge chip’s configuration. Not the printer MCU.
Short UUID |
Properties |
Handle |
Role |
|---|---|---|---|
|
write-without-response, write |
25 |
AT command write |
|
notify |
27 |
AT response notify |
See BLE Bridge Chip for command details.
Other Services#
See Other BLE Services for details on services that were probed but did not produce observable responses relevant to printing.
Printing Protocol#
The M08F uses GS v 0 (raster bit image) as the sole printing command. Print jobs consist of: initialise → set density (optional) → send raster bands → feed paper.
Print Job Structure#
ESC @ # 1b 40 — initialise printer state
ESC N 04 <density> # 1b 4e 04 XX — set print density (optional)
┌─ repeat for each band ──────────────────────────────────────────────────────┐
│ GS v 0 <mode> <wL> <wH> <hL> <hH> # - raster header │
│ <bitmap data> # - w × h bytes │
└─────────────────────────────────────────────────────────────────────────────┘
ESC d <n> # 1b 64 XX — feed paper (standalone)
All bytes are written to ff02 as a single chunked stream. The printer
processes commands sequentially.
GS v 0 — Raster Bit Image#
Byte(s) |
Value |
Description |
|---|---|---|
0–2 |
|
GS v 0 command prefix |
3 |
|
Scale mode (see table below) |
4–5 |
|
Width in bytes, 16-bit little-endian (pixels ÷ 8) |
6–7 |
|
Height in rows, 16-bit little-endian |
8+ |
|
Raw 1-bit bitmap: |
Bitmap format: 1 bit per pixel. 1 = black, 0 = white. Most significant
bit of each byte is the leftmost pixel. Rows are sequential top-to-bottom.
Band height: The maximum height per GS v 0 command is at least 256 rows (tested). For images taller than this, send multiple GS v 0 commands back-to-back. Each band needs its own full header.
GS v 0 Scaling Modes#
Mode |
Name |
Effect |
Data impact |
|---|---|---|---|
|
Normal |
1:1 rendering |
Full resolution |
|
Double width |
Each pixel doubled horizontally |
Send half-width data for full-width output. Left-edge aligned, expands rightward. |
|
Double height |
Skips every other scan line |
Same data size. Draft/low-density mode, NOT actual 2× height. |
|
Quadruple |
Mode 01 + mode 02 combined |
Half-width data, draft vertical quality |
Print origin: Left-aligned. Narrow images are NOT automatically centered. Mode 0 may appear centered because the print head width exceeds the image width symmetrically.
0x0A (LF) Byte Handling#
Outside raster data:
0x0Ais ALWAYS interpreted as a line feed (~5mm paper advance per instance), even embedded in arbitrary binary data.Inside GS v 0 raster data:
0x0Abytes are consumed as raw bitmap data — no escaping needed. The printer correctly counts expected bytes from the raster header.
Bridge Chip Routing#
Standalone ESC-prefix commands (1b XX) may be intercepted by the BLE bridge
chip instead of being relayed to the printer MCU. This manifests as an
\r\nERROR\r\n response on ff81 (the AT channel).
Workaround: Prepend ESC @ (1b 40) before other ESC commands, or embed
them in a larger write payload. ESC @ itself is always relayed correctly.
Print Completion Signal#
The printer fires 1a 0f 0c on ff01 the moment the motor stops after a
print job. This is a real-time motor-stop signal that can be used as a
print-complete indicator.
Condition |
Approx. delay after last chunk |
|---|---|
Normal print with paper |
~1–2s |
No paper loaded |
~1s |
Lid opened mid-print |
~2.5s |
Paper pulled out mid-print |
~5s |
Paper ran out mid-print |
~5s after |
The third byte (0c) is invariant across all tested conditions.
1a 06 88 fires when the trailing edge passes the rear sensor, which sits
about 10 mm (~80 rows) before the print head; a raster that fits the sheet
sees this event shortly before its own motor stop and the printer keeps
answering queries. When the raster is taller than the sheet, the remaining
writes are acknowledged immediately (discarded), 1a 0f 0c follows about 5 s
after the paper event, and the printer sits in an error state in which it
ignores all 1f 11 XX queries; the LED is red. Loading a new sheet (and
in the tested case reconnecting) restores query responses. A driver that waits
for a sheet must therefore treat query timeouts as “not ready” rather than as
a transport failure.#
Device Information and Status Queries#
Phomemo Custom Commands (1f 11 XX)#
The primary information API. These are Phomemo-specific extensions (NOT standard
ESC/POS). Write to ff02; responses arrive on ff01 in 1a <sub-type> <data> format. ff03 echoes 01 01 for every command sent as Write Command
(uninformative; silent for Write Request).
Command |
Label |
Response format |
Example |
Notes |
|---|---|---|---|---|
|
Battery |
|
|
Live value, changes with charge state |
|
Firmware |
|
|
|
|
Serial (ASCII) |
|
|
Full serial number |
|
Serial (short) |
|
|
Possibly hardware revision |
|
Paper state |
|
|
Reports the combined state of all three optical sensors (see Physical Characteristics). |
|
Lid state |
|
|
Reliable in all conditions |
|
Device timer |
|
|
Auto-off timer setting. See Device Configuration. |
Response routing: The sub-type byte in the response does NOT always match
the query parameter. Parsers must identify responses by the 1a <sub-type>
prefix, not by which query was sent.
Non-Responding Query Bytes#
Full sweep of 1f 11 XX for XX = 00–1f confirmed the following values
produce no ff01 response: 00, 01, 02, 03, 04, 05, 06, 0a,
0b, 0c, 0d, 10, 14–1f.
Note: 1f 11 02 (3 bytes) is non-responding, but the 4-byte variant
1f 11 02 XX is a separate sub-command space. A sweep of 1f 11 02 00 through
1f 11 02 0f produced no ff01 responses — these are either write-only
settings or unimplemented. The phomemo-tools init sequence uses 1f 11 02 04
(suspected concentration-related setting).
Standard ESC/POS Queries (Non-Functional)#
The following standard ESC/POS information commands produce nothing but the
ff03 write echo (01 01, Write Command only) and no data on ff01. The MCU does not implement them. The 1f 11 XX commands
provide this information instead.
Command |
Bytes |
Standard purpose |
|---|---|---|
GS I n=1 |
|
Model ID |
GS I n=2 |
|
Type ID |
GS I n=65 |
|
Firmware version |
GS I n=67 |
|
Printer name |
GS I n=68 |
|
Serial number |
DLE EOT n=1 |
|
Printer status |
DLE EOT n=2 |
|
Offline status |
DLE EOT n=4 |
|
Error status |
DLE EOT n=5 |
|
Near-end paper sensor |
DLE EOT n=6 |
|
Paper present sensor |
GS r 1 |
|
Paper sensor status |
DLE EOT queries return 01 01 even under error conditions (no paper, lid open,
mid-print errors). They do not reflect actual device state on this printer.
Device Events#
All events arrive on ff01 as 1a <sub-type> <data> messages. Events can be
spontaneous (triggered by physical state changes) or solicited (responses
to 1f 11 XX queries). Both use the same format.
Event Sub-Types#
Sub-type |
Meaning |
Spontaneous |
Queryable via |
|---|---|---|---|
|
Serial (short) |
No |
|
|
Battery level |
No |
|
|
Lid state |
Yes |
|
|
Paper sensor |
Yes |
|
|
Firmware version |
No |
|
|
Serial (ASCII) |
No |
|
|
Device timer |
No |
|
|
Motor stop / print complete |
Yes |
— |
Spontaneous Events#
Bytes |
Meaning |
|---|---|
|
Lid opened |
|
Lid closed |
|
Paper inserted / gripped by roller |
|
Paper removed / absent |
|
Motor stopped (print complete) |
Event Byte Format#
Byte 1:
1a— fixed message typeByte 2: sub-type (see table above)
Byte 3: state/data
Sub-types
05/06: bit 0 = state (1=active,0=inactive). Upper bits differ by sub-type (0x98base for lid,0x88base for paper).Sub-type
0f: always0c(invariant across all tested conditions).
Batched Notifications#
Multiple 3-byte events can arrive concatenated in a single BLE notification.
Parsers must consume 3 bytes at a time. Observed batch sizes: 6 bytes (2 events)
and 9 bytes (3 events).
Example: 1a 05 98 1a 06 88 = lid closed + paper absent (single notification).
Sensor Behaviour#
Three optical paper sensors: Two front sensors at the paper entry detect insertion and trigger the auto-feed motor (AND logic — both must detect paper). One rear sensor behind the print head detects paper fully loaded. The BLE
1a 06 89event fires only when the rear sensor is triggered, confirming paper is positioned and ready to print.- Auto-feed sequence: Front sensors detect paper → motor starts pulling → paper reaches rear sensor → motor stops →1a 06 89fires. If paper is held just out of roller reach, the motor spins continuously until paper is released or withdrawn.- Motor inhibit with lid open: The auto-feed motor does not start when the lid is open, even if front sensors are covered. Safety interlock. Manually covering all three sensors with lid open still triggers1a 06 89(the event is sensor-based, not motor-based).- AND logic on front sensors: Both front sensors must detect paper before the motor starts. Both lid buttons must be depressed before lid-closed fires. No individual left/right bits.- Lid-close snapshot: Closing the lid always triggers a paper state report in the same notification batch.- Paper sensor unreliable with lid open: Reports paper-present even when no paper is loaded (front sensor false positive). Always query lid state before trusting paper state.Paper events suppressed while lid is open: Removing paper while the lid is open may not fire a
1a 06 88event immediately. The paper-absent event appears later, batched with the lid-closed event when the lid is subsequently closed.Spurious lid events during paper pull: Forcefully pulling paper out mid-print can trigger a
1a 05 98(lid-closed) event even though the lid was not physically touched — likely due to mechanical coupling between the paper tension and the lid switch mechanism. Drivers should not assume a lid event during printing reflects actual lid state.
Paper Feed and Transport#
Command |
Bytes |
Behaviour |
|---|---|---|
ESC d |
|
Feed |
ESC J |
|
Feed |
GS V 1 |
|
Cut command. Non-functional — no cutter hardware. |
|
|
Line feed. Triggers ~5mm paper advance per byte. Works standalone outside raster data. |
Paper Eject#
To eject a sheet, keep a couple of short feeds queued so the motor runs without pauses, and stop on the printer’s own paper-absent event:
1f 11 11 # → 1a 06 88 absent, or no answer: a few feeds, done
1b 64 78 ×2 # two 15mm feeds queued
loop:
wait ≤0.7 s for 1a 06 88 # trailing edge passed the rear sensor
no event: 1b 64 78 # keep the queue two deep
1b 64 a0 # 20mm clears the rollers
The feed runs at about 21 mm/s (a full A4 sheet cleared the rear sensor in 13.9 s), so 0.7 s per 15 mm section keeps the queue at depth two without piling feeds up; what is still queued when the sensor clears is the only travel beyond the clearance. Commands execute in order, so a status query behind a feed is answered once the feed has run (usable as a completion signal). No answer means the overrun error state (see Print Completion Signal), in which feed commands are also ignored until the printer recovers.#
Density and Print Quality#
ESC N 04 — Concentration (functional)#
The only working density command on the M08F.
Bytes |
Description |
|---|---|
|
Set print concentration. Persistent until ESC @ reset or another ESC N 04 call. |
Value range |
Visual density |
|---|---|
0–4 |
Light |
5 |
Medium |
6+ |
Dark (saturated, no further increase) |
Tested density mappings: low=1, medium=5, high=6.
Must be preceded by ESC @ or embedded in a larger write to avoid bridge chip interception.
ESC 7 — Heat Settings (non-functional)#
1b 37 <max_dots> <heat_time> <heat_interval> — standard ESC/POS heat command.
Has no visible effect on the M08F. Tested heat_time values 40–200 with no
change in print density.
ESC E — Emphasis (non-functional)#
1b 45 <n> — standard ESC/POS bold mode. Has no visible effect on the M08F.
Device Configuration#
Auto-Off Timer#
Operation |
Command |
Notes |
|---|---|---|
Query |
|
|
Set |
|
Must be preceded by ESC @ |
Value 00 = disabled. Non-zero values set the auto-off timeout in 5-minute
increments (value 01 = 5 min, value 02 = 10 min, etc.). Timer counts
from last activity.
An open BLE connection without traffic does not count as activity. With the timer at 10 minutes, a link left idle after connecting was dropped by the printer switching itself off about 9.5 minutes later; with the timer at 5 minutes, after 5.1 minutes. Holding a connection open therefore does not keep the printer awake; a host that keeps the link between jobs sees it end at the auto-off.
Queries do count as activity, each of them on its own: with the timer at 5
minutes, a link carrying only the battery query (1f 11 08), only the lid
query (1f 11 12) or only the paper query (1f 11 11) every 30 seconds was
still up after 8 minutes, and periodic lid, paper and battery queries held a
link for 14.6 minutes against a 10-minute timer. A host that polls the
printer keeps it on for as long as it polls.
The timer setting survives a power cycle: set to 5 minutes, read back as 5 after the printer was switched off and on again.
(Measured 2026-11-04.)
BLE Bridge Chip (AT Interface)#
Service ff80, write to ff82, responses on ff81.
Command Format#
AT+COMMAND?\r\n → \r\n+COMMAND:VALUE\r\n\r\nOK\r\n
or \r\nERROR\r\n
Query form (? suffix) is required. Plain AT and action forms return ERROR.
Both \r and \r\n terminators are accepted.
Known Commands#
Command |
Response |
Notes |
|---|---|---|
|
|
BLE device name |
|
|
Internal UART baud rate |
All others |
|
Comprehensive sweep of HM-10 and JDY command sets — all return ERROR. Custom/locked firmware. |
OTA Firmware Update Mode#
1f 11 0f triggers an immediate reboot into OTA (Over-The-Air firmware
update) mode.
Behaviour |
Detail |
|---|---|
Response |
|
New BLE name |
|
New MAC prefix |
|
Recovery |
Power cycle (long-hold off, then on) returns to normal mode |
The firmware update mode has not been interrogated further at time of writing. I suspect the device likely exposes different BLE services not present during normal operation when in OTA mode. This might enable ways to access more device data, dump vendor firmware, or flash custom firmware.
Non-Functional ESC/POS Commands#
Commands tested and observed to have no effect on the M08F:
Command |
Bytes |
Standard purpose |
M08F behaviour |
|---|---|---|---|
ESC 7 |
|
Heat/density settings |
No visible effect |
ESC a |
|
Text alignment |
No effect on raster images |
ESC E |
|
Bold/emphasis |
No visible effect |
ESC J |
|
Feed (dots) |
Non-functional standalone |
GS V |
|
Paper cut |
No effect (no cutter) |
GS I |
|
Printer info queries |
Returns generic |
DLE EOT |
|
Status queries |
Returns |
GS r |
|
Paper sensor status |
Returns |
Text mode |
ASCII chars |
Print text with built-in font |
No built-in font — chars ignored, only |
NAK |
|
Alt. concentration (M02/M02S) |
No effect except |
Other BLE Services#
The following services produced no clear or interpretable responses when probed with ESC/POS commands, AT commands, and raw bytes. Some channels occasionally produced data (noted per-entry), but nothing that could be reliably attributed to a specific function. They may serve purposes such as firmware updates, or features that require specific activation sequences.
Service 0000ff10: Unknown (possibly firmware update)#
UUID |
Properties |
Handle |
Status |
|---|---|---|---|
|
write-without-response, notify |
10 |
Silent — accepts writes, no response |
|
write-without-response, notify |
13 |
Silent — accepts writes, no response |
Service 000018f0: contains 2af0 (“Electric Current Specification”)#
2af0 is a standardised Bluetooth GATT UUID that the BLE spec names “Electric
Current Specification”. This label is assigned by the Bluetooth SIG, not by
Phomemo — whether it reflects the intended purpose or is a coincidental UUID
choice is unknown. No charging or current-related data was observed.
UUID |
Properties |
Handle |
Status |
|---|---|---|---|
|
write-without-response, write |
31 |
Silent |
|
notify |
33 |
Fires |
Service 0000fee7: Tencent BLE SDK#
UUID fee7 is assigned to “Tencent Holdings Limited” in the Bluetooth SIG’s
16-bit UUID registry. This is the Tencent LLSync IoT BLE SDK,
used for device registration, cloud pairing, and WeChat integration via the
Tencent “连连” (LianLian) IoT app. The SDK is open source with protocol
documentation (primarily in Chinese).
The fec7/fec8 pair implements a request/response protocol: command packets
written to fec7 produce indication responses on fec8. The fec8 packet
observed on connect is the device’s identity handshake — a protobuf-encoded
message containing protocol version, device capabilities, and BLE MAC address:
fe 01 00 1a 27 11 00 01 0a 00 18 84 80 04 20 01 28 02 3a 06 60 6e 41 23 0b d6
Bytes 1a, 27, 3a are valid protobuf field tags. The final 6 bytes
(60 6e 41 23 0b d6) match the device’s BLE MAC address.
Interacting with this channel further would require implementing the LLSync
handshake sequence, which is specific to Phomemo’s app-level registration flow
and not related to print functionality.
UUID |
Properties |
Handle |
Status |
|---|---|---|---|
|
write |
17 |
Silent — no response observed to probe writes |
|
indicate |
19 |
Fires a 26-byte structured packet on connect (last 6 bytes = BLE MAC address). Not required for printing. |
|
read |
22 |
Empty string |
Service 49535343-fe7d-...: ISSC BLE UART#
The UUID prefix 49535343 is ASCII for “ISSC” — Integrated System Solution
Corporation, a Taiwanese semiconductor company now part of Microchip Technology.
ISSC produced BLE chips with a built-in transparent UART-over-BLE service: write
bytes to one characteristic, receive bytes on another. This UUID profile is
widely reused across cheap BLE modules, either because they use actual
ISSC/Microchip silicon or because they cloned the service for app compatibility.
Its presence on the M08F suggests the BLE bridge chip may be based on
ISSC/Microchip silicon, or the firmware includes this profile alongside the
primary ff00 print service. The 1e4d notify characteristic fired a delayed
paper state event on connect in one test, hinting it may act as a secondary
data relay — but this was not consistently reproducible.
UUID |
Properties |
Handle |
Status |
|---|---|---|---|
|
write |
2 |
Silent |
|
write-without-response, write |
4 |
Silent |
|
notify |
6 |
Fired |
Service e7810a71-...: Unknown#
UUID |
Properties |
Handle |
Status |
|---|---|---|---|
|
write-without-response, write, notify |
37 |
Silent |
Physical Characteristics#
Property |
Value |
|---|---|
Print technology |
Direct thermal |
Print resolution |
203 DPI |
Print width |
1680 px = 210 bytes/row (A4 variant; Phomemo also sells a Letter variant with a 216 mm head, not tested here) |
Paper feed |
Manual single-sheet insertion (no tray) |
Paper sensors (front) |
2 optical sensors at paper entry — AND logic, both must detect paper. Triggers auto-feed motor. No direct BLE event. |
Paper sensor (rear) |
1 optical sensor behind print head, center. Detects paper fully loaded. Triggers |
Lid sensors |
2 push buttons (AND logic — both must be depressed) |
Connectivity |
BLE (primary), USB (charging) |
Power button |
Long hold (~3s) = power off. No BLE events from button presses. |
LED indicator |
Red/green. Not controllable via BLE. Indicates print-ready state: green when lid is closed AND paper is present; red otherwise. |
Charging |
USB. Charge state not reported over BLE. |
Sheet Geometry (A4)#
Property |
Value |
|---|---|
Top margin |
~10 mm from the leading paper edge to raster row 0 (sensor-to-head distance, hardware) |
Bottom edge |
Row 2300 of a full-width raster lands ~1 mm above the bottom edge |
Overrun |
Raster past the sheet end leaves the printer muted (see Print Completion); the driver keeps a 5 mm bottom margin (A4: 2253 rows after the 10 mm lead) |
Sheet insertion |
|
Verified photo job |
1680x2178 raster at high density: 457 KB in 79.6 s (5.6 KiB/s), clean, 10 mm margins |
Row pitch |
100 rows = 12.5 mm at 203 dpi (measured: 12–12.5 mm) |
Oversize raster |
Paper runs out mid-job, see Print Completion Signal |
Driver Implementation Notes#
Below are recommendations for implementing a printer driver or script.
Minimum Viable Print Sequence#
# 1. Connect to ff02 write characteristic
# 2. Build and send:
init = b"\x1b\x40" # ESC @
density = b"\x1b\x4e\x04\x05" # ESC N 04 05 (medium)
header = b"\x1d\x76\x30\x00" + width_le + height_le # GS v 0
data = bitmap_bytes # 1-bit raster
feed = b"\x1b\x64\x14" # ESC d 20
payload = init + density + header + data + feed
# 3. Chunk into 244-byte segments, write each to ff02 with response=True
# 4. Listen for 1a 0f 0c on ff01 = print complete
Key Parameters#
Parameter |
Tested value |
|---|---|
Chunk size |
244 bytes (MTU - 3) |
Write mode |
Write Request ( |
Inter-chunk delay |
none (ATT response paces sender) |
Band height |
256 rows per GS v 0 command |
Print-complete signal |
|
Density (low) |
|
Density (medium) |
|
Density (high) |
|
Paper eject |
|
Printable A4 rows |
2253 (10 mm top, 5 mm bottom) |
Pre-Print State Check#
Before printing, query device state to avoid errors:
# 1. Query lid state
write(ff02, b"\x1f\x11\x12") # → expect 1a 05 98 (closed)
# 2. Query paper state (only if lid closed!)
write(ff02, b"\x1f\x11\x11") # → expect 1a 06 89 (paper present)
# 3. Query battery
write(ff02, b"\x1f\x11\x08") # → 1a 04 <percent>
Paper state is unreliable when lid is open — always check lid first.
Short Sheet Detection#
Two signals tell a driver that the inserted sheet is shorter than the page:
1a 06 88arriving while a large part of the raster is still unsent. The event normally fires ~80 rows before the end of a page that fits (the sensor sits ahead of the head), so only an early event means a short sheet; thephomemolibrary uses “more than 25 % of rows unsent”.No answer to a
1f 11 XXquery after the motor stop. The overrun error state mutes queries; a reconnect clears it.
A5 on the M08F#
A5 is fed long edge first (210 mm wide, 148 mm long). A client renders A5 portrait (148 x 210); the driver rotates the raster a quarter turn counter-clockwise, so the document’s top edge ends up at the sheet’s left edge. 1062 rows are printable after the 10 mm lead and 5 mm bottom margin.
Error Detection During Print#
DLE EOT queries always return 01 01 regardless of state. Monitor ff01 for
spontaneous events during print instead:
Event |
Meaning |
Action |
|---|---|---|
|
Lid opened |
Pause/abort — print head exposed |
|
Paper absent |
Paper ran out or was pulled out |
|
Motor stopped |
Print complete (or aborted) |
Printer Application#
The repository’s printer application splits the
driver in two: a PAPPL application in C speaks IPP and raster to the
network and hands each page as 8-bit grayscale rows to phomemo-bridge
over a unix socket; the bridge (Python, phomemo.bridge) owns the BLE link
and applies everything above: the pre-print state check, continuous
write-with-response streaming, short sheet and lid detection, and the
sensor-driven eject.