文章目錄
- 一、準備工作
- 1. 硬件準備
- 2. 開發環境
- 二、小程序配置
- 1. 修改app.json
- 三、完整代碼實現
- 1. pages/index/index.wxml
- 2. pages/index/index.wxss
- 3. pages/index/index.js
- 四、ESC/POS指令說明
- 五、測試流程
- 六、常見問題解決
- 七、進一步優化建議
下面我將提供一個完整的微信小程序通過藍牙連接熱敏打印機(支持ESC/POS指令)打印訂單小票的詳細實現方案。本教程假設您使用的是常見的藍牙熱敏打印機(如佳博、芯燁等品牌)。
一、準備工作
1. 硬件準備
- 藍牙熱敏小票打印機(支持
ESC/POS
指令) - 確保打印機已充電/通電
- 知道打印機的藍牙名稱(如"GBTP-XXXX")
2. 開發環境
- 微信開發者工具
- 已注冊微信小程序賬號
- 小程序已開通藍牙權限(在
app.json
中配置)
二、小程序配置
1. 修改app.json
{"pages": ["pages/index/index"],"permission": {"scope.userLocation": {"desc": "你的位置信息將用于藍牙設備搜索"}},"requiredPrivateInfos": ["getLocation", "chooseAddress", "startBluetoothDevicesDiscovery"]
}
三、完整代碼實現
1. pages/index/index.wxml
<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
.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
Page({data: {devices: [], // 搜索到的設備列表connected: false, // 是否已連接deviceId: '', // 當前連接的設備IDserviceId: '', // 藍牙服務UUIDcharacteristicId: '', // 藍牙特征值UUIDstatus: '未連接', // 狀態提示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或FFE0if (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);// 轉換為ArrayBufferconst 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
提供了完整的藍牙連接和打印功能實現,您可以根據實際需求進行調整和擴展。實際開發中,建議參考您使用的打印機型號的特定指令集文檔進行微調。