likes
comments
collection
share

如何使用小程序进行蓝牙打印

作者站长头像
站长
· 阅读数 12

小程序蓝牙打印流程

需求全流程

  1. 初始化蓝牙模块,并搜索蓝牙打印机
  2. 选择蓝牙打印,并建立链接
  3. 获取蓝牙打印机服务,获取写入特征码
  4. 转换数据,向蓝牙打印机写入数据

代码细化

  1. 初始化蓝牙模块,并搜索蓝牙打印机

     wx.openBluetoothAdapter({
             success: (res) => {
                 console.log('openBluetoothAdapter success', res)
                 // 系统蓝牙模块打开。,并且蓝牙初始化成功后开始搜索蓝牙打印机
                 startBluetoothDevicesDiscovery()
             },
             fail: (res) => {
                 // 一般如果系统蓝牙模块启动失败,或者设备不支持蓝牙则会初始化失败
                 console.log('openBluetoothAdapter fail', res)
                 if (res.errCode === 10001) {
                     wx.showModal({
                         title: '错误',
                         content: '蓝牙初始化失败, 请打开蓝牙后重试。',
                         showCancel: false
                     })
                     // 当蓝牙设备状态发生改变后,需要重新扫描蓝牙打印机列表
                     wx.onBluetoothAdapterStateChange((res) => {
                         console.log('onBluetoothAdapterStateChange', res)
                         if (res.available) {
                         // 取消监听,否则stopBluetoothDevicesDiscovery后仍会继续触发                          onBluetoothAdapterStateChange,
                         // 导致再次调用startBluetoothDevicesDiscovery
                             wx.onBluetoothAdapterStateChange(() => { })
                             startBluetoothDevicesDiscovery()
                         }
                     })
                 }
             }
         })
    

    开始搜索蓝牙打印机

     const startBluetoothDevicesDiscovery = () => {
         if (_discoveryStarted) {
             return
         }
         _discoveryStarted = true
         // 开始搜索蓝牙
         wx.startBluetoothDevicesDiscovery({
             success: (res) => {
                 console.log('startBluetoothDevicesDiscovery success', res)
                 // 搜索到蓝牙打印机后,会以事件回调的形式触发,需要注册事件
                 onBluetoothDeviceFound()
             },
             fail: (res) => {
                 console.log('startBluetoothDevicesDiscovery fail', res)
             }
         })
     }
    

    当蓝牙查找到

     // 当蓝牙查找到
     const onBluetoothDeviceFound = () => {
         wx.onBluetoothDeviceFound((res) => {
             res.devices.forEach(device => {
                 // 排除非法命名的设备
                 if (!device.name && !device.localName) {
                     return
                 }
                 const foundDevices = devices
                 const idx = inArray(foundDevices, 'deviceId', device.deviceId)
                 const data = {}
                 if (idx === -1) {
                     devices[foundDevices.length] = device
                 } else {
                     devices[idx] = device
                 }
                 devices = devices.map(item => {
                     item.feild = item.name
                     return item
                 })
                 // 将其存入数据仓库,会在其他地方用到
                 store.commit('bluetooth/UPDATE_DEVICES', devices)
                 console.log('devices', store.state.bluetooth.devices)
             })
         })
     }
    
  2. 选择蓝牙打印,并建立链接

     const _createBLEConnection = (deviceId, name) => {
         wx.showLoading()
         wx.createBLEConnection({
             deviceId,
             success: () => {
                 console.log('createBLEConnection success')
                 connected = true
                 name = name
                 deviceId = deviceId
                 // 获取蓝牙服务和写入特征码
                 getBLEDeviceServices(deviceId)
                 wx.setStorage({
                     key: LAST_CONNECTED_DEVICE,
                     data: name + ':' + deviceId
                 })
             },
             complete() {
                 wx.hideLoading()
             },
             fail: (res) => {
                 console.log('createBLEConnection fail', res)
                 wx.showModal({
                     title: '错误',
                     content: '蓝牙连接失败',
                     showCancel: false
                 })
             }
         })
         // 建立连接后停止搜索蓝牙,搜索蓝牙还是挺耗性能的(费电)
         stopBluetoothDevicesDiscovery()
     }
    
  3. 获取蓝牙打印机服务,获取写入特征码

    链接蓝牙后获取打印服务

     wx.getBLEDeviceServices({
             deviceId,
             success: (res) => {
                 console.log('getBLEDeviceServices', res)
                 for (let i = 0; i < res.services.length; i++) {
                     if (res.services[i].isPrimary) {
                         getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
                         return
                     }
                 }
             }
         })
    

    获取蓝牙打印机的所有打印服务后,从打印服务中获取打印特征码

     wx.getBLEDeviceCharacteristics({
             deviceId,
             serviceId,
             success: (res) => {
                 console.log('getBLEDeviceCharacteristics success', res)
                 // 这里会存在特征值是支持write,写入成功但是没有任何反应的情况
                 // 只能一个个去试
                 for (let i = 0; i < res.characteristics.length; i++) {
                     const item = res.characteristics[i]
                     if (item.properties.write) {
                         canWrite = true
                         _deviceId = deviceId
                         _serviceId = serviceId
                         _characteristicId = item.uuid
                         console.log(_deviceId, _serviceId, _characteristicId)
                         break
                     }
                 }
             },
             fail(res) {
                 console.error('getBLEDeviceCharacteristics', res)
             }
         })
    

    打印的准备工作已完成,接下来就可以开始着手处理打印数据了

  4. 转换数据,向蓝牙打印机写入数据

    使用 sfBluetoothPrint 进行转换打印数据,蓝牙打印机只能读取 ArrayBuffer

     import HrPrint from '../sfBluetoothPrint/lib/index'
     ​
     const printObj = new HrPrint({
         bytes: 200, // 传输速率,一般的低功耗蓝牙每秒只能传输 20 字节,蓝牙5.2设备提高传输阈值
     })
    

    执行打印

     const writeBLECharacteristicValue = (str) => {
         const params = {
             wayBillData: str,           // 打印数据(打印指令集的str数组)
             deviceId: _deviceId,        // 设备id
             serviceId: _serviceId,      // 打印id
             writeId: _characteristicId, // 写入特征码
         }
         console.log(params)
         return printObj.print(params)
     }
    

    printObj.print 方法实际上就是做了一个转换和断点续传

     BluetoothPrint.prototype.writeBLECharacteristicValueUseArray = function writeBLECharacteristicValueUseArray(_ref5) {
         var wayBillData = _ref5.wayBillData;
         var deviceId = _ref5.deviceId;
         var serviceId = _ref5.serviceId;
         var writeId = _ref5.writeId;
     ​
         // 向蓝牙设备发送一个0x00的16进制数据
         var _this = this;
         return new Promise(function (resolve, reject) {
           if (wayBillData.length > 0) {
             (function () {
               var item = wayBillData.shift();
               var cpcl = item.content.replace(/\n/g, '\r\n');
               var buffer = _utilsIndexJs.stringToBuffer2(cpcl);
               var bytes = buffer.byteLength;
               var bytesLength = _this.bytes;
               if (bytes > bytesLength) {
                 var tmpBuffer = undefined;
                 for (var y = 0; y < bytes; y += bytesLength) {
                   tmpBuffer = buffer.slice(y, y + bytesLength);
                   _this.writeToBLE({ tmpBuffer: tmpBuffer, byts: y, deviceId: deviceId, serviceId: serviceId, writeId: writeId }).then(function (res) {
                     if (res >= bytes - bytesLength && wayBillData.length > 0) {
                       _this.writeBLECharacteristicValueUseArray({ wayBillData: wayBillData, deviceId: deviceId, serviceId: serviceId, writeId: writeId });
                     } else {
                       resolve();
                     }
                   })['catch'](function (res) {
                     reject(res);
                   });
                 }
               } else {
                 _this.writeToBLE({ tmpBuffer: buffer, byts: null, deviceId: deviceId, serviceId: serviceId, writeId: writeId }).then(function (res) {
                   if (arr.length > 0) {
                     _this.writeBLECharacteristicValueUseArray({ wayBillData: wayBillData, deviceId: deviceId, serviceId: serviceId, writeId: writeId });
                   } else {
                     resolve();
                   }
                 })['catch'](function (res) {
                   reject(res);
                 });
               }
             })();
           }
         });
       };
    

    打印指令集转码

     var stringToBuffer2 = function stringToBuffer2(str) {
       var buffer = new ArrayBuffer(sumStrLength(str) * 4);
       var dataView = new DataView(buffer);
       var data = str.toString();
       var p = 0; //ArrayBuffer 偏移量
       for (var i = 0; i < data.length; i++) {
         if (isCN(data[i])) {
           //是中文
           //调用GBK 转码
           var t = gbk.encode(data[i]);  // gbk.js 详见 http://www.vuln.cn/2901 
           for (var j = 0; j < 2; j++) {
             //var code = t[j * 2] + t[j * 2 + 1];
             var code = t[j * 3 + 1] + t[j * 3 + 2];
             var temp = parseInt(code, 16);
             //var temp = strToHexCharCode(code);
             dataView.setUint8(p++, temp);
           }
         } else {
           var temp = data.charCodeAt(i);
           dataView.setUint8(p++, temp);
         }
       }
       return buffer;
     };
    

