记录–uni-app实现蓝牙打印小票

  • 记录–uni-app实现蓝牙打印小票已关闭评论
  • 126 次浏览
  • A+
所属分类:Web前端
摘要

基于uni-app开发,调用官方蓝牙相关api实现连接蓝牙与向蓝牙热敏打印机发送字节流,可打印文字,二维码,图片,调整字体大小等,本文提供大概思路


这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录--uni-app实现蓝牙打印小票

说明

基于uni-app开发,调用官方蓝牙相关api实现连接蓝牙与向蓝牙热敏打印机发送字节流,可打印文字,二维码,图片,调整字体大小等,本文提供大概思路

结构

记录--uni-app实现蓝牙打印小票

  • bluetooth.js 蓝牙连接相关模块封装
  • commands.js 打印十六进制相关代码库
  • gbk.js 编码转换库地址
  • printerjobs.js 打印实现库

bluetooth.js

蓝牙连接相关封装代码

class Bluetooth {      constructor() {         this.isOpenBle = false;         this.deviceId = "";         this.serviceId = "";         this.writeId = "";         this.notifyId = "";         this.BluetoothConnectStatus = false this.printStatus = false this.init()     }     init() {         this.closeBluetoothAdapter().then(() = >{             console.log("init初始关闭蓝牙模块") this.openBluetoothAdapter().then(() = >{                 console.log("init初始化蓝牙模块") this.reconnect() //自动连接蓝牙设备             })          })     }      showToast(title) {         uni.showToast({             title: title,             icon: 'none',             'duration': 2000         });     }      openBluetoothAdapter() {         return new Promise((resolve, reject) = >{             uni.openBluetoothAdapter({                 success: res = >{                     this.isOpenBle = true;                     resolve(res);                 },                 fail: err = >{                     this.showToast(`蓝牙未打开`);                     reject(err);                 },             });         });      }      startBluetoothDevicesDiscovery() {         if (!this.isOpenBle) {             this.showToast(`初始化蓝牙模块失败`) return;         }          let self = this;         uni.showLoading({             title: '蓝牙搜索中'         }) return new Promise((resolve, reject) = >{             setTimeout(() = >{                 uni.startBluetoothDevicesDiscovery({                     success: res = >{                         resolve(res)                     },                     fail: res = >{                         self.showToast(`搜索设备失败` + JSON.stringify(err));                         reject(err);                     }                 })             },             300);         });     }      stopBluetoothDevicesDiscovery() {         let self = this;         return new Promise((resolve, reject) = >{             uni.stopBluetoothDevicesDiscovery({                 success: e = >{                     uni.hideLoading();                 },                 fail: e = >{                     uni.hideLoading();                     self.showToast(`停止搜索蓝牙设备失败` + JSON.stringify(err));                 }             })         });     }      createBLEConnection() {         //设备deviceId         let deviceId = this.deviceId;         let self = this;          // uni.showLoading({         // 	mask: true,         // 	title: '设别连接中,请稍候...'         // })         console.log(this.deviceId);         return new Promise((resolve, reject) = >{             uni.createBLEConnection({                 deviceId,                 success: (res) = >{                     console.log("res:createBLEConnection " + JSON.stringify(res));                     resolve(res)                 },                 fail: err = >{                     uni.hideLoading();                     self.showToast(`停止搜索蓝牙设备失败` + JSON.stringify(err));                     reject(err);                 }             })         });     }      //获取蓝牙设备所有服务(service)     getBLEDeviceServices() {         let _serviceList = [];         let deviceId = this.deviceId;         let self = this;          return new Promise((resolve, reject) = >{             setTimeout(() = >{                 uni.getBLEDeviceServices({                     deviceId,                     success: res = >{                         for (let service of res.services) {                             if (service.isPrimary) {                                 _serviceList.push(service);                             }                         }                         uni.hideLoading();                         console.log("_serviceList: " + JSON.stringify(_serviceList));                         resolve(_serviceList)                     },                     fail: err = >{                         uni.hideLoading();                         // self.showToast(`获取设备Services` + JSON.stringify(err));                         reject(err);                     },                 })             },             500);         });     }      //获取蓝牙设备某个服务中所有特征值(characteristic)     getBLEDeviceCharacteristics() {         // console.log("getBLEDeviceCharacteristics")         let deviceId = this.deviceId;         let serviceId = this.serviceId;          let self = this;         return new Promise((resolve, reject) = >{             uni.getBLEDeviceCharacteristics({                 deviceId,                 serviceId,                 success: res = >{                     for (let _obj of res.characteristics) {                         //获取notify                         if (_obj.properties.notify) {                             self.notifyId = _obj.uuid;                             uni.setStorageSync('notifyId', self.notifyId);                         }                         //获取writeId                         if (_obj.properties.write) {                             self.writeId = _obj.uuid;                             uni.setStorageSync('writeId', self.writeId);                         }                     }                      //console.log("res:getBLEDeviceCharacteristics " + JSON.stringify(res));                     let result = {                         'notifyId': self.notifyId,                         'writeId': self.writeId                     };                     // self.showToast(`获取服务中所有特征值OK,${JSON.stringify(result)}`);                     this.BluetoothStatus = true resolve(result)                 },                 fail: err = >{                     self.showToast(`getBLEDeviceCharacteristics` + JSON.stringify(err));                     reject(err);                 }             })         });     }      //断开联链接     closeBLEConnection() {         let deviceId = this.deviceId;         uni.closeBLEConnection({             deviceId,             success(res) {                 console.log("closeBLEConnection" + res)             }         })     }      notifyBLECharacteristicValue() {         let deviceId = this.deviceId;         let serviceId = this.serviceId;         let characteristicId = this.notifyId;          uni.notifyBLECharacteristicValueChange({             state: true,             // 启用 notify 功能             deviceId,             serviceId,             characteristicId,             success(res) {                 uni.onBLECharacteristicValueChange(function(res) {                     console.log('onBLECharacteristicValueChange', res);                 });             },             fail(res) {                 console.log('notifyBLECharacteristicValueChange failed:' + res.errMsg);              }         });     }      writeBLECharacteristicValue(buffer) {         let deviceId = this.deviceId;         let serviceId = this.serviceId;         let characteristicId = this.writeId;          // console.log(deviceId);         // console.log(serviceId);         // console.log(characteristicId);         return new Promise((resolve, reject) = >{             uni.writeBLECharacteristicValue({                 deviceId,                 serviceId,                 characteristicId,                 value: buffer,                 success(res) {                     // console.log('message发送成功', JSON.stringify(res));                     resolve(res);                 },                 fail(err) {                     // console.log('message发送失败', JSON.stringify(err));                     reject(err);                 }             });         });     }      closeBluetoothAdapter() {         return new Promise((resolve, reject) = >{             uni.closeBluetoothAdapter({                 success: res = >{                     resolve()                 }             });         })      }      //若APP在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,无需进行搜索操作。     reconnect() { (async() = >{             try {                 this.deviceId = this.deviceId || uni.getStorageSync("deviceId");                 this.serviceId = this.serviceId || uni.getStorageSync("serviceId");                 console.log("this.deviceId", this.deviceId) console.log("this.serviceId", this.serviceId) let result1 = await this.createBLEConnection();                 console.log("createBLEConnection: " + JSON.stringify(result1));                  let result2 = await this.getBLEDeviceServices();                 console.log("getBLEDeviceServices: " + JSON.stringify(result2));                  let result3 = await this.getBLEDeviceCharacteristics();                 console.log("getBLEDeviceCharacteristics: " + JSON.stringify(result3));                 this.BluetoothConnectStatus = true this.showToast("蓝牙打印设备连接成功")                 // this.writeId = uni.getStorageSync("writeId");                 // this.notifyId = uni.getStorageSync("notifyId");             } catch(err) {                 console.log("err: " + err);                 // this.showToast("蓝牙打印设备连接失败")             }          })();     } }  export default Bluetooth;

commands.js

打印机ESC/POS十六进制编码库

/**  * 修改自https://github.com/song940/node-escpos/blob/master/commands.js  * ESC/POS _ (Constants)  */ var _ = {   LF: [0x0a],   FS: [0x1c],   FF: [0x0c],   GS: [0x1d],   DLE: [0x10],   EOT: [0x04],   NUL: [0x00],   ESC: [0x1b],   EOL: 'n', };  /**  * [FEED_CONTROL_SEQUENCES Feed control sequences]  * @type {Object}  */ _.FEED_CONTROL_SEQUENCES = {   CTL_LF: [0x0a],   // Print and line feed   CTL_GLF: [0x4a, 0x00],   // Print and feed paper (without spaces between lines)   CTL_FF: [0x0c],   // Form feed   CTL_CR: [0x0d],   // Carriage return   CTL_HT: [0x09],   // Horizontal tab   CTL_VT: [0x0b],   // Vertical tab };  _.CHARACTER_SPACING = {   CS_DEFAULT: [0x1b, 0x20, 0x00],   CS_SET: [0x1b, 0x20] };  _.LINE_SPACING = {   LS_DEFAULT: [0x1b, 0x32],   LS_SET: [0x1b, 0x33] };  /**  * [HARDWARE Printer hardware]  * @type {Object}  */ _.HARDWARE = {   HW_INIT: [0x1b, 0x40], // Clear data in buffer and reset modes   HW_SELECT: [0x1b, 0x3d, 0x01], // Printer select   HW_RESET: [0x1b, 0x3f, 0x0a, 0x00], // Reset printer hardware   Print:[0x1b, 0x64,0x01] //Print and feed paper };  /**  * [CASH_DRAWER Cash Drawer]  * @type {Object}  */ _.CASH_DRAWER = {   CD_KICK_2: [0x1b, 0x70, 0x00], // Sends a pulse to pin 2 []   CD_KICK_5: [0x1b, 0x70, 0x01], // Sends a pulse to pin 5 [] };  /**  * [MARGINS Margins sizes]  * @type {Object}  */ _.MARGINS = {   BOTTOM: [0x1b, 0x4f], // Fix bottom size   LEFT: [0x1b, 0x6c], // Fix left size   RIGHT: [0x1b, 0x51], // Fix right size };  /**  * [PAPER Paper]  * @type {Object}  */ _.PAPER = {   PAPER_FULL_CUT: [0x1d, 0x56, 0x00], // Full cut paper   PAPER_PART_CUT: [0x1d, 0x56, 0x01], // Partial cut paper   PAPER_CUT_A: [0x1d, 0x56, 0x41], // Partial cut paper   PAPER_CUT_B: [0x1d, 0x56, 0x42], // Partial cut paper };  /**  * [TEXT_FORMAT Text format]  * @type {Object}  */ _.TEXT_FORMAT = {   TXT_NORMAL: [0x1b, 0x21, 0x00], // Normal text   TXT_2HEIGHT: [0x1b, 0x21, 0x10], // Double height text   TXT_2WIDTH: [0x1b, 0x21, 0x20], // Double width text   TXT_4SQUARE: [0x1b, 0x21, 0x30], // Double width & height text    TXT_UNDERL_OFF: [0x1b, 0x2d, 0x00], // Underline font OFF   TXT_UNDERL_ON: [0x1b, 0x2d, 0x01], // Underline font 1-dot ON   TXT_UNDERL2_ON: [0x1b, 0x2d, 0x02], // Underline font 2-dot ON   TXT_BOLD_OFF: [0x1b, 0x45, 0x00], // Bold font OFF   TXT_BOLD_ON: [0x1b, 0x45, 0x01], // Bold font ON   TXT_ITALIC_OFF: [0x1b, 0x35], // Italic font ON   TXT_ITALIC_ON: [0x1b, 0x34], // Italic font ON    TXT_FONT_A: [0x1b, 0x4d, 0x00], // Font type A   TXT_FONT_B: [0x1b, 0x4d, 0x01], // Font type B   TXT_FONT_C: [0x1b, 0x4d, 0x02], // Font type C    TXT_ALIGN_LT: [0x1b, 0x61, 0x00], // Left justification   TXT_ALIGN_CT: [0x1b, 0x61, 0x01], // Centering   TXT_ALIGN_RT: [0x1b, 0x61, 0x02], // Right justification };  /**  * [BARCODE_FORMAT Barcode format]  * @type {Object}  */ _.BARCODE_FORMAT = {   BARCODE_TXT_OFF: [0x1d, 0x48, 0x00], // HRI barcode chars OFF   BARCODE_TXT_ABV: [0x1d, 0x48, 0x01], // HRI barcode chars above   BARCODE_TXT_BLW: [0x1d, 0x48, 0x02], // HRI barcode chars below   BARCODE_TXT_BTH: [0x1d, 0x48, 0x03], // HRI barcode chars both above and below    BARCODE_FONT_A: [0x1d, 0x66, 0x00], // Font type A for HRI barcode chars   BARCODE_FONT_B: [0x1d, 0x66, 0x01], // Font type B for HRI barcode chars    BARCODE_HEIGHT: function (height) { // Barcode Height [1-255]     return [0x1d, 0x68, height];   },   BARCODE_WIDTH: function (width) {   // Barcode Width  [2-6]     return [0x1d, 0x77, width];   },   BARCODE_HEIGHT_DEFAULT: [0x1d, 0x68, 0x64], // Barcode height default:100   BARCODE_WIDTH_DEFAULT: [0x1d, 0x77, 0x01], // Barcode width default:1    BARCODE_UPC_A: [0x1d, 0x6b, 0x00], // Barcode type UPC-A   BARCODE_UPC_E: [0x1d, 0x6b, 0x01], // Barcode type UPC-E   BARCODE_EAN13: [0x1d, 0x6b, 0x02], // Barcode type EAN13   BARCODE_EAN8: [0x1d, 0x6b, 0x03], // Barcode type EAN8   BARCODE_CODE39: [0x1d, 0x6b, 0x04], // Barcode type CODE39   BARCODE_ITF: [0x1d, 0x6b, 0x05], // Barcode type ITF   BARCODE_NW7: [0x1d, 0x6b, 0x06], // Barcode type NW7   BARCODE_CODE93: [0x1d, 0x6b, 0x48], // Barcode type CODE93   BARCODE_CODE128: [0x1d, 0x6b, 0x49], // Barcode type CODE128 };  /**  * [IMAGE_FORMAT Image format]  * @type {Object}  */ _.IMAGE_FORMAT = {   S_RASTER_N: [0x1d, 0x76, 0x30, 0x00], // Set raster image normal size   S_RASTER_2W: [0x1d, 0x76, 0x30, 0x01], // Set raster image double width   S_RASTER_2H: [0x1d, 0x76, 0x30, 0x02], // Set raster image double height   S_RASTER_Q: [0x1d, 0x76, 0x30, 0x03], // Set raster image quadruple };  /**  * [BITMAP_FORMAT description]  * @type {Object}  */ _.BITMAP_FORMAT = {   BITMAP_S8: [0x1b, 0x2a, 0x00],   BITMAP_D8: [0x1b, 0x2a, 0x01],   BITMAP_S24: [0x1b, 0x2a, 0x20],   BITMAP_D24: [0x1b, 0x2a, 0x21] };  /**  * [GSV0_FORMAT description]  * @type {Object}  */ _.GSV0_FORMAT = {   GSV0_NORMAL: [0x1d, 0x76, 0x30, 0x00],   GSV0_DW: [0x1d, 0x76, 0x30, 0x01],   GSV0_DH: [0x1d, 0x76, 0x30, 0x02],   GSV0_DWDH: [0x1d, 0x76, 0x30, 0x03] };  /**  * [BEEP description]  * @type {string}  */ _.BEEP = [0x1b, 0x42]; // Printer Buzzer pre hex  /**  * [COLOR description]  * @type {Object}  */  _.COLOR = {   0: [0x1b, 0x72, 0x00], // black   1: [0x1b, 0x72, 0x01] // red };  /**  * [exports description]  * @type {[type]}  */ module.exports = _;

printerjobs.js

const commands = require('./commands'); const gbk = require('./gbk');  const printerJobs = function() { 	this._queue = Array.from(commands.HARDWARE.HW_INIT); 	this._enqueue = function(cmd) { 		this._queue.push.apply(this._queue, cmd); 	} };  /**  * 增加打印内容  * @param  {string} content  文字内容  */ printerJobs.prototype.text = function(content) { 	if (content) { 		let uint8Array = gbk.encode(content); 		let encoded = Array.from(uint8Array); 		this._enqueue(encoded); 	} 	return this; };  /**  * 打印文字  * @param  {string} content  文字内容  */ printerJobs.prototype.print = function(content) { 	this.text(content); 	// this._enqueue(commands.LF); 	return this; }; printerJobs.prototype.printL = function(content) { 	this.text(content); 	this._enqueue(commands.LF); 	return this; };  printerJobs.prototype.printImage = function(content) {  	if (content) { 		const cmds = [].concat([29, 118, 48, 0], content); 		// console.log("cmds",cmds) 		this._enqueue(cmds); 		this._enqueue(commands.LF); 	}  	return this; };   /**  * 打印文字并换行  * @param  {string}  content  文字内容  */ printerJobs.prototype.println = function(content = '') { 	return this.print(content + commands.EOL); };  /**  * 设置对齐方式  * @param {string} align 对齐方式 LT/CT/RT  */ printerJobs.prototype.setAlign = function(align) { 	this._enqueue(commands.TEXT_FORMAT['TXT_ALIGN_' + align.toUpperCase()]); 	return this; };  /**  * 设置字体  * @param  {string} family A/B/C  */ printerJobs.prototype.setFont = function(family) { 	this._enqueue(commands.TEXT_FORMAT['TXT_FONT_' + family.toUpperCase()]); 	return this; };  /**  * 设定字体尺寸  * @param  {number} width 字体宽度 1~2  * @param  {number} height 字体高度 1~2  */ printerJobs.prototype.setSize = function(width, height) { 	if (2 >= width && 2 >= height) { 		this._enqueue(commands.TEXT_FORMAT.TXT_NORMAL); 		if (2 === width && 2 === height) { 			this._enqueue(commands.TEXT_FORMAT.TXT_4SQUARE); 		} else if (1 === width && 2 === height) { 			this._enqueue(commands.TEXT_FORMAT.TXT_2HEIGHT); 		} else if (2 === width && 1 === height) { 			this._enqueue(commands.TEXT_FORMAT.TXT_2WIDTH); 		} 	} 	return this; };  /**  * 设定字体是否加粗  * @param  {boolean} bold  */ printerJobs.prototype.setBold = function(bold) { 	if (typeof bold !== 'boolean') { 		bold = true; 	} 	this._enqueue(bold ? commands.TEXT_FORMAT.TXT_BOLD_ON : commands.TEXT_FORMAT.TXT_BOLD_OFF); 	return this; };  /**  * 设定是否开启下划线  * @param  {boolean} underline  */ printerJobs.prototype.setUnderline = function(underline) { 	if (typeof underline !== 'boolean') { 		underline = true; 	} 	this._enqueue(underline ? commands.TEXT_FORMAT.TXT_UNDERL_ON : commands.TEXT_FORMAT.TXT_UNDERL_OFF); 	return this; };  /**  * 设置行间距为 n 点行,默认值行间距是 30 点  * @param {number} n 0≤n≤255  */ printerJobs.prototype.setLineSpacing = function(n) { 	if (n === undefined || n === null) { 		this._enqueue(commands.LINE_SPACING.LS_DEFAULT); 	} else { 		this._enqueue(commands.LINE_SPACING.LS_SET); 		this._enqueue([n]); 	} 	return this; };  /**  * 打印空行  * @param {number} n  */ printerJobs.prototype.lineFeed = function(n = 1) { 	return this.print(new Array(n).fill(commands.EOL).join('')); };  /**  *  设置字体颜色,需要打印机支持  *  @param  {number} color - 0 默认颜色黑色 1 红色  */ printerJobs.prototype.setColor = function(color) { 	this._enqueue(commands.COLOR[color === 1 ? 1 : 0]); 	return this; };  /**  * https://support.loyverse.com/hardware/printers/use-the-beeper-in-a-escpos-printers  * 蜂鸣警报,需要打印机支持  * @param  {number} n    蜂鸣次数,1-9  * @param  {number} t 蜂鸣长短,1-9  */ printerJobs.prototype.beep = function(n, t) { 	this._enqueue(commands.BEEP); 	this._enqueue([n, t]); 	return this; };  /**  * 清空任务  */ printerJobs.prototype.clear = function() { 	this._queue = Array.from(commands.HARDWARE.HW_RESET); 	// this._enqueue(commands.HARDWARE.Print); 	return this; };  /**  * 返回ArrayBuffer  */ printerJobs.prototype.buffer = function() { 	return new Uint8Array(this._queue).buffer; };  module.exports = printerJobs;

代码实现

封装蓝牙连接,搜索,断开相关操作模块

蓝牙搜索

startBluetoothDeviceDiscovery() {     let self = this;     self.tempDeviceList = [];      uni.startBluetoothDevicesDiscovery({         success: res = >{             uni.onBluetoothDeviceFound(devices = >{                 // console.log("发现设备: " + JSON.stringify(devices));                 if (!self.tempDeviceList.some(item = >{                     return item.deviceId === devices.devices[0].deviceId || item.name === devices.devices[0].name                 })) {                     // console.log("new", devices.devices)                     self.tempDeviceList.push(devices.devices[0])                 }             });              this.connect = false this.$refs.popup.open()         },         fail: err = >{             uni.showToast({                 title: '搜索设备失败' + JSON.stringify(err),                 icon: 'none'             })         }     }) },

搜索完成选择设备

async select_deviceId(item) {     this.deviceId = item.deviceId;     this.$bluetooth.deviceId = item.deviceId;     uni.setStorageSync('deviceId', this.$bluetooth.deviceId);     this.serviceList = [];      try {         //1.链接设备         let result = await this.$bluetooth.createBLEConnection();         //2.寻找服务         let result2 = null setTimeout(async() = >{             result2 = await this.$bluetooth.getBLEDeviceServices();             console.log("获取服务: " + JSON.stringify(result2));             this.serviceList = result2;              console.log("serviceList", this.serviceList.length)              if (this.serviceList[2].uuid) {                 this.select_service(this.serviceList[2].uuid)             } else {                 uni.showToast({                     title: '不是打印设备',                     icon: 'none'                 })             }          },         1000)      } catch(e) {         //TODO handle the exception         console.log("e: " + JSON.stringify(e));     } },

选中服务

async select_service(res) {     console.log("select_service", res)      this.$bluetooth.serviceId = res;     console.log("this.$bluetooth.serviceId", this.$bluetooth.serviceId) uni.setStorageSync('serviceId', res);      try {         let result = await this.$bluetooth.getBLEDeviceCharacteristics();         console.log("resultresult", result) this.$refs.popup.close()          uni.showToast({             title: "连接成功"         })         // this.pickUpOnce()     } catch(e) {         //TODO handle the exception         console.log("e: " + JSON.stringify(e));     } },

打印内容组合

async writeBLECharacteristicValueTongzhishu() { 	let Qrcode_res = await this.get_Qrcode() 	let sign = await this.getSign() 	console.log("sign") 	let printerJobs = new PrinterJobs() 	let p = printerJobs.setAlign('ct') 		.setBold(true) 		.printL("打印测试")  	let buffer = printerJobs.buffer(); 	this.printbuffs(buffer); },

打印内容组合

主要是实现打印编码推送循环,手机蓝牙可能出现编码发送失败情况,这个时候就是要循环保证每个字节准确推送

printbuffs(buffer, fun) {     console.log("printbuffs", buffer.byteLength) const maxChunk = 8;     let p = Promise.resolve();     for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) {         let subPackage = buffer.slice(i, i + maxChunk <= length ? (i + maxChunk) : length);         p = p.then(() = >{             if (i == 0) {                 this.$bluetooth.printStatus = true this.$refs.loading.open();              }             if ((i + maxChunk) >= length) {                 console.log("printEnd")                  setTimeout(() = >{                     this.$bluetooth.printStatus = false this.$refs.loading.close();                 },                 1000)             }             return this.printbuff(subPackage)         })     }     p = p.then(() = >{         console.log("printEve") fun()     })  }, async printbuff(buffer) {     while (true) {         try {             await this.$bluetooth.writeBLECharacteristicValue(buffer);             break;         } catch(e) {}     } },

本文转载于:

https://juejin.cn/post/7001752261722537991

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--uni-app实现蓝牙打印小票