公司最近有一个项目需求需要标签打印机打印出标签来给一线员工门店使用, 为了控制成本, 还有能实现离线使用, 只能选择便宜的蓝牙标签打印机, 我也即将踏上关于蓝牙模块的成长历程.
搜素设备
根据官方文档的蓝牙指引开始, 我们首先初始化蓝牙 uni.openBluetoothAdapter
, 然后在成功之后就可以监听蓝牙状态变化uni.onBluetoothAdapterStateChange
和监听蓝牙连接和断开uni.onBLEConnectionStateChange
(这里需要注意, 这个方法在APP上可能无效, 还是要在指定时间维护状态), 再监听搜索到新设备的回调uni.onBluetoothDeviceFound
.
1 | uni.openBluetoothAdapter({ |
成功注册好回调函数之后, 再调用开始搜索周围设备方法uni.startBluetoothDevicesDiscovery
开始搜索, 这里可以指定服务ID, 搜索出来的设备就可以自动过滤.
连接设备
搜索到指定设备之后, 就可以链接设备了, 直接调用uni.createBLEConnection
, 通过搜索列表获取到的设备ID连接, 然后就可以获取蓝牙服务和特征值, 然后通过uni.notifyBLECharacteristicValueChange
启用低功耗蓝牙设备特征值变化时的notify功能,订阅特征值(UniAPP存在一个问题, 连接设备之后不能立即获取服务, 会获取不到, 需要设置3秒左右的延迟).
如果还不理解蓝牙协议服务和特征值可以查看 BLE4.0教程二 蓝牙协议之服务与特征值分析
1 | async function onConnect(deviceId) { |
写入低功耗蓝牙数据
写入二进制数据要注意蓝牙最大传输单元, IOS默认支持160字节, 安卓默认20字节, 虽然安卓可以通过uni.setBLEMTU
设置最大传输单元, 但是部分设备可能会设置失败, 并且安卓安卓还会返回设置成功, 直接写入超标长度的数据会直接让设备卡死. 所以最好还是直接拆分数据分片写入 :
1 | async function writeData(cmd: Uint8Array, mtu: number, deviceId: string, serviceId: string, characteristicId: string) { |
TSPL协议适配
我手上目前的设备都是使用TSPL协议的设备, 他们差异也就再部分设备需要反正发送数据, 也就是内容需要颠倒180°, 需要把我发送的指令从后打印出来. 还有部分数据对文字颠倒的渲染也不一样.
由于我们打印的内容是动态不固定, 可能出现换行这些情况, 打印指令也就需要计算文字宽度, 也就判断下中文占满一个文字宽度, 英文只有一半
1 | function getStrLen(str: string) { |
通过计算这一行是否超出纸宽, 来判断是否换行, 还有根据文字大写来确定下一行的Y轴坐标. 动态拼接文本指令就可以了 TEXT x,y,"font",rotation,x-multiplication,y-multiplication,"content"
然后把整个指令内容转为GBK的二进制数据, 分片发送给设备就可以打印了
1 | function toGBKBytes(e: string) { |
打印指令一览
1 | DIRECTION 1 |