机型等参数

  • HSPOS
  • 点密度:576点/行(8dots/mm,203dpi)
  • 接口类型: 蓝牙(Bluetooth2.0,4.0双模,支持Android,IOS)
  • 打印方式:图形打印(位图)
  • 打印指令集: ESC/POS

基本思路

1、 实现蓝牙连接

**B12.js方法封装

class BluetoothTools {constructor() { this.decimalData = []; //获取buffer之前的二进制数据集合;this.deviceId = null;this.initStatus = 0; //蓝牙初始化结果;0:初始化中,1-成功;2-失败;this.linked = false; //蓝牙是否为连接状态;this.connectChangeHandler = null;}// 初始化;init(connectChangeHandler) {this.connectChangeHandler = connectChangeHandler;return new Promise((resolve, reject) => {uni.openBluetoothAdapter({success: res => {this.initStatus = 1;this.onConnectChange(connectChangeHandler)resolve(res)},fail: err => {this.initStatus = 2;console.log('蓝牙初始化失败:', err);this.toast('蓝牙不可用!')reject(err)}})})}// 获取蓝牙状态,是否可用checkCanUse() {return new Promise((resolve, reject) => {uni.getBluetoothAdapterState({success: res => {// console.log(res);resolve(res)},fail: err => {// console.log(err);reject(err)}})})} // 开始搜索蓝牙startSearch(cb) {return new Promise(async (resolve, reject) => {// if (this.initStatus === 2) {//  this.toast('蓝牙初始化失败!');//  reject();//     return false;// } else if (this.initStatus === 0) {//    this.toast('蓝牙未初始化!');//   reject();//     return false;// }if (this.initStatus !== 1) {await this.init(this.connectChangeHandler);}this.onFound(cb);uni.startBluetoothDevicesDiscovery({allowDuplicatesKey: false,services: [],success: (res) => {// console.log('开始搜索蓝牙设备!', res);resolve(res)},fail: err => {// console.log('开启搜索失败:', err);this.toast('开始搜索失败!')reject(err)}})})}// 监听单一设备搜索结果onFound(cb) {uni.onBluetoothDeviceFound(res => {// console.log('蓝牙设备:', res.devices);cb(res.devices) })}// 停止搜索;stopSearch() {return new Promise((resolve, reject) => {uni.stopBluetoothDevicesDiscovery({success: (res) => {resolve(res)},fail: (err) => {// this.toast('蓝牙停止搜索失败,请重试!');console.log('蓝牙停止搜索失败:', err);reject(err)}})})}// 关闭蓝牙模块(打印完后调用)closeAdapter() {return new Promise((resolve, reject) => {uni.closeBluetoothAdapter({success: res => {resolve(res);},fail: (err) => {console.log('关闭失败:', err);// this.toast('蓝牙模块关闭失败!')reject(err);}})})}toast(msg) {uni.showToast({title: msg,icon: 'none',duration: 2200})}// 根据deviceId连接蓝牙;connectBLE(deviceId) {return new Promise(async (resolve, reject) => {if (this.initStatus !== 1) {await this.init(this.connectChangeHandler).catch(err => {reject(err)});}this.checkCanUse().then(async data => {// console.log(data);if (data.available) {uni.createBLEConnection({deviceId: deviceId,success: (res) => {resolve(res)},fail: err => {console.log('蓝牙连接失败!', err);this.toast('蓝牙连接失败,请重试!')reject(err)}})} else {this.toast('蓝牙不可用')reject()}}).catch(err => {this.toast('蓝牙不可用!!')reject()})})}//  根据deviceId断开蓝牙链接;closeBLE(deviceId) {return new Promise((resolve, reject) => {if (!deviceId) {resolve();return;}uni.closeBLEConnection({deviceId: deviceId,success: (res) => {// console.log('蓝牙已断开!', res);resolve(res)},fail: err => {console.log('蓝牙断开失败!', err);// this.toast('蓝牙断开失败, 请重试!')reject(err)}})})}// 监听蓝牙连接状态;onConnectChange(cb) {uni.onBLEConnectionStateChange(res => {console.log('监听蓝牙连接状态:', res);if (res.connected) {this.linked = true;this.deviceId = res.deviceId;} else {this.linked = false;this.deviceId = null;}cb(res);})}
}export default BluetoothTools;

*** vue文件中内容

<template><view class=""><view class="bluetooth_container"><view class="title">已配对设备</view><view class="matched_list" v-if="matchedList && matchedList.length"><view class="flex" v-for="(item, index) in matchedList" :key="item.deviceId" @longpress="showModal2(item.deviceId)"><image class="img" src="/static/print.png" mode=""></image><text class="name">{{item.localName ? item.localName : item.name ? item.name : '--'}}</text><text class="link linked" @click="showModal" v-if="linkedDeviceId === item.deviceId">已连接</text><block v-else><image v-if="selectedDeviceId==item.deviceId && isConnectting" class="load_img" src="/static/loadding.gif" mode=""></image><text v-else class="link unlink" @click="connectHandler(item)">未连接</text></block></view></view><view class="empty_box" v-else><u-empty mode="search" text="暂无配对设备,快快添加吧~"></u-empty></view><view class="block"></view><view class="title search_title"><text>扫描可用设备</text><view class="stop" v-if="isSearching" @click="stopSearch"><text>停止</text></view><view class="img_box" v-else @click="startSearch"><image class="img img1" src="/static/refresh.png" mode=""></image></view><view class="load_box" v-if="isSearching"><image class="load_img" src="/static/loadding.gif" mode=""></image></view></view><view class="list"><view class="flex" v-for="(item, index) in deviceList" :key="index"><image class="img img2" src="/static/print.png" mode=""></image><text class="name">{{item.localName ? item.localName : item.name ? item.name : '--'}}</text><image v-if="selectedDeviceId==item.deviceId && isConnectting" class="load_img" src="/static/loadding.gif" mode=""></image><text v-else class="link" @click="connectHandler(item,index)">连接</text></view></view></view><u-modal v-model="modalShow" show-cancel-button content="是否断开连接?" @confirm="closeConnect"></u-modal><u-modal v-model="modalShow2" show-cancel-button content="是否忽略此设备?" @confirm="deleteDevice"></u-modal></view>
</template><script>import B12s from '@/common/b12s.js';const b12s = new B12s();export default {data() {return {// 蓝牙相关;matchedList: [], //已配对的列表;deviceList: [], //搜索到的设备列表;initCode: 0, //蓝牙初始化结果;0:初始化中,1-成功;2-失败;selectedDeviceId: '', //当前操作的蓝牙设备id; isConnectting: false, //蓝牙正在连接中;linkedDeviceId: '', //已连接的蓝牙mac地址;isSearching: false, //是否正在搜索蓝牙设备;b12s: null, //蓝牙工具;}},async onLoad(options) {this.initBluetooth();},async onUnload() {this.stopSearch();b12s.closeBLE(this.linkedDeviceId);b12s.closeAdapter();},methods: {// 初始化蓝牙;async initBluetooth() {this.getMatchedList();await b12s.init(this.onConnectChange);this.autoConnect();},// 如果有配对历史,自动连接最后一个;autoConnect() {let length = this.matchedList.lengthif (length <= 0) return;let item = this.matchedList[length - 1];this.connectHandler(item);},// 开始搜索蓝牙;async startSearch() {await b12s.startSearch(this.getDeviceList);this.isSearching = true;},// 停止搜索async stopSearch() {await b12s.stopSearch();this.isSearching = false;},// 获取历史配对列表;getMatchedList() {let list = uni.getStorageSync('__bluetooth_list_');if (list) {this.matchedList = list;}console.log(this.matchedList);},// 获取可用设备列表;getDeviceList(devices) {for (let i = 0; i < devices.length; i++) {let name = devices[i].name || devices[i].localName;if (name) {let dLIndex = this.deviceList.findIndex(item => item.deviceId === devices[i].deviceId);let mLIndex = this.matchedList.findIndex(item => item.deviceId === devices[i].deviceId);if (dLIndex < 0 && mLIndex < 0) {this.deviceList.push({name: name,deviceId: devices[i].deviceId})}}}},// 点击连接;async connectHandler(item, index = -1) {this.stopSearch();await b12s.closeBLE(this.linkedDeviceId)this.selectedDeviceId = item.deviceId;this.isConnectting = true;await b12s.connectBLE(item.deviceId).catch(err => {this.isConnectting = false;});// this.linkedDeviceId = item.deviceId;this.isConnectting = false;let indexRes = this.matchedList.findIndex(itm => itm.deviceId === item.deviceId);if (indexRes < 0) {this.matchedList.push(item);index !== -1 && this.deviceList.splice(index, 1);this.saveStorage()}},// 断开连接closeConnect() {b12s.closeBLE(this.linkedDeviceId);this.modalShow = false;},// 监听蓝牙连接状态;onConnectChange(res) {if (res.connected) {// this.$u.toast('蓝牙已连接');this.linkedDeviceId = res.deviceId;} else {// this.$u.toast('蓝牙已断开');this.linkedDeviceId = '';}},// 删除已缓存设备;deleteDevice() {this.matchedList = this.matchedList.filter(item => item.deviceId !== this.selectedDeviceId);this.saveStorage()},// 缓存已配对蓝牙;saveStorage() {uni.setStorageSync('__bluetooth_list_', this.matchedList)}}}
</script><style lang="scss" scoped>.bluetooth_container {padding: 30rpx;.load_img {width: 40rpx;height: 40rpx;}.title {font-size: 34rpx;font-weight: bold;.img {margin-left: 20rpx;vertical-align: middle;width: 36rpx;height: 36rpx;}}.search_title {display: flex;justify-content: space-between;margin-top: 60rpx;.img_box {flex: 1;}.load_img {width: 40rpx;height: 40rpx;}.stop {flex: 1;margin-left: 30rpx;text {font-size: 24rpx;font-weight: normal;background-color: #fa3534;color: #ffffff;padding: 10rpx 20rpx;border-radius: 30rpx;}}}.matched_list {// padding: 30rpx 0;.flex {display: flex;margin: 30rpx 0;justify-content: space-between;align-items: center;.img {width: 40rpx;height: 40rpx;}.name {flex: 1;margin-left: 40rpx;}.link {padding: 10rpx 40rpx;background-color: #F2F2F2;border-radius: 40rpx;font-size: 28rpx;}.unlink {color: #606266;}.linked {color: #19be6b;}}}.empty_box {padding: 90rpx 0;}.list {.flex {display: flex;margin: 30rpx 0;justify-content: space-between;align-items: center;.img {width: 40rpx;height: 40rpx;}.name {flex: 1;margin-left: 40rpx;}.link {padding: 10rpx 40rpx;background-color: #F2F2F2;border-radius: 40rpx;color: #2979ff;font-size: 28rpx;}}}.btns {display: flex;justify-content: space-around;margin-top: 200rpx;.btn {margin: 0;width: 40%;}}}</style>

2、获取位图信息

vue页面中拿到像素(位图)信息;

<template><view class=""><canvas id="canvas" canvas-id="canvas" class="canvas"></canvas></view>
</template><script>import B12s from '@/common/b12s.js';const b12s = new B12s();export default {methods: {//画图;drawImage() {const ctx = uni.createCanvasContext('canvas');uni.chooseImage({success: res => {ctx.drawImage(res.tempFilePaths[0], 0, 0, 150, 100)ctx.draw()}})},// 获取canvas的像素信息;getImageInfo() {return new Promise((resolve, reject) => {uni.canvasGetImageData({canvasId: 'canvas',x: 0,y: 0,width: _this.canvas2ImgWidth, height: _this.canvas2ImgHeight,success(res) {resolve(res)},fail: err => {this.$u.toast('获取图片数据失败')reject(err)}})})},async printHandler() {uni.hideLoading();uni.showLoading({title: '打印中',mask: true})const res = await this.getImgInfo();b12s.printImage(res)}}}
</script>

3、开始打印;

b12s.js

class BluetoothTools {constructor() {this.commands = {init: [0x1b, 0x40], // 清理buffer数据,重置模式;ASC2: ESC @print: [0x0a], //开始打印;printL5: [0x1b, 0x64, 0x05],printL6: [0x1b, 0x64, 0x06],printL8: [0x1b, 0x64, 0x08],printL10: [0x1b, 0x64, 0x10],}this.decimalData = []; //获取buffer之前的二进制数据集合;this.deviceId = null;this.initStatus = 0; //蓝牙初始化结果;0:初始化中,1-成功;2-失败;this.linked = false; //蓝牙是否为连接状态;this.timer = null;this.writeTime = 0;this.byteLength = this.isAndroid() ? 200 : 500;this.connectChangeHandler = null;}/*** 低功耗蓝牙API*/// 根据deviceId 设置传输字节最大值;setMTU(deviceId) {return new Promise((resolve, reject) => {uni.setBLEMTU({deviceId: deviceId,mtu: 512,success: (res) => {console.log('设置MTu成功', res);resolve(res)},fail: (err) => {console.log('设置mtu失败', err);this.toaset('设置mtu失败!')reject(err)}})})}// 根据deviceId/mac地址 查询serviceId列表;getServiceId(deviceId) {return new Promise((resolve, reject) => {uni.getBLEDeviceServices({deviceId: deviceId,success: res => {resolve(res)},fail: err => {console.log('获取serviceId失败', err);reject(err)}})})}// 根据deviceId 和 serviceId 获取特征Id;getCharId(deviceId, serviceId) {return new Promise((resolve, reject) => {uni.getBLEDeviceCharacteristics({deviceId: deviceId,serviceId: serviceId,success: res => {resolve(res)},fail: err => {console.log('获取特征id失败', err);reject(err)}})})}// 写入数据;writeBLE(deviceId, serviceId, charId, buffer) {// console.log(deviceId, serviceId, charId, buffer.byteLength);// return;let _this = this;return new Promise((resolve, reject) => {uni.writeBLECharacteristicValue({deviceId: deviceId, //设备Id、mac地址;serviceId: serviceId, //服务id;characteristicId: charId, //特征id;value: buffer, //指令buffer数据;writeType: 'write', //'writeNoResponse',success: res => {// console.log('数据写入成功', res);_this.writeTime = 0;resolve(res)},fail: err => {console.log('写入失败:', Date.now());reject(err);}})})}/*** 具体功能实现*/// 打印传入的位图信息async printImage(res) {if (!this.deviceId) {this.toast('未检测到蓝牙设备id');this.hideLoading()return;}this.isAndroid() && await this.setMTU(this.deviceId);const imgArr = this.getImgArray(res);// return;const { serviceId, charId } = await this.getWriteIds(this.deviceId);this.startPrint(this.deviceId, serviceId, charId, imgArr);}// 开始打印;async startPrint(deviceId, serviceId, charId, cmd) {// 初始化;let initArr = Array.from(this.commands.init).concat(Array.from(this.commands.print));let initBuffer = new Uint8Array(initArr).buffer;await this.writeMidWare(deviceId, serviceId, charId, initBuffer)// let cmds = Array.from(this.commands.init).concat(Array.from(cmd)).concat(Array.from(this.commands.printL10));// 分别传输指令;let cmds = Array.from(cmd);console.log(cmds.length);if (cmds.length > this.byteLength) {let cmdArrs = [];let newCmds = Array.from(cmds);let length = Math.ceil(cmds.length / this.byteLength);for (let i = 0; i < length; i++) {cmdArrs.push(newCmds.slice(this.byteLength * i, this.byteLength * (i + 1)));}for (let i = 0; i < cmdArrs.length; i++) {console.log(i, cmdArrs.length);let buffer = new Uint8Array(cmdArrs[i]).buffer;await this.writeMidWare(deviceId, serviceId, charId, buffer)}} else {let buffer = new Uint8Array(cmds).buffer;cmds.length && await this.writeMidWare(deviceId, serviceId, charId, buffer)}// 打印空行;let printLCmd = Array.from(this.commands.printL5);let printLBuffer = new Uint8Array(printLCmd).buffer;await this.writeMidWare(deviceId, serviceId, charId, printLBuffer);}// 处理安卓写入失败的问题;writeMidWare(deviceId, serviceId, charId, buffer) {let _this = this;return new Promise((resolve, reject) => {this.writeBLE(deviceId, serviceId, charId, buffer).then(res => {resolve(res)}).catch(err => {// console.log(111111, _this.writeTime);if (_this.writeTime < 50) {_this.writeTime++;return new Promise((reso, reje) => {clearTimeout(this.timer)this.timer = setTimeout(() => {reso(this.writeMidWare(deviceId, serviceId, charId, buffer))}, 100)})} else {_this.writeTime = 0;_this.hideLoading();_this.toast('数据写入失败,请走纸后重试!');reject(err);}}).then(res => {resolve(res)})})}// 位图信息转换为打印指令;getImgArray(res) {var w = res.width;var width = parseInt((res.width + 7) / 8 * 8 / 8);var height = res.height;let data = [29, 118, 48, 0];// let data = [0x1d, 0x76, 0x30, 0];data.push(parseInt(width % 256));data.push(parseInt(width / 256));data.push(parseInt(res.height % 256));data.push(parseInt(res.height / 256));var bits = new Uint8Array(height * width);for (let y = 0; y < height; y++) {for (let x = 0; x < w; x++) {var color = res.data[(y * w + x) * 4 + 1];if (color > 128) {bits[parseInt(y * width + x / 8)] |= (0x80 >> (x % 8));}}}for (let i = 0; i < bits.length; i++) {data.push((~bits[i]) & 0xFF)}return data;}// 获取写入数据需要的ids;async getWriteIds(deviceId) {return new Promise(async (resolve, reject) => {let serviceIdData = await this.getServiceId(deviceId);let services = serviceIdData.services;// console.log(services);let charId,serviceId;for (let i = 0; i < services.length; i++) {const chars = await this.getCharId(deviceId, services[i].uuid);// console.log(services[i].uuid, chars);const charItem = chars.characteristics.filter(item => item.properties.write)[0];if (charItem) {charId = charItem.uuid;serviceId = services[i].uuid;// break;// console.log(charId, serviceId);}}resolve({ charId: charId, serviceId: serviceId })})}isAndroid() {return uni.getSystemInfoSync().osName === 'android'}
}export default BluetoothTools;

1、安卓设备有数据限制,如果传输数据过多。虽然writeBLE回调成功,但是会丢数据。可能会导致打印错乱、打印乱码。有些机型会直接不打印。

2、安卓设备必须要用到 writeMidware中的循环写入数据方式。将初始化,写入数据、打印空行这三个步骤分开。

所有的安卓设备都可能会在写入数据的时候,写入失败。一般过200ms后重新写入可能会成功,本方法中,是按照正常方式循环写入数据。一旦失败后会延迟100ms写入数据,不断尝试最多50次。

关于文中栅格位图涉及到的进制转换;

十六进制有两个字节组成,每个字节八位。每个字节有2的8次方=256种;256*256=65536;所以是从0-65535;而65536转为16进制是0x10000;所以这里两个字节的十六进制最大是0xffff;
高位字节0xff;低位字节也是0xff;
576转为十六进制是0x240;高位是0x02;低位是0x40;
576 % 256 = 64;//低位; Math.floor(576 / 256) = 2;//高位;
而64转为十六进制就是 0x40; 2转为十六进制0x02;
所以位图的长度表达方式为:L + H * 256 ; 64 + 2 * 256 = 576;

waitting complete。。。

uni-app(android、ios) 使用蓝牙便携式打印机(热敏打印机)相关推荐

  1. 微信内置浏览器无法下载app(Android/ios)软件 微信内下载链接打不开的解决方法

    很多朋友的APP推广链接需要在微信中进行的网页宣传.传播.下载等等,但是各位朋友一定发现了微信中是屏蔽掉了APP的下载链接的.但是微信最为一个最大的社交平台,为了自身的利益,屏蔽掉了所有APK的下载链 ...

  2. Adobe源码泄漏?3行代码搞定,Flash动画无缝导入Android/iOS/cocos2dx(一)

    [注] iOS代码已重构,效率提升90%,200层动画不卡.[2016.10.27] 项目介绍 项目名称:FlashAnimationToMobile 源码. 使用方法点这里. 这是一个把flash中 ...

  3. 关于便携式打印机程序开发(一、原生安卓蓝牙调用)

    关于便携式打印机程序开发(一.原生安卓蓝牙调用) 综述 软硬件 SDK集成到项目 CPCL协议开发 综述 使用android程序,调用蓝牙,和打印机配对之后,可以连接打印机,通过(WIFI.蓝牙.US ...

  4. android 退出app代码_uniapp退出APP应用(IOS+安卓)

    前言: 近几日使用uni-app 开发移动应用APP,在用户首次安装的时候,需要仔细阅读隐私政策后,方可继续使用APP,否则就直接退出APP 废话结束, 正式进入开发 前置条件: 开发环境:windo ...

  5. 省钱兄校园跑腿源码(公众号+APP+小程序+Android+IOS)校园跑腿社区跑腿同城跑腿任务兼职小程序uniapp前端模版

    开源代码是用户端uniapp部分源码,使用hbuilder导入即可运行 只提供参考学习使用!已经获得软著!不可商业使用!感谢支持 h5体验地址 h5:https://paotui.xianmxkj.c ...

  6. android蓝牙通信_Flutter通过BasicMessageChannel实现Flutter 与Android iOS 的双向通信

    题记: --不到最后时刻,千万别轻言放弃,无论结局成功与否,只要你拼博过,尽力过,一切问心无愧. 通过 Flutter 来进行移动应用开发,打包 Android .iOS 双平台应用程序,在调用如相机 ...

  7. 前端MUI+H5+HBuilderX开发APP(IOS,android),后台Springboot,java学习与实践文章,更新中(二)

    前端MUI+H5+HBuilderX开发APP(IOS,android),后台Springboot,项目搭建,图标设置等,更新中(二) 新建我的第一个APP manifest.json: 图标设置: ...

  8. 便携式打印机连接蓝牙方式

    蓝牙接口是支持设备短距离通信(一般是10m之内)的无线电技术,能在移动电话.PDA.无线耳机.笔记本电脑.相关外设等众多设备之间进行无线信息交换,蓝牙的标准是IEEE802.15,工作在2.4GHz频 ...

  9. 蓝牙聊天App设计1:Android Studio制作蓝牙聊天通讯软件(UI界面设计)

    前言:蓝牙聊天App设计全部有三篇文章(一.UI界面设计,二.蓝牙搜索配对连接实现,三.蓝牙连接聊天),这篇文章是一.UI界面设计 课程1:Android Studio小白安装教程,以及第一个Andr ...

最新文章

  1. T-Mobile旗下网站又曝安全漏洞 允许任何人查看他人账户信息
  2. bzoj1492: [NOI2007]货币兑换Cash
  3. MediatR-进程内的消息通信框架
  4. Python自动化运维开发----基础(八)字符串
  5. Linux下Nodejs安装
  6. java 日期相减得分钟_java日期相减得到分钟??????
  7. Comparable接口
  8. linux面试命令问题,面试常见的Linux命令及问题整理
  9. java实现redis批量lpush,redis lpush list命令
  10. DDNS设置(自用)
  11. SuperMap iDesktop常见问题解答集锦(八)
  12. 2012百度移动开发者大会汇报
  13. 【jQuery进阶】子菜单插件Slight Submenu
  14. 身份证的行政区划代码
  15. 苹果App卡审原因猜测分析
  16. Excel 删除数据temp 恢复
  17. 为什么我要选择使用 Yarn 来做 Docker 的调度引擎
  18. C++计算机视觉库OpenCV在Visual Studio 2022的配置方法
  19. Python必学的4个实战项目,拿走不谢
  20. log4j.properties详解

热门文章

  1. 微信公众号实现路径规划和导航功能
  2. unity 3d中使用BMFont制作清晰字体
  3. 影响拼多多店铺流量的五大指标你知道多少?
  4. 古人为啥说“男不养猫 女不养狗”
  5. kubernetes_22_基于containerd部署kubernetes v1.20.5
  6. Java设计模式 --- 七大常用设计模式示例归纳
  7. 音视频同步录音监控的需求水涨船高
  8. 什么样的内容能够使读者产生共鸣?可以从这几个方面来看
  9. linux内核保留内存,Linux内核开机保留大块内存的方法
  10. Check Point防火墙开启远程管理服务