通过 Web Bluetooth 驱动打印机打印

分类:JavaScript     发布时间:2023-12-06     最后更新:2023-12-06     浏览数:883
写了一个简单的例子,通过 Web Bluetooth 发送 TSPL 指令驱动打印机打印。

需求

网页通过蓝牙发送 TSPL 指令到打印机。

前置条件

  • 电脑需要有蓝牙
  • 低功耗蓝牙打印机(关键是低功耗)

image.png

表1

  • 了解蓝牙基础概念和通讯原理

概念

Web Bluetooth API

网络蓝牙 API 提供了与蓝牙低功耗外围设备连接和交互的能力。Web Bluetooth API 现在还是一项实验性的功能,用于生产环境需谨慎。

image.png

实现

1. 配对

const pair = async () => {
  return navigator.bluetooth
    .requestDevice({
      filters: [{ services: [SERVICE_UUID] }],
    })
    .then((device) => {
      selectedDevice = device;
      return device;
    })
    .catch((err) => {
      console.log(err.message);
      return "";
    });
};

2. 连接 GATT server 并获取 打印特性

const getPrintCharacteristic = async () => {
  if (!selectedDevice) {
    return Promise.reject(new Error("没有配对设备"));
  }

  if (printCharacteristic) {
    return Promise.resolve(printCharacteristic);
  }

  return Promise.resolve(selectedDevice)
    .then((device) => {
      console.log(`设备名称 ${device.name}`);
      console.log("连接到 GATT 服务器......");
      return device.gatt.connect();
    })
    .then((server) => server.getPrimaryService(SERVICE_UUID))
    .then((service) => service.getCharacteristic(CHARACTERISTIC_UUID))
    .then((characteristic) => {
      printCharacteristic = characteristic;
      return characteristic;
    });
};

3. 发送命令

const sendPrinterData = (cmd) => {
  if (!printCharacteristic) {
    console.log("无法打印:打印特性属性为空");
    return;
  }
  const encoder = new TextEncoder("utf-8");
  const text = encoder.encode(cmd);
  return printCharacteristic.writeValue(text).then(() => {
    console.log("发送完毕");
  });
};

完整代码(仅供参考)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Web Bluetooth 示例</title>
</head>
<body>
  <button id="connectBtn">连接</button>
  <button id="sendBtn">发送</button>
  <script src="./bluetooth.js"></script>
</body>
</html>
// 以下这两个值,在标准的 UUID 文档中找不到。应该是设备制造商没有按标准执行,我是从 https://github.com/WebBluetoothCG/demos/blob/gh-pages/bluetooth-printer/index.html 获取的
const SERVICE_UUID = '000018f0-0000-1000-8000-00805f9b34fb'; // 打印服务的 UUID
const CHARACTERISTIC_UUID = '00002af1-0000-1000-8000-00805f9b34fb'; // 写特性的 UUID

let selectedDevice;
let printCharacteristic;

const sendPrinterData = (cmd) => {
  if (!printCharacteristic) {
    console.log("无法打印:打印特性属性为空");
    return;
  }
  const encoder = new TextEncoder("utf-8");
  const text = encoder.encode(cmd);
  return printCharacteristic.writeValue(text).then(() => {
    console.log("发送完毕");
  });
};

const getPrintCharacteristic = async () => {
  if (!selectedDevice) {
    return Promise.reject(new Error("没有配对设备"));
  }

  if (printCharacteristic) {
    return Promise.resolve(printCharacteristic);
  }

  return Promise.resolve(selectedDevice)
    .then((device) => {
      console.log(`设备名称 ${device.name}`);
      console.log("连接到 GATT 服务器......");
      return device.gatt.connect();
    })
    .then((server) => server.getPrimaryService(SERVICE_UUID))
    .then((service) => service.getCharacteristic(CHARACTERISTIC_UUID))
    .then((characteristic) => {
      printCharacteristic = characteristic;
      return characteristic;
    });
};

const handleSend = () => {
  getPrintCharacteristic()
    .then(() => {
      sendPrinterData("SELFTEST\r\n"); // 发送打印自检页
    })
    .catch((err) => {
      console.log("发送指令失败:", err.message);
    });
};

const pair = async () => {
  return navigator.bluetooth
    .requestDevice({
      filters: [{ services: [SERVICE_UUID] }],
    })
    .then((device) => {
      selectedDevice = device;
      return device;
    })
    .catch((err) => {
      console.log(err.message);
      return "";
    });
};

const handleConnect = async () => {
  const device = await pair();
  console.log("配对设备:", device);
};

const init = () => {
  const btn = document.getElementById("connectBtn");
  btn.addEventListener("click", handleConnect);

  const sendBtn = document.getElementById("sendBtn");
  sendBtn.addEventListener("click", handleSend);
};

init();

注意&问题

  1. 由于 Web Bluetooth 功能还在实验阶段,有些功能还需要打开特性开关,如 Bluetooth.getDevices() 接口,可以通过在浏览器地址输入 chrome://flags/#enable-web-bluetooth-new-permissions-backend,进入 Experiments 面板打开 web-bluetooth-new-permissions-backend 特性
  2. printCharacteristic.writeValue 一次性只能最多发送 512 字节的数据,数据太大,比如发送图片,要分包发送,可以参考 demo,但是有些打印机一次最多发送的字节数比 512 小,最好是连接的打印机获取一次最多能发送的字节数。
  3. 传输数据很慢,我测试过几台打印机,发现打印图片超级慢。因为图片的数据太大了,也可能是打印机性能不好。具体性能瓶颈卡在哪里还不知道怎样排查,如果你有这方面的经验,欢迎评论。

参考

https://developer.mozilla.org/zh-CN/docs/Web/API/Bluetooth

https://zhuanlan.zhihu.com/p/20657057

https://blog.csdn.net/tanqth/article/details/108151033

https://github.com/WebBluetoothCG/demos

上一篇: 通过 WebUSB 驱动打印机打印 下一篇: Windows 11 搭建 Rust USB 开发环境之 GNU 版