通过 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...

相关推荐
java1234_小锋4 小时前
【技术专题】HTML5 网页技术 - HTML5 Canvas 基础以及动画入门
前端·html·html5
知了学历孙老师8 小时前
小自考的校考和统考,出题方和难度差在哪
java·大数据·前端
ShineWinsu10 小时前
对于JavaScript:操作元素的解析
开发语言·前端·javascript·面试·web·dom·元素
福兮说10 小时前
JSON 格式正确,数据就可靠吗?我用一份自编配置检查类型与空值
java·前端·json
程序员老赵12 小时前
Docker 部署青龙面板:轻松搭建定时任务管理平台
前端·docker·开源
Swot12 小时前
Asciidoctor 公式数字下标错位问题:原因分析与解决方案
前端
Swot12 小时前
leo-editor 中将 Markdown 内容转成 asciidoc 格式
前端
狒狒AI12 小时前
Remotion / GSAP+Three / HyperFrames / Skia 四栈实测:代码渲染知识视频的选栈方法(附开源技能)
前端·人工智能·aigc
修炼的dance12 小时前
浏览器里导出H.265,8.5秒和55.5秒差在硬件
前端
补码缩写12 小时前
WebCodecs选编码器,探测之后还要验解码和播放
前端