通过 Web Bluetooth 驱动打印机打印

需求

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

前置条件

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

表1

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

概念

Web Bluetooth API

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

实现

1. 配对

js 复制代码
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 并获取 打印特性

js 复制代码
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. 发送命令

js 复制代码
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("发送完毕");
  });
};

完整代码(仅供参考)

html 复制代码
<!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>
js 复制代码
// 以下这两个值,在标准的 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. 传输数据很慢,我测试过几台打印机,发现打印图片超级慢。因为图片的数据太大了,也可能是打印机性能不好。具体性能瓶颈卡在哪里还不知道怎样排查,如果你有这方面的经验,欢迎评论。

参考

developer.mozilla.org/zh-CN/docs/...

zhuanlan.zhihu.com/p/20657057

blog.csdn.net/tanqth/arti...

github.com/WebBluetoot...

相关推荐
卸任7 小时前
Vibe coding工具使用心得
前端·ai编程
brien98 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance8 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M8 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写8 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李8 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易9 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马9 小时前
前沿视界
前端
kyriewen9 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易9 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员