title: uni-app小程序蓝牙打印踩坑之路
date: 2020-08-16 16:20:58
tags:

  • uni-app
  • 吐槽
    categories: 工作
    copyright: false
    top_img: /img/bg4.png

查资料

就在上周,我司的产品经理甩给了我一个打印机,顺便给了我一个安卓和IOSSDK(心里一万只草泥马,我写的是小程序你给我安卓的SDK干嘛??)

说在小程序里入库时加个打印标签的功能 .我嘴角微微上扬,这对面向Google和百度的我来说并不是什么难事.于是确定好了需求细节后就开始着手开始捣鼓了.

一开始搜索了相关的文章,下载了几个demo,结果都是不行,控制台没报错,当时打印机就是没反应,于是我又开始了我的google百度之旅. 后来我发现demo打印不出东西完全是因为打印机的问题,产品经理给的打印机型号是精臣的B3S系列, 我去问了天猫的客服,客服又让我填了一个需求反馈表单,第二天他们的技术人员会来联系我.

时间来到第二天,精臣的技术人员联系到我, 我说我需要做一个小程序的蓝牙打印功能, 技术人员说他们公司目前出的打印机机型,都是不支持指令打印的,只目前打印传输的是图片 . 而巧的是小程序的低功耗蓝牙对传输速率有限制, 所以只支持指令打印.

你说起不起,不懂行的产品经理,给了个安卓的SDK就算了,还给了个不支持小程序低功耗蓝牙打印的打印机.

uniapp - 图1

既然他们的产品不支持, 那就换一个打印机, 于是我又在天猫探索,发现了一款便携式标签打印机是支持小程序低功耗蓝牙打印的. 我买的这款是佳博M322 . 然后用demo测试了一下 , 结果不出我所料,是没问题的.

demo地址

github上放了两个demo,一个是uniapp的,一个是原生的.因为我是用uniapp写的,本人又比较懒,直接把uniapp的拉过来了. 需要的自取 .

实现

