Skip to content

marklife-l11 — session frame with uncompressed raster

The wire protocol of the narrow-tape chassis: P12, P15, P15R, A1, R15, LP15, LP90 and the P12 whitelabels (LP25, BARABOGO P12). A handful of fixed byte strings open and close a print session, and the raster travels uncompressed behind an ESC/POS-shaped GS v 0 header. It is the smallest protocol in the catalogue and the only one with two units confirmed behind it.

Provenance

Bench-confirmed: the P12 and P15 print this stream over USB, Bluetooth SPP and BLE (through the harness, 2026-09-13). Both ack a job with a single 0xAA. The job layout agrees with the public prior art at tomLadder/thermoprint (REVERSE_ENGINEERING.md § 3.2), which documents the same family independently.

Inferred: the head width of every chassis except the P12 (96 dots, 12 bytes per row, from a BLE HCI capture); the P15 is assumed equal because the two share tape stock. The 10 FF 10 00 density variant thermoprint documents for the P15 family is not implemented.

Opcode vocabulary

OpcodeBytesDescription
Wakeup00 × 15Fifteen NULs before the enable.
Enable10 FF F1 02Open the print session.
Density1F 70 02 ddSet density; dd is 1 light, 2 normal, 3 dark. Opt-in — see below.
Raster1D 76 30 qq wL wH hL hHGS v 0: quality qq (0..3, always 0 here), width in bytes and height in dots, little-endian 16. wBytes × h raw rows follow.
Feed1B 4A nnESC J — feed nn dots. Continuous stock, nn = 100.
Position to gap1D 0CAdvance to the next label edge. Die-cut and gap stock.
Stop10 FF F1 45Close the print session.

Job byte stream

[density]      1F 70 02 dd        — only when the caller asks for one
[wakeup]       00 × 15
[enable]       10 FF F1 02
[raster]       1D 76 30 00 wL wH hL hH  +  wBytes × h raw rows
[advance]      1B 4A 64            (continuous)   /   1D 0C   (die-cut, gap)
[stop]         10 FF F1 45

A P12 job for 15 mm continuous tape at 96 dots wide is therefore 00 × 15 · 10 FF F1 02 · 1D 76 30 00 0C 00 hh hh · <12 × h bytes> · 1B 4A 64 · 10 FF F1 45.

Raster

widthBytes = ceil(widthPx / 8), rows are MSB-first with 1 = dark, and there is no length field: the chassis reads exactly widthBytes × heightDots bytes after the header. That is LabelBitmap.data verbatim, so the encoder performs no repacking.

Density is opt-in

A captured P12 job carries no density command; the chassis prints at its stored density. The encoder follows that: 1F 70 02 dd is emitted only when print() is called with density or densityLevel, and every diagnostic print goes out without one.

thermoprint records a second density form for the P15 family, 10 FF 10 00 tt, in place of 1F 70 02. It is not implemented; an explicit-density job on a P15, P15R, LP15 or A1 sends the 1F 70 02 form and the effect is unverified.

L11 is short and uncompressed, and the P12 has tolerated every packet size and pacing it has been given. The family-wide pacing still applies (95 bytes per packet on the P15, 90 on the P12, 30 ms between packets): the chassis are on the same BLE stack as the S2, which drops oversized packets, and a job that spans several packets is the normal case on a label of any length. See Link layer.

Status

The only reply observed is 0xAA after an accepted job. No query is wired.