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

Angular通过WebUSB API向MP-4200 TH发送ESC/POS指令实现打印切纸可行吗?

问题描述

我已在Angular应用中通过WebUSB API实现向MP-4200 TH热敏打印机打印纯文本,现希望通过该API向打印机发送ESC/POS指令,实现打印并让打印机自动完成切纸操作,请问是否可以实现?

现有代码

app.component.ts

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})

export class AppComponent {

  private usbDevice: any;

  private textToProint: any[] = [
    'SIZE 48 mm,25 mm',
    'CLS',
    'TEXT 30,10,"4",0,1,1,"HackerNoon"',
    'TEXT 30,50,"2",0,1,1,"WebUSB API"',
    'PRINT 1',
    'END',
  ];

  async connect() {
    try {
      this.usbDevice = await (navigator as any).usb.requestDevice({ filters: [{ VendorID: 0x0B1B, ProductID: 0x0003 }] });
      console.log('Connected to USB device:', this.usbDevice);
    } catch (error) {
      console.error('Error connecting to USB device:', error);
    }
  }

  async disconnect() {
    try {
      if (this.usbDevice) {
        await this.usbDevice.close();
        console.log('Disconnected from USB device.');
      }
    } catch (error) {
      console.error('Error disconnecting from USB device:', error);
    }
  }

  async send() {
    console.log(this.usbDevice, 'Sending');
    try {
      if (this.usbDevice) {
        console.log("USB device opened:", this.usbDevice.configuration.interfaces[1]);
        await this.usbDevice.open()
          .then(() => this.usbDevice.selectConfiguration(1))
          .then(() => this.usbDevice.claimInterface(this.usbDevice.configuration.interfaces[1]?.interfaceNumber))


        await this.usbDevice.transferOut(
          this.usbDevice.configuration.interfaces[1]?.alternate.endpoints.find((obj: any) => obj.direction === 'out').endpointNumber,
          new Uint8Array(
            new TextEncoder().encode(this.textToProint.join('\r\n'))
          )
        );
        await this.usbDevice.releaseInterface(0);
        await this.usbDevice.close();

        console.log("Data sent to printer:");
      }

    } catch (error) {
      console.error("Error connecting to  printer:", error);
    }
  }

}

app.component.html

<div>
  <body>
 <button style="background-color: blue; cursor: pointer; color: aliceblue; border-radius: 12px; margin: 2px; padding: 4px; align-items: center;" (click)="connect()">Connect to USB Device</button>
    <button style="padding: 4px; margin: 4px; cursor: pointer; border-radius: 12px; background-color: blueviolet; color: white;" (click)="send()">Send Data</button>
    <button style="padding: 4px; margin: 4px; cursor: pointer; border-radius: 12px; background-color: darkred; color: white;" (click)="disconnect()">Disconnect</button>
  </body>
</div>
解答

完全可以实现。你当前代码使用的是打印机厂商自定义指令集(如SIZE、CLS、PRINT),而ESC/POS是热敏打印机通用指令标准,MP-4200 TH支持该标准,只需替换指令内容即可实现打印+自动切纸。

修改要点

  • 构造ESC/POS二进制指令:ESC/POS指令由控制字符(如ESC 0x1B、GS 0x1D)和参数组成,需直接构造Uint8Array字节数组,不能用TextEncoder转字符串(会丢失控制字符)。
  • 核心ESC/POS指令:
    • 初始化打印机:ESC @(0x1B, 0x40)
    • 设置字体大小:GS ! n(n为参数,控制字体倍宽倍高)
    • 自动切纸:GS V 0(0x1D, 0x56, 0x00,全切纸)或ESC i(0x1B, 0x69,部分切纸,可根据打印机支持选择)

修改后的app.component.ts关键代码

export class AppComponent {
  private usbDevice: any;

  // 构造ESC/POS指令字节数组
  private getEscPosData(): Uint8Array {
    const encoder = new TextEncoder();
    const parts: Uint8Array[] = [];

    // 初始化打印机
    parts.push(new Uint8Array([0x1B, 0x40]));
    // 设置字体为2倍宽高(GS ! 0x10)
    parts.push(new Uint8Array([0x1D, 0x21, 0x10]));
    // 打印标题文本
    parts.push(encoder.encode("HackerNoon\n"));
    // 恢复默认字体大小
    parts.push(new Uint8Array([0x1D, 0x21, 0x00]));
    // 打印副标题
    parts.push(encoder.encode("WebUSB API\n"));
    // 空两行
    parts.push(encoder.encode("\n\n"));
    // 全切纸指令(GS V 0)
    parts.push(new Uint8Array([0x1D, 0x56, 0x00]));

    // 合并所有字节数组
    const totalLength = parts.reduce((sum, arr) => sum + arr.length, 0);
    const result = new Uint8Array(totalLength);
    let offset = 0;
    for (const arr of parts) {
      result.set(arr, offset);
      offset += arr.length;
    }
    return result;
  }

  async send() {
    console.log(this.usbDevice, 'Sending');
    try {
      if (this.usbDevice) {
        await this.usbDevice.open()
          .then(() => this.usbDevice.selectConfiguration(1))
          .then(() => this.usbDevice.claimInterface(this.usbDevice.configuration.interfaces[1]?.interfaceNumber));

        const endpoint = this.usbDevice.configuration.interfaces[1]?.alternate.endpoints.find((obj: any) => obj.direction === 'out').endpointNumber;
        // 发送ESC/POS指令
        await this.usbDevice.transferOut(endpoint, this.getEscPosData());

        await this.usbDevice.releaseInterface(this.usbDevice.configuration.interfaces[1]?.interfaceNumber);
        await this.usbDevice.close();

        console.log("ESC/POS指令已发送,打印机将执行打印并切纸");
      }
    } catch (error) {
      console.error("打印机操作出错:", error);
    }
  }

  // connect和disconnect方法保持不变
  async connect() { /* 原有代码 */ }
  async disconnect() { /* 原有代码 */ }
}

注意事项

  • 若GS V 0切纸指令无效,可尝试替换为ESC i(0x1B, 0x69)实现部分切纸。
  • 原代码中releaseInterface(0)存在错误,应释放之前claimInterface获取的接口编号(即this.usbDevice.configuration.interfaces[1]?.interfaceNumber),否则可能导致后续连接失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 07:24:54