前言
前阶段由于公司项目需要,要通过蓝牙连接打印机打印东西。由于公司技术栈是vue,并用cordova打包,因此寻找和使用合适的cordova插件成了解决这个需求的关键,不过cordova插件库的插件还是挺多的。
经过一番探索和调研,在IOS上面大部分人使用的都是cordova-plugin-ble-central这个插件,这个插件还是很不错的,而且有很多人还特地翻译成了中文版。具体的api就不一一介绍了,作为一个api调用师,这点基础还是要有的(误)。重点说名几个常用的:
ble.startScan,ble.stopScan
一般获取周围的蓝牙列表时通常使用这两个api,方便在于可以控制在几秒之后停止扫描。使用如下
ble.startScan([],function(info) {
list.push(info); //需要将扫描到的蓝牙数据存储
}, function (error) {
console.log('error');
});
setTimeout(function() {
ble.stopScan(
function() {
callback&&callback(list); //停止后 将拿到的列表传出去
},
function(error) {
console.log('error');
}
);
}, 3000);
正常情况的话,我们这时候能拿到周围的蓝牙信号列表了,但是有很多是不知名的蓝牙信号,没有名字,只有一个id,记得过滤。获取到的蓝牙信息格式如下:
{
"name": "TI SensorTag",
"id": "BD922605-1B07-4D55-8D09-B66653E51BBA",
"rssi": -79,
"advertising": /* ArrayBuffer or map */
}
一般用到的也就是name和id。android和ios在id的显示上面还长得不一样。
ble.connect
接着就开始连接了,连接的话,比较简单,就是传一个蓝牙的id,进去就可以了。
ble.connect(id,successFn,failFn);
ble.write
接着就是写入,前面的几个阶段还是挺顺利的。但是难点就是在这个写入的操作,这里卡了我好久,真的差点因此引咎辞职了(哭)。问题就出在不了解打印机的操作原理。正常在连接之后,在IOS端会返回一串信息。
{
"name": "*******",
"id": "BD922605-1B07-4D55-8D09-B66653E51BBA",
"advertising": {},
"rssi": -50,
"services": ["1800", "180a", "49535343-fe7d-4ae5-8fa9-9fafd205e455"],
"characteristics": [{
"service": "180a",
"characteristic": "2a2a",
"properties": ["Read"]
}, {
"service": "49535343-fe7d-4ae5-8fa9-9fafd205e455",
"characteristic": "49535343-6daa-4d02-abf6-19569aca69fe",
"properties": ["Read", "Write"]
}, {
"service": "49535343-fe7d-4ae5-8fa9-9fafd205e455",
"characteristic": "49535343-aca3-481c-91ec-d85e28a60318",
"properties": ["Write", "Notify"],
"descriptors": [{
"uuid": "2902"
}]
}, {
"service": "49535343-fe7d-4ae5-8fa9-9fafd205e455",
"characteristic": "49535343-8841-43f4-a8d4-ecbe34729bb3",
"properties": ["WriteWithoutResponse", "Write"]
}]
}
大致类似这样的,我们需要做的就是拿到属性properties为WriteWithoutResponse"和"Write的对象的service和characteristic。所谓properties就是蓝牙的操作权限,拥有了上面的两个值,我们对蓝牙打印机的操作才有反应,之前我测试使用的都是仅仅有一个Write的属性,因此什么反应都没有。
ble.write(id,serverID,characteristicID,data,successFn,failFn);
IOS上面 的写入需要三个参数值,id就是蓝牙的id,serverID和characteristicID就是上面获取的两个值。还需注意的一点就是一些蓝牙打印机的命令和中文文字的写入问题,也就是上面代码的 data。
let data = new Uint8Array([0x1B,0x40,0x1B]);
let buffer = data.buffer;
而且,传入的格式必须通过Uint8Array这样的类型化数组,然后通过data.buffer取到ArrayBuffer写入进去。
中文问题
但是文字如果也是这样写入的话,打印出来基本就是乱码了,问题在于蓝牙打印机接受的是GBK形式的字符,而我们传入的文字基本是utf-8的。因此需要做文字格式的转换。
const iconvLite = require('iconv-lite');
let result = iconvLite.encode(this.string, 'GBK');
通过引入iconv-lite这个转换包,我们就可以拿到传入文字的GBK形式了。感谢Node,而且这个包还是内置的,直接引用就有了。
至此,在IOS上面我们就能顺利打印东西出来了
andorid打印问题
上面那个插件应该也能支持在android上面的打印功能,不过上面部分方法需要的参数实在是多。如果仅仅是要开发android手机上的功能的话,推荐另一个插件cordova-plugin-bluetooth-serial。介绍几个常用的方法
bluetoothSerial.list
注意这是获取已配对的蓝牙设备,因此就需要事先匹配好蓝牙设备。
bluetoothSerial.list(success, failure);
bluetoothSerial.connect;
连接方法也比较简单,直接传入一个蓝牙id就行。不过,要是通过上面的list获取到的内容太的话会以address这样的参数来返回蓝牙设备的id。
bluetoothSerial.connect(id,successFn,failFn);
bluetoothSerial.write
写入的话也是比较直接,data也是ArrayBuffer的形式,具体格式问题参照cordova-plugin-ble-central写入参数的处理方法。
bluetoothSerial.write(data, success, failure);
总结
其实两个插件的官方说明都是能同时支持android和ios的蓝牙操作,但是实际中的效果怎么样要大家自己操作才能知道。由于时间问题,两个插件并没有深入研究,我的项目中是直接将两种插件结合起来,并能实现打印的效果。不过,最好的情况还是使用一个插件直接解决。
许久未写博客,文章的规范和描述有点粗糙。以后还需继续改进。 (需要吐槽的是,new Uint8Array([]),传入几个打印机的16进制操作码,掘金的敏感字系统竟然报错,搞得我找了好久,有点坑)