iOS蓝牙打印小票:发票二维码指令全解析
2025.09.19 13:00浏览量:2简介:本文深入解析iOS设备通过蓝牙打印小票时,如何嵌入发票二维码的指令实现方法。涵盖蓝牙打印机连接、二维码生成、指令封装及异常处理等核心环节,为开发者提供完整的解决方案。
一、蓝牙打印技术基础与设备适配
1.1 CoreBluetooth框架原理
iOS系统通过CoreBluetooth框架实现与蓝牙设备的通信,该框架采用GATT(Generic Attribute Profile)协议栈,支持BLE 4.0及以上设备。开发者需掌握CBPeripheralManager(服务端)和CBCentralManager(客户端)的协作机制,其中打印设备通常作为服务端提供特征值(Characteristic)供iOS端写入数据。
1.2 打印机协议解析
主流蓝牙小票打印机(如佳博、汉印)采用ESC/POS指令集,其数据传输格式包含:
- 初始化指令:
\x1B\x40(重置打印机状态) - 文字对齐:
\x1B\x61\x00(左对齐)/\x01(居中)/\x02(右对齐) - 二维码模式:
\x1D\x28\x6B\x03\x00\x31\x50\x30(设置QR码模型1,版本2) - 打印指令:
\x0A(换行)/\x0C(走纸)
1.3 设备发现与连接管理
import CoreBluetoothclass BluetoothPrinterManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {private var centralManager: CBCentralManager!private var targetPeripheral: CBPeripheral?private var writeCharacteristic: CBCharacteristic?override init() {super.init()centralManager = CBCentralManager(delegate: self, queue: nil)}func centralManagerDidUpdateState(_ central: CBCentralManager) {if central.state == .poweredOn {central.scanForPeripherals(withServices: [CBUUID(string: "FFE0")], options: nil)}}func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {if peripheral.name?.contains("Printer") == true {targetPeripheral = peripheralcentral.stopScan()central.connect(peripheral, options: nil)}}func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {peripheral.delegate = selfperipheral.discoverServices([CBUUID(string: "FFE0")])}func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {guard let services = peripheral.services else { return }for service in services {peripheral.discoverCharacteristics([CBUUID(string: "FFE1")], for: service)}}func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {guard let characteristics = service.characteristics else { return }for characteristic in characteristics {if characteristic.uuid.uuidString == "FFE1" {writeCharacteristic = characteristicprint("打印机准备就绪")}}}}
二、发票二维码生成与指令封装
2.1 二维码数据编码规范
根据《增值税发票税控码规范》,二维码需包含:
- 发票代码(10-12位数字)
- 发票号码(8位数字)
- 开票日期(YYYYMMDD)
- 校验码(6-20位字符)
- 金额(含税合计,单位元)
示例数据结构:
{"code": "123456789012","number": "87654321","date": "20230815","checkCode": "ABC123","amount": "100.50"}
2.2 二维码指令序列构建
完整打印指令需包含:
- 初始化序列
- 文字信息(商户名称、交易时间等)
- 二维码指令块
- 切割指令(如需)
- 走纸指令
struct PrintCommandBuilder {static func buildInvoiceQRCommand(invoiceData: InvoiceData) -> Data {var commands = Data()// 1. 初始化打印机commands.append("\x1B\x40".data(using: .ascii)!)// 2. 设置中文编码(如需)commands.append("\x1B\x74\x01".data(using: .ascii)!) // GBK编码// 3. 添加标题let title = "电子发票".centerAligned(width: 32)commands.append(title.data(using: .ascii)!)commands.append("\x0A".data(using: .ascii)!)// 4. 构建二维码数据let qrContent = """FPCODE:\(invoiceData.code)NUMBER:\(invoiceData.number)DATE:\(invoiceData.date)CHECK:\(invoiceData.checkCode)AMOUNT:\(invoiceData.amount)"""// 5. 二维码指令(模型2,版本5,纠错L)let qrCommand = "\x1D\x28\x6B\x04\x00\x31\x50\x30\x05" +String(format: "%02X", qrContent.count) +qrContent +"\x1D\x28\x6B\x03\x00\x31\x51\x30"commands.append(qrCommand.data(using: .ascii)!)// 6. 走纸commands.append("\x0A\x0A\x0C".data(using: .ascii)!)return commands}}extension String {func centerAligned(width: Int) -> String {let padding = (width - count) / 2return String(repeating: " ", count: padding) + self}}
三、完整打印流程实现
3.1 打印状态管理
class PrinterSession {private let manager = BluetoothPrinterManager()private var isPrinting = falsefunc printInvoice(invoice: InvoiceData, completion: @escaping (Bool, Error?) -> Void) {guard !isPrinting else {completion(false, PrinterError.busy)return}isPrinting = truelet command = PrintCommandBuilder.buildInvoiceQRCommand(invoiceData: invoice)DispatchQueue.global().async {let semaphore = DispatchSemaphore(value: 0)self.manager.writeData(command) { success, error inself.isPrinting = falsecompletion(success, error)semaphore.signal()}semaphore.wait()}}}extension BluetoothPrinterManager {func writeData(_ data: Data, completion: @escaping (Bool, Error?) -> Void) {guard let characteristic = writeCharacteristic else {completion(false, PrinterError.notConnected)return}targetPeripheral?.writeValue(data, for: characteristic, type: .withResponse) { error inif let error = error {completion(false, error)} else {completion(true, nil)}}}}
3.2 异常处理机制
需重点处理的异常场景:
- 连接超时:设置10秒超时重试机制
- 指令格式错误:验证打印机支持的二维码版本
- 数据截断:检查特征值MTU(通常20字节)
- 打印卡纸:监听打印机状态特征值
enum PrinterError: Error {case notConnectedcase busycase timeoutcase invalidDatacase unknown(Error)}class RetryPolicy {static func withRetry<T>(maxAttempts: Int = 3,delay: TimeInterval = 2,operation: @escaping () -> (T?, Error?)) -> (T?, Error?) {var lastError: Error?for attempt in 1...maxAttempts {let result = operation()if let value = result.0 {return (value, nil)} else if let error = result.1 {lastError = errorif attempt < maxAttempts {Thread.sleep(forTimeInterval: delay)}}}return (nil, lastError ?? PrinterError.unknown(NSError(domain: "", code: 0, userInfo: nil)))}}
四、性能优化与兼容性处理
4.1 指令分包传输
当打印数据超过特征值MTU时,需实现分包逻辑:
extension Data {func chunked(for characteristic: CBCharacteristic) -> [Data] {let mtu = characteristic.maximumWriteValueLength - 3 // 减去ATT头开销var chunks = [Data]()var offset = 0while offset < count {let endIndex = min(offset + mtu, count)let chunk = subdata(in: offset..<endIndex)chunks.append(chunk)offset = endIndex}return chunks}}
4.2 打印机型号适配
不同厂商指令差异处理:
protocol PrinterProtocol {func initialize() -> Datafunc setQRModel(version: Int, errorCorrection: Int) -> Datafunc printQR(data: String) -> Data}class GenericPrinter: PrinterProtocol {func initialize() -> Data { "\x1B\x40".data(using: .ascii)! }// ...其他默认实现}class HanxinPrinter: GenericPrinter {override func setQRModel(version: Int, errorCorrection: Int) -> Data {// 汉印特殊指令格式return "\x1D\x28\x6B\x03\x00\x31\x52\x30".data(using: .ascii)!}}
五、实际应用建议
- 打印预览:在发送指令前,使用Core Graphics生成预览图
- 日志记录:保存每次打印的指令和结果,便于故障排查
- 固件更新:检查打印机是否支持动态二维码版本调整
- 能耗优化:在非打印时段断开蓝牙连接
通过上述技术实现,开发者可以构建稳定可靠的iOS蓝牙打印系统,满足电子发票二维码的合规打印需求。实际开发中需结合具体打印机型号的文档进行指令微调,并通过大量测试验证兼容性。

发表评论
登录后可评论,请前往 登录 或 注册