需求
网页通过蓝牙发送 TSPL 指令到打印机。
前置条件
- 电脑需要有蓝牙
- 低功耗蓝牙打印机(关键是低功耗)
表1
- 了解蓝牙基础概念和通讯原理
概念
Web Bluetooth API
网络蓝牙 API 提供了与蓝牙低功耗外围设备连接和交互的能力。Web Bluetooth API 现在还是一项实验性的功能,用于生产环境需谨慎。
实现
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();
注意&问题
- 由于 Web Bluetooth 功能还在实验阶段,有些功能还需要打开特性开关,如
Bluetooth.getDevices()
接口,可以通过在浏览器地址输入chrome://flags/#enable-web-bluetooth-new-permissions-backend
,进入Experiments
面板打开web-bluetooth-new-permissions-backend
特性 printCharacteristic.writeValue
一次性只能最多发送512
字节的数据,数据太大,比如发送图片,要分包发送,可以参考 demo,但是有些打印机一次最多发送的字节数比512
小,最好是连接的打印机获取一次最多能发送的字节数。- 传输数据很慢,我测试过几台打印机,发现打印图片超级慢。因为图片的数据太大了,也可能是打印机性能不好。具体性能瓶颈卡在哪里还不知道怎样排查,如果你有这方面的经验,欢迎评论。
参考
https://developer.mozilla.org/zh-CN/docs/Web/API/Bluetooth
https://zhuanlan.zhihu.com/p/20657057