PrintMaster BLE
A robust, brand-agnostic Flutter library for Zhuhai Quin based thermal printers.
Formerly known as marklife_label_printer, this library communicates directly with the firmware protocol used by millions of portable thermal printers sold under different brands.
| Brand | Models | Status |
|---|---|---|
| Marklife | P11, P12, P15, P50, P50S | ✅ Verified |
| Phomemo | D30, D35, Q30, M02 | ✅ Compatible |
| Vretti | HP3, HP4 | ✅ Compatible |
| Niimbot | D11, B21 | ❌ Incompatible (DRM/RFID) |
Features
- Genuine Protocol implementation: Uses the native 0x1F command set.
- High-Quality Image Processing:
- Floyd-Steinberg Dithering: Converts color/grayscale images to 1-bit monochrome using error diffusion for superior print quality.
- Luminance Conversion: ITU-R BT.601 standard formula (0.299R + 0.587G + 0.114B) for accurate grayscale representation.
- Error Diffusion: Distributes quantization error to 4 neighboring pixels (7/16 right, 3/16 bottom-left, 5/16 bottom, 1/16 bottom-right).
- Bit Packing: Optimized bitmap encoding (White=0, Black=1) for thermal printer heat control.
- Multi-Platform Support:
- ✅ Android: Fully tested and verified
- ✅ macOS: Fully tested and verified
- ⚠️ Web: Experimental (connects but printing not yet working)
- 🔄 iOS: Expected to work (not yet tested)
- Fast: Optimized for BLE packet chunking and flow control (200 bytes/chunk).
- Smart Compression:
- ZLIB (RFC 1950) with manual header construction for 1KB window compatibility.
- Level 0 (Stored) compression to reduce printer CPU load.
- Adler-32 checksum validation.
- Smart Discovery: Handles Service UUID obfuscation on macOS and Web.
Known Issues
- Long Images: Very tall images (> 500-1000px height) may fail to print or print partially due to printer buffer limitations.
- Recommendation: Split long content into multiple smaller print jobs or wait for the upcoming "Image Slicing" feature.
Installation
Add this to your pubspec.yaml:
dependencies:
print_master_ble: ^0.1.0 # Check pub.dev for latest version
flutter_blue_plus: ^2.0.2
image: ^4.6.0
Usage
1. Device Discovery & Connection
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
// Start scanning for devices
await FlutterBluePlus.startScan(timeout: Duration(seconds: 4));
// Listen to scan results
FlutterBluePlus.scanResults.listen((results) {
for (ScanResult r in results) {
// Filter by device name
if (r.device.platformName.contains('P50') ||
r.device.platformName.contains('Marklife')) {
// Found a compatible printer
print('Found: ${r.device.platformName}');
}
}
});
// Stop scanning
await FlutterBluePlus.stopScan();
2. Connect and Send Data
You'll need to implement a Bluetooth handler to manage the connection and data transmission. See the complete example in examples/lib/logic/bluetooth_handler.dart which includes:
- Service and characteristic discovery
- Flow control management (Protocol 0x01)
- Data chunking (200 bytes)
- Progress tracking
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
// Connect to device
await device.connect(license: License.free);
// Discover services
List<BluetoothService> services = await device.discoverServices();
// Find printer service (UUID: 0xFF00 or alternatives)
BluetoothService? printerService = services.firstWhere(
(s) => s.uuid.toString().toLowerCase().contains('ff00'),
);
// Find write characteristic (UUID: 0xFF02)
BluetoothCharacteristic? writeChar = printerService.characteristics.firstWhere(
(c) => c.uuid.toString().toLowerCase().contains('ff02'),
);
3. Print an Image
import 'dart:typed_data';
import 'package:image/image.dart' as img;
import 'package:print_master_ble/print_master_ble.dart';
// Load and decode your image
Uint8List imageBytes = ...; // From file, network, etc.
img.Image? image = img.decodeImage(imageBytes);
if (image != null) {
// Generate complete print payload (includes dithering + compression)
Uint8List payload = await PrintPort.generatePrintPayload(
image,
printerWidth: 384, // P50S width
alignment: PrintAlignment.center,
);
// Send to printer via your Bluetooth handler
// (See examples/lib/logic/bluetooth_handler.dart for complete implementation)
await bluetoothHandler.sendDataSerial(payload);
}
4. Two-Phase Printing (Advanced)
For better control, you can split the print job into header+image and footer:
// Phase 1: Send header + image data
Uint8List headerAndImage = await PrintPort.generatePrintHeaderAndImage(
image,
printerWidth: 384,
alignment: PrintAlignment.center,
);
await bluetoothHandler.sendDataSerial(headerAndImage);
// Phase 2: Wait for printer, then send footer (stop + feed)
await Future.delayed(Duration(seconds: 2));
Uint8List footer = PrintPort.generatePrintFooter();
await bluetoothHandler.sendDataSerial(footer);
5. Complete Example
For a full working example with UI, see the examples/ directory. It includes:
- Bluetooth device scanning and filtering
- Connection management with flow control
- Image picker integration
- Real-time print progress tracking
- Terminal-style logging UI
Run the example:
cd examples
fvm flutter run
Compatibility Note
This library is designed for printers that communicate using the Print Master or Phomemo app protocols. If your printer requires the "Niimbot" app, this library will NOT work due to RFID encryption.
License
MIT