uniapp对接打印机和电子秤
连接电子秤和打印机,最难的不是连接蓝牙和电子成,而是打印机。因为打印机涉及到向打印机写数据操作,然后这个写的数据需要做一个编码转换。难就难在编码转换。如果是
java
那就是一句代码的事情,而js
就没有那么简单了。其实js
也是一句代码的事情,打印机接收的的编码为GBK
,但是hbuilderx
编码为UTF-8
。编码转换我们可以使用encoding.js
或者GBK.js
,但是这些引入方式为var encode = require("./encoding.js");
。Vue3
已经废弃require
这种引入方式,所以没有办法引入 。使用new TextDecoder('gbk').decode(codes)
可以解决,但是只能在浏览器里面解决,手机不支持这个命令。所有将编码转换成GBK
,并且转换后返回10
进制的数组打印。打印机编码可以设置为UTF-8
,但是不靠谱,不通用,这样也面临着数据转成10
进制后似乎也有问题。我找到一个文件进过更改后可以解决这个问题,下面对接打印机详细讲解。
一、连接蓝牙
参考:https://uniapp.dcloud.net.cn/api/system/bluetooth.html#openbluetoothadapter
- 初始化蓝牙模块
- 开始搜寻附近的蓝牙外围设备
- 监听寻找到新设备的事件(这里可以设备信息)
- 连接低功耗蓝牙设备。
- 获取蓝牙设备所有服务(service)。
- 根据设备获取蓝牙特征值(一个设备特征值很多,这个设备 支持读,写,异步监听等就看这个特征值了。)
1.1、初始化蓝牙
// 初始化蓝牙
uni.openBluetoothAdapter({success: (res) => {that.connectLog.push("蓝牙初始化完成")},fail: (res) => {}
});
1.2、开始搜寻附近的蓝牙外围设备
这异步需要初始化蓝牙完成在操作
uni.startBluetoothDevicesDiscovery({// services: ['0000FFE0'],success: res => {},fail: res => {that.connectLog.push("查找设备失败")}
});
1.3、发现外围设备
这一步才是真正的搜索附近的蓝牙设备
uni.onBluetoothDeviceFound(res => {console.log(res)
});
这里会频繁的调用,因为搜索到一个设备调用一次。
注意:deviceId,name要保存下来,后面需要使用。
1.4、连接蓝牙设备
uni.createBLEConnection({ //创建蓝牙连接,连接低功耗蓝牙设备deviceId: item.deviceId, //传入刚刚获取的uuidsuccess(res) {},fail(res) {that.connectLog.push("创建连接失败")}
})
这里的
deviceId
既是uni.onBluetoothDeviceFound
中过去到的deviceId
1.5、获取蓝牙设备所有服务
蓝牙已经连接成功了,获取这个蓝牙设备有哪些服务,获取蓝牙特征值需要使用蓝牙服务的uuid
。因为每个蓝牙有很多服务,每个服务的特征值不同,需要根据蓝牙特征值来寻找是否可以写,可以读,可以监听。
uni.getBLEDeviceServices({ //获取蓝牙设备所有服务deviceId: deviceId,success(res) { //为什么要用延时,因为不用延时就拿不到所有的服务,在上一步,连接低功耗蓝牙//设备的时候,需要一个600-1000毫秒的时间后,再去获取设备所有服务,不给延时就会一直返回错误码10004console.log("蓝牙可用服务:", res)},fail(res) {console.log("搜索蓝牙服务失败:", res)that.connectLog.push("搜索蓝牙服务失败")}
})
这里的
deviceId
既是uni.onBluetoothDeviceFound
中过去到的deviceId
1.6、获取蓝牙特征值
uni.getBLEDeviceCharacteristics({ //获取蓝牙设备某个服务中所有特征值deviceId: that.deviceId,serviceId: item.uuid, //这个serviceId可以在上一步获取中拿到,也可以在success(res) {console.log("获取特征值:", res)},fail(res) {}
})
这里的
deviceId
既是uni.onBluetoothDeviceFound
中获取到的deviceId
serviceId
获取蓝牙服务时获取到的服务uuid
一台蓝牙设备有多个服务,一个服务可以获取多个蓝牙特征值,需要循环所有的蓝牙服务,去获取蓝牙特征值,找到符合自己需求的特征值即可,除非蓝牙设备不支持。
1.7、连接蓝牙完整步骤
// 第一步 在页面显示的时候判断是否已经初始化完成蓝牙适配器若成功,则开始查找设备
openBluetoothAdapter() {let that = this// 初始化蓝牙uni.openBluetoothAdapter({success: (res) => {// 初始化完毕开始搜索that.StartBluetoothDeviceDiscovery()},fail: (res) => {}});
},
/*** 第二步 在页面显示的时候判断是都已经初始化完成蓝牙适配器若成功,则开始查找设备*/
StartBluetoothDeviceDiscovery() {let that = thisuni.startBluetoothDevicesDiscovery({// services: ['0000FFE0'],success: res => {that.OnBluetoothDeviceFound();},fail: res => {that.connectLog.push("查找设备失败")}});
},
/*** 第三步 发现外围设备*/
OnBluetoothDeviceFound() {let that = thisuni.onBluetoothDeviceFound(res => {res.devices.forEach(device => { //这一步就是去筛选找到的蓝牙中,有没有你匹配的名称if (device.name === "MPT-II") {// 连接蓝牙that.CreateBLEConnection(device)// 找到需要连接的蓝牙了,可以关闭蓝牙搜索了。that.StopBluetoothDevicesDiscovery()}})});
},//
/*** 第四步 停止搜索蓝牙设备*/
StopBluetoothDevicesDiscovery() {let that = thisuni.stopBluetoothDevicesDiscovery({success: res => {console.log("第四步 找到匹配的蓝牙后就关掉蓝牙搜寻:", JSON.stringify(res))},fail: res => {console.log('第四步 停止搜索蓝牙设备失败,错误码:' + res.errCode);}});
},
// 第五步 创建蓝牙连接,连接低功耗蓝牙设备
CreateBLEConnection(item) {let that = thisuni.createBLEConnection({ //创建蓝牙连接,连接低功耗蓝牙设备deviceId: item.deviceId, //传入刚刚获取的uuidsuccess(res) {that.GetBLEDeviceServices(item.deviceId) //获取蓝牙设备所有服务(service)。},fail(res) {that.connectLog.push("创建连接失败")}})
},//第六步 获取蓝牙设备所有服务(service)。
GetBLEDeviceServices(deviceId) {let that = thissetTimeout(() => {uni.getBLEDeviceServices({ //获取蓝牙设备所有服务deviceId: deviceId,success(res) { //为什么要用延时,因为不用延时就拿不到所有的服务,在上一步,连接低功耗蓝牙//设备的时候,需要一个600-1000毫秒的时间后,再去获取设备所有服务,不给延时就会一直返回错误码10004console.log("蓝牙可用服务:", res)that.GetBLEDeviceCharacteristics(deviceId, res) //获取蓝牙设备某个服务中所有特征值},fail(res) {console.log("搜索蓝牙服务失败:", res)that.connectLog.push("搜索蓝牙服务失败")}})}, 1000)
},
// 第七步 获取蓝牙特征值
GetBLEDeviceCharacteristics(deviceId, item) {let that = this// 获取当前连接这个蓝牙的可用服务setTimeout(() => {item.services.forEach((services) => {console.log("服务:", services)uni.getBLEDeviceCharacteristics({ //获取蓝牙设备某个服务中所有特征值deviceId: deviceId,serviceId: services.uuid, //这个serviceId可以在上一步获取中拿到,也可以在success(res) {console.log("特征值", res)// 循环筛选蓝牙特征值,筛选到符合打印机的特征值为止res.characteristics.forEach((ch) => {// 判断是否支持打印if (ch.properties.write) {// 连接蓝牙最终就是使用者三个值。console.log("蓝牙设备deviceId:", deviceId)console.log("使用蓝牙服务uuid:", services.uuid)console.log("特征值uuid:", ch.uuid)// 这里不能结束最外层循环,就让他全部循环完成把,反正也不多。return}})},fail(res) {that.connectLog.push("获取特征值失败")console.log("获取蓝牙设备某个服务中所有特征值失败:", JSON.stringify(res))}})})}, 2000)
},
二、对接电子秤
2.1、使用
uni.notifyBLECharacteristicValueChange({state: true, // 启用 notify 功能deviceId: that.deviceId, // 蓝牙设备 deviceIdserviceId: that.serviceId, // 蓝牙服务uuidcharacteristicId: that.characteristicId, // 蓝牙特征值uuidsuccess(res) {console.log('订阅电子秤成功', res.errMsg)// 电子秤回调事件uni.onBLECharacteristicValueChange(function(res) {const buffer = new Uint8Array(res.value);var dataString = "";for (var i = 0; i < buffer.length; i++) {dataString += String.fromCharCode(buffer[i]);}that.weight = dataString})}
})
需要的三个参数,连接蓝牙,的时候就说过怎么获取了。
这里千万需要注意: uni.onBLECharacteristicValueChange
返回的是重量,这里要看电子秤厂商给我们返回啥。我的电子秤是和厂商说好的,值返回重量,所以这里直接把ArrayBuffer转成普通文本,我就能得到了重量。
2.2、完整案列
注意:连接电子秤部分,我直接将电子秤的蓝牙名称写死了,这样方便操作,今后电子秤蓝牙名称不同的,需要更改。
<template><!-- 打印 --><view class="balance-box"><!-- 操作区 --><view class="operation-box"><button @click="openBluetoothAdapter">连接蓝牙</button><input class="balance-input" :value="weight" placeholder="这里是电子秤返回数据" /></view><!-- 显示连接蓝牙日志 --><view class="connect-log"><view v-for="(log,index) in connectLog" :key="index">{{log}}</view></view></view>
</template><script>export default {data() {return {connectLog: [], // 日志weight: null, // 获取到的重量deviceId: null, // 蓝牙设备的 deviceId// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取serviceId: null, // 蓝牙服务uuid// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取characteristicId: null, // 蓝牙特征值uuid}},methods: {openBluetoothAdapter() {let that = this// 初始化蓝牙uni.openBluetoothAdapter({success: (res) => {// 初始化完毕开始搜索that.StartBluetoothDeviceDiscovery()},fail: (res) => {}});},/*** 第二步 在页面显示的时候判断是都已经初始化完成蓝牙适配器若成功,则开始查找设备*/StartBluetoothDeviceDiscovery() {let that = thisuni.startBluetoothDevicesDiscovery({// services: ['0000FFE0'],success: res => {that.OnBluetoothDeviceFound();},fail: res => {// that.connectLog.push("查找设备失败")}});},/*** 第三步 发现外围设备*/OnBluetoothDeviceFound() {let that = thisuni.onBluetoothDeviceFound(res => {res.devices.forEach(device => { //这一步就是去筛选找到的蓝牙中,有没有你匹配的名称if (device.name === "ANDZ") {// 连接蓝牙that.CreateBLEConnection(device)// 找到需要连接的蓝牙了,可以关闭蓝牙搜索了。that.StopBluetoothDevicesDiscovery()}})});},// /*** 第四步 停止搜索蓝牙设备*/StopBluetoothDevicesDiscovery() {let that = thisuni.stopBluetoothDevicesDiscovery({success: res => {console.log("第四步 找到匹配的蓝牙后就关掉蓝牙搜寻:", JSON.stringify(res))},fail: res => {console.log('第四步 停止搜索蓝牙设备失败,错误码:' + res.errCode);}});},// 第五步 创建蓝牙连接,连接低功耗蓝牙设备CreateBLEConnection(item) {let that = thisuni.createBLEConnection({ //创建蓝牙连接,连接低功耗蓝牙设备deviceId: item.deviceId, //传入刚刚获取的uuidsuccess(res) {that.GetBLEDeviceServices(item.deviceId) //获取蓝牙设备所有服务(service)。},fail(res) {}})},//第六步 获取蓝牙设备所有服务(service)。GetBLEDeviceServices(deviceId) {let that = thissetTimeout(() => {uni.getBLEDeviceServices({ //获取蓝牙设备所有服务deviceId: deviceId,success(res) { //为什么要用延时,因为不用延时就拿不到所有的服务,在上一步,连接低功耗蓝牙//设备的时候,需要一个600-1000毫秒的时间后,再去获取设备所有服务,不给延时就会一直返回错误码10004console.log("蓝牙可用服务:", res)that.GetBLEDeviceCharacteristics(deviceId, res) //获取蓝牙设备某个服务中所有特征值},fail(res) {console.log("搜索蓝牙服务失败:", res)}})}, 1000)},// 第七步 获取蓝牙特征值GetBLEDeviceCharacteristics(deviceId, item) {let that = this// 获取当前连接这个蓝牙的可用服务setTimeout(() => {item.services.forEach((services) => {console.log("服务:", services)uni.getBLEDeviceCharacteristics({ //获取蓝牙设备某个服务中所有特征值deviceId: deviceId,serviceId: services.uuid, //这个serviceId可以在上一步获取中拿到,也可以在success(res) {console.log("特征值", res)// 循环筛选蓝牙特征值,筛选到符合打印机的特征值为止res.characteristics.forEach((ch) => {// 判断是否支持打印if (ch.properties.notify) {// 连接蓝牙最终就是使用者三个值。// console.log("蓝牙设备deviceId:", deviceId)// console.log("使用蓝牙服务uuid:", services.uuid)// console.log("特征值uuid:", ch.uuid)that.deviceId = deviceIdthat.serviceId = services.uuidthat.characteristicId = ch.uuid// 这里不能结束最外层循环,就让他全部循环完成把,反正也不多。setTimeout(() => {that.onNotifyBLECharacteristicValueChange()}, 1500)return}})},fail(res) {console.log("获取蓝牙设备某个服务中所有特征值失败:", JSON.stringify(res))}})})}, 2000)},// 监听电子秤onNotifyBLECharacteristicValueChange() {let that = thisuni.notifyBLECharacteristicValueChange({state: true, // 启用 notify 功能deviceId: that.deviceId, // 蓝牙设备 deviceIdserviceId: that.serviceId, // 蓝牙服务uuidcharacteristicId: that.characteristicId, // 蓝牙特征值uuidsuccess(res) {console.log('订阅电子秤成功', res.errMsg)// 电子秤回调事件uni.onBLECharacteristicValueChange(function(res) {const buffer = new Uint8Array(res.value);var dataString = "";for (var i = 0; i < buffer.length; i++) {dataString += String.fromCharCode(buffer[i]);}that.weight = dataString})}})},}}
</script><style lang="scss">.balance-box {display: flex;.operation-box {width: 70%;overflow: auto;.balance-input {height: 80rpx;border-radius: 10rpx;border: 2rpx solid saddlebrown;}}.connect-log {display: flex;flex-direction: column;position: sticky;top: 0;width: 30%;color: #fff;height: 100vh;overflow: auto;background-color: rgba(0, 0, 0, 0.2);view {margin: 10rpx 0;}}}
</style>
三、对接打印机
3.1、对接打印机
// 第八步 发送二进制数据
WriteBLECharacteristicValue() {// returnlet that = this// 打印的内容uni.writeBLECharacteristicValue({// 蓝牙设备 deviceIddeviceId: that.deviceId,// 蓝牙服务uuidserviceId: that.serviceId,// 蓝牙特征值uuidcharacteristicId: that.characteristicId,// 打印的数据,ArrayBuffer 类型,数据为 10进制或者16进制。编码方式:GBKvalue: [178, 226, 202, 212, 163, 161, 163, 161, 163, 161, 13, 10],success(res) {console.log("打印成功")},fail(res) {console.log("打印失败", res)}})
},
对接打印机,难点在value的值,这个是ArrayBuffer类型的,这个里面可以放10进制和16进制,我是使用10进制的。例如:[27,100,2]表示走纸2行[27,100,3]表示走纸三行。为什么是[27,100,n]这个要看打印机的指令说明文档,里面有10进制和16进制的。不同厂商的打印机指令有差异。
并且value的数据最多只能有20个,多了需要拆分开,批量发送。
3.2、编码转换
编码转换就是在前言所说的这个困惑,由于我使用的是Vue3
,所以不支持var encode = require("./encoding.js");
方式引入。编码转换的js
有 encoding.js
和 GBK.js
,但是这两种都是require
引入的方式,所以Vue3
无法使用,uniapp
的手机端 不支持 new TextDecoder('gbk').decode(codes)
,百思不得其解的时刻,偶然间发现 GBK.js
的另外一个版本有改动的希望,所以我使用 GBK.js
的另外一个版本改动了一下,成功解决这个困惑。
3.2.1、GBK文件改动
文件来源:https://github.com/EtherDream/str2gbk
这个文件在Vue3
中可以使用,但是有一个地方使用了 new TextDecoder('gbk').decode(codes)
,因为手机端不支持这个,但是浏览器支持。看看GBK.js
源代码。
最终看到这里,因为这个js
文件里面的 table
数组的数据的来源是,根据codes
参数通过 new TextDecoder('gbk').decode(codes)
转换得到的,而 codes
参数又是通过一段算法计算得到,都与我们的参数无关,可以说是一段死的数据。那么我们就可以在浏览器里面执行,将最终转换后的 table
集合数据获取到,直接卸载文件里面,这样就可以解决 UTF-8
编码转为GBK
编码的问题,并且转换后 是10
进制的 Uint8Array
数组,刚好符合打印条件。转换后的gbk.js
这个文件已经放在当前目录的 doc
文件夹下面。
3.2.2、使用GBK.js
- 引入文件
import gbk from "./gbk.js"
- 使用
let data = gbk("这是打印的内容,不得有误。")
console.log(data)
- 返回数据案例
213,226,202,199,180,242,211,161,181,196,196,218,200,221,163,172,178,187,181,195,211,208,206,243,161,163
是个数组,只是打印出来的时候是这样子而已。这个数据可以直接打印了,只是不能超过20个,多的话需要拆分发送,3.3拆分打印数据有现成方法。
3.3、拆分打印数据
因为 value
最多只能发送20个,所以需要拆分批量发送。
/*** 拆分打印数据并打印,将uint8Array打印的数据拆分成,最多 20* @param {Object} deviceId 蓝牙设备deviceId* @param {Object} serviceId 服务uuid* @param {Object} characteristicId 蓝牙特征值uuid* @param {Object} uint8Array 打印数据*/
senBlData(deviceId, serviceId, characteristicId, uint8Array) {var that = this;console.log('************deviceId = [' + deviceId + '] serviceId = [' + serviceId +'] characteristics=[' + characteristicId + "]")var uint8Buf = Array.from(uint8Array);function split_array(datas, size) {var result = {};var j = 0for (var i = 0; i < datas.length; i += size) {result[j] = datas.slice(i, i + size)j++}console.log(result)return result}var sendloop = split_array(uint8Buf, 20);function realWriteData(sendloop, i) {var data = sendloop[i]if (typeof(data) == "undefined") {return}console.log("第【" + i + "】次写数据" + data)var buffer = new ArrayBuffer(data.length)var dataView = new DataView(buffer)for (var j = 0; j < data.length; j++) {dataView.setUint8(j, data[j]);}// 调动打印机打印uni.writeBLECharacteristicValue({deviceId,serviceId,characteristicId,value: buffer,success(res) {realWriteData(sendloop, i + 1);},fail(e) {console.log("点错误:", e)realWriteData(sendloop, i + 1);}})}var i = 0;realWriteData(sendloop, i);
},
3.4、打印指令讲解
打印指令就像是命令,发送什么样的命令干什么样的事情。打印机的指令有十进制和十六进制两种,如果使用十六进制的,会遇到 x011,0x0a,0x1c,0x10
等,而这里的 x0
在C
语言里面表示指令的意思,没有其他作用,但是发送的时候得这样写,真正的指令是后面的 11,0a,1c,10
。
不同打印机的指令不同,估计大多都一样把,我目前使用的打印机是 “便捷式打印机MoilePrinter”这种。
特备强调:打印机如果设置了,没有恢复,那么后面打印的所有内容都是按照设置后的打印,除非发送设置默认命令或者关机重启。例如:将行间距设置为150,那么后面打印的所有内容都是150行间距,如果发送了恢复行间距了,后面打印的行间距为默认行间距。
3.4.1、打印机指令使用说明
首先拿着打印机指令说明,需要看看打印机参数。比如:编码方式,多少点每行。这些都是需要使用的到,必须了解。
说明书里面的指令列表可以不同看,最关键的是指令详解。
案列
- 走纸60 行
// 打印的内容
uni.writeBLECharacteristicValue({// 蓝牙设备 deviceIddeviceId: that.deviceId,// 蓝牙服务uuidserviceId: that.serviceId,// 蓝牙特征值uuidcharacteristicId: that.characteristicId,// 打印的数据,ArrayBuffer 类型,数据为 10进制或者16进制。编码方式:GBKvalue: [27,100,60],success(res) {console.log("打印成功")},fail(res) {console.log("打印失败", res)}
})
- 打印
这是测试打印机
并走纸 2 行
printData() {// 打印的数据let command = []// 打印的数据,这里的 \r\n表示换行,必须要进过转码let data = gbk("这是测试打印机")data.forEach((d) => {command.push(d)})// 打印指令let instruct = [27, 100, 2]// 将打印指令增加到 打印的数据中,因为是打印为在走纸两行,所以说 27,100,2 要增加在打印数据的后面,【 27,100,2 这三个数字就相当于中文对人说 ,走纸两 行。而这里是对机器说走纸两行,机器的走纸两行的语言就是 27,100,2】instruct.forEach((d) => {command.push(d)})// 这里打印的数据可能超过20个字符了,所有拆分批量打印。this.senBlData(this.deviceId, this.serviceId, this.characteristicId, command)
},
“这是测试打印机”这几个中文通过 gbk()
转码后得到的Uint8Array
数组是 213, 226, 202, 199, 178, 226, 202, 212, 180, 242, 211, 161, 187, 250
,打印并走纸两行 的十进制是 27,100,2
。是先打印然后在走纸,所以走纸的命令要增加在后面,所以最终打印数据为:
213, 226, 202, 199, 178, 226, 202, 212, 180, 242, 211, 161, 187, 250, 27, 100, 2
3.4.1、打印并进纸n行
27 100 n
0 ≤ n ≤ 255
3.4.2、设置行间距
- 实例值
27, 51, 255
3.4.3、设置行间距为默认
- 参数
27, 50
这个必须是这样,没有其他任何参数,因为是设置为默认。
3.4.4、字体大小
- 设置字2倍宽
29, 33, 16
- 设置字体2倍高度
29, 33, 1
- 设置字体2倍宽和2倍高
直接在打印数组里面增加就行。
29, 33, 1, 29, 33, 16
- 恢复字体宽高
直接使用 29, 33, 0 字体的宽高都恢复默认了。
29, 33, 0
3.4.5、字体对齐方式
- 字体居左
27,97,0 或者 27,97,48
- 字体居中
27,97,1 或者 27,97,49
- 字体居右
27,97,2 或者 27,97,50
3.4.6、打印一维码
设置一维条码可读字符(HRI)打印位置
- 实例
29,72,0
29,72,1
29,72,2
29,72,3
或者
29,72,48
29,72,49
29,72,50
29,72,51
设置一维条码高度
- 实例值
29,104,100
设置一维条码宽度
- 实例值
29,119,3
打印一维条码
这里分为 A 和 B两种情况,并且参数都有了,自己仔细观察。
- 实例值
29,107,0,4829,107,65,11,48
// 打印一维码
let instruct = [29, 72, 2, 29, 104, 100, 29, 119, 6, 29, 107, 0, 48]
// let instruct = [29, 72, 3, 29, 104, 255, 29, 119, 1, 29, 107, 65, 11, 50]
let data = gbk("12345678901")
3.4.7、二维码打印
特别注意:这里的k计算出来是多个,那么二维码打印的内容必须就是多少个字节,如果k计算出来是2个字节,打印的内容才有1个或者3个字节都是错的。
- 实例值
29,107,97,0,1,2,0
// 打印二维码
let instruct = [29,107,97,0,1,2,0]
// 里面必须 2 个字节,一个汉字 2个字节
let data = gbk("12")
注意:这里如果是中文,那么将中文通过 gkb()
方法转换后,得到GKB
编码的10进制,那么扫描出来的编码也是GBK
,二维码里面最好不要保存中文。或者不要进行GBK转码,直接获取10进制数组。
3.4.8、完整案例
<template><!-- 打印 --><view class="print-box"><!-- 操作区 --><view class="operation-box"><button @click="onPrint">打印</button></view><!-- 显示连接蓝牙日志 --><view class="connect-log"><view v-for="(log,index) in connectLog" :key="index">{{log}}</view></view></view>
</template><script>import gbk from "./gbk.js"export default {data() {return {connectLog: [], // 日志deviceId: null, // 蓝牙设备的 deviceId// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取serviceId: null, // 蓝牙服务uuid// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取characteristicId: null, // 蓝牙特征值uuid}},methods: {// 打印onPrint() {// 判断是否连接蓝牙,已连接直接打印,未连接就去连接。if (this.deviceId) {this.printData()} else {this.openBluetoothAdapter()}},// 第一步 在页面显示的时候判断是否已经初始化完成蓝牙适配器若成功,则开始查找设备openBluetoothAdapter() {let that = this// 初始化蓝牙uni.openBluetoothAdapter({success: (res) => {that.connectLog.push("初始化蓝牙成功")// 初始化完毕开始搜索that.StartBluetoothDeviceDiscovery()},fail: (res) => {that.connectLog.push("初始化蓝牙失败")}});},/*** 第二步 在页面显示的时候判断是都已经初始化完成蓝牙适配器若成功,则开始查找设备*/StartBluetoothDeviceDiscovery() {let that = thisuni.startBluetoothDevicesDiscovery({// services: ['0000FFE0'],success: res => {that.connectLog.push("查找设备")that.OnBluetoothDeviceFound();},fail: res => {that.connectLog.push("查找设备失败")}});},/*** 第三步 发现外围设备*/OnBluetoothDeviceFound() {let that = thisuni.onBluetoothDeviceFound(res => {res.devices.forEach(device => { //这一步就是去筛选找到的蓝牙中,有没有你匹配的名称that.connectLog.push(`查找到设备${device.name}`)if (device.name === "MPT-II") {// 连接蓝牙that.CreateBLEConnection(device)// 找到需要连接的蓝牙了,可以关闭蓝牙搜索了。that.StopBluetoothDevicesDiscovery()}})});},// /*** 第四步 停止搜索蓝牙设备*/StopBluetoothDevicesDiscovery() {let that = thisuni.stopBluetoothDevicesDiscovery({success: res => {that.connectLog.push("关闭蓝牙搜索")console.log("第四步 找到匹配的蓝牙后就关掉蓝牙搜寻:", JSON.stringify(res))},fail: res => {that.connectLog.push("关闭蓝牙搜索失败")console.log('第四步 停止搜索蓝牙设备失败,错误码:' + res.errCode);}});},// 第五步 创建蓝牙连接,连接低功耗蓝牙设备CreateBLEConnection(item) {let that = thisthat.connectLog.push("创建蓝牙连接" + item.name)uni.createBLEConnection({ //创建蓝牙连接,连接低功耗蓝牙设备deviceId: item.deviceId, //传入刚刚获取的uuidsuccess(res) {that.connectLog.push("蓝牙连接成功")that.GetBLEDeviceServices(item.deviceId) //获取蓝牙设备所有服务(service)。},fail(res) {that.connectLog.push("蓝牙连接失败")}})},//第六步 获取蓝牙设备所有服务(service)。GetBLEDeviceServices(deviceId) {let that = thisthat.connectLog.push("开始搜索蓝牙服务")setTimeout(() => {uni.getBLEDeviceServices({ //获取蓝牙设备所有服务deviceId: deviceId,success(res) { //为什么要用延时,因为不用延时就拿不到所有的服务,在上一步,连接低功耗蓝牙//设备的时候,需要一个600-1000毫秒的时间后,再去获取设备所有服务,不给延时就会一直返回错误码10004console.log("蓝牙可用服务:", res)that.connectLog.push("搜索到蓝牙服务")that.GetBLEDeviceCharacteristics(deviceId, res) //获取蓝牙设备某个服务中所有特征值},fail(res) {that.connectLog.push("搜索蓝牙服务失败")console.log("搜索蓝牙服务失败:", res)}})}, 1000)},// 第七步 获取蓝牙特征值GetBLEDeviceCharacteristics(deviceId, item) {let that = thisthat.connectLog.push("开始获取蓝牙特征值")// 获取当前连接这个蓝牙的可用服务setTimeout(() => {item.services.forEach((services) => {console.log("服务:", services)uni.getBLEDeviceCharacteristics({ //获取蓝牙设备某个服务中所有特征值deviceId: deviceId,serviceId: services.uuid, //这个serviceId可以在上一步获取中拿到,也可以在success(res) {that.connectLog.push("蓝牙特征值获取成功")console.log("特征值", res)// 循环筛选蓝牙特征值,筛选到符合打印机的特征值为止res.characteristics.forEach((ch) => {// 判断是否支持打印机if (ch.properties.write) {// 连接蓝牙最终就是使用者三个值。// console.log("蓝牙设备deviceId:", deviceId)// console.log("使用蓝牙服务uuid:", services.uuid)// console.log("特征值uuid:", ch.uuid)that.deviceId = deviceIdthat.serviceId = services.uuidthat.characteristicId = ch.uuid// 这里不能结束最外层循环,就让他全部循环完成把,反正也不多。that.connectLog.push("找打符合条件特征值")return}})},fail(res) {that.connectLog.push("蓝牙特征值获取失败")console.log("获取蓝牙设备某个服务中所有特征值失败:", JSON.stringify(res))}})})}, 2000)},// 第八步 发送二进制数据WriteBLECharacteristicValue() {// returnlet that = this// 打印的内容uni.writeBLECharacteristicValue({// 蓝牙设备 deviceIddeviceId: that.deviceId,// 蓝牙服务uuidserviceId: that.serviceId,// 蓝牙特征值uuidcharacteristicId: that.characteristicId,// 打印的数据,ArrayBuffer 类型,数据为 10进制或者16进制。编码方式:GBKvalue: [27, 100, 60],success(res) {console.log("打印成功")},fail(res) {console.log("打印失败", res)}})},printData() {// 打印的数据let command = []// 打印的数据,这里的 \r\n表示换行,必须要进过转码// let data = gbk(// "这是打印的内容,我要写很多很多文字,然后打印出来看看看,什么行间距啊,什么间距啥的有没有设置好。如果没有设置好,自己看看到底是怪哪里,是我的编写问题,就更改。是厂商的问题,就寻找厂商。")// 设置行间距// let instruct = [27, 51, 255]// 设置行间距为默认 // let instruct = [27, 50]// 设置开始打印的位置// let instruct = [29, 33, 1, 29, 33, 16]// 字体恢复默认// let instruct = [29, 33, 0]// let data = gbk("这是打印的内容,反正是很多的那种,不止一行数据,自己看着办,结束。")// 居中字体// let instruct = [27, 97, 49]// 字体居中// let instruct = [27, 97, 1]// 字体居左// let instruct = [27, 97, 0]// let data = gbk("字体居左")// 字体居右// let instruct = [27, 97, 1]// let data = gbk("字体居右")// // 打印一维码// let instruct = [29, 72, 2, 29, 104, 100, 29, 119, 6, 29, 107, 0, 48]// // let instruct = [29, 72, 3, 29, 104, 255, 29, 119, 1, 29, 107, 65, 11, 50]// let data = gbk("12345678901")// 打印二维码let instruct = [29,107,97,0,1,2,0]// 里面必须 2 个字节,一个汉字 2个字节let data = gbk("12")// 将打印指令增加到 打印的数据中instruct.forEach((d) => {command.push(d)})data.forEach((d) => {command.push(d)})command.push(27)command.push(100)command.push(2)// 这里打印的数据可能超过20个字符了,所有拆分批量打印。this.senBlData(this.deviceId, this.serviceId, this.characteristicId, command)},/*** 拆分打印数据并打印,将uint8Array打印的数据拆分成,最多 20* @param {Object} deviceId 蓝牙设备deviceId* @param {Object} serviceId 服务uuid* @param {Object} characteristicId 蓝牙特征值uuid* @param {Object} uint8Array 打印数据*/senBlData(deviceId, serviceId, characteristicId, uint8Array) {var that = this;console.log('************deviceId = [' + deviceId + '] serviceId = [' + serviceId +'] characteristics=[' + characteristicId + "]")var uint8Buf = Array.from(uint8Array);function split_array(datas, size) {var result = {};var j = 0for (var i = 0; i < datas.length; i += size) {result[j] = datas.slice(i, i + size)j++}console.log(result)return result}var sendloop = split_array(uint8Buf, 20);function realWriteData(sendloop, i) {var data = sendloop[i]if (typeof(data) == "undefined") {return}console.log("第【" + i + "】次写数据" + data)var buffer = new ArrayBuffer(data.length)var dataView = new DataView(buffer)for (var j = 0; j < data.length; j++) {dataView.setUint8(j, data[j]);}// 调动打印机打印uni.writeBLECharacteristicValue({deviceId,serviceId,characteristicId,value: buffer, // 打印的数据,ArrayBuffer 类型,数据为 10进制或者16进制。编码方式:GBKsuccess(res) {realWriteData(sendloop, i + 1);},fail(e) {console.log("点错误:", e)realWriteData(sendloop, i + 1);}})}var i = 0;realWriteData(sendloop, i);},}}
</script><style lang="scss">.print-box {display: flex;.operation-box {width: 70%;overflow: auto;.device-box {background-color: #8d98cc;margin: 20rpx;}.services-css {margin: 20rpx;background-color: #8d98cc;.char-css {margin: 20rpx;background-color: #ffffdc;}}}.connect-log {display: flex;flex-direction: column;position: sticky;top: 0;width: 30%;color: #fff;height: 100vh;overflow: auto;background-color: rgba(0, 0, 0, 0.2);}}
</style>