A Swift package for communicating with Phomemo T02 thermal printers via Bluetooth Low Energy.
- ✅ Complete T02 Protocol Implementation - ESC/POS command generation
- ✅ Bluetooth Low Energy Support - CoreBluetooth integration for macOS/iOS
- ✅ Automatic Image Processing - Converts any image to T02 format
- ✅ Type-Safe API - Swift enums for commands and settings
- ✅ Cross-Platform - Works on macOS 13+ and iOS 16+
- ✅ Comprehensive Tests - Full test suite with Swift Testing
- ✅ Command-Line Tool - Included for testing and standalone use
Via Xcode:
- File → Add Package Dependencies...
- Enter:
https://github.com/jpurnell/T02Protocol - Select version and add to target
Via Package.swift:
dependencies: [
.package(url: "https://github.com/jpurnell/T02Protocol", from: "0.0.1")
]import T02Protocol
import UIKit
// Create protocol instance
let protocol = T02Protocol()
// Load your image
let image = UIImage(named: "label")!
// Generate print data
let printData = try protocol.generatePrintData(from: image, feedLines: 4)
// Send to printer via Bluetooth
yourBluetoothManager.send(data: printData)The package includes a ready-to-use printing tool:
# Print an image
swift run T02PrintTool bt image.png
# Create test image
swift run T02PrintTool create test.png
# Debug protocol output
swift run T02PrintTool debug image.pngNote: Printer must be powered on within last 30 seconds (BLE advertising period).
let protocol = T02Protocol()
// Complete print job
let data = try protocol.generatePrintData(from: image, feedLines: 4)
// Custom feed lines (0-255)
let data = try protocol.generatePrintData(from: image, feedLines: 10)// Initialize printer
let initCmd = protocol.cmdInitPrinter()
// Set alignment
let centerCmd = protocol.cmdSetJustification(.center)
let leftCmd = protocol.cmdSetJustification(.left)
let rightCmd = protocol.cmdSetJustification(.right)
// Feed paper
let feedCmd = try protocol.cmdFeedLines(4)
// Raster image header
let header = protocol.cmdRasterHeader(
widthBytes: 48,
lines: 200,
mode: .normal
)// Convert image to T02 format (384px wide, 1-bit monochrome)
let converted = try protocol.convertImage(yourImage)T02Protocol.widthDots // 384 (50mm at 203 DPI)
T02Protocol.widthBytes // 48 (384 / 8)
T02Protocol.dpi // 203
T02Protocol.maxLinesPerBlock // 255 (protocol limit)
T02Protocol.defaultFeedLines // 4enum Justification {
case left
case center
case right
}enum PrintMode {
case normal // 1x size
case doubleWidth // 2x width
case doubleHeight // 2x height
case quadruple // 4x size
}import T02Protocol
import CoreBluetooth
import AppKit
class PrinterManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
let protocol = T02Protocol()
var centralManager: CBCentralManager!
func print(image: NSImage) {
// Generate print data
let printData = try! protocol.generatePrintData(from: image)
// Connect and send via CoreBluetooth
// ... BLE implementation ...
}
}import T02Protocol
import UIKit
class PhotoPrinter {
let protocol = T02Protocol()
func print(photo: UIImage) {
do {
let printData = try protocol.generatePrintData(from: photo, feedLines: 4)
sendViaBluetooth(printData)
} catch {
print("Print failed: \(error)")
}
}
}import SwiftUI
import T02Protocol
struct PrinterView: View {
@State private var image: UIImage?
let protocol = T02Protocol()
var body: some View {
VStack {
if let image = image {
Image(uiImage: image)
.resizable()
.scaledToFit()
Button("Print") {
printImage(image)
}
}
}
}
func printImage(_ image: UIImage) {
do {
let data = try protocol.generatePrintData(from: image)
// Send to printer
} catch {
print("Error: \(error)")
}
}
}ESC @ (0x1b 0x40) - Initialize printer
ESC a n (0x1b 0x61 n) - Set justification (0=left, 1=center, 2=right)
ESC d n (0x1b 0x64 n) - Feed n lines (0-255)
GS v 0 (0x1d 0x76 0x30) - Raster bit image
Input → Resize to 384px → Grayscale → Invert → 1-bit → Pack bytes → Protocol
[Header]
ESC @ (init)
ESC a 1 (center)
[Body - repeated for blocks]
GS v 0 (raster header)
- width: 48 bytes
- lines: 1-255
[bitmap data]
[Footer]
ESC d N (feed)
# Run all tests
swift test
# Run specific test
swift test --filter T02ProtocolTests
# With verbose output
swift test --verbose- Swift: 5.9 or later
- macOS: 13.0 (Ventura) or later
- iOS: 16.0 or later
- Xcode: 15.0 or later (for development)
- Printer: Phomemo T02
- Connection: Bluetooth Low Energy (BLE)
- Paper Width: 50mm
- Resolution: 203 DPI
- Print Width: 384 dots (48 bytes)
This package provides the protocol layer. For complete BLE printing:
- Scan for devices named "T02"
- Connect using CoreBluetooth
- Discover services and writable characteristics
- Generate print data with this package
- Send in small chunks (20 bytes recommended)
- Wait 50ms between chunks
See T02PrintTool/CoreBluetoothConnection.swift for reference implementation.
do {
let data = try protocol.generatePrintData(from: image)
} catch T02Protocol.ProtocolError.imageHeightExceedsMaximum {
// Image too tall
} catch T02Protocol.ProtocolError.invalidFeedLines {
// Feed lines out of range
} catch {
// Other errors
}Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- Based on reverse-engineered ESC/POS protocol
- Python reference implementation compatibility
- Community contributions and testing
- phomemo-tools - Linux CUPS drivers
- Original protocol research and implementation
- Documentation: See inline code documentation
- Examples: Check
INTEGRATION_EXAMPLE.md - Issues: GitHub issue tracker
- Discussions: GitHub discussions