使用了vuemixin:

  1. var tsc = require('./tsc.js')
  2. var esc = require('./esc.js')
  3. import {mapGetters,mapMutations} from 'vuex'
  4. import {SET_CONNECTBLUETOOTH,SET_CURRPRINTER} from '../../../store/mutation-types.js'
  5. import {checkErr} from '../Utils/public.js'
  6. export default {
  7. data() {
  8. return {
  9. devices: [],
  10. currDev: null,
  11. connId: '',
  12. piaojuText:'',
  13. }
  14. },
  15. computed:{
  16. ...mapGetters(['isConnectBluetooth','currPrinter'])
  17. },
  18. methods: {
  19. ...mapMutations({
  20. setConnectBlue:SET_CONNECTBLUETOOTH,
  21. setCurrentPrinter:SET_CURRPRINTER
  22. }),
  23. destroyed: function() {
  24. console.log("destroyed----------")
  25. if (this.connId != '') {
  26. uni.closeBLEConnection({
  27. deviceId: this.connId,
  28. success(res) {
  29. console.log(res)
  30. }
  31. })
  32. }
  33. },
  34. searchBle() {
  35. var that = this
  36. console.log("initBule")
  37. uni.showLoading({
  38. title:'搜索蓝牙中'
  39. })
  40. uni.openBluetoothAdapter({//初始化蓝牙模块
  41. success(res) {
  42. console.log("打开 蓝牙模块")
  43. console.log(res)
  44. that.onDevice()//监听寻找到新设备的事件
  45. uni.getBluetoothAdapterState({//获取本机蓝牙适配器状态
  46. success: function(res) {
  47. console.log(res)
  48. if (res.available) {
  49. if (res.discovering) {
  50. uni.hideLoading()
  51. that.stopFindBule()
  52. }
  53. //搜索蓝牙
  54. //开始搜寻附近的蓝牙外围设备
  55. console.log("开始搜寻附近的蓝牙外围设备")
  56. uni.startBluetoothDevicesDiscovery({
  57. success(res) {
  58. console.log(res)
  59. setTimeout(()=>{
  60. console.log('10秒倒计时')
  61. that.stopFindBule()
  62. let printer = that.devices.find(item=>item.deviceId === that.lastPrinter.deviceId)
  63. if(!printer&&that.connectStatus){
  64. uni.showModal({
  65. title: '提示',
  66. showCancel: false,
  67. confirmText: '确定',
  68. content: '没找到指定打印机',
  69. });
  70. }
  71. },10000)
  72. }
  73. })
  74. } else {
  75. uni.hideLoading()
  76. console.log('本机蓝牙不可用')
  77. }
  78. },
  79. })
  80. }
  81. })
  82. },
  83. onDevice(){
  84. console.log("监听寻找到新设备的事件---------------")
  85. var that = this
  86. //监听寻找到新设备的事件
  87. uni.onBluetoothDeviceFound(function(devices) {
  88. console.log('--------------new-----------------------'+JSON.stringify(devices))
  89. var re = JSON.parse(JSON.stringify(devices))
  90. console.log(re.devices[0].name + " " + re.devices[0].deviceId)
  91. let name = re.devices[0].name
  92. if (name != "未知设备") {
  93. let deviceId = re.devices[0].deviceId
  94. that.devices.push({
  95. name: name,
  96. deviceId: deviceId,
  97. services: []
  98. })
  99. }
  100. })
  101. },
  102. stopFindBule() {
  103. console.log("停止搜寻附近的蓝牙外围设备---------------")
  104. uni.stopBluetoothDevicesDiscovery({
  105. success(res) {
  106. console.log(res)
  107. }
  108. })
  109. },
  110. //连接
  111. onConn(item) {
  112. var that = this
  113. uni.showLoading({
  114. title:'连接蓝牙中'
  115. })
  116. console.log("连接蓝牙---------------" + item.deviceId)
  117. let deviceId = item.deviceId
  118. uni.createBLEConnection({//连接低功耗蓝牙设备
  119. deviceId: deviceId,
  120. complete(res) {
  121. console.log(res)
  122. if (res.errMsg == "createBLEConnection:ok") {
  123. console.log("连接蓝牙-[" + item.name + "]--成功")
  124. that.connId = deviceId;
  125. setTimeout(function() {
  126. that.getBLEServices(deviceId)//获取蓝牙设备所有服务(service)
  127. console.log(item,'lastPrinter存到本地')
  128. that.currDev = item
  129. that.setCurrentPrinter(item)
  130. uni.setStorageSync('lastPrinter',item)
  131. }, 2000)
  132. } else {
  133. uni.hideLoading()
  134. console.log(res)
  135. }
  136. //连接成功 关闭搜索
  137. that.stopFindBule()
  138. },
  139. })
  140. },
  141. getBLEServices(_deviceId) {
  142. var that = this;
  143. let deviceId = _deviceId
  144. console.log("获取蓝牙设备所有服务(service)。---------------")
  145. uni.showLoading({
  146. title:'获取蓝牙设备服务中.',
  147. icon:'none'
  148. })
  149. uni.getBLEDeviceServices({
  150. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
  151. deviceId: deviceId,
  152. complete(res) {
  153. console.log(res)
  154. let serviceId = ""
  155. for (var s = 0; s < res.services.length; s++) {
  156. console.log(res.services[s].uuid)
  157. let serviceId = res.services[s].uuid
  158. uni.getBLEDeviceCharacteristics({//获取蓝牙设备某个服务中所有特征值(characteristic)
  159. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
  160. deviceId: deviceId,
  161. // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
  162. serviceId: serviceId,
  163. success(res) {
  164. var re = JSON.parse(JSON.stringify(res))
  165. console.log('deviceId = [' + deviceId + '] serviceId = [' + serviceId + ']')
  166. for (var c = 0; c < re.characteristics.length; c++) {
  167. if (re.characteristics[c].properties.write == true) {
  168. let uuid = re.characteristics[c].uuid
  169. console.log(' deviceId = [' + deviceId + '] serviceId = [' + serviceId + '] characteristics=[' +
  170. uuid)
  171. for (var index in that.devices) {
  172. if (that.devices[index].deviceId == deviceId) {
  173. that.devices[index].services.push({
  174. serviceId: serviceId,
  175. characteristicId: uuid
  176. })
  177. break
  178. }
  179. }
  180. console.log(JSON.stringify(that.devices))
  181. }
  182. }
  183. }
  184. })
  185. }
  186. uni.hideLoading()
  187. that.setConnectBlue(true)
  188. uni.showToast({
  189. title:"连接蓝牙成功",
  190. icon:'success'
  191. })
  192. },
  193. fail(res) {
  194. that.setConnectBlue(false)
  195. let msg = checkErr(res.errCode)
  196. uni.showModal({
  197. title: '提示',
  198. showCancel: false,
  199. confirmText: '确定',
  200. content: `${msg}`,
  201. });
  202. uni.hideLoading()
  203. },
  204. })
  205. },
  206. senBlData(deviceId, serviceId, characteristicId,uint8Array) {
  207. console.log('************deviceId = [' + deviceId + '] serviceId = [' + serviceId + '] characteristics=[' +characteristicId+ "]")
  208. var uint8Buf = Array.from(uint8Array);
  209. function split_array(datas,size){
  210. var result = {};
  211. var j = 0
  212. for (var i = 0; i < datas.length; i += size) {
  213. result[j] = datas.slice(i, i + size)
  214. j++
  215. }
  216. console.log(result)
  217. return result
  218. }
  219. var sendloop = split_array(uint8Buf, 20);
  220. // console.log(sendloop.length)
  221. return new Promise((resolve,reject)=>{
  222. function realWriteData(sendloop, i) {
  223. var data = sendloop[i]
  224. if(typeof(data) == "undefined"){
  225. resolve('单次打印结束')
  226. return
  227. }
  228. console.log("第【" + i + "】次写数据"+data)
  229. var buffer = new ArrayBuffer(data.length)
  230. var dataView = new DataView(buffer)
  231. for (var j = 0; j < data.length; j++) {
  232. dataView.setUint8(j, data[j]);
  233. }
  234. uni.writeBLECharacteristicValue({
  235. deviceId,
  236. serviceId,
  237. characteristicId,
  238. value: buffer,
  239. success: (res)=>{
  240. realWriteData(sendloop, i + 1);
  241. },
  242. fail:(err)=>{
  243. let msg = checkErr(err.errCode)
  244. uni.showModal({
  245. title: '提示',
  246. showCancel: false,
  247. confirmText: '确定',
  248. content: `${msg}`,
  249. });
  250. uni.hideLoading()
  251. return
  252. }
  253. })
  254. }
  255. var i = 0;
  256. realWriteData(sendloop, i)
  257. })
  258. },
  259. senBleLabel(form) {
  260. return new Promise((resolve,reject)=>{
  261. //标签模式
  262. uni.showLoading({
  263. title:'标签打印中'
  264. })
  265. let deviceId = this.currPrinter.deviceId;
  266. let serviceId = this.currPrinter.services[0].serviceId;
  267. let characteristicId = this.currPrinter.services[0].characteristicId;
  268. var command = tsc.jpPrinter.createNew()
  269. // console.log(command)
  270. if(this.ReagentPrintModel == '3'){
  271. command.setSize(40, 30)
  272. command.setGap(2)
  273. command.setCls()
  274. command.setText(16,8, "TSS16.BF2", 1, 1, `名称: ${form.CommonName}`)
  275. command.setText(16,40, "TSS16.BF2", 1, 1, `纯度: ${form.Consistency} 规格: ${form.Model}`)
  276. command.setBarCode(16, 72, "128M", 48, 1, 2,2,form.Barcode)
  277. command.setText(16,176, "TSS16.BF2", 1, 1, '开瓶时间:')
  278. command.setBar(96,192, 160, 2)
  279. // command.setQR(16, 160, "L", 3, "A", "977767937@qq.com")
  280. command.setPagePrint()
  281. }else{
  282. command.setSize(40, 30)
  283. command.setGap(2)
  284. command.setCls()
  285. command.setText(16,8, "TSS16.BF2", 1, 1, `品名: ${form.CommonName}`)
  286. command.setText(16,40, "TSS16.BF2", 1, 1, `位置: ${this.position}`)
  287. command.setText(16,72, "TSS16.BF2", 1, 1, `规格: ${form.Model} 纯度: ${form.Consistency}`)
  288. command.setBarCode(16, 104, "128M", 48, 1, 2,2,form.Barcode)
  289. // command.setQR(16, 160, "L", 3, "A", "977767937@qq.com")
  290. command.setPagePrint()
  291. }
  292. this.senBlData(deviceId, serviceId, characteristicId,command.getData()).then((res)=>{
  293. // console.log(res)
  294. uni.hideLoading()
  295. resolve(res)
  296. })
  297. })
  298. },
  299. senBleLabel2(){
  300. //票据模式
  301. let deviceId = this.currPrinter.deviceId;
  302. let serviceId = this.currPrinter.services[0].serviceId;
  303. let characteristicId = this.currPrinter.services[0].characteristicId;
  304. var command = esc.jpPrinter.createNew()
  305. command.init()
  306. command.setText(this.piaojuText);
  307. command.setPrintAndFeedRow(1)
  308. this.senBlData(deviceId, serviceId, characteristicId,command.getData())
  309. }
  310. }
  311. }

