使用手机打印,按照连接方式区分,可以分为 局域网打印、云打印 和 蓝牙打印三种。之前已经写过两篇分别介绍了网络打印和云打印,这一篇咱就来聊聊蓝牙打印。
蓝牙入门
蓝牙分为两种:传统蓝牙和低功耗蓝牙。市面上大部分打印机,都同时支持这两者。
传统蓝牙(classic bluetooth),也叫 Bluetooth BR/EDR(Basic Rate 基础速率/Enhanced Data Rate 增强数据率),也叫SPP(Serial Port Profile),主要应用在蓝牙2.0/2.1。传统蓝牙连接慢(几百毫秒),数据包长度较长,传输速率快。一般用于语音、音乐等数据量较大的场景。
低功耗蓝牙,也叫BLE(Bluetooth Low Energy),主要应用在蓝牙4.0/4.1/4.2。BLE连接快(几毫秒),数据包短,传输速率慢。一般用于键盘鼠标,温度传感器等数据量较小的场景。
蓝牙的传输速率只有网络速率的零头(蓝牙5.0理论速率 1.4Mbps,Wifi6单天线即可达到1200Mbps),在使用图片指令打印较宽尺寸的纸张(如241或A4)时,传输速率是最大的瓶颈。为了传输速率稍微快一丢丢,在能使用SPP的打印场景,应当优先使用SPP。
然而,iOS只能搜到通过MFi认证的传统蓝牙设备。大部分打印机厂商都没交苹果税,iOS是搜不到打印机的。比如,我手头一台旧款中崎打印机,只提供传统蓝牙,iOS就用不了。所幸,苹果对BLE没有做限制。在iOS上能搜到的打印机,基本上用的是BLE。出于兼容考虑,本文后续内容,只讨论BLE蓝牙打印。
开始调试蓝牙之前,除了打开手机的蓝牙以外,也要开启位置权限。因为在安卓6.0以后,必须要申请location权限才能支持BLE的一些功能,比如搜索设备。安卓做出这种限制也是非常严谨,因为蓝牙是一种近距离通信技术,一旦蓝牙设备连上手机之后,蓝牙设备就可以知道自己的位置就是手机的位置,相当于变相获取了手机位置,需要给用户相应的提示。
连接打印机
BLE蓝牙设备分两种角色:中央设备(Central)和外围设备(Peripheral)。手机是中央设备,蓝牙打印机是外围设备。连接只能建立在中央和外围设备之间,一个中央设备可以连多个外围设备,但中央不能连接中央,外围也不能连接外围。
外围设备会定期发广播(通常100ms一次),中央设备搜索广播,与外围设备建立链接。连上打印机设备(device)以后,可以查看这台设备提供了哪些服务(service);每个服务下,又可以查看提供了哪些特征值(characteristic);每个特征值,又可以查看提供哪些能力(propertie);能力分为:read(读)、write(写)、notify(通知)、indicate(指示)等。
蓝牙打印机有某个具体的characteristic,负责接收打印数据。但是不同品牌的打印机,这个characteristic是不一样的。先通过广播包中的 LocalName字段(蓝牙设备名),区分打印机型号;再用不同方式找到characteristic。我对接了十几个不同品牌打印机,总结了三种方式:
- 跟打印机厂商合作定制的型号,固定使用某个特征值,跟市面上其他型号都区分开。目前我合作过的厂商包括 商为、芝柯、普贴、趣印等。
- 只有一个特征值,如凯盛诺打印机;或所有特征值的uuid都一样,如斑马打印机。这种直接找一个具备write能力的特征值都能用。
- 找第一个具备 write能力、且具备notify或indicate能力的特征值。这种方法兼容市面上大部分的打印机,目前我对接过的有 芯烨、佳博、精臣、得力、映美 等。
发送数据
知道该用哪个特征值,手机就可以往这个characteristic发送打印数据了。发数据的方式有两种:有回复写(write with response)和 无回复写(write without response)。有回复写的时候,手机发完数据包,打印机会回复写入响应,确认数据写入成功;无回复写的时候,手机只管发数据,不用确认数据是否发送成功。通常两种都可以,但也有一些打印机会要求用其中一种,这个对接的时候跟厂商确认一下即可。
打印机会限制每次传输数据的大小,一次性发送太多数据会失败,所以需要把打印机数据分批次写入。如果想兼容大部分的打印机,建议每次写入数据20字节。如果想要更快的传输速率,也可以跟厂商沟通定制,比如我们合作的一个厂商,给我们定制了一款蓝牙缓冲区500字节的设备,单次就能写几百字节的数据。
发送给打印机的打印指令,称为“页面描述语言”(Page description language),通常打印小票用 ESC,打印标签用 TSPL 或 CPCL,之前讲网络打印那篇博客已经介绍过了,此处不再赘述。有些打印机在通用指令的基础上,还会增加一些私有的指令,如设置打印浓度、设置打印速度、走纸、切纸、清除缓冲区等。手机端没有打印机驱动,要么对接厂商提供的SDK,要么自己生成指令的时候,自己加上这些特殊指令。
Show me the code!
我用微信小程序实现了一个打印的Demo,流程和用到的API如下:
下面讲下细节。首先,搜索蓝牙的时候,经常能搜到很多其他设备蓝牙信号。我们过滤掉LocalName为空的设备,方便用户选择打印机发出的信号。搜索蓝牙也是一个耗系统资源的行为,在搜到设备后要及时停止搜索,并把deviceId缓存起来,这样下次需要打印的时候可以直接连接,不用再搜索。
确定device并建立连接,获取打印所需的 serviceId 和 characteristicId(上文已经讲过方案),就可以开始发数据了。发完数据及时断开连接,避免占用打印机。serviceId 和 characteristicId 也可以存起来,但iOS下每次打印都得先调用wx.getBLEDeviceServices()和wx.getBLEDeviceCharacteristics(),否则写数据可能会失败。
传输的打印指令都是二进制数据,Demo里用了最简单的ESC指令,转为ArrayBuffer。如果需要打印中文,还得先把内容转化为中文编码,不然打印机会把utf8中文字符转成16进制打处理,就都是乱码了。打印自检页,通常有显示支持的指令集和编码,现在国内大部分打印机用的GB18030编码。网上的中文编码库都比较重,所以我自己写了个简单高效的,具体看Demo代码即可。
结语
与网络打印和云打印相比,蓝牙传输速度慢慢,抗干扰能力也差。尤其纸张较宽的针式打印机,或需要打印数据量较大的图片指令,不建议使用蓝牙打印。
但蓝牙也有不可替代的优势:成本低、应用广,大部分小票和标签打印机都支持;适用车销、摆摊等缺少网络场景;手机连接便捷,不需要配置网络等。
本文介绍了BLE蓝牙打印的入门知识,并提供了微信小程序蓝牙打印demo。感谢合作的各家硬件厂商,让我在对接过程中也学到了不少知识。
本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。