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

相关推荐
JinSoooo2 分钟前
pnpm monorepo 联调方案
前端·pnpm·monorepo
m0_7482449611 分钟前
【AI系统】LLVM 前端和优化层
前端·状态模式
明弟有理想11 分钟前
Chrome RCE 漏洞复现
前端·chrome·漏洞·复现
平行线也会相交12 分钟前
云图库平台(二)前端项目初始化
前端·vue.js·云图库平台
shimmer00813 分钟前
抖音小程序登录(前端通过tt.login获取code换取openId)
前端·小程序·状态模式
嘤嘤怪呆呆狗23 分钟前
【开发问题记录】使用 Docker+Jenkins+Jenkins + gitee 实现自动化部署前端项目 CI/CD(centos7为例)
前端·vue.js·ci/cd·docker·gitee·自动化·jenkins
鱼钓猫的小鱼干28 分钟前
table 表格转成 excell 导出
前端·vue·excel
一只搬砖的猹30 分钟前
cJson系列——常用cJson库函数
linux·前端·javascript·python·物联网·mysql·json
懒羊羊我小弟1 小时前
包管理工具npm、yarn、pnpm、cnpm详解
前端·npm·node.js·yarn·cnpm
ppo_wu1 小时前
更改 pnpm 的全局存储位置
前端·vue