页面:

  1. async success2print(form){
  2. console.log(form)
  3. for(let i=0;i<form.length;i++){
  4. console.log(`第${i+1}次打印`)
  5. let res = await this.senBleLabel(form[i])
  6. console.log(res)
  7. }
  8. uni.showToast({
  9. title:'入库成功',
  10. icon:'success'
  11. })
  12. },

原来的demo中是只能打印一次的,无法连续打印, 就将代码改了改,支持连续打印.

打印的核心函数就是senBleLabelsenBlData,其中senBleLabel是设置标签模版,senBlData定义数据,把数据分成最大长度为20的二进制数据.然后依次写入设备特征值.

模版设置

参考demo文件里的佳博标签打印机编程手册tspl v1.0.8, 值得一提的就是算好距离然后打印就完事了.

由于这台打印机的DPI为203(DPI可以在打印机的机身参数上面查看).

200DPI: 1mm = 8dots

  1. command.setText(16,8, "TSS16.BF2", 1, 1, `名称: ${form.CommonName}`)

比如这一段是设置文字,16代表x轴16dots(2mm),y轴是8dots(1mm),TSS16.BF2是文字类型,具体参数可以去手册里查看,这里不多做解释,因为我自己也是看着手册来的uniapp - 图2

40mm×30mm的标签 dots尺寸就是, 320×240.

那支笔画一画一目了然.

uniapp - 图3

最终打印出来的效果.各位可以根据自己的需求修改…