@chnak/deli-printer
v1.0.4
Published
得力 DL-886AW 蓝牙标签打印机 Node.js SDK(TSPL 协议)
Maintainers
Readme
Deli DL-886AW 蓝牙标签打印机 SDK
Node.js + TSPL 协议 + 蓝牙 SPP 串口通讯的轻量级 SDK。 专为得力 DL-886AW 设计,支持自动端口扫描、中文文本、QR 码、条码、矩形框。
✨ 特性
- 📡 自动端口扫描 — 蓝牙端口号会变?没关系,用设备名/MAC/VID 一键定位
- 🇨🇳 完美中文支持 — 内置
TSS24.BF2字体,GBK 编码 - 📐 直觉化坐标 — 所有坐标用 mm 标注,告别 dots 单位计算
- 🎨 5 种 Widget — text / qrcode / barcode / box / line
- 🖨️ 预览模式 — 不连打印机也能生成 TSPL 指令调试
- 🔌 零依赖 — 除
serialport+iconv-lite外无第三方依赖(不依赖有 bug 的 printer-command-generator)
📦 安装
方式一:从 npm 安装(推荐)
npm install @chnak/deli-printer依赖(自动安装):
serialport— 串口通讯iconv-lite— GBK 编码(中文支持)
方式二:从源码运行
git clone <repo-url>
cd deli-printer
npm install📖 使用方式
从 npm 安装后:
const DeliPrinter = require('@chnak/deli-printer');从源码运行:
const DeliPrinter = require('./deli-sdk');下文示例均以 npm 方式为准。
🚀 快速开始
1. 按设备名自动扫描端口(推荐)
const DeliPrinter = require('@chnak/deli-printer');
const printer = new DeliPrinter({
name: '0168', // 设备标识(蓝牙 MAC 末 4 位)
page: { width: 40, height: 30 }, // 标签尺寸 mm
});
await printer.open(); // 自动扫描并连接
await printer.printLabel({
widgets: [
{ type: 'text', x: 2, y: 2, content: '拿铁咖啡', fontSize: 4 },
{ type: 'text', x: 2, y: 9, content: '¥29.90', fontSize: 4 },
{ type: 'qrcode', x: 12, y: 14, content: 'COF-001', size: 6 },
{ type: 'text', x: 14, y: 27, content: 'COF-001', fontSize: 2 },
],
});
await printer.close();2. 指定固定端口
const printer = new DeliPrinter({
port: 'COM14', // 固定端口
});3. 仅预览(不连打印机)
await printer.open({ preview: true });
const cmds = await printer.printLabel({ widgets: [...] });
console.log(cmds); // 输出 TSPL 指令字符串
await printer.close();4. 作为 npm 包发布后使用
# 发布
npm login
npm publish --access public
# 在其他项目中使用
npm install @chnak/deli-printer// 其他项目入口文件
const DeliPrinter = require('@chnak/deli-printer');
// ...🔍 端口自动扫描
Windows 蓝牙 SPP 配对后会分配虚拟 COM 端口,但 friendlyName 只显示通用名(如「蓝牙链接上的标准串行 (COM14)」),真正的设备标识藏在 PNP ID 中。
SDK 提供三种匹配方式:
// 1. 列出所有串口
const ports = await DeliPrinter.listPorts();
// → [{ path: 'COM4', pnpId: '...VID_05D6_PID_000A...', friendlyName: '蓝牙链接上的标准串行 (COM4)' }, ...]
// 2. 按设备标识查找(按优先级匹配)
await DeliPrinter.findPort('05D6'); // → COM4 (按得力厂商 VID)
await DeliPrinter.findPort('000A'); // → COM4 (按 DL-886AW 产品 PID)
await DeliPrinter.findPort('0168'); // → COM14 (按蓝牙 MAC 末 4 位 hex)
await DeliPrinter.findPort('CH340'); // → COMx (按 USB 转串口芯片名)
// 3. 找不到时打印所有端口供手动选择
await DeliPrinter.findPort('NONEXISTENT_DEVICE');
// → null,并自动打印 [scan] 发现 N 个串口:...匹配优先级:
| 优先级 | 匹配位置 | 适用场景 |
|--------|----------|----------|
| 1 | friendlyName / manufacturer | USB 转串口设备(CH340、CP2102 等) |
| 2 | PNP ID 中的 VID/PID | 得力蓝牙 SPP(VID=05D6, PID=000A) |
| 3 | PNP ID 末段 hex 末几位 | 蓝牙 MAC 地址(如 28D41E190168 末 4 位 0168 |
推荐:使用 MAC 末 4 位
const printer = new DeliPrinter({ name: '0168' }); // 最精确为什么不是 VID?因为 VID 只能识别厂商,万一你有多个得力设备就分不清了。MAC 地址每台设备唯一。
📐 Widget 类型
所有坐标 x / y / width / height / length 单位都是 mm(除条码 height 用 dots)。
1. text — 文本
// 推荐:fontSize = 3 的倍数,中英文字高一致
{ type: 'text', x: 2, y: 2, content: '拿铁咖啡', fontSize: 6 } // 中文 6mm
{ type: 'text', x: 2, y: 9, content: '¥29.90', fontSize: 6 } // 英文 6mm ✓ 一致
// 高级:scale 直接指定 TSPL scale,绕开舍入
{ type: 'text', x: 2, y: 2, content: '拿铁咖啡', scale: 2 } // 中文 6mm
{ type: 'text', x: 2, y: 9, content: '¥29.90', scale: 4 } // 英文 6mm ✓ 等高参数:
x, y— 起始坐标(mm)content— 文本内容fontSize— 字高(mm),SDK 自动选 TSPL scale;推荐用 3 的倍数让中英文字高一致scale— 可选,直接指定 TSPL scale(整数),绕开 fontSize 舍入误差font— 可选,内置字体名(默认自动选择:中文=TSS24.BF2, 英文=Font 1)
⚠️ 字体高度一致性
中英文字体基础尺寸差一倍(24 vs 12 dots),同样的 fontSize 经过舍入后实际字高会不一致:
| fontSize | 中文 scale | 中文字高 | 英文 scale | 英文字高 | 一致? | |----------|-----------|---------|-----------|---------|------| | 1 | 1 | 3mm | 1 | 1.5mm | ❌ | | 3 | 1 | 3mm | 2 | 3mm | ✅ | | 4 | 1 | 3mm | 3 | 4.5mm | ❌ 偏差 1.5mm | | 6 | 2 | 6mm | 4 | 6mm | ✅ | | 9 | 3 | 9mm | 6 | 9mm | ✅ | | 12 | 4 | 12mm | 8 | 12mm | ✅ |
结论:fontSize 必须是 3 的倍数(3, 6, 9, 12...) 才能中英文字高一致。
如需任意字高,可改用 scale 直接控制:
// 任意字高都让中英文字高一致
{ type: 'text', content: '拿铁咖啡', scale: 1 } // 3mm
{ type: 'text', content: '¥29.90', scale: 2 } // 3mm ✓
{ type: 'text', content: '拿铁咖啡', scale: 2 } // 6mm
{ type: 'text', content: '¥29.90', scale: 4 } // 6mm ✓
{ type: 'text', content: '拿铁咖啡', scale: 3 } // 9mm
{ type: 'text', content: '¥29.90', scale: 6 } // 9mm ✓规律:英文字体 scale 必须是中文的 2 倍 才能等高。
完整字体映射(DL-886AW 实测):
| fontSize | 中文实际字高 | 英文实际字高 | 中文 scale | 英文 scale | |----------|------------|------------|-----------|-----------| | 1 | 3mm | 1.5mm | 1 | 1 | | 3 | 3mm | 3mm | 1 | 2 | | 4 | 3mm | 4.5mm | 1 | 3 | | 6 | 6mm | 6mm | 2 | 4 | | 9 | 9mm | 9mm | 3 | 6 | | 12 | 12mm | 12mm | 4 | 8 |
中文自动用 TSS24.BF2(内置宋体 24 dots),英文/数字用 Font 1(12 dots)。
2. qrcode — 二维码
{ type: 'qrcode', x: 12, y: 14, content: 'https://example.com', size: 6 }参数:
x, y— 左上角坐标(mm)content— 编码内容size— 每个 module 的 dots 数(1-10),SDK 内部会做版本检测
⚠️ 重要:QR 码实际尺寸计算
QR 码实际宽度 ≠ size × content.length,而是:
实际宽度(mm) = size × modules / 8
其中 modules = 21 + 4 × version(V1=21, V2=25, V3=29, V4=33...)SDK 会自动检测版本。'COF-001' 编码后是 Version 1(21×21 modules),size=6 时实际宽度 = 6 × 21 / 8 = 15.75mm。
居中示例(40mm 宽标签):
const size = 6, version = 1; // modules = 21
const qrWidth = size * (17 + 4 * version); // = 126 dots = 15.75mm
const x = (40 - qrWidth / 8) / 2; // ≈ 12.13mm
{ type: 'qrcode', x, y: 14, content: 'COF-001', size }3. barcode — 条形码
{ type: 'barcode', x: 2, y: 13, content: 'COF-001', height: 60, narrow: 2, wide: 2, codeType: '128' }参数:
x, y— 起始坐标(mm)content— 条码内容height— 条码高度(dots,非 mm)narrow— 窄条宽度(dots)wide— 宽条宽度(dots),可选默认等于narrowcodeType— 条码类型:128/39/EAN13/EAN8/UPCA/UPCE/CODABAR/93等
4. box — 矩形边框
{ type: 'box', x: 1, y: 1, width: 38, height: 28, thickness: 2 }参数:
x, y— 左上角(mm)width, height— 宽高(mm)thickness— 边框粗细(dots),默认 1
5. line — 水平线
{ type: 'line', x: 2, y: 12, length: 36, thickness: 2 }水平线(用 BARCODE 模拟)。参数:
x, y— 起点(mm)length— 长度(mm)thickness— 线粗(dots),默认 1
⚙️ 全部配置项
const printer = new DeliPrinter({
// 连接方式
port: 'COM14', // 串口号(与 name 二选一,name 优先)
name: '0168', // 设备名关键词,自动扫描
baudRate: 9600, // 波特率(DL-886AW 固定 9600)
// 标签尺寸
page: { width: 40, height: 30 }, // 标签尺寸 (mm)
gap: 2, // 标签间距 (mm)
gapOffset: 0, // GAP 偏移 (mm),几乎总是 0
gapMode: 'GAP', // 'GAP' | 'BLINE'(黑标纸)
// 打印参数
direction: 0, // 方向:0=底部出纸(DL-886AW 必须用 0)
density: 10, // 浓度 0-15(默认 10)
speed: 4, // 速度 1-6(默认 4)
offset: 0, // 打印偏移 (mm),用于微调
// 编码
encoding: 'gbk', // 'gbk'(中文必须)| 'utf8'
codepage: 'GB18030', // CODEPAGE 指令值
});运行时修改配置
printer.setConfig({ gap: 3, direction: 0 });
printer.setConfig({ page: { width: 50, height: 30 } });临时改每张标签的尺寸
await printer.printLabel({
page: { width: 50, height: 30 }, // 仅本次用 50×30mm
widgets: [...],
});🎬 命令
说明:如果你是通过 npm 安装的
@chnak/deli-printer,需要先 clone 源码到本地来运行 demo(demo 文件不包含在 npm 包内),或参考 项目结构 自行组织文件。以下命令假设你在源码目录下运行。
Demo 脚本(deli-sdk-demo.js)
# 列出所有 demo
node deli-sdk-demo.js list
# 打印单个 demo
node deli-sdk-demo.js 1 # 英文 + QR
node deli-sdk-demo.js 2 # 中文商品价签
node deli-sdk-demo.js 3 # 条形码
node deli-sdk-demo.js 4 # 组合 + 矩形框
# 打印所有 demo
node deli-sdk-demo.js all
# 预览(不连打印机,输出 TSPL 指令)
node deli-sdk-demo.js preview 1
node deli-sdk-demo.js preview 2独立预览脚本(deli-sdk-preview.js)
node deli-sdk-preview.js 3 # 只生成 demo 3 的 TSPL 指令🖼️ Demo 模板
| 编号 | 名称 | 内容 | |------|------|------| | 1 | 英文文本 + QR | 英文 + 二维码 | | 2 | 中文商品价签 | 中文商品名 + 价格 + 居中二维码 + SKU | | 3 | 条码 + QR | 条形码 + 二维码 + 文本 | | 4 | 完整组合 | 边框 + 标题 + 商品信息 + QR |
所有 demo 统一 40×30mm 标签,参考 deli-sdk-demo.js 的 templates 对象。
🔧 故障排查
中文乱码 / 打印出 ?
SDK 默认已用 GBK 编码 + CODEPAGE GB18030。如仍乱码:
- 确认打印机固件支持中文(打印出
TSS24.BF2字样说明字体已加载) - 检查串口波特率是否为 9600
- 确认
encoding: 'gbk'(不要用 utf8)
打印错位 / 倒置 / 左右翻转
- DL-886AW 必须用
direction: 0(SDK 默认) - 用
offset微调位置(正数下移,负数上移)
端口找不到 / 连接失败
# 查看所有串口
[System.IO.Ports.SerialPort]::GetPortNames()
# 查看蓝牙设备对应的 COM 端口
Get-PnpDevice | Where-Object {$_.Class -eq 'Bluetooth'} | Select-Object FriendlyName, InstanceId如果设备名带 (NEW) 后缀,说明 Windows 蓝牙注册了多个端口。可以:
await DeliPrinter.printPorts(); // 列出所有端口
await DeliPrinter.findPort('0168'); // 按 MAC 末 4 位精确定位需要强制启用 SPP 服务时可用 force-spp.ps1(PowerShell 脚本)。
二维码位置不对 / 居中偏移
- 确认文本长度对应的 QR 版本(V1=21 modules → 短文本)
- 用公式计算实际宽度:
size × modules / 8(mm) - 居中 x =
(page.width - qrWidth) / 2
参考 deli-sdk-demo.js 第 2 个 demo 中的注释。
📝 协议要点
- 物理层:蓝牙 SPP → 虚拟 COM 端口 → 串口 9600 8N1
- 指令集:TSPL(TSC Printer Language)
- 分辨率:203 DPI = 8 dots/mm
- 中文方案:内置
TSS24.BF2字体 + GBK 编码 +CODEPAGE GB18030 - 打印流程:每张标签前发
SIZE / GAP / DIRECTION / CODEPAGE / CLS,渲染所有 widget 后PRINT 1,1
🗂️ 项目结构
deli-printer/
├── deli-sdk.js # 核心 SDK(DeliPrinter 类,对应 npm 包入口)
├── deli-sdk-demo.js # Demo 模板 + 交互运行脚本
├── deli-sdk-preview.js # 独立预览脚本
├── DIAGNOSIS.md # 打印机诊断记录(ESC/TSPL 状态查询等)
├── force-spp.ps1 # PowerShell:强制启用蓝牙 SPP 服务
├── package.json # npm 包配置(name: @chnak/deli-printer)
└── README.md # 本文件
### 发布到 npm
```bash
npm login # 登录 npm 账号
npm publish --access public # 发布 scoped 包发布后其他人可直接:
npm install @chnak/deli-printer然后 require('@chnak/deli-printer') 使用。
---
## 📄 License
MIT
---
## 🔗 相关链接
- **npm 包**:[@chnak/deli-printer](https://www.npmjs.com/package/@chnak/deli-printer)
- **GitHub**:<repo-url>
- **TSPL 指令参考**:见 `DIAGNOSIS.md`