APP小程序便携式蓝牙打印小票和单据插件共享
前言
在IT行业,尤其是在移动应用开发领域,uni-app是一个备受开发者喜爱的框架,它允许开发者用一套代码编写,然后跨多个平台进行部署,包括iOS、Android、H5等。由于网上涉及uni-app进行蓝牙适配器开发的资料相对较少和一些编解码数据的Web API(如TextEncoder)不可用的限制,本文将聚焦于利用uni-app进行蓝牙适配器的插件开发,与热敏打印机进行交互的JavaScript指令,这对于现场需要打印小票或单据的零售、餐饮、物流等行业尤为重要。
概念
字符集和字符编码
在开始之前,本文希望大家能够先理清楚两个概念:字符集、字符编码。
为什么这很重要?
在蓝牙适配器开发中,尤其是在与打印机交互时,字符编码的准确性至关重要。打印机需要知道如何解释从设备发送来的数据,如果字符编码不匹配,可能会导致打印乱码或其他错误。例如,如果打印机被设置为使用GB18030编码,而发送的数据使用的是UTF-8编码,那么打印出来的内容可能会出现乱码。
字符集
平时我们生活中使用的文字、标点符号、图形符号、数字,这些可以统称为字符。
由非常多个字符组合后产生的集合,这个集合称为字符集。
也就是说,我们可以人为的根据某个规则归纳一些我们使用的文字、符号,这些文字、符号的集合就称为一个字符集,并且可以根据不同的规则划分不同的字符集。
字符编码
那字符编码是什么?
我们知道计算机内部使用的是二进制运算,如果要想让计算机识别我们人类的文字、符号、数字,就需要一个转换规则,把我们人类使用的这些字符转换成计算机认识的二进制,也就是0和1。
这个转换的规则就称为字符编码。
总结
理解字符集和字符编码的区别对于任何涉及文本处理和设备间通讯的开发工作都是非常重要的。在开发蓝牙适配器与打印机交互的应用时,确保你的应用使用与打印机兼容的字符编码,以避免通讯混乱和打印错误。
GB18030字符编码
GB18030字符编码采用多字节编码方式,每个字可以由1个、2个或4个字节组成。
GB18030字符编码有2个版本:
1、GB18030-2000:2000年发布,GBK的取代版本,它的主要特点是在GBK基础上增加了CJK统一汉字扩充A的汉字。
GB18030-2000编码方式如下图:
2、GB18030-2005:2005年发布,在GB18030-2000基础上增加了CJK统一汉字扩充B的汉字。
GB18030-2005编码方式如下图:
如果你只需要使用中文字符集,那么选用GB18030字符编码基本能满足你的需求。
主流的打印指令类型
打印指令,也称为打印控制命令,是计算机通过特定的打印控制语言发送软件命令,以控制打印机的操作,解释并执行打印数据,从而获得打印结果。打印控制语言是打印机实现复杂功能的基础,它直接影响到打印输入的质量。因此,深入了解打印机控制语言对于选择和使用打印机至关重要。
目前主流的打印机控制语言主要有以下几种:
Epson公司的ESC命令集:这是一种广泛用于普通打印机的命令集,如针式打印机和票据打印机。ESC命令集是这些领域的实际工业标准。
HP公司的PCL命令集:PCL是低档激光打印机、喷墨打印机的工业标准。它是一种命令解释型语言,与高级编程语言中的BASIC类似,对每一条命令打印机立即响应执行。
Adobe公司的PostScript命令集:简称PS,这是一种用于高档排版领域的页面描述语言,几乎占据了这个领域的垄断地位。PS是一种编译型语言,与高级编程语言中的C类似,对数条命令组成的完整程序进行编译、解释,然后驱动打印。
其它命令集:包括CPCL命令集(主要用于移动打印机)、TSPL命令集(用于标签打印机),以及其它打印机厂商自己开发的打印机语言,如EPSON的ESC/page,佳能的CaPSYL,施乐的XES、JDL,IBM的IPDS,DEC的ANSI/Sixel等。这些语言各具特点,适用于不同的打印机和应用场景。
使用ESC、PCL、PostScript的软件比例占全部软件的75%以上,这三种打印控制命令语言对于打印应用程序设计起到了举足轻重的作用。通过初步了解这些打印机控制语言,我们可以更全面地认识它们的特点和应用范围,从而更好地选择和使用打印机。
TSPL指令集
TSPL是由台湾TSC Auto ID Technology Co., Ltd. 开发的一种专有的打印机控制语言。它主要用于TSC品牌的热敏标签打印机。TSPL指令集提供了丰富的功能,包括:
文本打印:设置字体大小、样式、对齐方式等。
条形码打印:支持多种条形码类型,如Code 39、Code 128、EAN-13等。
图像打印:支持位图和矢量图打印。
标签格式设置:定义标签的宽度、高度、间隙、黑标等。
打印机控制:如切纸、走纸等操作。
TSPL指令集通常以文本形式发送给打印机,打印机根据指令执行相应的打印操作。
ESC指令集
ESC指令集是由EPSON公司开发的一种打印机控制语言,被广泛用于各种品牌的针式和热敏打印机。ESC指令集同样提供了丰富的功能,包括:
文本打印:设置字体大小、样式、对齐方式等。
条形码打印:支持多种条形码类型。
图像打印:支持位图打印。
打印机控制:如切纸、走纸等操作。
ESC指令集的指令通常以ESC字符(ASCII码为27)开头,后跟一个或多个控制字符。
具体解决方案
uniapp中实现蓝牙打印功能
1. 初始化蓝牙适配器
首先,需要在onMounted
生命周期钩子中调用uni.openBluetoothAdapter
方法来打开蓝牙适配器。
onMounted(() => {
console.log('Component is mounted');
// 打开蓝牙适配器
uni.openBluetoothAdapter();
});
2. 搜索附近的蓝牙设备
在searchBle
函数中,使用uni.startBluetoothDevicesDiscovery
方法开始搜索附近的蓝牙设备。
async function searchBle() {
await uni.openBluetoothAdapter();
onDevice();
const { available, discovering } = await uni.getBluetoothAdapterState();
if (available) {
if (discovering) {
await stopFindBule();
}
await uni.startBluetoothDevicesDiscovery();
} else {
console.log('Bluetooth is not available on this device');
}
}
3. 连接蓝牙设备
当用户选择一个设备时,调用onConn
函数来创建与该设备的连接。
async function onConn(item) {
await uni.createBLEConnection({ deviceId: item.deviceId });
connId.value = item.deviceId;
currDev.value = item;
setTimeout(() => {
getBLEServices(item.deviceId);
}, 2000);
}
4. 获取设备的服务和特征
在getBLEServices
函数中,获取连接设备的服务和特征。
async function getBLEServices(deviceId) {
const services = await uni.getBLEDeviceServices({ deviceId });
// 遍历服务,获取特征
for (const service of services.services) {
const characteristics = await uni.getBLEDeviceCharacteristics({
deviceId,
serviceId: service.uuid
});
// 获取特征信息
for (const characteristic of characteristics.characteristics) {
if (characteristic.properties.write) {
currDev.value.services.push({
serviceId: service.uuid,
characteristicId: characteristic.uuid
});
}
}
}
}
5. 发送打印指令
在senBleLabel
函数中,使用jpPrinter
类构建打印指令,并调用senBlData
函数发送数据到打印机。
function senBleLabel() {
if (!currDev.value) {
console.error('No device connected');
return;
}
let deviceId = currDev.value.deviceId;
let serviceId = currDev.value.services[0].serviceId;
let characteristicId = currDev.value.services[0].characteristicId;
let command = new jpPrinter();
// 设置打印指令
command.setSize(40, 80);
command.setGap(2);
command.setCls();
// 添加文本和二维码
command.setText(10, 10, "TSS24.BF2", 2, 2, "店铺:小世界");
command.setQR(50, 300, "L", 5, "A", "Hello world!");
// 发送指令数据
senBlData(deviceId, serviceId, characteristicId, command.getData());
}
6. 断开连接
在组件卸载时,如果当前有连接的设备,需要调用uni.closeBLEConnection
方法断开连接。
onUnmounted(() => {
if (connId.value !== '') {
uni.closeBLEConnection({
deviceId: connId.value,
success(res) {
console.log('BLE connection closed:', res);
}
});
}
7. 处理蓝牙适配器的状态变化
在onDevice
函数中,监听蓝牙适配器的状态变化,以便在适配器状态改变时做出相应的处理。
function onDevice() {
// 监听蓝牙适配器状态变化
uni.onBluetoothAdapterStateChange((res) => {
if (!res.available) {
// 如果蓝牙适配器不可用,则停止搜索并断开连接
stopFindBule();
if (connId.value !== '') {
uni.closeBLEConnection({
deviceId: connId.value,
success(res) {
console.log('BLE connection closed:', res);
}
});
}
}
});
}
8. 停止搜索蓝牙设备
在stopFindBule
函数中,使用uni.stopBluetoothDevicesDiscovery
方法停止搜索蓝牙设备。
async function stopFindBule() {
const discovering = await uni.getBluetoothAdapterState();
if (discovering) {
await uni.stopBluetoothDevicesDiscovery();
}
}
9. 发送打印数据
在senBlData
函数中,将构建好的打印指令数据通过蓝牙发送给打印机。
async function senBlData(deviceId, serviceId, characteristicId, data) {
const buffer = new Uint8Array(data);
await uni.writeBLECharacteristicValue({
deviceId,
serviceId,
characteristicId,
value: buffer.buffer,
success(res) {
console.log('Data sent to printer:', res);
},
fail(error) {
console.error('Failed to send data to printer:', error);
}
});
}
插件分享--数据格式与编解码器的封装
本文封装了与蓝牙打印机交互的逻辑,具体设计如下:
编码字符集文件:实现了一个JavaScript编解码器,专注于UTF-16字符串的编码和解码,用于在发送数据到打印机时进行字符编码和解码。它定义了各种字符集的索引,例如GB18030、UTF-8等,以确保发送到打印机的数据能够正确地被解析和打印。包含范围检查、数组操作、字典转换、字符串与码点数组的转换、ASCII检测、流操作等基本功能。还实现了编解码器接口,处理了多种编码格式,并提供了索引处理和错误处理机制。这些功能共同构成了一个灵活且强大的文本编码解码解决方案,适用于多种编码格式转换的场景,尤其是在需要确保文本数据正确性和一致性的环境中。
自定义的编码器和解码器文件:一个自定义的编码器和解码器,它支持Vue 3和uni-app框架。提供了将文本数据从一种编码格式转换为另一种编码格式的功能。这对于确保打印机能正确解析和打印不同编码格式的数据至关重要。
TSC指令集的JavaScript类文件:这个文件是与蓝牙打印机交互的TSC指令集的JavaScript类。它包含多种方法来初始化打印机、设置打印内容、条码、二维码、打印位置、对齐方式以及其他打印参数。例如,它提供了设置文本内容、条码和二维码的方法,如
setText(content)
、setBarcode(data)
和setQRCode(data)
。此外,它还提供了打印位置和对齐方法,如setPrintPosition(x, y)
和setAlignment(alignment)
,以及其他打印设置方法,如setLeftMargin(margin)
、setPrintAreaWidth(width)
、setBuzzer(sound)
和printBitmap(bitmapData)
。最后,它还提供了执行打印、换行和走纸操作的打印操作方法,如print()
、feedLine()
和feedPaper()
。ESC指令集的JavaScript类文件:这个文件是与蓝牙打印机交互的ESC指令集的JavaScript类。它包含多种方法来初始化打印机、设置打印内容、条码、二维码、打印位置、对齐方式以及其他打印参数。与TSC指令集的JavaScript类文件类似,它提供了设置文本内容、条码和二维码的方法,如
setText(content)
、setBarcode(data)
和setQRCode(data)
。此外,它还提供了打印位置和对齐方法,如setPrintPosition(x, y)
和setAlignment(alignment)
,以及其他打印设置方法,如setLeftMargin(margin)
、setPrintAreaWidth(width)
、setBuzzer(sound)
和printBitmap(bitmapData)
。最后,它还提供了执行打印、换行和走纸操作的打印操作方法,如print()
、feedLine()
和feedPaper()
。
打印失败常见问题总结:
在uni-app中实现蓝牙打印功能时,可能会遇到一些常见问题,例如数据格式和编码问题、蓝牙服务和特征值的选择、以及不同手机操作系统之间的差异。以下是对这些问题的总结和解决建议:
1. 数据格式和编码问题
解决方案:确保发送的数据格式符合打印机所支持的指令集,如ESC/POS或TSC。检查数据编码是否为GB18030,如果打印机设置为这个编码,发送的数据也需要是这个编码。
2. 蓝牙服务和特征值的选择
解决方案:找到正确的服务和特征值进行数据传输。检查特征值的属性,确保支持写操作。如果有多个特征值都支持写操作,通常任选其一即可,但有时不同的特征值可能对应打印机的不同功能或模式。
3. 不同手机操作系统的差异
解决方案:对于安卓设备,如果发现无法一次性接收较大的数据包,可以尝试将数据分包发送,每个数据包不超过20字节,以提高兼容性。
最后
TSPL,ESC指令集打印插件,其他指令集可根据手册结合本插件的编解码器自行增添 - DCloud 插件市场
本插件现已开源上架至插件市场,访问上述链接即可下载,内含源码和使用案例,这些资源可帮助你更好地理解和应用蓝牙打印功能,满足你的业务需求。