以下是完整代码

 import store from '@/store'
 import HrPrint from '../sfBluetoothPrint/lib/index'
 // 解决断开提示多次触发的问题
 let onece = false
 const printObj = new HrPrint({
     bytes: 200,
 })
 let _discoveryStarted = false
 let devices = []
 let connected = false
 let chs = []
 let name = null
 let deviceId = null
 let canWrite = false
 ​
 let _characteristicId = null
 let _serviceId = null
 let _deviceId = null
 ​
 const LAST_CONNECTED_DEVICE = 'last_connected_device'
 ​
 const inArray = (arr, key, val) => {
     for (let i = 0; i < arr.length; i++) {
         if (arr[i][key] === val) {
             return i
         }
     }
     return -1
 }
 //将字符串转换成ArrayBufer
 function string2buffer(str) {
     if (!str) return
     var val = ''
     for (var i = 0; i < str.length; i++) {
         val += str.charCodeAt(i).toString(16)
     }
     str = val
     val = ''
     let length = str.length
     let index = 0
     let array = []
     while (index < length) {
         array.push(str.substring(index, index + 2))
         index = index + 2
     }
     val = array.join(',')
     // 将16进制转化为ArrayBuffer
     return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
         return parseInt(h, 16)
     })).buffer
 }
 ​
 ​
 ​
 // 初始化蓝牙
 const init = () => {
     wx.openBluetoothAdapter({
         success: (res) => {
             console.log('openBluetoothAdapter success', res)
             startBluetoothDevicesDiscovery()
         },
         fail: (res) => {
             console.log('openBluetoothAdapter fail', res)
             if (res.errCode === 10001) {
                 wx.showModal({
                     title: '错误',
                     content: '蓝牙初始化失败, 请打开蓝牙后重试。',
                     showCancel: false
                 })
                 wx.onBluetoothAdapterStateChange((res) => {
                     console.log('onBluetoothAdapterStateChange', res)
                     if (res.available) {
                         // 取消监听,否则stopBluetoothDevicesDiscovery后仍会继续触发onBluetoothAdapterStateChange,
                         // 导致再次调用startBluetoothDevicesDiscovery
                         wx.onBluetoothAdapterStateChange(() => { })
                         startBluetoothDevicesDiscovery()
                     }
                 })
             }
         }
     })
 ​
     wx.onBLEConnectionStateChange((res) => {
         // 该方法回调中可以用于处理连接意外断开等异常情况
         console.log('onBLEConnectionStateChange',
             `device ${res.deviceId} state has changed, connected: ${res.connected}`)
         connected = res.connected
         if (!res.connected && !onece) {
             wx.showModal({
                 title: '错误',
                 content: '蓝牙连接已断开',
                 showCancel: false
             })
             onece = true
         }
         if (res.connected) {
             onece = false
         }
     })
     wx.onBLECharacteristicValueChange((res) => {
         console.log('onBLECharacteristicValueChange', res)
     })
 }
 // 查找蓝牙
 const startBluetoothDevicesDiscovery = () => {
     if (_discoveryStarted) {
         return
     }
     _discoveryStarted = true
     wx.startBluetoothDevicesDiscovery({
         success: (res) => {
             console.log('startBluetoothDevicesDiscovery success', res)
             onBluetoothDeviceFound()
         },
         fail: (res) => {
             console.log('startBluetoothDevicesDiscovery fail', res)
         }
     })
 }
 // 停止查找蓝牙
 const stopBluetoothDevicesDiscovery = () => {
     wx.stopBluetoothDevicesDiscovery({
         complete: () => {
             console.log('stopBluetoothDevicesDiscovery')
             _discoveryStarted = false
         }
     })
 }
 // 获取蓝牙查找状态
 const getBluetoothAdapterState = () => {
     wx.getBluetoothAdapterState({
         success: (res) => {
             console.log('getBluetoothAdapterState', res)
             if (res.discovering) {
                 onBluetoothDeviceFound()
             } else if (res.available) {
                 startBluetoothDevicesDiscovery()
             }
         }
     })
 }
 // 当蓝牙查找到
 const onBluetoothDeviceFound = () => {
     wx.onBluetoothDeviceFound((res) => {
         res.devices.forEach(device => {
             if (!device.name && !device.localName) {
                 return
             }
             const foundDevices = devices
             const idx = inArray(foundDevices, 'deviceId', device.deviceId)
             const data = {}
             if (idx === -1) {
                 devices[foundDevices.length] = device
             } else {
                 devices[idx] = device
             }
             devices = devices.map(item => {
                 item.feild = item.name
                 return item
             })
             store.commit('bluetooth/UPDATE_DEVICES', devices)
             console.log('devices', store.state.bluetooth.devices)
         })
     })
 }
 // 建立蓝牙连接
 const createBLEConnection = (target) => {
     const deviceId = target.deviceId
     const name = target.name
     _createBLEConnection(deviceId, name)
 }
 ​
 const _createBLEConnection = (deviceId, name) => {
     wx.showLoading()
     wx.createBLEConnection({
         deviceId,
         success: () => {
             console.log('createBLEConnection success')
             connected = true
             name = name
             deviceId = deviceId
             getBLEDeviceServices(deviceId)
             wx.setStorage({
                 key: LAST_CONNECTED_DEVICE,
                 data: name + ':' + deviceId
             })
         },
         complete() {
             wx.hideLoading()
         },
         fail: (res) => {
             console.log('createBLEConnection fail', res)
             wx.showModal({
                 title: '错误',
                 content: '蓝牙连接失败',
                 showCancel: false
             })
         }
     })
     stopBluetoothDevicesDiscovery()
 }
 ​
 // 关闭蓝牙连接
 const closeBLEConnection = () => {
     wx.closeBLEConnection({
         deviceId
     })
     connected = false
     chs = []
     canWrite = false
 }
 // 获取蓝牙服务
 const getBLEDeviceServices = (deviceId) => {
     wx.getBLEDeviceServices({
         deviceId,
         success: (res) => {
             console.log('getBLEDeviceServices', res)
             for (let i = 0; i < res.services.length; i++) {
                 if (res.services[i].isPrimary) {
                     getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
                     return
                 }
             }
         }
     })
 }
 // 所有服务中的所有特征值
 const getBLEDeviceCharacteristics = (deviceId, serviceId) => {
     wx.getBLEDeviceCharacteristics({
         deviceId,
         serviceId,
         success: (res) => {
             console.log('getBLEDeviceCharacteristics success', res)
             // 这里会存在特征值是支持write,写入成功但是没有任何反应的情况
             // 只能一个个去试
             for (let i = 0; i < res.characteristics.length; i++) {
                 const item = res.characteristics[i]
                 if (item.properties.write) {
                     canWrite = true
                     _deviceId = deviceId
                     _serviceId = serviceId
                     _characteristicId = item.uuid
                     console.log(_deviceId, _serviceId, _characteristicId)
                     break
                 }
             }
         },
         fail(res) {
             console.error('getBLEDeviceCharacteristics', res)
         }
     })
 }
 // 关闭蓝牙模块
 const closeBluetoothAdapter = () => {
     wx.closeBluetoothAdapter()
     _discoveryStarted = false
 }
 ​
 // 快速连接
 const createBLEConnectionWithDeviceId = (e) => {
     const lastDevice = wx.getStorageSync(LAST_CONNECTED_DEVICE)
     // 小程序在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备
     const device = lastDevice
     if (!device) {
         return
     }
     const index = device.indexOf(':')
     const name = device.substring(0, index)
     const deviceId = device.substring(index + 1, device.length)
     console.log('createBLEConnectionWithDeviceId', name + ':' + deviceId)
     wx.openBluetoothAdapter({
         success: (res) => {
             console.log('openBluetoothAdapter success', res)
             _createBLEConnection(deviceId, name)
         },
         fail: (res) => {
             console.log('openBluetoothAdapter fail', res)
             if (res.errCode === 10001) {
                 wx.showModal({
                     title: '错误',
                     content: '未找到蓝牙设备, 请打开蓝牙后重试。',
                     showCancel: false
                 })
                 wx.onBluetoothAdapterStateChange((res) => {
                     console.log('onBluetoothAdapterStateChange', res)
                     if (res.available) {
                         // 取消监听
                         wx.onBluetoothAdapterStateChange(() => { })
                         _createBLEConnection(deviceId, name)
                     }
                 })
             }
         }
     })
 }
 // 执行打印
 const writeBLECharacteristicValue = (str) => {
     const params = {
         wayBillData: str,
         deviceId: _deviceId,
         serviceId: _serviceId,
         writeId: _characteristicId,
     }
     console.log(params)
     return printObj.print(params)
 }
 ​
 ​
 export {
     init,
     closeBluetoothAdapter,
     closeBLEConnection,
     createBLEConnection,
     stopBluetoothDevicesDiscovery,
     writeBLECharacteristicValue
 }
 ​