通过微信小程序如何实现获取蓝牙打印机并实现打印能力,之前做过一个测试Dome,能够获取附近的蓝牙打印机设备并实现打印,今天开放出来供大家参考。
wxml
<!--右下角搜索-->
<view class="ly-cass-box">
<view class="ly-cass" bindtap="openBluetoothAdapter">
<image src="/images/search.png" style="width: 70rpx;height: 70rpx;" />
</view>
<text class="ly-text">{{ly_text}}</text>
</view>
<view style="margin: 80rpx 20rpx 30rpx 20rpx;">
<view class="has-devices-list-container">
<view class="tip-search">搜索到的设备</view>
<view class="devices-list">
<view wx:for="{{devices}}" wx:key="index" class="devices-item">
<view style="flex:2">{{item.name? item.name:item.localName}}</view>
<button style="flex:1;" id="{{index}}" bindtap="_createBLEConnection">连接</button>
</view>
</view>
</view>
</view>
以下图片左侧是布局样式,列表中是扫描出来的附近可以连接的蓝牙设备,右侧是我在一个超市扫描附近的设备后的实际打印效果。PS:实际打印前我已和超市收银员沟通,我在做测试代码实际打印效果,请不要用在未经许可的非法用途。
wxss
/* 搜索 */
.ly-cass-box{
width: 150rpx;
height: auto;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: fixed;
bottom:70rpx;
right: 50rpx;
z-index: 500;
}
.ly-cass{
width: 120rpx;
height: 120rpx;
background-color: #f4f4f5;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.ly-text{
margin-top: 20rpx;
background-color: #eee;
padding: 2rpx 8rpx 2rpx 8rpx;
border-radius: 6rpx;
font-size: 25rpx;
}
.no-open-gps-tip {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
color: #fa3534;
font-weight: 400;
font-size: 30rpx;
background: rgba(235, 207, 48, 0.8);
padding: 30rpx;
}
.devices-item {
width: 100%;
display: flex;
justify-content: space-between;
margin-top: 20rpx;
align-items: flex-end;
padding-bottom: 5rpx;
border-bottom: 1px solid #f4f4f5;
}
.devices-list {
width: 100%;
padding: 0 20rpx;
display: flex;
flex-direction: column;
}
.tip-search {
width: 100%;
margin: 20rpx 0;
text-align: left;
font-size: 16px;
color: #2979ff;
}
.has-devices-list-container {
width: 100%;
display: flex;
flex-direction: column;
margin: 30rpx 0;
}
这是我刚开发上线的的两个小游戏,欢迎大家扫码体验!
以上两个《蛇王传说》《番茄花园》已上线微信/抖音平台运营。
《番茄花园》游戏源码已上架Cocos Store 商店,欢迎围观!Cocos StoreCocos商城 Creator扩展https://store.cocos.com/app/detail/6122
js
const LAST_CONNECTED_DEVICE = 'last_connected_device';
const PrinterJobs = require('../../printer/printerjobs');
const printerUtil = require('../../printer/printerutil');
Page({
data: {
ly_text: "点击搜索",
connected_ly: false, //蓝牙按钮是否显示
blue_list_ly: false, //蓝牙连接列表显示
discoveryStarted: false,
devices: [], //已发现的蓝牙设备列表
name: '', //已连接的设备名称
deviceId: '', //已连接的设备deviceId
chs: [],
canWrite: false,
},
/**
* 第一步 判断初始化蓝牙模块是否可用
*/
openBluetoothAdapter() {
if (!wx.openBluetoothAdapter) {
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
})
return
}
this.openBluetoothAdapters()
},
/**
* 第二步 初始化蓝牙模块
*/
openBluetoothAdapters() {
this.setData({
ly_text: '搜索设备中'
})
wx.openBluetoothAdapter({ //请求打开蓝牙情况
success: res => {
//console.log('初始化蓝牙模块->res:', res)
this.startBluetoothDevicesDiscovery(); //打开蓝牙后 开始搜索
},
fail: err => {
console.log('初始化蓝牙模块->err:', err)
// 错误码 错误信息 说明
// 0 ok 正常
// -1 already connect 已连接
// 10000 not init 未初始化蓝牙适配器
// 10001 not available 当前蓝牙适配器不可用
// 10002 no device 没有找到指定设备
// 10003 connection fail 连接失败
// 10004 no service 没有找到指定服务
// 10005 no characteristic 没有找到指定特征
// 10006 no connection 当前连接已断开
// 10007 property not support 当前特征不支持此操作
// 10008 system error 其余所有系统上报的异常
// 10009 system not support Android 系统特有,系统版本低于 4.3 不支持 BLE
// 10012 operate time out 连接超时
// 10013 invalid_data 连接 deviceId 为空或者是格式不正确
// object.fail 回调函数返回的 state 参数(仅 iOS)
// 状态码 说明
// 0 未知
// 1 重置中
// 2 不支持
// 3 未授权
// 4 未开启
if (err.errCode === 10001) { //10001 当前蓝牙适配器不可用
wx.showModal({
title: '错误',
content: '当前蓝牙适配器不可用,请打开手机蓝牙后重试!',
showCancel: false
});
//监听蓝牙适配器状态变化事件
wx.onBluetoothAdapterStateChange(res => {
console.log('蓝牙适配器是否可用->res:', res);
if (res.available) { //available=true 蓝牙适配器可用
wx.onBluetoothAdapterStateChange(() => {});
this.startBluetoothDevicesDiscovery();
}
})
} else {
wx.showModal({
title: '错误',
content: `错误码:[${err.errCode}] 错误信息:[${err.errMsg}]`,
showCancel: false
});
}
}
});
},
/**
* 第三步 开始搜寻附近的蓝牙外围设备
*/
startBluetoothDevicesDiscovery() {
this.data.discoveryStarted = true
wx.startBluetoothDevicesDiscovery({
success: res => {
console.log('开始搜寻附近的蓝牙外围设备->res', res)
this.onBluetoothDeviceFound(); //蓝牙搜索成功后监听搜索
},
fail: (err) => {
console.log('开始搜寻附近的蓝牙外围设备->err', err)
}
})
},
/**
* 第四步 监听搜索到新设备的事件
*/
onBluetoothDeviceFound() {
wx.onBluetoothDeviceFound(res => {
res.devices.forEach(device => {
if (!device.name && !device.localName) {
return
}
let foundDevices = this.data.devices || []
let idx = this.inArray(foundDevices, 'deviceId', device.deviceId);
if (idx === -1) {
this.data.devices.push(device);
console.log('发现新设备:', device); //添加新设备
} else {
this.data.devices[idx] = device; //更新设备数据
}
})
if (this.data.devices.length >= 1) {
this.setData({
blue_list_ly: true,
ly_text: '发现设备'
})
} else {
this.setData({
ly_text: '未发现设备'
})
}
this.setData({
devices: this.data.devices
})
})
},
/**
* 第五步 连接蓝牙低功耗设备
* @param {手动点击连接蓝牙事件}
* @param {创建连接蓝牙}
* @param {如果已经连接过可直接连接}
*/
_createBLEConnection(e) {
let idx = e.currentTarget.id
let name = this.data.devices[idx].name
let deviceId = this.data.devices[idx].deviceId
this.setData({
name,
deviceId
})
console.log(name)
//连接蓝牙低功耗设备。
// 若小程序在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,无需再次进行搜索操作
wx.createBLEConnection({
deviceId,
success: (res) => {
console.log('连接蓝牙低功耗设备->res:', res);
this.setData({
blue_list_ly: false,
connected_ly: true,
ly_text: '已连接'
})
//获取蓝牙->保存到缓存
this.getBLEDeviceServices(deviceId);
wx.setStorage({
key: LAST_CONNECTED_DEVICE,
data: {
name,
deviceId
}
})
},
fail: (err) => {
console.log('连接蓝牙低功耗设备->err', err.errno);
this.setData({
connected_ly: false,
ly_text: '连接失败'
})
}
})
this.stopBluetoothDevicesDiscovery();
},
/**
* 第六步 停止搜寻附近的蓝牙外围设备
* @param {蓝牙连接成功关闭监听搜索}
* @param {蓝牙搜索比较消耗资源}
*/
stopBluetoothDevicesDiscovery() {
wx.stopBluetoothDevicesDiscovery({
complete: () => {
console.log('停')
this.data.discoveryStarted = false
}
})
},
/**
* 第七步 断开与蓝牙低功耗设备的连接
* @param {蓝牙连接成功关闭搜索}
* @param {功能}
*/
closeBLEConnection(e) {
wx.closeBLEConnection({
deviceId: e.deviceId
})
this.connected_ly = false;
},
/**
* 第八步 获取蓝牙低功耗设备所有服务
* @param {蓝牙功能查询}
* @param {蓝牙连接成功后}
* @param {找到主要服务功能}
*/
getBLEDeviceServices(deviceId) {
wx.getBLEDeviceServices({
deviceId,
success: (res) => {
for (let i = 0; i < res.services?.length; i++) {
//该服务是否为主服务
if (res.services[i].isPrimary) {
this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid);
return
}
}
}
})
},
/**
* 第九步 获取蓝牙低功耗设备某个服务中所有特征
* @param {蓝牙功能特征查询}
* @param {主要功能的特性}
* @param {找到主要服务功能的特征}
* @param {到此步骤连接结束}
*/
getBLEDeviceCharacteristics(deviceId, serviceId) {
//获取蓝牙低功耗设备某个服务中所有特征 (characteristic)
// read boolean 该特征是否支持 read 操作
// write boolean 该特征是否支持 write 操作
// notify boolean 该特征是否支持 notify 操作
// indicate boolean 该特征是否支持 indicate 操作
// writeNoResponse boolean 该特征是否支持无回复写操作
// writeDefault boolean 该特征是否支持有回复写操作
let name = this.data.name
wx.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success: res => {
console.log('获取蓝牙低功耗设备某个服务中所有特征 (characteristic)->res:', res)
for (let i = 0; i < res.characteristics?.length; i++) {
const item = res.characteristics[i]
if (item.properties.write) {
this.setData({
canWrite: true
})
console.log('可以连接')
this._deviceId = deviceId
this._serviceId = serviceId
this._characteristicId = item.uuid
wx.setStorage({
key: "BlueKey",
data: {
_close: true,
_name: name,
_deviceId: deviceId,
_serviceId: serviceId,
_characteristicId: item.uuid,
}
})
//pring
this.writeBLECharacteristicValue()
break;
}
}
setTimeout(() => {
if (this.data.canWrite) {
this.setData({
connected_ly: true,
ly_text: '已连接'
})
} else {
wx.showToast({
icon: 'error',
title: '您当前选择的设备不支持打印功能,请重新选择!',
duration: 3000
})
}
}, 1000)
},
fail: (res) => {
console.error('获取蓝牙特征失败', res)
}
})
},
/**
* 第十步 编写蓝牙需要打印的内容
* @param {编写蓝牙需要打印的内容}
* @param {打印按钮的事件}
* @param {打印功能前准备}
*/
writeBLECharacteristicValue() {
let pd = {
client: '测试',
name: '张三',
sex: '男',
iPhone: '18888888888',
idcard: '888888888888888888',
}
var that = this
setTimeout(() => {
let printerJobs = new PrinterJobs();
let dayun1 = '打印机自检' + pd?.contactinfo?.client + '\r\n' +
'姓名:' + pd?.name + '\r\n' +
'性别:' + pd?.sex + '\r\n' +
'联系方式:' + pd?.iPhone + '\r\n' +
'身份证号码:' + pd?.idcard + '\r\n'
printerJobs
.setSize(2, 2)
.setAlign('CT')
.print('! 0 100 203 100 1\r\n法决定书\r\nPRINT\r\n')
.setSize(1, 1).setAlign('LT')
.print(dayun1)
let buffer = printerJobs.buffer();
// console.log('ArrayBuffer', 'length: ' + buffer.byteLength, ' hex: ' + this.ab2hex(
// buffer));
// 1.并行调用多次会存在写失败的可能性
// 2.建议每次写入不超过20字节
// 分包处理,延时调用
const maxChunk = 20;
const delay = 20;
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);
setTimeout(this._writeBLECharacteristicValue, j * delay, subPackage);
}
// this.lanyardShow = false;
// this.$refs.uUpload.clear();
// this.clearFormData();
wx.showToast({
title: '打印成功',
icon: 'success'
})
}, 5000)
},
/**
* 第十一步
* @param {最终的打印}
* @param {由第十步骤调用}
* @param {轮询打印}
* @param {打印机输出}
* @param {打印结束}
*/
_writeBLECharacteristicValue(buffer) {
wx.writeBLECharacteristicValue({
deviceId: this._deviceId,
serviceId: this._serviceId,
characteristicId: this._characteristicId,
value: buffer,
success(res) {
console.log('打印成功->res:', res)
},
fail(res) {
console.log('打印失败', res)
}
})
},
/**
* 第十二步
* @param {蓝牙相关事件}
* @param {和以上打印不衔接}
* @param {关闭蓝牙}
*/
closeBluetoothAdapter() {
wx.closeBluetoothAdapter()
this.data.discoveryStarted = false
},
/**
* 第十三步
* @param {判断蓝牙是否已经连接}
* @param {只支持wx. 不支持wx.}
* @param {只支持安卓, 不支持苹果}
*/
isBluetoothDevicePaired() {
var that = this
wx.isBluetoothDevicePaired({
deviceId: wx.getStorageSync("BlueKey")?._deviceId,
success(res) {
console.log(res, "判断连接成功")
that.setData({
connected_ly: true,
ly_text: '连接成功'
})
},
fail(err) {
console.log(err, "判断连接失败");
that.setData({
ly_text: '点击搜索'
})
}
})
},
/**
* 第十四步
* @param {蓝牙相关资源事件}
* @param {搜索 资源 打印}
* @param {转换}
*/
inArray(arr, key, val) {
for (let i = 0; i < arr.length; i++) {
if (arr[i][key] === val) {
return i
}
}
return -1
},
ab2hex(buffer) { // ArrayBuffer转16进度字符串示例
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join(',')
},
str2ab(str) {
let buffer = new ArrayBuffer(str?.length)
let dataView = new DataView(buffer)
for (let i = 0; i < str?.length; i++) {
dataView.setUint8(i, str?.charAt(i).charCodeAt(0))
}
return buffer;
},
/**
* 第十五步
* @param {进入页面就自动连接}
* @param {该方法存在BUG}
* @param {目前该方法先不投放使用}
*/
createBLEConnectionWithDeviceId(e) {
//创建蓝牙链接
wx.openBluetoothAdapter({
success: (res) => {
let ly_data = {
name: wx.getStorageSync("BlueKey")?._deviceId,
deviceId: wx.getStorageSync("BlueKey")?._name
}
this._createBLEConnection(ly_data);
},
fail: (res) => {
if (res.errCode === 10001) {
wx.showModal({
title: '错误',
content: '未找到蓝牙设备, 请打开蓝牙后重试。',
showCancel: false
});
this.connected_ly = false
} else if (res.errCode === -1 || res.errCode === 10010) { //已连接
this.data.connected_ly = true;
}
}
})
},
/**
* 第十六步
* @param {获取蓝牙适配状态}
* @param {在蓝牙连接成功后调用查看}
* @param {判断连接用}
*/
getBluetoothAdapterState() {
wx.getBluetoothAdapterState({
success: (res) => {
console.log(res)
if (res.available) {
this.data.connected_ly = true
this.data.ly_text = "已连接"
console.log("蓝牙已经连接", res)
} else {
this.connected_ly = false;
this.data.ly_text = "点击连接"
console.log("蓝牙已经断开")
}
}
})
},
})