文章目录
- 一、准备工作
-
- [1. 硬件准备](#1. 硬件准备)
- [2. 开发环境](#2. 开发环境)
- 二、小程序配置
-
- [1. 修改app.json](#1. 修改app.json)
- 三、完整代码实现
-
- [1. pages/index/index.wxml](#1. pages/index/index.wxml)
- [2. pages/index/index.wxss](#2. pages/index/index.wxss)
- [3. pages/index/index.js](#3. pages/index/index.js)
- 四、ESC/POS指令说明
- 五、测试流程
- 六、常见问题解决
- 七、进一步优化建议

下面我将提供一个完整的微信小程序通过蓝牙连接热敏打印机(支持ESC/POS指令)打印订单小票的详细实现方案。本教程假设您使用的是常见的蓝牙热敏打印机(如佳博、芯烨等品牌)。
一、准备工作
1. 硬件准备
- 蓝牙热敏小票打印机(支持
ESC/POS
指令) - 确保打印机已充电/通电
- 知道打印机的蓝牙名称(如"GBTP-XXXX")
2. 开发环境
- 微信开发者工具
- 已注册微信小程序账号
- 小程序已开通蓝牙权限(在
app.json
中配置)
二、小程序配置
1. 修改app.json
json
{
"pages": ["pages/index/index"],
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于蓝牙设备搜索"
}
},
"requiredPrivateInfos": ["getLocation", "chooseAddress", "startBluetoothDevicesDiscovery"]
}
三、完整代码实现
1. pages/index/index.wxml
xml
<view class="container">
<button bindtap="searchPrinter">搜索蓝牙打印机</button>
<view wx:for="{{devices}}" wx:key="deviceId">
<view class="device-item" bindtap="connectPrinter" data-device="{{item}}">
{{item.name}} ({{item.deviceId}})
</view>
</view>
<button bindtap="printTest" disabled="{{!connected}}">打印测试小票</button>
<button bindtap="printOrder" disabled="{{!connected}}">打印订单</button>
<view class="status">状态: {{status}}</view>
</view>
2. pages/index/index.wxss
css
.container {
padding: 20px;
}
.device-item {
padding: 15px;
border-bottom: 1px solid #eee;
}
button {
margin-top: 15px;
}
.status {
margin-top: 20px;
color: #666;
font-size: 14px;
}
3. pages/index/index.js
javascript
Page({
data: {
devices: [], // 搜索到的设备列表
connected: false, // 是否已连接
deviceId: '', // 当前连接的设备ID
serviceId: '', // 蓝牙服务UUID
characteristicId: '', // 蓝牙特征值UUID
status: '未连接', // 状态提示
orderInfo: { // 示例订单数据
orderNo: '20231115001',
date: '2023-11-15 14:30',
items: [
{name: '有机青菜', price: 12.5, quantity: 2},
{name: '新鲜番茄', price: 8.0, quantity: 3},
{name: '优质土豆', price: 5.5, quantity: 1}
],
total: 47.5,
address: '北京市朝阳区科技园A座1001室',
phone: '138****1234'
}
},
// 搜索蓝牙设备
searchPrinter() {
this.setData({status: '正在搜索蓝牙设备...'});
// 初始化蓝牙模块
wx.openBluetoothAdapter({
success: (res) => {
console.log('蓝牙适配器初始化成功', res);
this.startDiscovery();
},
fail: (err) => {
console.error('蓝牙适配器初始化失败', err);
this.setData({status: '蓝牙初始化失败,请检查手机蓝牙是否开启'});
}
});
},
// 开始搜索设备
startDiscovery() {
wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false,
success: (res) => {
console.log('开始搜索设备', res);
this.setData({status: '正在搜索设备...'});
// 监听找到新设备事件
wx.onBluetoothDeviceFound((res) => {
const devices = res.devices.filter(device =>
device.name && device.name.indexOf('GBTP') !== -1
);
if (devices.length > 0) {
this.setData({
devices: devices,
status: `找到${devices.length}个打印机设备`
});
wx.stopBluetoothDevicesDiscovery(); // 找到设备后停止搜索
}
});
},
fail: (err) => {
console.error('搜索设备失败', err);
this.setData({status: '搜索设备失败'});
}
});
},
// 连接打印机
connectPrinter(e) {
const device = e.currentTarget.dataset.device;
this.setData({status: `正在连接${device.name}...`});
// 连接设备
wx.createBLEConnection({
deviceId: device.deviceId,
success: (res) => {
console.log('设备连接成功', res);
this.setData({
deviceId: device.deviceId,
status: `已连接${device.name}`
});
this.getBLEDeviceServices(device.deviceId);
},
fail: (err) => {
console.error('设备连接失败', err);
this.setData({status: '连接失败'});
}
});
},
// 获取蓝牙服务
getBLEDeviceServices(deviceId) {
wx.getBLEDeviceServices({
deviceId: deviceId,
success: (res) => {
console.log('获取服务成功', res.services);
for (const service of res.services) {
// 通常蓝牙打印机的服务UUID是FF00或FFE0
if (service.uuid.startsWith('FFE0') || service.uuid.startsWith('FF00')) {
this.setData({serviceId: service.uuid});
this.getBLEDeviceCharacteristics(deviceId, service.uuid);
break;
}
}
},
fail: (err) => {
console.error('获取服务失败', err);
}
});
},
// 获取蓝牙特征值
getBLEDeviceCharacteristics(deviceId, serviceId) {
wx.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: (res) => {
console.log('获取特征值成功', res.characteristics);
for (const characteristic of res.characteristics) {
// 寻找可写的特征值
if (characteristic.properties.write) {
this.setData({
characteristicId: characteristic.uuid,
connected: true
});
console.log('打印机已准备好');
break;
}
}
},
fail: (err) => {
console.error('获取特征值失败', err);
}
});
},
// 打印测试小票
printTest() {
this.setData({status: '正在打印测试小票...'});
// ESC/POS指令
const buffer = new ArrayBuffer(100);
const dataView = new Uint8Array(buffer);
// 初始化打印机
dataView[0] = 0x1B;
dataView[1] = 0x40;
// 设置居中
dataView[2] = 0x1B;
dataView[3] = 0x61;
dataView[4] = 0x01;
// 设置字体大小
dataView[5] = 0x1D;
dataView[6] = 0x21;
dataView[7] = 0x11;
// 打印文本
const text = "测试小票\n";
for (let i = 0; i < text.length; i++) {
dataView[8 + i] = text.charCodeAt(i);
}
// 换行
const lineBreak = "\n\n\n\n";
for (let i = 0; i < lineBreak.length; i++) {
dataView[8 + text.length + i] = lineBreak.charCodeAt(i);
}
// 切纸
dataView[8 + text.length + lineBreak.length] = 0x1D;
dataView[9 + text.length + lineBreak.length] = 0x56;
dataView[10 + text.length + lineBreak.length] = 0x42;
dataView[11 + text.length + lineBreak.length] = 0x00;
// 发送数据
this.writeBLECharacteristicValue(buffer);
},
// 打印订单
printOrder() {
this.setData({status: '正在打印订单...'});
// 创建缓冲区
const commands = [];
// 初始化打印机
commands.push(0x1B, 0x40);
// 设置居中
commands.push(0x1B, 0x61, 0x01);
// 设置大字体
commands.push(0x1D, 0x21, 0x11);
// 打印标题
this.addTextToCommand(commands, "蔬菜配送订单\n");
// 恢复默认字体
commands.push(0x1D, 0x21, 0x00);
// 订单信息
this.addTextToCommand(commands, `订单号: ${this.data.orderInfo.orderNo}\n`);
this.addTextToCommand(commands, `日期: ${this.data.orderInfo.date}\n\n`);
// 商品列表
this.addTextToCommand(commands, "----------------------------\n");
this.addTextToCommand(commands, "商品名称 单价 数量 小计\n");
this.addTextToCommand(commands, "----------------------------\n");
this.data.orderInfo.items.forEach(item => {
const line = `${item.name} ${item.price} ${item.quantity} ${(item.price * item.quantity).toFixed(2)}\n`;
this.addTextToCommand(commands, line);
});
this.addTextToCommand(commands, "----------------------------\n");
this.addTextToCommand(commands, `总计: ¥${this.data.orderInfo.total}\n\n`);
// 配送信息
this.addTextToCommand(commands, `配送地址: ${this.data.orderInfo.address}\n`);
this.addTextToCommand(commands, `联系电话: ${this.data.orderInfo.phone}\n\n`);
// 感谢语
commands.push(0x1B, 0x61, 0x01); // 居中
this.addTextToCommand(commands, "感谢您的惠顾!\n");
this.addTextToCommand(commands, "期待再次为您服务\n\n\n");
// 切纸
commands.push(0x1D, 0x56, 0x42, 0x00);
// 转换为ArrayBuffer
const buffer = new ArrayBuffer(commands.length);
const dataView = new Uint8Array(buffer);
commands.forEach((value, index) => {
dataView[index] = value;
});
// 发送数据
this.writeBLECharacteristicValue(buffer);
},
// 辅助方法:添加文本到命令数组
addTextToCommand(commands, text) {
for (let i = 0; i < text.length; i++) {
commands.push(text.charCodeAt(i));
}
},
// 写入蓝牙特征值
writeBLECharacteristicValue(buffer) {
wx.writeBLECharacteristicValue({
deviceId: this.data.deviceId,
serviceId: this.data.serviceId,
characteristicId: this.data.characteristicId,
value: buffer,
success: (res) => {
console.log('写入成功', res);
this.setData({status: '打印指令已发送'});
},
fail: (err) => {
console.error('写入失败', err);
this.setData({status: '打印失败'});
}
});
}
});
四、ESC/POS指令说明
-
打印机初始化:0x1B 0x40
-
设置对齐方式:
- 左对齐:
0x1B 0x61 0x00
- 居中:
0x1B 0x61 0x01
- 右对齐:
0x1B 0x61 0x02
- 左对齐:
-
设置字体大小:
0x1D 0x21 0x00
- 正常大小0x1D 0x21 0x11
- 双倍宽高
-
换行 :
0x0A
-
切纸 :
0x1D 0x56 0x42 0x00
五、测试流程
- 打开微信开发者工具,导入本项目
- 点击"搜索蓝牙打印机"按钮
- 在设备列表中找到您的打印机并点击连接
- 连接成功后,点击"打印测试小票"测试基本功能
- 点击"打印订单"打印完整的订单信息
六、常见问题解决
-
找不到设备:
- 确保打印机蓝牙已开启并可被发现
- 检查打印机是否支持
BLE
(蓝牙4.0及以上) - 修改代码中的设备名称过滤条件(如GBTP)
-
连接失败:
- 确保打印机未被其他设备连接
- 尝试重启打印机蓝牙
-
打印乱码:
- 检查
ESC/POS
指令是否正确 - 确保打印机支持接收的指令集
- 检查
-
权限问题:
- 确保小程序已获取蓝牙相关权限
- 在手机上授权小程序使用蓝牙
七、进一步优化建议
- 添加打印机断开重连机制
- 实现打印任务队列,防止并发打印冲突
- 根据打印机型号调整指令集
- 添加打印状态回调,提供更好的用户反馈
- 实现打印内容模板化,便于维护
这个 Demo
提供了完整的蓝牙连接和打印功能实现,您可以根据实际需求进行调整和扩展。实际开发中,建议参考您使用的打印机型号的特定指令集文档进行微调。