DUPLICATED FROM https://github.com/jeremy46231/MXW01-catprinter
This is a questionably-accurate and probably incomplete document describing the MXW01's BLE protocol. Please help update it if you have more information!
When it is on, the printer exposes one BLE service with three characteristics.
- Main service:
0000ae30-0000-1000-8000-00805f9b34fb1AE01: Control characteristic (0000ae01-0000-1000-8000-00805f9b34fb)- Type: Write without response
- Purpose: Sending control commands (Status Request, Print Request, Set Intensity, Flush, etc.).
AE02: Notify characteristic (0000ae02-0000-1000-8000-00805f9b34fb)- Type: Notify
- Purpose: Receiving status responses, acknowledgments, and print completion notifications from the printer.
AE03: Data characteristic (0000ae03-0000-1000-8000-00805f9b34fb)- Type: Write without response
- Purpose: Sending bulk image data to the printer.
All messages sent to the printer begin with a 0x22 0x21 preamble.
Commands sent to the printer via the Control Characteristic (AE01) follow
this structure:
| Field | Length (Bytes) | Value | Description |
|---|---|---|---|
| Preamble | 2 | 0x22 0x21 |
|
| Command ID | 1 | Command ID | See the Command Reference |
| Fixed (unknown) | 1 | 0x00 |
Appears fixed, unknown purpose |
| Length (LE) | 2 | 0x0000 - 0xFFFF |
Length of the Payload field, Little Endian |
| Payload | Variable | Command-specific | See the Command Reference |
| CRC8 | 1 | 0x00 - 0xFF |
CRC checksum (see below) |
| Footer | 1 | 0xFF |
- Algorithm: CRC-8 / DALLAS-MAXIM
- Parameters:
- Polynomial:
0x07(x^8 + x^2 + x^1 + x^0) - Initial Value:
0x00 - Reflect Input:
False - Reflect Output:
False - XOR Output:
0x00
- Polynomial:
- Scope: Calculated only over the
Payloaddata field.
Notifications received from the printer via the Notify Characteristic (AE02)
generally follow the same structure as commands:
| Field | Length (Bytes) | Value | Description |
|---|---|---|---|
| Preamble | 2 | 0x22 0x21 |
|
| Command ID | 1 | Command ID | See the Command Reference |
| Unknown | 1 | Variable byte | Unknown. Observed as non-zero (eg, 0x03) in some A1 responses |
| Length (LE) | 2 | 0x0000 - 0xFFFF |
Length of the Payload field, Little Endian |
| Payload | Variable | Response-specific data | See the Command Reference |
| Footer | 1 | 0xFF |
| ID (Hex) | Name | Direction | Payload | Description |
|---|---|---|---|---|
| Printing & Core Status | ||||
A1 |
Get Status | Send | 0x00 |
Request printer status (paper, temp, battery, state) |
A1 |
Status Response | Receive | See below | Response to Get Status request |
A2 |
Set Print Intensity | Send | Intensity, 0x00-0xFF |
Set printing darkness (0x5D is a good choice) |
A9 |
Print Request | Send | line_count_le(2), 0x30, 0x01 |
Initiate print: total lines, fixed 0x30, mode (0=1bpp)2 |
A9 |
Print Response | Receive | Status byte (0x00 = OK) |
Acknowledgment for the Print Request. Wait for this |
AD |
Print Data Flush | Send | 0x00 |
Signal end of image data transfer via AE03 |
AA |
Print Complete | Receive | Unknown | Physical print process finished (unknown payload) |
| Optional Status/Info | ||||
AB |
Get Battery Level | Send | 0x00 |
Request battery level |
AB |
Battery Level Response | Receive | Battery byte | Response containing battery level |
B1 |
Get Version | Send | 0x00 |
Request firmware version / printer type |
B1 |
Version Response | Receive | version_utf8(N), unknown, type byte |
Response with version string and type code |
B0 |
Get Print Type | Send | 0x00 |
Request printer type information |
B0 |
Print Type Response | Receive | Type byte (e.g., 0x01, 0xFF, 0x31) |
Response with type code |
A7 |
Query Count | Send | 0x00? |
Unknown |
A7 |
Query Count Response | Receive | Unknown (Often FF FF...) |
Response to Query Count. Unknown |
| Optional Control | ||||
AC |
Cancel Print | Send | 0x00? |
Attempt to cancel ongoing print job |
| Unknown | ||||
A3 |
Unknown | ? | ? | Purpose unknown |
AE |
Unknown | ? | ? | Purpose unknown |
B2 |
Unknown | ? | ? | Purpose unknown ("learn"?) |
B3 |
Unknown | ? | ? | Purpose unknown ("sign/encryption"?) |
A1 Status Payload: TODO: more precisely describe this
Payload[6]: Status code (0=Standby, 1=Printing, etc.) ifPayload[12]is OKPayload[9]: Battery level (approx)Payload[10]: Temperature (approx)Payload[12]: Overall Status Flag (0 = OK, Non-zero = Error)Payload[13](if Flag!=0): Error Code (1/9=No Paper, 4=Overheated, 8=Low Battery). Requires length check
This is a standard sequence that you can implement to communicate with the printer.
- Connect: Establish BLE connection to the printer
- Discover: Find the Main Service and the Control (
AE01), Notify (AE02), and Data (AE03) characteristics - Start Notifications: Enable notifications on the Notify characteristic
(
AE02) - (Optional) Set Intensity: Send
A2command with intensity byte toAE01 - Check Status:
- Send
A1command ([0x00]payload) toAE01 - Wait for
A1notification onAE02 - Parse the notification payload to ensure the printer is ready (Status Flag OK, no paper error, etc.). Abort if not ready
- Send
- Send Print Request:
- Calculate
line_count(total height in pixels of the image data buffer) - Send
A9command ([line_count_le(2), 0x30, 0x00]payload for 1bpp) toAE01 - Wait for
A9notification onAE02 - Check the response payload (first byte should be
0x00). Abort if request rejected
- Calculate
- Transfer Image Data:
- Send the prepared (and potentially padded) image byte buffer to the Data
Characteristic (
AE03) - Send data in chunks with a small delay (
~0.01s - 0.05s) between chunks to avoid overwhelming the printer buffer
- Send the prepared (and potentially padded) image byte buffer to the Data
Characteristic (
- Flush Data:
- Send
ADcommand ([0x00]payload) toAE01after the last data chunk has been sent
- Send
- Wait for Completion:
- Wait for the
AAnotification onAE02, indicating the printer has finished the physical print
- Wait for the
- Disconnect: Stop notifications and disconnect from the printer
Pretty simple, pack the bits into bytes, left to right, top to bottom, black is 1, white is 0, pad with zeroes to 4320 bytes minimum.
- Input: A 1-bit image (black = 1, white = 0), 384 pixels wide
- Byte Packing: Pixels are processed row by row. Each row of 384 pixels is
converted into 48 bytes (
384 / 8 = 48). - Bit Order: Within each byte, the least significant bit (bit 0) corresponds to the leftmost pixel of the 8-pixel group. The most significant mit (bit 7) corresponds to the rightmost pixel.
- Padding: The final concatenated byte buffer containing all image rows
might need padding with
0x00bytes at the end if its total length is less than a minimum threshold (probably 4320 bytes / 90 lines).
Footnotes
-
A comment in rbaron/catprinter suggests that the main
0000ae30-...service sometimes shows up as0000af30-...instead, particularly on Macs, though that repo is not for the MXW01. ↩ -
I think there are other print modes, like the "HD" option in the official app, which I think might send 4 bits per pixel instead, but I'm not sure. ↩