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

相关推荐
林强1813 小时前
前端文件预览docx、pptx和xlsx
前端
像是套了虚弱散6 小时前
DevEco Studio与Web联合开发:打造鸿蒙混合应用的全景指南
开发语言·前端·华为·harmonyos·鸿蒙
衬衫chenshan6 小时前
【CTF】强网杯2025 Web题目writeup
前端
飞翔的佩奇7 小时前
【完整源码+数据集+部署教程】【天线&水】舰船战舰检测与分类图像分割系统源码&数据集全套:改进yolo11-repvit
前端·python·yolo·计算机视觉·数据集·yolo11·舰船战舰检测与分类图像分割系统
哆啦A梦15887 小时前
点击Top切换数据
前端·javascript·vue.js
程序猿追8 小时前
Vue组件化开发
前端·html
艾德金的溪8 小时前
redis-7.4.6部署安装
前端·数据库·redis·缓存
小光学长8 小时前
基于Vue的2025年哈尔滨亚冬会志愿者管理系统5zqg6m36(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
@PHARAOH9 小时前
WHAT - 受控组件和非受控组件
前端·javascript·react.js
生莫甲鲁浪戴9 小时前
Android Studio新手开发第二十六天
android·前端·android studio