通过 WebUSB 驱动打印机打印

分类:JavaScript     发布时间:2023-11-13     最后更新:2023-11-13     浏览数:923
这篇文章详细叙述了网页如何通过 USB 发送 ESC/POS 指令到打印机,并介绍了什么是 WebUSB,什么是 ESC/POS 指令。

需求

网页通过 USB 发送 ESC/POS 指令到打印机。

概念

什么是 WebUSB?

WebUSB 是一个 Web API,它允许网页通过 USB 连接与本地的 USB 设备进行通信。通过 WebUSB,网页可以与各种类型的 USB 设备进行直接交互,而无需通过平台特定的驱动程序或中间件。

使用 WebUSB,开发人员可以创建具有以下功能的网页应用程序:

  • 识别和连接可用的 USB 设备。
  • 与 USB 设备进行数据交换,包括读取和写入设备的数据端点。
  • 监听 USB 设备上的事件,例如设备连接和断开连接。

由于 WebUSB 使用了 USB 设备的通用性标准,因此它可以与各种类型的设备进行通信,例如打印机、扫描仪、键盘、鼠标、游戏控制器等。这使得开发人员可以创建具有更高级别的交互和控制的 Web 应用程序,而无需依赖于特定平台或操作系统。

需要注意的是,为了保护用户安全和隐私,WebUSB 需要用户的明确授权才能访问 USB 设备。用户将通过浏览器的权限提示决定是否允许网页应用程序与指定的 USB 设备进行通信。

什么是 ESC/POS 指令?

ESC/POS(Epson Standard Code for Printers)是一种打印机指令集,由爱普生(Epson)公司创建并广泛使用。ESC/POS 指令集包括一系列控制命令,以控制打印机的各种操作,例如打印文本、绘制条形码、切纸等等。它被应用到广泛的打印机应用程序中,例如收银系统、票据打印、咨询机等等。

ESC/POS 指令集为打印机提供了许多有用和高级的特性。例如,它支持各种字体和字号、颜色、对齐方式、旋转、加粗、下划线、倾斜等功能,以及各种类型的条形码和二维码。此外,ESC/POS 指令集还支持自定义 logo 和图像,以及打印多个副本和自动切纸等功能。

ESC/POS 命令是通过向打印机发送 ASCII 字符串来实现的。对于不同的打印机,其 ESC/POS 指令集可能非常相似,但也存在一些差异。因此,开发人员应该了解所使用打印机的文档以正确使用其 ESC/POS 指令集。

实战

流程:获取设备(配对,或者从已配对的列表中获取)-> 初始化(打开设备,选择配置,声明接口)-> 发送 ESC/POS 命令

配对

const getDevice = async () => {
  const device = await navigator.usb
    .requestDevice({ filters: [] })
    .then((device) => {
      return device;
    })
    .catch(() => {
      return null;
    });
  return device;
};

初始化设备

 const initDevice = async (device) => {
  await device.open();
  const { configurationValue, interfaces } = device.configuration;
  await device.selectConfiguration(configurationValue || 0);
  await device.claimInterface(interfaces[0].interfaceNumber || 0);
  return device;
};

发送 ESC/POS 指令

const sendCmd = async (device) => {
  const cmd = new Uint8Array([0x1f, 0x1b, 0x1f, 0x67, 0x00]);
  const { outEndpoint } = getEndpoint(device);
  device.transferOut(outEndpoint, cmd);
};

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebUSB 驱动打印机打印</title>
</head>
<body>
  <button id="connectBtn">连接</button>
  <button id="sendBtn">打印自检页</button>
  <script src="./usb.js"></script>
</body>
</html>
let selectedDevice; // 当前选择的设备

const getEndpoint = (device) => {
  let inEndpoint = undefined;
  let outEndpoint = undefined;

  for (const { alternates } of device.configuration.interfaces) {
    const alternate = alternates[0];
    const USB_PRINTER_CLASS = 7;
    if (alternate.interfaceClass !== USB_PRINTER_CLASS) {
      continue;
    }

    for (const endpoint of alternate.endpoints) {
      if (endpoint.type !== "bulk") {
        continue;
      }

      if (endpoint.direction === "in") {
        inEndpoint = endpoint.endpointNumber;
      } else if (endpoint.direction === "out") {
        outEndpoint = endpoint.endpointNumber;
      }
    }
  }

  return {
    inEndpoint,
    outEndpoint,
  };
};

const connect = async () => {
  let device = await getDevice();
  if (!device) {
    return;
  }
  selectedDevice = device;
  await initDevice(device);
};

const getDevice = async () => {
  const device = await navigator.usb
    .requestDevice({ filters: [] })
    .then((device) => {
      return device;
    })
    .catch(() => {
      return null;
    });
  return device;
};

const initDevice = async (device) => {
  await device.open();
  const { configurationValue, interfaces } = device.configuration;
  await device.selectConfiguration(configurationValue || 0);
  await device.claimInterface(interfaces[0].interfaceNumber || 0);
};

const sendCmd = async () => {
  if (!selectedDevice) {
    console.warn("请先配对设备");
    return;
  }
  const cmd = new Uint8Array([0x1f, 0x1b, 0x1f, 0x67, 0x00]);
  const { outEndpoint } = getEndpoint(selectedDevice);
  selectedDevice.transferOut(outEndpoint, cmd);
};

const init = () => {
  navigator.usb.addEventListener("connect", (e) => {
    console.log("新连上的设备", e.device);
  });

  navigator.usb.addEventListener("disconnect", (e) => {
    console.log("断开的设备", e.device);
  });

  const connectBtn = document.querySelector("#connectBtn");
  connectBtn.addEventListener("click", connect);

  const sendBtn = document.querySelector("#sendBtn");
  sendBtn.addEventListener("click", sendCmd);
};

document.addEventListener("DOMContentLoaded", init);
上一篇: bwip-js 自定义字体 下一篇: 通过 Web Bluetooth 驱动打印机打印