You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter蓝牙连接Phomemo M120热敏打印机无法打印问题排查

蓝牙打印Phomemo M120无内容排查方案

问题场景

通过Flutter应用使用flutter_blue_plus连接Phomemo M120热敏标签打印机,已成功建立连接、找到可写蓝牙特征,但使用esc_pos_utils生成的ESC/POS指令或自定义TSPL指令发送后,打印机仅吐出空白标签,无任何内容。厂商官方APP可正常打印,打印机硬件无故障。

排查步骤与代码优化建议

1. 确认指令集兼容性

Phomemo M120是标签专用打印机,默认采用TSPL指令集,而非通用小票打印机的ESC/POS。优先聚焦TSPL指令的调试,暂时放弃ESC/POS的尝试。

2. 修复TSPL指令的字节编码问题

当前TSPL代码使用command.codeUnits转换字节,这会采用UTF-16编码,而TSPL指令要求ASCII编码。修改转码逻辑:

import 'dart:convert';

List<int> printText(String text) {
    List<String> commands = [];

    // 设置标签尺寸(匹配实际耗材)
    commands.add('SIZE 40 mm,30 mm\r\n');
    // 清除缓冲区
    commands.add('CLS\r\n');
    // 添加文本(使用打印机支持的字体)
    commands.add('TEXT 10,10,"0",0,1,1,"$text"\r\n');
    // 打印1张标签
    commands.add('PRINT 1\r\n');

    // 改用ASCII编码转换指令
    List<int> bytes = commands
        .map((command) => ascii.encode(command))
        .expand((codeUnit) => codeUnit)
        .toList();

    return bytes;
}

注意:TSPL指令通常用\r\n作为换行符,而非单纯的\n,部分打印机对换行符格式敏感。

3. 确保蓝牙数据完整发送

  • 检查并调整MTU值:MTU决定单次蓝牙传输的最大字节数,过小会导致指令截断。连接后主动请求更大的MTU:
    await device.connect();
    // 请求MTU(建议设为512,需打印机支持)
    await device.requestMtu(512);
    _mtu = await device.mtu.first;
    
  • 分批发送长指令:如果指令长度超过MTU,手动拆分并分批发送,每批发送后添加短暂延时:
    List<int> bytes = printText("测试内容");
    int chunkSize = _mtu - 3; // 预留蓝牙协议头空间
    for (int i = 0; i < bytes.length; i += chunkSize) {
      int end = (i + chunkSize) > bytes.length ? bytes.length : i + chunkSize;
      await _characteristic!.write(bytes.sublist(i, end), allowLongWrite: false);
      await Future.delayed(Duration(milliseconds: 100));
    }
    

4. 验证蓝牙特征是否正确

当前代码指定的特征UUID为0000ff02-0000-1000-8000-00805f9b34fb,但部分Phomemo打印机的打印特征是0000ff01-0000-1000-8000-00805f9b34fb。建议遍历所有可写特征,逐一测试:

for (BluetoothCharacteristic c in characteristics) {
  if (c.properties.write || c.properties.writeWithoutResponse) {
    // 标记所有可写特征,逐个测试打印
    print("可写特征UUID: ${c.characteristicUuid}");
  }
}

5. 核对TSPL指令细节

  • 移除多余的END\n指令:TSPL无需该指令,PRINT 1已足够触发打印。
  • 匹配实际标签尺寸:如果你的标签不是40mm×30mm,修改SIZE指令参数为实际尺寸(可在厂商APP中查看)。
  • 确认字体参数:"3"可能不是打印机内置字体,改用"0"(默认字体)或"TSS24.BF2"这类官方文档标注的字体。

用户提供的原始代码

蓝牙连接代码

connectDevice(BluetoothDevice device) async {
    // listen for disconnection
    device.connectionState.listen((BluetoothConnectionState state) async {
      if (state == BluetoothConnectionState.disconnected) {
        // typically, start a periodic timer that tries to periodically reconnect.
        // Note: you must always re-discover services after disconnection!
        // TODO - do something
      }
    });

    // Connect to the device
    // Note: You should always call `connectionState.listen` before you call connect!
    await device.connect();
    _connectedDevice = device;

    // Yank the MTU
    _mtu = await device.mtu.first;

    // Note: You must call discoverServices after every connection!
    List<BluetoothService> services = await device.discoverServices();
    _services = services;

    services.forEach((service) async {
      // Reads all characteristics
      var characteristics = service.characteristics;
      for (BluetoothCharacteristic c in characteristics) {
        print(c.toString());
        if (c.characteristicUuid.toString() ==
                "0000ff02-0000-1000-8000-00805f9b34fb" &&
            service.serviceUuid.toString() ==
                "0000ff00-0000-1000-8000-00805f9b34fb") {
          _characteristic = c;
        }
        // List<int> value = await c.read();
        // print(value);
      }
    });
  }

ESC/POS打印代码

final profile = await CapabilityProfile.load();
    final generator = Generator(PaperSize.mm58, profile);
    List<int> bytes = [];

    bytes += generator.text("abc TEST 123");
    bytes += generator.qrcode('example.com');
    await _characteristic!.write(bytes, allowLongWrite: true);

TSPL打印代码

List<int> printText(String text) {
    List<String> commands = [];

    // Set fixed label size
    commands.add('SIZE 40 mm,30 mm\n');

    // Clear the buffer
    commands.add('CLS\n');

    // Add text, we assume standard text properties
    commands.add('TEXT 10,10,"3",0,1,1,"$text"\n');

    // Print the label
    commands.add('PRINT 1\n');

    // END
    commands.add('END\n');

    // Convert commands to bytes
    List<int> bytes = commands
        .map((command) => command.codeUnits)
        .expand((codeUnit) => codeUnit)
        .toList();

    return bytes;
  }

内容的提问来源于stack exchange,提问作者Peter Myers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:40:58