0%
[/]JOSHIDEAS
SYSTEM_BLOG
TIME:13:33:20
STATUS:ONLINE
~/blog/brother-raster-protocol-label-printing
$cat brother-raster-protocol-label-printing.md_

Brother Raster Protocol: Low-Level Label Printing

#Printing#Protocol#Node.js

# Brother Raster Protocol: Low-Level Label Printing

Thermal label printers speak a binary language. This guide demystifies the Brother raster protocol—ESC commands, bitmap generation, and direct TCP socket communication.

## Protocol Fundamentals

Brother printers accept raw binary data over TCP port 9100. The protocol consists of:

  1. >Initialization commands - Reset and configure printer
  2. >Media information - Label size and type
  3. >Raster data - 1-bit bitmap rows
  4. >Termination - End print job
┌────────────────────────────────────────┐
│           Brother Printer              │
│                                        │
│   ┌────────────────────────────────┐   │
│   │     TCP Socket (Port 9100)     │   │
│   └────────────────────────────────┘   │
│              ▲                         │
│              │                         │
│   ┌──────────┴──────────┐              │
│   │  Command Interpreter │              │
│   └─────────────────────┘              │
│              │                         │
│   ┌──────────▼──────────┐              │
│   │   Print Head         │              │
│   └─────────────────────┘              │
└────────────────────────────────────────┘

## Initialization Sequence

Every print job starts with these commands:

javascript
// ESC @ - Initialize printer const INIT = Buffer.from([0x1B, 0x40]); // ESC i a - Set raster mode const RASTER_MODE = Buffer.from([0x1B, 0x69, 0x61, 0x01]); // ESC i z - Media information function mediaInfo(width, height, continuous = false) { return Buffer.from([ 0x1B, 0x69, 0x7A, continuous ? 0x0A : 0x02, // Media type width & 0xFF, // Width (low byte) (width >> 8) & 0xFF, // Width (high byte) height & 0xFF, // Height (low byte) (height >> 8) & 0xFF, // Height (high byte) 0x00, 0x00 // Page number ]); }

## Command Reference

CommandBytesDescription
ESC @1B 40Initialize
ESC i a1B 69 61 01Raster mode
ESC i z1B 69 7A ...Media info
ESC i M1B 69 4D 40Auto-cut mode
ESC i K1B 69 4B 08Cut each label
g67 00 NN ...Raster line
Z5AZero line (blank)
0x1A1AEnd job

## 1-Bit Bitmap Generation

Labels use monochrome bitmaps—each pixel is one bit:

javascript
const sharp = require('sharp'); async function imageToRaster(imagePath, targetWidth) { // Load and resize image const image = await sharp(imagePath) .resize(targetWidth) .grayscale() .raw() .toBuffer({ resolveWithObject: true }); const { data, info } = image; const { width, height } = info; // Convert to 1-bit with threshold const threshold = 128; const bytesPerRow = Math.ceil(width / 8); const rasterData = []; for (let y = 0; y < height; y++) { const row = Buffer.alloc(bytesPerRow); for (let x = 0; x < width; x++) { const pixel = data[y * width + x]; const byteIndex = Math.floor(x / 8); const bitIndex = 7 - (x % 8); // Black pixel = 1, White = 0 if (pixel < threshold) { row[byteIndex] |= (1 << bitIndex); } } rasterData.push(row); } return { rasterData, width, height }; }

## Sending Raster Lines

Each raster line has a header followed by bitmap data:

javascript
function createRasterLine(rowData) { // g command + length bytes + data const length = rowData.length; return Buffer.concat([ Buffer.from([0x67, 0x00, length]), // g 00 NN rowData ]); } function createBlankLine() { // Z command for empty line return Buffer.from([0x5A]); } function buildPrintJob(rasterData) { const commands = []; for (const row of rasterData) { // Check if row is all zeros const isBlank = row.every(byte => byte === 0); if (isBlank) { commands.push(createBlankLine()); } else { commands.push(createRasterLine(row)); } } return Buffer.concat(commands); }

## TCP Socket Communication

Direct socket connection for reliable printing:

javascript
const net = require('net'); class BrotherPrinter { constructor(ip, port = 9100) { this.ip = ip; this.port = port; } async print(labelBuffer) { return new Promise((resolve, reject) => { const socket = new net.Socket(); const timeout = setTimeout(() => { socket.destroy(); reject(new Error('Print timeout')); }, 30000); socket.connect(this.port, this.ip, () => { socket.write(labelBuffer, () => { clearTimeout(timeout); socket.end(); }); }); socket.on('close', () => resolve()); socket.on('error', (err) => { clearTimeout(timeout); reject(err); }); }); } }

## Complete Print Function

Putting it all together:

javascript
async function printLabel(printerIp, imagePath, labelWidth = 300) { const printer = new BrotherPrinter(printerIp); // Generate raster data const { rasterData, width, height } = await imageToRaster(imagePath, labelWidth); // Build command sequence const commands = Buffer.concat([ // Initialize Buffer.from([0x1B, 0x40]), // Raster mode Buffer.from([0x1B, 0x69, 0x61, 0x01]), // Media info mediaInfo(width, height), // Auto-cut after each label Buffer.from([0x1B, 0x69, 0x4D, 0x40]), Buffer.from([0x1B, 0x69, 0x4B, 0x08]), // Margins Buffer.from([0x1B, 0x69, 0x64, 0x00, 0x00]), // Compression off Buffer.from([0x4D, 0x00]), // Raster data buildPrintJob(rasterData), // End job Buffer.from([0x1A]) ]); await printer.print(commands); }

## Debugging Tips

"When prints look wrong, the problem is almost always in the raster byte order."

Common issues:

  1. >Mirror image - Bit order reversed in each byte
  2. >Stretched/compressed - Wrong bytes-per-row calculation
  3. >Partial print - Missing termination command
  4. >No output - Wrong media info for label size
javascript
// Debug helper: visualize raster data in console function visualizeRaster(rasterData) { for (const row of rasterData.slice(0, 20)) { let line = ''; for (const byte of row) { for (let bit = 7; bit >= 0; bit--) { line += (byte & (1 << bit)) ? '█' : ' '; } } console.log(line); } }

## Conclusion

The Brother raster protocol is straightforward once you understand the binary format. Direct TCP communication bypasses driver issues and provides complete control over the printing process.

This approach enables custom label generation, barcode printing, and integration with warehouse management systems without proprietary SDKs.